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 {
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 *);
......
......@@ -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. */
......@@ -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;
......
......@@ -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. */
......
......@@ -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)
......
......@@ -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