summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Grimm <jgrimm@touki.austin.ibm.com>2003-04-30 23:24:14 -0500
committerJon Grimm <jgrimm@touki.austin.ibm.com>2003-04-30 23:24:14 -0500
commitc5d01dcf2fc03aff6dcce7bc9a7cead9287d5269 (patch)
tree3d772efd985ad7a4b77aae06213d0f0fc8984052
parent7b3a0a8c5edbba2ba03acfcf1dbf202f2f1d5c6d (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.h43
-rw-r--r--net/sctp/chunk.c50
-rw-r--r--net/sctp/output.c3
-rw-r--r--net/sctp/outqueue.c49
-rw-r--r--net/sctp/sm_make_chunk.c2
-rw-r--r--net/sctp/sm_statefuns.c3
-rw-r--r--net/sctp/socket.c15
-rw-r--r--net/sctp/ulpevent.c14
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
*