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
...@@ -459,9 +459,13 @@ struct sctp_datamsg { ...@@ -459,9 +459,13 @@ struct sctp_datamsg {
struct list_head track; struct list_head track;
/* Reference counting. */ /* Reference counting. */
atomic_t refcnt; atomic_t refcnt;
/* When is this message no longer interesting to the peer? */
unsigned long expires_at;
/* Did the messenge fail to send? */ /* Did the messenge fail to send? */
int send_error; int send_error;
char send_failed; char send_failed;
/* Control whether fragments from this message can expire. */
char can_expire;
}; };
struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *, struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *,
...@@ -473,6 +477,9 @@ void sctp_datamsg_hold(struct sctp_datamsg *); ...@@ -473,6 +477,9 @@ void sctp_datamsg_hold(struct sctp_datamsg *);
void sctp_datamsg_free(struct sctp_datamsg *); void sctp_datamsg_free(struct sctp_datamsg *);
void sctp_datamsg_track(struct sctp_chunk *); void sctp_datamsg_track(struct sctp_chunk *);
void sctp_datamsg_assign(struct sctp_datamsg *, 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 /* RFC2960 1.4 Key Terms
* *
...@@ -545,18 +552,6 @@ struct sctp_chunk { ...@@ -545,18 +552,6 @@ struct sctp_chunk {
/* We fill this in if we are calculating RTT. */ /* We fill this in if we are calculating RTT. */
unsigned long sent_at; 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? */ /* What is the origin IP address for this chunk? */
union sctp_addr source; union sctp_addr source;
/* Destination address for this chunk. */ /* Destination address for this chunk. */
...@@ -570,6 +565,18 @@ struct sctp_chunk { ...@@ -570,6 +565,18 @@ struct sctp_chunk {
* go. It is NULL if we have no preference. * go. It is NULL if we have no preference.
*/ */
struct sctp_transport *transport; 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 *); void sctp_chunk_hold(struct sctp_chunk *);
......
...@@ -55,8 +55,8 @@ void sctp_datamsg_init(struct sctp_datamsg *msg) ...@@ -55,8 +55,8 @@ void sctp_datamsg_init(struct sctp_datamsg *msg)
atomic_set(&msg->refcnt, 1); atomic_set(&msg->refcnt, 1);
msg->send_failed = 0; msg->send_failed = 0;
msg->send_error = 0; msg->send_error = 0;
msg->can_expire = 0;
INIT_LIST_HEAD(&msg->chunks); INIT_LIST_HEAD(&msg->chunks);
INIT_LIST_HEAD(&msg->track);
} }
/* Allocate and initialize datamsg. */ /* Allocate and initialize datamsg. */
...@@ -84,7 +84,7 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg) ...@@ -84,7 +84,7 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
notify = msg->send_failed ? -1 : 0; notify = msg->send_failed ? -1 : 0;
/* Release all references. */ /* Release all references. */
list_for_each_safe(pos, temp, &msg->track) { list_for_each_safe(pos, temp, &msg->chunks) {
list_del(pos); list_del(pos);
chunk = list_entry(pos, struct sctp_chunk, frag_list); chunk = list_entry(pos, struct sctp_chunk, frag_list);
/* Check whether we _really_ need to notify. */ /* Check whether we _really_ need to notify. */
...@@ -146,7 +146,6 @@ void sctp_datamsg_free(struct sctp_datamsg *msg) ...@@ -146,7 +146,6 @@ void sctp_datamsg_free(struct sctp_datamsg *msg)
void sctp_datamsg_track(struct sctp_chunk *chunk) void sctp_datamsg_track(struct sctp_chunk *chunk)
{ {
sctp_chunk_hold(chunk); sctp_chunk_hold(chunk);
list_add_tail(&chunk->frag_list, &chunk->msg->track);
} }
/* Assign a chunk to this datamsg. */ /* Assign a chunk to this datamsg. */
...@@ -179,6 +178,20 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, ...@@ -179,6 +178,20 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
if (!msg) if (!msg)
return NULL; 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? */ /* What is a reasonable fragmentation point right now? */
max = asoc->pmtu; max = asoc->pmtu;
if (max < SCTP_MIN_PMTU) if (max < SCTP_MIN_PMTU)
...@@ -191,7 +204,6 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, ...@@ -191,7 +204,6 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
/* Subtract out the overhead of a data chunk header. */ /* Subtract out the overhead of a data chunk header. */
max -= sizeof(struct sctp_data_chunk); max -= sizeof(struct sctp_data_chunk);
whole = 0; whole = 0;
/* If user has specified smaller fragmentation, make it so. */ /* If user has specified smaller fragmentation, make it so. */
...@@ -289,3 +301,27 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, ...@@ -289,3 +301,27 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
sctp_datamsg_free(msg); sctp_datamsg_free(msg);
return NULL; 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) ...@@ -356,6 +356,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
if (sctp_chunk_is_data(chunk)) { if (sctp_chunk_is_data(chunk)) {
if (!chunk->has_tsn) { if (!chunk->has_tsn) {
sctp_chunk_assign_ssn(chunk);
sctp_chunk_assign_tsn(chunk); sctp_chunk_assign_tsn(chunk);
/* 6.3.1 C4) When data is in flight and when allowed /* 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, ...@@ -627,6 +628,8 @@ static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
rwnd = 0; rwnd = 0;
asoc->peer.rwnd = rwnd; asoc->peer.rwnd = rwnd;
/* Has been accepted for transmission. */
chunk->msg->can_expire = 0;
finish: finish:
return retval; return retval;
......
...@@ -251,8 +251,7 @@ void sctp_outq_teardown(struct sctp_outq *q) ...@@ -251,8 +251,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
chunk = list_entry(lchunk, struct sctp_chunk, chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list); transmitted_list);
/* Mark as part of a failed message. */ /* Mark as part of a failed message. */
chunk->msg->send_failed = 1; sctp_datamsg_fail(chunk, q->error);
/* Generate a SEND FAILED event. */
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
} }
} }
...@@ -262,6 +261,7 @@ void sctp_outq_teardown(struct sctp_outq *q) ...@@ -262,6 +261,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
list_del(lchunk); list_del(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk, chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list); transmitted_list);
sctp_datamsg_fail(chunk, q->error);
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
} }
...@@ -270,6 +270,7 @@ void sctp_outq_teardown(struct sctp_outq *q) ...@@ -270,6 +270,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
list_del(lchunk); list_del(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk, chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list); transmitted_list);
sctp_datamsg_fail(chunk, q->error);
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
} }
...@@ -277,7 +278,7 @@ void sctp_outq_teardown(struct sctp_outq *q) ...@@ -277,7 +278,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
while ((chunk = sctp_outq_dequeue_data(q))) { while ((chunk = sctp_outq_dequeue_data(q))) {
/* Mark as send failure. */ /* Mark as send failure. */
chunk->msg->send_failed = 1; sctp_datamsg_fail(chunk, q->error);
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
} }
...@@ -800,27 +801,25 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) ...@@ -800,27 +801,25 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
*/ */
if (chunk->sinfo.sinfo_stream >= if (chunk->sinfo.sinfo_stream >=
asoc->c.sinit_num_ostreams) { 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); sctp_chunk_free(chunk);
continue; continue;
} }
/* Now do delayed assignment of SSN. This will /* Has this chunk expired? */
* probably change again when we start supporting if (sctp_datamsg_expires(chunk)) {
* large (> approximately 2^16) size messages. sctp_datamsg_fail(chunk, 0);
*/ sctp_chunk_free(chunk);
sctp_chunk_assign_ssn(chunk); continue;
}
/* If there is a specified transport, use it. /* If there is a specified transport, use it.
* Otherwise, we want to use the active path. * Otherwise, we want to use the active path.
*/ */
new_transport = chunk->transport; new_transport = chunk->transport;
if (new_transport == NULL || if (!new_transport || !new_transport->active)
!new_transport->active)
new_transport = asoc->peer.active_path; new_transport = asoc->peer.active_path;
/* Change packets if necessary. */ /* Change packets if necessary. */
......
...@@ -987,7 +987,7 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, ...@@ -987,7 +987,7 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb,
retval->has_tsn = 0; retval->has_tsn = 0;
retval->has_ssn = 0; retval->has_ssn = 0;
retval->rtt_in_progress = 0; retval->rtt_in_progress = 0;
retval->sent_at = jiffies; retval->sent_at = 0;
retval->singleton = 1; retval->singleton = 1;
retval->end_of_packet = 0; retval->end_of_packet = 0;
retval->ecn_ce_done = 0; retval->ecn_ce_done = 0;
......
...@@ -2372,7 +2372,8 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep, ...@@ -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 * PMTU. In cases, such as loopback, this might be a rather
* large spill over. * 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 /* If this is the next TSN, consider reneging to make
* room. Note: Playing nice with a confused sender. A * room. Note: Playing nice with a confused sender. A
......
...@@ -782,7 +782,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -782,7 +782,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
long timeo; long timeo;
__u16 sinfo_flags = 0; __u16 sinfo_flags = 0;
struct sctp_datamsg *datamsg; struct sctp_datamsg *datamsg;
struct list_head *pos, *temp; struct list_head *pos;
int msg_flags = msg->msg_flags; int msg_flags = msg->msg_flags;
SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %d)\n", 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, ...@@ -1089,9 +1089,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
} }
/* Now send the (possibly) fragmented message. */ /* 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); chunk = list_entry(pos, struct sctp_chunk, frag_list);
list_del_init(pos);
sctp_datamsg_track(chunk); sctp_datamsg_track(chunk);
/* Do accounting for the write space. */ /* Do accounting for the write space. */
...@@ -1099,7 +1098,11 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -1099,7 +1098,11 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
chunk->transport = chunk_tp; 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); err = sctp_primitive_SEND(asoc, chunk);
/* Did the lower layer accept the chunk? */ /* Did the lower layer accept the chunk? */
if (err) if (err)
......
...@@ -425,6 +425,9 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed( ...@@ -425,6 +425,9 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
struct sctp_send_failed *ssf; struct sctp_send_failed *ssf;
struct sk_buff *skb; 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. */ /* Make skb with more room so we can prepend notification. */
skb = skb_copy_expand(chunk->skb, skb = skb_copy_expand(chunk->skb,
sizeof(struct sctp_send_failed), /* headroom */ sizeof(struct sctp_send_failed), /* headroom */
...@@ -434,7 +437,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed( ...@@ -434,7 +437,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
goto fail; goto fail;
/* Pull off the common chunk header and DATA header. */ /* 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. */ /* Embed the event fields inside the cloned skb. */
event = sctp_skb2event(skb); event = sctp_skb2event(skb);
...@@ -475,7 +479,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed( ...@@ -475,7 +479,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
* This field is the total length of the notification data, including * This field is the total length of the notification data, including
* the notification header. * 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 /* Socket Extensions for SCTP
* 5.3.1.4 SCTP_SEND_FAILED * 5.3.1.4 SCTP_SEND_FAILED
...@@ -496,6 +501,11 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed( ...@@ -496,6 +501,11 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
*/ */
memcpy(&ssf->ssf_info, &chunk->sinfo, sizeof(struct sctp_sndrcvinfo)); 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 /* Socket Extensions for SCTP
* 5.3.1.4 SCTP_SEND_FAILED * 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