diff options
| author | Jon Grimm <jgrimm@touki.austin.ibm.com> | 2003-04-30 23:24:14 -0500 |
|---|---|---|
| committer | Jon Grimm <jgrimm@touki.austin.ibm.com> | 2003-04-30 23:24:14 -0500 |
| commit | c5d01dcf2fc03aff6dcce7bc9a7cead9287d5269 (patch) | |
| tree | 3d772efd985ad7a4b77aae06213d0f0fc8984052 | |
| parent | 7b3a0a8c5edbba2ba03acfcf1dbf202f2f1d5c6d (diff) | |
[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.
| -rw-r--r-- | include/net/sctp/structs.h | 43 | ||||
| -rw-r--r-- | net/sctp/chunk.c | 50 | ||||
| -rw-r--r-- | net/sctp/output.c | 3 | ||||
| -rw-r--r-- | net/sctp/outqueue.c | 49 | ||||
| -rw-r--r-- | net/sctp/sm_make_chunk.c | 2 | ||||
| -rw-r--r-- | net/sctp/sm_statefuns.c | 3 | ||||
| -rw-r--r-- | net/sctp/socket.c | 15 | ||||
| -rw-r--r-- | net/sctp/ulpevent.c | 14 |
8 files changed, 119 insertions, 60 deletions
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 7e9cd2a87ac5..d53ae24b1c63 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -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; }; diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index ca014b17829c..421561cabe4f 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -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 @@ errout: 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; +} diff --git a/net/sctp/output.c b/net/sctp/output.c index e7d68d051f45..449e6219d7b1 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -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; diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index ead23cffeafe..11c584c5e1bc 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -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, diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 70b28f69645b..0b2c25d04baa 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -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; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 78cdc0c2a403..bb6b27d52d9b 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -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 diff --git a/net/sctp/socket.c b/net/sctp/socket.c index f6793893dd73..fcb259f93471 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -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; diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 936cebb87519..4a9d9d2b5a5e 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -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 * |
