summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSridhar Samudrala <sri@us.ibm.com>2003-07-10 22:31:12 -0700
committerSridhar Samudrala <sri@us.ibm.com>2003-07-10 22:31:12 -0700
commitc15987c4447eb8d266d24f968beb00754db8b036 (patch)
treef5f22685c33d186ef8997578ddccb512d1717ab3
parentc3d5155c77fa1ef4b190530369e1453e614bb913 (diff)
parent691eb68428db04ad52ffa8a5613977ec76786589 (diff)
Merge us.ibm.com:/home/sridhar/BK/linux-2.5.75
into us.ibm.com:/home/sridhar/BK/lksctp-2.5.75
-rw-r--r--include/net/sctp/sm.h3
-rw-r--r--include/net/sctp/ulpevent.h4
-rw-r--r--net/sctp/associola.c2
-rw-r--r--net/sctp/chunk.c4
-rw-r--r--net/sctp/output.c37
-rw-r--r--net/sctp/outqueue.c14
-rw-r--r--net/sctp/sm_make_chunk.c10
-rw-r--r--net/sctp/sm_sideeffect.c2
-rw-r--r--net/sctp/sm_statefuns.c4
-rw-r--r--net/sctp/socket.c20
-rw-r--r--net/sctp/transport.c1
-rw-r--r--net/sctp/ulpevent.c213
-rw-r--r--net/sctp/ulpqueue.c9
13 files changed, 185 insertions, 138 deletions
diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h
index e9814d30bd46..a779754deaf6 100644
--- a/include/net/sctp/sm.h
+++ b/include/net/sctp/sm.h
@@ -231,7 +231,8 @@ struct sctp_chunk *sctp_make_data_empty(struct sctp_association *,
struct sctp_chunk *sctp_make_ecne(const struct sctp_association *,
const __u32);
struct sctp_chunk *sctp_make_sack(const struct sctp_association *);
-struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc);
+struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc,
+ const struct sctp_chunk *chunk);
struct sctp_chunk *sctp_make_shutdown_ack(const struct sctp_association *asoc,
const struct sctp_chunk *);
struct sctp_chunk *sctp_make_shutdown_complete(const struct sctp_association *,
diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h
index d3ad0c86583d..6e43599919ee 100644
--- a/include/net/sctp/ulpevent.h
+++ b/include/net/sctp/ulpevent.h
@@ -40,6 +40,7 @@
* Jon Grimm <jgrimm@us.ibm.com>
* La Monte H.P. Yarroll <piggy@acm.org>
* Karl Knutson <karl@athena.chicago.il.us>
+ * Sridhar Samudrala <sri@us.ibm.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
@@ -72,9 +73,10 @@ static inline struct sctp_ulpevent *sctp_skb2event(struct sk_buff *skb)
}
struct sctp_ulpevent *sctp_ulpevent_new(int size, int flags, int gfp);
-struct sctp_ulpevent *sctp_ulpevent_init(struct sctp_ulpevent *, int flags);
+void sctp_ulpevent_init(struct sctp_ulpevent *, int flags);
void sctp_ulpevent_free(struct sctp_ulpevent *);
int sctp_ulpevent_is_notification(const struct sctp_ulpevent *);
+void sctp_queue_purge_ulpevents(struct sk_buff_head *list);
struct sctp_ulpevent *sctp_ulpevent_make_assoc_change(
const struct sctp_association *asoc,
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index aabeeae601a0..8b6558cf7aa2 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -877,7 +877,7 @@ void sctp_assoc_migrate(struct sctp_association *assoc, struct sock *newsk)
/* Delete the association from the old endpoint's list of
* associations.
*/
- list_del(&assoc->asocs);
+ list_del_init(&assoc->asocs);
/* Decrement the backlog value for a TCP-style socket. */
if (sctp_style(oldsk, TCP))
diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c
index 421561cabe4f..8e23ef559fad 100644
--- a/net/sctp/chunk.c
+++ b/net/sctp/chunk.c
@@ -85,7 +85,7 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
/* Release all references. */
list_for_each_safe(pos, temp, &msg->chunks) {
- list_del(pos);
+ list_del_init(pos);
chunk = list_entry(pos, struct sctp_chunk, frag_list);
/* Check whether we _really_ need to notify. */
if (notify < 0) {
@@ -294,7 +294,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
errout:
list_for_each_safe(pos, temp, &msg->chunks) {
- list_del(pos);
+ list_del_init(pos);
chunk = list_entry(pos, struct sctp_chunk, frag_list);
sctp_chunk_free(chunk);
}
diff --git a/net/sctp/output.c b/net/sctp/output.c
index bc321f6019fd..262a6aa06b49 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -97,6 +97,7 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet,
packet->source_port = sport;
packet->destination_port = dport;
skb_queue_head_init(&packet->chunks);
+ packet->size = SCTP_IP_OVERHEAD;
packet->vtag = 0;
packet->ecn_capable = 0;
packet->get_prepend_chunk = NULL;
@@ -219,9 +220,8 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
/* Both control chunks and data chunks with TSNs are
* non-fragmentable.
*/
- if (packet_empty) {
-
- /* We no longer do refragmentation at all.
+ if (packet_empty || !sctp_chunk_is_data(chunk)) {
+ /* We no longer do re-fragmentation.
* Just fragment at the IP layer, if we
* actually hit this condition
*/
@@ -229,7 +229,7 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
packet->ipfragok = 1;
goto append;
- } else { /* !packet_empty */
+ } else {
retval = SCTP_XMIT_PMTU_FULL;
goto finish;
}
@@ -283,20 +283,18 @@ int sctp_packet_transmit(struct sctp_packet *packet)
__u8 has_data = 0;
struct dst_entry *dst;
- /* Do NOT generate a chunkless packet... */
- if (skb_queue_empty(&packet->chunks))
+ /* Do NOT generate a chunkless packet. */
+ chunk = (struct sctp_chunk *)skb_peek(&packet->chunks);
+ if (unlikely(!chunk))
return err;
/* Set up convenience variables... */
- chunk = (struct sctp_chunk *) (packet->chunks.next);
sk = chunk->skb->sk;
/* Allocate the new skb. */
nskb = dev_alloc_skb(packet->size);
- if (!nskb) {
- err = -ENOMEM;
- goto out;
- }
+ if (!nskb)
+ goto nomem;
/* Make sure the outbound skb has enough header room reserved. */
skb_reserve(nskb, SCTP_IP_OVERHEAD);
@@ -468,9 +466,11 @@ int sctp_packet_transmit(struct sctp_packet *packet)
if (!nskb->dst)
goto no_route;
- SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n",
+ SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb len %d\n",
nskb->len);
+
(*tp->af_specific->sctp_xmit)(nskb, tp, packet->ipfragok);
+
out:
packet->size = SCTP_IP_OVERHEAD;
return err;
@@ -486,7 +486,20 @@ no_route:
* required.
*/
/* err = -EHOSTUNREACH; */
+err:
+ /* Control chunks are unreliable so just drop them. DATA chunks
+ * will get resent or dropped later.
+ */
+
+ while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks))) {
+ if (!sctp_chunk_is_data(chunk))
+ sctp_chunk_free(chunk);
+ }
goto out;
+nomem:
+ err = -ENOMEM;
+ printk("%s alloc_skb failed.\n", __FUNCTION__);
+ goto err;
}
/********************************************************************
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index b7bd2bf6e6a2..d26292af9ff1 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -258,7 +258,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
/* Throw away chunks that have been gap ACKed. */
list_for_each_safe(lchunk, temp, &q->sacked) {
- list_del(lchunk);
+ list_del_init(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
sctp_datamsg_fail(chunk, q->error);
@@ -267,7 +267,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
/* Throw away any chunks in the retransmit queue. */
list_for_each_safe(lchunk, temp, &q->retransmit) {
- list_del(lchunk);
+ list_del_init(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
sctp_datamsg_fail(chunk, q->error);
@@ -445,7 +445,7 @@ void sctp_retransmit_mark(struct sctp_outq *q,
/* Move the chunk to the retransmit queue. The chunks
* on the retransmit queue is always kept in order.
*/
- list_del(lchunk);
+ list_del_init(lchunk);
sctp_retransmit_insert(lchunk, q);
}
}
@@ -1007,7 +1007,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
struct sctp_association *asoc = q->asoc;
struct sctp_transport *transport;
struct sctp_chunk *tchunk;
- struct list_head *lchunk, *transport_list, *pos;
+ struct list_head *lchunk, *transport_list, *pos, *temp;
sctp_sack_variable_t *frags = sack->variable;
__u32 sack_ctsn, ctsn, tsn;
__u32 highest_tsn, highest_new_tsn;
@@ -1115,14 +1115,12 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_sackhdr *sack)
"%p is 0x%x.\n", __FUNCTION__, asoc, ctsn);
/* Throw away stuff rotting on the sack queue. */
- list_for_each(lchunk, &q->sacked) {
+ list_for_each_safe(lchunk, temp, &q->sacked) {
tchunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
tsn = ntohl(tchunk->subh.data_hdr->tsn);
- if (TSN_lte(tsn, ctsn)) {
- lchunk = lchunk->prev;
+ if (TSN_lte(tsn, ctsn))
sctp_chunk_free(tchunk);
- }
}
/* ii) Set rwnd equal to the newly received a_rwnd minus the
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 2408465dda8a..655c895b7ad8 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -667,7 +667,8 @@ nodata:
}
/* Make a SHUTDOWN chunk. */
-struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc)
+struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc,
+ const struct sctp_chunk *chunk)
{
struct sctp_chunk *retval;
sctp_shutdownhdr_t shut;
@@ -683,6 +684,9 @@ struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc)
retval->subh.shutdown_hdr =
sctp_addto_chunk(retval, sizeof(shut), &shut);
+
+ if (chunk)
+ retval->transport = chunk->transport;
nodata:
return retval;
}
@@ -1089,7 +1093,7 @@ void sctp_chunk_free(struct sctp_chunk *chunk)
{
/* Make sure that we are not on any list. */
skb_unlink((struct sk_buff *) chunk);
- list_del(&chunk->transmitted_list);
+ list_del_init(&chunk->transmitted_list);
/* Release our reference on the message tracker. */
if (chunk->msg)
@@ -1850,7 +1854,7 @@ clean_up:
/* Release the transport structures. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports);
- list_del(pos);
+ list_del_init(pos);
sctp_transport_free(transport);
}
nomem:
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index b320c1c5e670..9a99495265f8 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -962,7 +962,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
asoc->overall_error_count = 0;
/* Generate a SHUTDOWN chunk. */
- new_obj = sctp_make_shutdown(asoc);
+ new_obj = sctp_make_shutdown(asoc, chunk);
if (!new_obj)
goto nomem;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 0e3f27b90020..7a20e554de4d 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -3862,7 +3862,7 @@ sctp_disposition_t sctp_sf_do_9_2_start_shutdown(
* in the Cumulative TSN Ack field the last sequential TSN it
* has received from the peer.
*/
- reply = sctp_make_shutdown(asoc);
+ reply = sctp_make_shutdown(asoc, NULL);
if (!reply)
goto nomem;
@@ -4179,7 +4179,7 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep,
switch (asoc->state) {
case SCTP_STATE_SHUTDOWN_SENT:
- reply = sctp_make_shutdown(asoc);
+ reply = sctp_make_shutdown(asoc, NULL);
break;
case SCTP_STATE_SHUTDOWN_ACK_SENT:
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 72029eb799eb..3d47c69a3f46 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -768,8 +768,8 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
}
/* Clean up any skbs sitting on the receive queue. */
- skb_queue_purge(&sk->sk_receive_queue);
- skb_queue_purge(&sctp_sk(sk)->pd_lobby);
+ sctp_queue_purge_ulpevents(&sk->sk_receive_queue);
+ sctp_queue_purge_ulpevents(&sctp_sk(sk)->pd_lobby);
/* On a TCP-style socket, block for at most linger_time if set. */
if (sctp_style(sk, TCP) && timeout)
@@ -1342,8 +1342,7 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
/* When only partial message is copied to the user, increase
* rwnd by that amount. If all the data in the skb is read,
- * rwnd is updated when the skb's destructor is called via
- * sctp_ulpevent_free().
+ * rwnd is updated when the event is freed.
*/
sctp_assoc_rwnd_increase(event->asoc, copied);
goto out;
@@ -1354,7 +1353,18 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
msg->msg_flags &= ~MSG_EOR;
out_free:
- sctp_ulpevent_free(event); /* Free the skb. */
+ if (flags & MSG_PEEK) {
+ /* Release the skb reference acquired after peeking the skb in
+ * sctp_skb_recv_datagram().
+ */
+ kfree_skb(skb);
+ } else {
+ /* Free the event which includes releasing the reference to
+ * the owner of the skb, freeing the skb and updating the
+ * rwnd.
+ */
+ sctp_ulpevent_free(event);
+ }
out:
sctp_release_sock(sk);
return err;
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 9728fc5b7bf2..485d77015399 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -117,6 +117,7 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
INIT_LIST_HEAD(&peer->transmitted);
INIT_LIST_HEAD(&peer->send_ready);
INIT_LIST_HEAD(&peer->transports);
+ sctp_packet_init(&peer->packet, peer, 0, 0);
/* Set up the retransmission timer. */
init_timer(&peer->T3_rtx_timer);
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index 4a9d9d2b5a5e..5071373e8e91 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -35,6 +35,7 @@
* Written or modified by:
* Jon Grimm <jgrimm@us.ibm.com>
* La Monte H.P. Yarroll <piggy@acm.org>
+ * Sridhar Samudrala <sri@us.ibm.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
@@ -46,10 +47,12 @@
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
-static void sctp_ulpevent_set_owner_r(struct sk_buff *skb,
- struct sctp_association *asoc);
-static void sctp_ulpevent_set_owner(struct sk_buff *skb,
- const struct sctp_association *asoc);
+static inline void sctp_ulpevent_set_owner(struct sctp_ulpevent *event,
+ const struct sctp_association *asoc);
+static inline void sctp_ulpevent_release_owner(struct sctp_ulpevent *event);
+static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
+ struct sctp_association *asoc);
+static void sctp_ulpevent_release_data(struct sctp_ulpevent *event);
/* Create a new sctp_ulpevent. */
struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags, int gfp)
@@ -62,30 +65,19 @@ struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags, int gfp)
goto fail;
event = sctp_skb2event(skb);
- event = sctp_ulpevent_init(event, msg_flags);
- if (!event)
- goto fail_init;
+ sctp_ulpevent_init(event, msg_flags);
+
return event;
-fail_init:
- kfree_skb(skb);
fail:
return NULL;
}
/* Initialize an ULP event from an given skb. */
-struct sctp_ulpevent *sctp_ulpevent_init(struct sctp_ulpevent *event,
- int msg_flags)
+void sctp_ulpevent_init(struct sctp_ulpevent *event, int msg_flags)
{
memset(event, sizeof(struct sctp_ulpevent), 0x00);
event->msg_flags = msg_flags;
- return event;
-}
-
-/* Dispose of an event. */
-void sctp_ulpevent_free(struct sctp_ulpevent *event)
-{
- kfree_skb(sctp_event2skb(event));
}
/* Is this a MSG_NOTIFICATION? */
@@ -189,7 +181,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_assoc_change(
* All notifications for a given association have the same association
* identifier. For TCP style socket, this field is ignored.
*/
- sctp_ulpevent_set_owner(skb, asoc);
+ sctp_ulpevent_set_owner(event, asoc);
sac->sac_assoc_id = sctp_assoc2id(asoc);
return event;
@@ -281,7 +273,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change(
* All notifications for a given association have the same association
* identifier. For TCP style socket, this field is ignored.
*/
- sctp_ulpevent_set_owner(skb, asoc);
+ sctp_ulpevent_set_owner(event, asoc);
spc->spc_assoc_id = sctp_assoc2id(asoc);
/* Sockets API Extensions for SCTP
@@ -336,7 +328,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
/* Copy the skb to a new skb with room for us to prepend
* notification with.
*/
- skb = skb_copy_expand(chunk->skb, sizeof(struct sctp_remote_error),
+ skb = skb_copy_expand(chunk->skb, sizeof(struct sctp_remote_error),
0, gfp);
/* Pull off the rest of the cause TLV from the chunk. */
@@ -346,10 +338,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
/* Embed the event fields inside the cloned skb. */
event = sctp_skb2event(skb);
- event = sctp_ulpevent_init(event, MSG_NOTIFICATION);
-
- if (!event)
- goto fail;
+ sctp_ulpevent_init(event, MSG_NOTIFICATION);
sre = (struct sctp_remote_error *)
skb_push(skb, sizeof(struct sctp_remote_error));
@@ -402,8 +391,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
* All notifications for a given association have the same association
* identifier. For TCP style socket, this field is ignored.
*/
- skb = sctp_event2skb(event);
- sctp_ulpevent_set_owner(skb, asoc);
+ sctp_ulpevent_set_owner(event, asoc);
sre->sre_assoc_id = sctp_assoc2id(asoc);
return event;
@@ -442,9 +430,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
/* Embed the event fields inside the cloned skb. */
event = sctp_skb2event(skb);
- event = sctp_ulpevent_init(event, MSG_NOTIFICATION);
- if (!event)
- goto fail;
+ sctp_ulpevent_init(event, MSG_NOTIFICATION);
ssf = (struct sctp_send_failed *)
skb_push(skb, sizeof(struct sctp_send_failed));
@@ -502,7 +488,7 @@ 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.
+ * ressemble a fragmented message.
*/
ssf->ssf_info.sinfo_flags = chunk->chunk_hdr->flags;
@@ -515,8 +501,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
* same association identifier. For TCP style socket, this field is
* ignored.
*/
- skb = sctp_event2skb(event);
- sctp_ulpevent_set_owner(skb, asoc);
+ sctp_ulpevent_set_owner(event, asoc);
ssf->ssf_assoc_id = sctp_assoc2id(asoc);
return event;
@@ -579,7 +564,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event(
* All notifications for a given association have the same association
* identifier. For TCP style socket, this field is ignored.
*/
- sctp_ulpevent_set_owner(skb, asoc);
+ sctp_ulpevent_set_owner(event, asoc);
sse->sse_assoc_id = sctp_assoc2id(asoc);
return event;
@@ -596,12 +581,12 @@ fail:
* 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
*/
struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
- struct sctp_chunk *chunk,
+ struct sctp_chunk *chunk,
int gfp)
{
struct sctp_ulpevent *event;
struct sctp_sndrcvinfo *info;
- struct sk_buff *skb, *list;
+ struct sk_buff *skb;
size_t padding, len;
/* Clone the original skb, sharing the data. */
@@ -627,24 +612,15 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
/* Fixup cloned skb with just this chunks data. */
skb_trim(skb, chunk->chunk_end - padding - skb->data);
- /* Set up a destructor to do rwnd accounting. */
- sctp_ulpevent_set_owner_r(skb, asoc);
-
/* Embed the event fields inside the cloned skb. */
event = sctp_skb2event(skb);
/* Initialize event with flags 0. */
- event = sctp_ulpevent_init(event, 0);
- if (!event)
- goto fail_init;
+ sctp_ulpevent_init(event, 0);
event->iif = sctp_chunk_iif(chunk);
- /* Note: Not clearing the entire event struct as
- * this is just a fragment of the real event. However,
- * we still need to do rwnd accounting.
- */
- for (list = skb_shinfo(skb)->frag_list; list; list = list->next)
- sctp_ulpevent_set_owner_r(list, asoc);
+
+ sctp_ulpevent_receive_data(event, asoc);
info = (struct sctp_sndrcvinfo *) &event->sndrcvinfo;
@@ -735,9 +711,6 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
return event;
-fail_init:
- kfree_skb(skb);
-
fail:
return NULL;
}
@@ -793,6 +766,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
*
* The association id field, holds the identifier for the association.
*/
+ sctp_ulpevent_set_owner(event, asoc);
pd->pdapi_assoc_id = sctp_assoc2id(asoc);
return event;
@@ -824,69 +798,114 @@ void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
}
}
-/* Do accounting for bytes just read by user. */
-static void sctp_rcvmsg_rfree(struct sk_buff *skb)
+/* Stub skb destructor. */
+static void sctp_stub_rfree(struct sk_buff *skb)
{
- struct sctp_association *asoc;
- struct sctp_ulpevent *event;
-
- /* Current stack structures assume that the rcv buffer is
- * per socket. For UDP style sockets this is not true as
- * multiple associations may be on a single UDP-style socket.
- * Use the local private area of the skb to track the owning
- * association.
- */
- event = sctp_skb2event(skb);
- asoc = event->asoc;
- sctp_assoc_rwnd_increase(asoc, skb_headlen(skb));
- sctp_association_put(asoc);
+/* WARNING: This function is just a warning not to use the
+ * skb destructor. If the skb is shared, we may get the destructor
+ * callback on some processor that does not own the sock_lock. This
+ * was occuring with PACKET socket applications that were monitoring
+ * our skbs. We can't take the sock_lock, because we can't risk
+ * recursing if we do really own the sock lock. Instead, do all
+ * of our rwnd manipulation while we own the sock_lock outright.
+ */
}
-/* Charge receive window for bytes received. */
-static void sctp_ulpevent_set_owner_r(struct sk_buff *skb,
- struct sctp_association *asoc)
+/* Hold the association in case the msg_name needs read out of
+ * the association.
+ */
+static inline void sctp_ulpevent_set_owner(struct sctp_ulpevent *event,
+ const struct sctp_association *asoc)
{
- struct sctp_ulpevent *event;
+ struct sk_buff *skb;
- /* The current stack structures assume that the rcv buffer is
- * per socket. For UDP-style sockets this is not true as
- * multiple associations may be on a single UDP-style socket.
- * We use the local private area of the skb to track the owning
- * association.
+ /* Cast away the const, as we are just wanting to
+ * bump the reference count.
*/
- sctp_association_hold(asoc);
+ sctp_association_hold((struct sctp_association *)asoc);
+ skb = sctp_event2skb(event);
skb->sk = asoc->base.sk;
- event = sctp_skb2event(skb);
- event->asoc = asoc;
+ event->asoc = (struct sctp_association *)asoc;
+ skb->destructor = sctp_stub_rfree;
+}
- skb->destructor = sctp_rcvmsg_rfree;
+/* A simple destructor to give up the reference to the association. */
+static inline void sctp_ulpevent_release_owner(struct sctp_ulpevent *event)
+{
+ sctp_association_put(event->asoc);
+}
+/* Do accounting for bytes received and hold a reference to the association
+ * for each skb.
+ */
+static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
+ struct sctp_association *asoc)
+{
+ struct sk_buff *skb, *frag;
+
+ skb = sctp_event2skb(event);
+ /* Set the owner and charge rwnd for bytes received. */
+ sctp_ulpevent_set_owner(event, asoc);
sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb));
+
+ /* Note: Not clearing the entire event struct as this is just a
+ * fragment of the real event. However, we still need to do rwnd
+ * accounting.
+ * In general, the skb passed from IP can have only 1 level of
+ * fragments. But we allow multiple levels of fragments.
+ */
+ for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+ sctp_ulpevent_receive_data(sctp_skb2event(frag), asoc);
+ }
}
-/* A simple destructor to give up the reference to the association. */
-static void sctp_ulpevent_rfree(struct sk_buff *skb)
+/* Do accounting for bytes just read by user and release the references to
+ * the association.
+ */
+static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
{
- struct sctp_ulpevent *event;
+ struct sk_buff *skb, *frag;
- event = sctp_skb2event(skb);
- sctp_association_put(event->asoc);
+ /* Current stack structures assume that the rcv buffer is
+ * per socket. For UDP style sockets this is not true as
+ * multiple associations may be on a single UDP-style socket.
+ * Use the local private area of the skb to track the owning
+ * association.
+ */
+
+ skb = sctp_event2skb(event);
+ sctp_assoc_rwnd_increase(event->asoc, skb_headlen(skb));
+
+ /* Don't forget the fragments. */
+ for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+ /* NOTE: skb_shinfos are recursive. Although IP returns
+ * skb's with only 1 level of fragments, SCTP reassembly can
+ * increase the levels.
+ */
+ sctp_ulpevent_release_data(sctp_skb2event(frag));
+ }
+ sctp_ulpevent_release_owner(event);
}
-/* Hold the association in case the msg_name needs read out of
- * the association.
+/* Free a ulpevent that has an owner. It includes releasing the reference
+ * to the owner, updating the rwnd in case of a DATA event and freeing the
+ * skb.
+ * See comments in sctp_stub_rfree().
*/
-static void sctp_ulpevent_set_owner(struct sk_buff *skb,
- const struct sctp_association *asoc)
+void sctp_ulpevent_free(struct sctp_ulpevent *event)
{
- struct sctp_ulpevent *event;
+ if (sctp_ulpevent_is_notification(event))
+ sctp_ulpevent_release_owner(event);
+ else
+ sctp_ulpevent_release_data(event);
- /* Cast away the const, as we are just wanting to
- * bump the reference count.
- */
- sctp_association_hold((struct sctp_association *)asoc);
- skb->sk = asoc->base.sk;
- event = sctp_skb2event(skb);
- event->asoc = (struct sctp_association *)asoc;
- skb->destructor = sctp_ulpevent_rfree;
+ kfree_skb(sctp_event2skb(event));
+}
+
+/* Purge the skb lists holding ulpevents. */
+void sctp_queue_purge_ulpevents(struct sk_buff_head *list)
+{
+ struct sk_buff *skb;
+ while ((skb = skb_dequeue(list)) != NULL)
+ sctp_ulpevent_free(sctp_skb2event(skb));
}
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
index 8a11bc28bd38..a02b2143239b 100644
--- a/net/sctp/ulpqueue.c
+++ b/net/sctp/ulpqueue.c
@@ -235,9 +235,9 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
out_free:
if (sctp_event2skb(event)->list)
- skb_queue_purge(sctp_event2skb(event)->list);
+ sctp_queue_purge_ulpevents(sctp_event2skb(event)->list);
else
- kfree_skb(sctp_event2skb(event));
+ sctp_ulpevent_free(event);
return 0;
}
@@ -289,7 +289,7 @@ static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
* payload was fragmented on the way and ip had to reassemble them.
* We add the rest of skb's to the first skb's fraglist.
*/
-static inline struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff *f_frag, struct sk_buff *l_frag)
+static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff *f_frag, struct sk_buff *l_frag)
{
struct sk_buff *pos;
struct sctp_ulpevent *event;
@@ -325,11 +325,10 @@ static inline struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff *
/* Remove the fragment from the reassembly queue. */
__skb_unlink(pos, pos->list);
-
+
/* Break if we have reached the last fragment. */
if (pos == l_frag)
break;
-
pos->next = pnext;
pos = pnext;
};