Commit c5d01dcf authored by Jon Grimm's avatar Jon Grimm

[SCTP] Add sinfo_timetolive support.

sinfo_timetolive lets the application specify 'timed reliability'
for a message.  That is, the message really only has a use to
the peer up to this timeout.  Without PR-SCTP, we can throw away
such timed out messages if we haven't yet assigned TSN/SSNs to
them.  If we have, there is nothing we can do until we support
PR-SCTP extension.  
parent 7b3a0a8c
......@@ -450,7 +450,7 @@ static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id)
}
/* Structure to track chunk fragments that have been acked, but peer
* fragments of the same message have not.
* fragments of the same message have not.
*/
struct sctp_datamsg {
/* Chunks waiting to be submitted to lower layer. */
......@@ -459,9 +459,13 @@ struct sctp_datamsg {
struct list_head track;
/* Reference counting. */
atomic_t refcnt;
/* When is this message no longer interesting to the peer? */
unsigned long expires_at;
/* Did the messenge fail to send? */
int send_error;
char send_failed;
/* Control whether fragments from this message can expire. */
char can_expire;
};
struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *,
......@@ -473,6 +477,9 @@ void sctp_datamsg_hold(struct sctp_datamsg *);
void sctp_datamsg_free(struct sctp_datamsg *);
void sctp_datamsg_track(struct sctp_chunk *);
void sctp_datamsg_assign(struct sctp_datamsg *, struct sctp_chunk *);
void sctp_datamsg_fail(struct sctp_chunk *, int error);
int sctp_datamsg_expires(struct sctp_chunk *);
/* RFC2960 1.4 Key Terms
*
......@@ -545,18 +552,6 @@ struct sctp_chunk {
/* We fill this in if we are calculating RTT. */
unsigned long sent_at;
__u8 rtt_in_progress; /* Is this chunk used for RTT calculation? */
__u8 resent; /* Has this chunk ever been retransmitted. */
__u8 has_tsn; /* Does this chunk have a TSN yet? */
__u8 has_ssn; /* Does this chunk have a SSN yet? */
__u8 singleton; /* Was this the only chunk in the packet? */
__u8 end_of_packet; /* Was this the last chunk in the packet? */
__u8 ecn_ce_done; /* Have we processed the ECN CE bit? */
__u8 pdiscard; /* Discard the whole packet now? */
__u8 tsn_gap_acked; /* Is this chunk acked by a GAP ACK? */
__u8 fast_retransmit; /* Is this chunk fast retransmitted? */
__u8 tsn_missing_report; /* Data chunk missing counter. */
/* What is the origin IP address for this chunk? */
union sctp_addr source;
/* Destination address for this chunk. */
......@@ -570,6 +565,18 @@ struct sctp_chunk {
* go. It is NULL if we have no preference.
*/
struct sctp_transport *transport;
__u8 rtt_in_progress; /* Is this chunk used for RTT calculation? */
__u8 resent; /* Has this chunk ever been retransmitted. */
__u8 has_tsn; /* Does this chunk have a TSN yet? */
__u8 has_ssn; /* Does this chunk have a SSN yet? */
__u8 singleton; /* Was this the only chunk in the packet? */
__u8 end_of_packet; /* Was this the last chunk in the packet? */
__u8 ecn_ce_done; /* Have we processed the ECN CE bit? */
__u8 pdiscard; /* Discard the whole packet now? */
__u8 tsn_gap_acked; /* Is this chunk acked by a GAP ACK? */
__u8 fast_retransmit; /* Is this chunk fast retransmitted? */
__u8 tsn_missing_report; /* Data chunk missing counter. */
};
void sctp_chunk_hold(struct sctp_chunk *);
......@@ -580,10 +587,10 @@ struct sctp_chunk *sctp_make_chunk(const struct sctp_association *, __u8 type,
__u8 flags, int size);
void sctp_chunk_free(struct sctp_chunk *);
void *sctp_addto_chunk(struct sctp_chunk *, int len, const void *data);
struct sctp_chunk *sctp_chunkify(struct sk_buff *,
struct sctp_chunk *sctp_chunkify(struct sk_buff *,
const struct sctp_association *,
struct sock *);
void sctp_init_addrs(struct sctp_chunk *, union sctp_addr *,
void sctp_init_addrs(struct sctp_chunk *, union sctp_addr *,
union sctp_addr *);
const union sctp_addr *sctp_source(const struct sctp_chunk *chunk);
......@@ -847,7 +854,7 @@ struct sctp_transport {
/* A flag which indicates the occurrence of a changeover */
char changeover_active;
/* A glag which indicates whether the change of primary is
* the first switch to this destination address during an
* active switch.
......@@ -1024,7 +1031,7 @@ struct sctp_bind_addr {
struct sctp_bind_addr *sctp_bind_addr_new(int gfp_mask);
void sctp_bind_addr_init(struct sctp_bind_addr *, __u16 port);
void sctp_bind_addr_free(struct sctp_bind_addr *);
int sctp_bind_addr_copy(struct sctp_bind_addr *dest,
int sctp_bind_addr_copy(struct sctp_bind_addr *dest,
const struct sctp_bind_addr *src,
sctp_scope_t scope, int gfp,int flags);
int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *,
......@@ -1613,7 +1620,7 @@ struct sctp_association {
/* Need to send an ECNE Chunk? */
char need_ecne;
/* Is it a temporary association? */
/* Is it a temporary association? */
char temp;
};
......
......@@ -55,8 +55,8 @@ void sctp_datamsg_init(struct sctp_datamsg *msg)
atomic_set(&msg->refcnt, 1);
msg->send_failed = 0;
msg->send_error = 0;
msg->can_expire = 0;
INIT_LIST_HEAD(&msg->chunks);
INIT_LIST_HEAD(&msg->track);
}
/* Allocate and initialize datamsg. */
......@@ -84,7 +84,7 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
notify = msg->send_failed ? -1 : 0;
/* Release all references. */
list_for_each_safe(pos, temp, &msg->track) {
list_for_each_safe(pos, temp, &msg->chunks) {
list_del(pos);
chunk = list_entry(pos, struct sctp_chunk, frag_list);
/* Check whether we _really_ need to notify. */
......@@ -94,7 +94,7 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
error = msg->send_error;
else
error = asoc->outqueue.error;
sp = sctp_sk(asoc->base.sk);
notify = sctp_ulpevent_type_enabled(SCTP_SEND_FAILED,
&sp->subscribe);
......@@ -107,8 +107,8 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
sent = SCTP_DATA_SENT;
else
sent = SCTP_DATA_UNSENT;
ev = sctp_ulpevent_make_send_failed(asoc, chunk, sent,
ev = sctp_ulpevent_make_send_failed(asoc, chunk, sent,
error, GFP_ATOMIC);
if (ev)
sctp_ulpq_tail_event(&asoc->ulpq, ev);
......@@ -146,7 +146,6 @@ void sctp_datamsg_free(struct sctp_datamsg *msg)
void sctp_datamsg_track(struct sctp_chunk *chunk)
{
sctp_chunk_hold(chunk);
list_add_tail(&chunk->frag_list, &chunk->msg->track);
}
/* Assign a chunk to this datamsg. */
......@@ -179,6 +178,20 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
if (!msg)
return NULL;
/* Note: Calculate this outside of the loop, so that all fragments
* have the same expiration.
*/
if (sinfo->sinfo_timetolive) {
struct timeval tv;
__u32 ttl = sinfo->sinfo_timetolive;
/* sinfo_timetolive is in milliseconds */
tv.tv_sec = ttl / 1000;
tv.tv_usec = ttl % 1000 * 1000;
msg->expires_at = jiffies + timeval_to_jiffies(&tv);
msg->can_expire = 1;
}
/* What is a reasonable fragmentation point right now? */
max = asoc->pmtu;
if (max < SCTP_MIN_PMTU)
......@@ -191,7 +204,6 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
/* Subtract out the overhead of a data chunk header. */
max -= sizeof(struct sctp_data_chunk);
whole = 0;
/* If user has specified smaller fragmentation, make it so. */
......@@ -289,3 +301,27 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
sctp_datamsg_free(msg);
return NULL;
}
/* Check whether this message has expired. */
int sctp_datamsg_expires(struct sctp_chunk *chunk)
{
struct sctp_datamsg *msg = chunk->msg;
/* FIXME: When PR-SCTP is supported we can make this
* check more lenient.
*/
if (!msg->can_expire)
return 0;
if (time_after(jiffies, msg->expires_at))
return 1;
return 0;
}
/* This chunk (and consequently entire message) has failed in its sending. */
void sctp_datamsg_fail(struct sctp_chunk *chunk, int error)
{
chunk->msg->send_failed = 1;
chunk->msg->send_error = error;
}
......@@ -356,6 +356,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
if (sctp_chunk_is_data(chunk)) {
if (!chunk->has_tsn) {
sctp_chunk_assign_ssn(chunk);
sctp_chunk_assign_tsn(chunk);
/* 6.3.1 C4) When data is in flight and when allowed
......@@ -627,6 +628,8 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
rwnd = 0;
asoc->peer.rwnd = rwnd;
/* Has been accepted for transmission. */
chunk->msg->can_expire = 0;
finish:
return retval;
......
......@@ -165,7 +165,7 @@ static inline int sctp_cacc_skip_3_1(struct sctp_transport *primary,
*/
static inline int sctp_cacc_skip_3_2(struct sctp_transport *primary, __u32 tsn)
{
if (primary->cacc.cycling_changeover &&
if (primary->cacc.cycling_changeover &&
TSN_lt(tsn, primary->cacc.next_tsn_at_change))
return 1;
return 0;
......@@ -174,7 +174,7 @@ static inline int sctp_cacc_skip_3_2(struct sctp_transport *primary, __u32 tsn)
/*
* SFR-CACC algorithm:
* 3) If the missing report count for TSN t is to be
* incremented according to [RFC2960] and
* incremented according to [RFC2960] and
* [SCTP_STEWART-2002], and CHANGEOVER_ACTIVE is set,
* then the sender MUST futher execute steps 3.1 and
* 3.2 to determine if the missing report count for
......@@ -251,8 +251,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
/* Mark as part of a failed message. */
chunk->msg->send_failed = 1;
/* Generate a SEND FAILED event. */
sctp_datamsg_fail(chunk, q->error);
sctp_chunk_free(chunk);
}
}
......@@ -262,6 +261,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
list_del(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
sctp_datamsg_fail(chunk, q->error);
sctp_chunk_free(chunk);
}
......@@ -270,6 +270,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
list_del(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
sctp_datamsg_fail(chunk, q->error);
sctp_chunk_free(chunk);
}
......@@ -277,7 +278,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
while ((chunk = sctp_outq_dequeue_data(q))) {
/* Mark as send failure. */
chunk->msg->send_failed = 1;
sctp_datamsg_fail(chunk, q->error);
sctp_chunk_free(chunk);
}
......@@ -800,27 +801,25 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
*/
if (chunk->sinfo.sinfo_stream >=
asoc->c.sinit_num_ostreams) {
/* Mark as failed send. */
chunk->msg->send_failed = 1;
chunk->msg->send_error = SCTP_ERROR_INV_STRM;
/* Free the chunk. */
/* Mark as s failed send. */
sctp_datamsg_fail(chunk, SCTP_ERROR_INV_STRM);
sctp_chunk_free(chunk);
continue;
}
/* Now do delayed assignment of SSN. This will
* probably change again when we start supporting
* large (> approximately 2^16) size messages.
*/
sctp_chunk_assign_ssn(chunk);
/* Has this chunk expired? */
if (sctp_datamsg_expires(chunk)) {
sctp_datamsg_fail(chunk, 0);
sctp_chunk_free(chunk);
continue;
}
/* If there is a specified transport, use it.
* Otherwise, we want to use the active path.
*/
new_transport = chunk->transport;
if (new_transport == NULL ||
!new_transport->active)
if (!new_transport || !new_transport->active)
new_transport = asoc->peer.active_path;
/* Change packets if necessary. */
......@@ -1024,10 +1023,10 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
/*
* SFR-CACC algorithm:
* On receipt of a SACK the sender SHOULD execute the
* On receipt of a SACK the sender SHOULD execute the
* following statements.
*
* 1) If the cumulative ack in the SACK passes next tsn_at_change
* 1) If the cumulative ack in the SACK passes next tsn_at_change
* on the current primary, the CHANGEOVER_ACTIVE flag SHOULD be
* cleared. The CYCLING_CHANGEOVER flag SHOULD also be cleared for
* all destinations.
......@@ -1041,7 +1040,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
}
}
/*
/*
* SFR-CACC algorithm:
* 2) If the SACK contains gap acks and the flag CHANGEOVER_ACTIVE
* is set the receiver of the SACK MUST take the following actions:
......@@ -1087,7 +1086,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
transport, sack, highest_new_tsn);
/*
* SFR-CACC algorithm:
* C) Let count_of_newacks be the number of
* C) Let count_of_newacks be the number of
* destinations for which cacc_saw_newack is set.
*/
if (transport->cacc.cacc_saw_newack)
......@@ -1097,7 +1096,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
list_for_each(pos, transport_list) {
transport = list_entry(pos, struct sctp_transport,
transports);
sctp_mark_missing(q, &transport->transmitted, transport,
sctp_mark_missing(q, &transport->transmitted, transport,
highest_new_tsn, count_of_newacks);
}
......@@ -1275,8 +1274,8 @@ static void sctp_check_transmitted(struct sctp_outq *q,
* the destination that the TSN was
* sent to.
*/
if (transport &&
sack->num_gap_ack_blocks &&
if (transport &&
sack->num_gap_ack_blocks &&
q->asoc->peer.primary_path->cacc.
changeover_active)
transport->cacc.cacc_saw_newack
......@@ -1529,12 +1528,12 @@ static void sctp_mark_missing(struct sctp_outq *q,
TSN_lt(tsn, highest_new_tsn_in_sack)) {
/* SFR-CACC may require us to skip marking
* this chunk as missing.
* this chunk as missing.
*/
if (!transport || !sctp_cacc_skip(primary, transport,
count_of_newacks, tsn)) {
chunk->tsn_missing_report++;
SCTP_DEBUG_PRINTK(
"%s: TSN 0x%x missing counter: %d\n",
__FUNCTION__, tsn,
......
......@@ -987,7 +987,7 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb,
retval->has_tsn = 0;
retval->has_ssn = 0;
retval->rtt_in_progress = 0;
retval->sent_at = jiffies;
retval->sent_at = 0;
retval->singleton = 1;
retval->end_of_packet = 0;
retval->ecn_ce_done = 0;
......
......@@ -2372,7 +2372,8 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
* PMTU. In cases, such as loopback, this might be a rather
* large spill over.
*/
if (asoc->rwnd_over || (datalen > asoc->rwnd + asoc->frag_point)) {
if (!asoc->rwnd || asoc->rwnd_over ||
(datalen > asoc->rwnd + asoc->frag_point)) {
/* If this is the next TSN, consider reneging to make
* room. Note: Playing nice with a confused sender. A
......
......@@ -782,7 +782,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
long timeo;
__u16 sinfo_flags = 0;
struct sctp_datamsg *datamsg;
struct list_head *pos, *temp;
struct list_head *pos;
int msg_flags = msg->msg_flags;
SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %d)\n",
......@@ -1089,9 +1089,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
}
/* Now send the (possibly) fragmented message. */
list_for_each_safe(pos, temp, &datamsg->chunks) {
list_for_each(pos, &datamsg->chunks) {
chunk = list_entry(pos, struct sctp_chunk, frag_list);
list_del_init(pos);
sctp_datamsg_track(chunk);
/* Do accounting for the write space. */
......@@ -1099,7 +1098,11 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
chunk->transport = chunk_tp;
/* Send it to the lower layers. */
/* Send it to the lower layers. Note: all chunks
* must either fail or succeed. The lower layer
* works that way today. Keep it that way or this
* breaks.
*/
err = sctp_primitive_SEND(asoc, chunk);
/* Did the lower layer accept the chunk? */
if (err)
......@@ -1108,7 +1111,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
}
sctp_datamsg_free(datamsg);
if (err)
if (err)
goto out_free;
else
err = msg_len;
......@@ -3755,7 +3758,7 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
/* If the association on the newsk is already closed before accept()
* is called, set RCV_SHUTDOWN flag.
*/
*/
if (sctp_state(assoc, CLOSED) && sctp_style(newsk, TCP))
newsk->shutdown |= RCV_SHUTDOWN;
......
......@@ -425,6 +425,9 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
struct sctp_send_failed *ssf;
struct sk_buff *skb;
/* Pull off any padding. */
int len = ntohs(chunk->chunk_hdr->length);
/* Make skb with more room so we can prepend notification. */
skb = skb_copy_expand(chunk->skb,
sizeof(struct sctp_send_failed), /* headroom */
......@@ -434,7 +437,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
goto fail;
/* Pull off the common chunk header and DATA header. */
skb_pull(skb, sizeof(sctp_data_chunk_t));
skb_pull(skb, sizeof(struct sctp_data_chunk));
len -= sizeof(struct sctp_data_chunk);
/* Embed the event fields inside the cloned skb. */
event = sctp_skb2event(skb);
......@@ -475,7 +479,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
* This field is the total length of the notification data, including
* the notification header.
*/
ssf->ssf_length = skb->len;
ssf->ssf_length = sizeof(struct sctp_send_failed) + len;
skb_trim(skb, ssf->ssf_length);
/* Socket Extensions for SCTP
* 5.3.1.4 SCTP_SEND_FAILED
......@@ -496,6 +501,11 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
*/
memcpy(&ssf->ssf_info, &chunk->sinfo, sizeof(struct sctp_sndrcvinfo));
/* Per TSVWG discussion with Randy. Allow the application to
* ressemble a fragmented message.
*/
ssf->ssf_info.sinfo_flags = chunk->chunk_hdr->flags;
/* Socket Extensions for SCTP
* 5.3.1.4 SCTP_SEND_FAILED
*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment