diff options
| author | Sridhar Samudrala <sri@us.ibm.com> | 2003-07-10 22:31:12 -0700 |
|---|---|---|
| committer | Sridhar Samudrala <sri@us.ibm.com> | 2003-07-10 22:31:12 -0700 |
| commit | c15987c4447eb8d266d24f968beb00754db8b036 (patch) | |
| tree | f5f22685c33d186ef8997578ddccb512d1717ab3 | |
| parent | c3d5155c77fa1ef4b190530369e1453e614bb913 (diff) | |
| parent | 691eb68428db04ad52ffa8a5613977ec76786589 (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.h | 3 | ||||
| -rw-r--r-- | include/net/sctp/ulpevent.h | 4 | ||||
| -rw-r--r-- | net/sctp/associola.c | 2 | ||||
| -rw-r--r-- | net/sctp/chunk.c | 4 | ||||
| -rw-r--r-- | net/sctp/output.c | 37 | ||||
| -rw-r--r-- | net/sctp/outqueue.c | 14 | ||||
| -rw-r--r-- | net/sctp/sm_make_chunk.c | 10 | ||||
| -rw-r--r-- | net/sctp/sm_sideeffect.c | 2 | ||||
| -rw-r--r-- | net/sctp/sm_statefuns.c | 4 | ||||
| -rw-r--r-- | net/sctp/socket.c | 20 | ||||
| -rw-r--r-- | net/sctp/transport.c | 1 | ||||
| -rw-r--r-- | net/sctp/ulpevent.c | 213 | ||||
| -rw-r--r-- | net/sctp/ulpqueue.c | 9 |
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; }; |
