Commit deabf854 authored by Jon Grimm's avatar Jon Grimm

[SCTP] Track partially acked message for SEND_FAILED.

For SEND_FAILED support, we need to keep around data chunk fragments
that have been acked, where other fragments of the same message
have not.  With this, Ardelle should be able to finish up the
SEND_FAILED support. 
parent eb643da0
......@@ -277,6 +277,7 @@ extern atomic_t sctp_dbg_objcnt_chunk;
extern atomic_t sctp_dbg_objcnt_bind_addr;
extern atomic_t sctp_dbg_objcnt_addr;
extern atomic_t sctp_dbg_objcnt_ssnmap;
extern atomic_t sctp_dbg_objcnt_datamsg;
/* Macros to atomically increment/decrement objcnt counters. */
#define SCTP_DBG_OBJCNT_INC(name) \
......
......@@ -6,10 +6,6 @@
*
* This file is part of the SCTP kernel reference Implementation
*
* This file is part of the implementation of the add-IP extension,
* based on <draft-ietf-tsvwg-addip-sctp-02.txt> June 29, 2001,
* for the SCTP kernel reference Implementation.
*
* These are definitions needed by the state machine.
*
* The SCTP reference implementation is free software;
......@@ -50,7 +46,6 @@
* be incorporated into the next SCTP release.
*/
#include <linux/types.h>
#include <linux/compiler.h>
#include <linux/slab.h>
......@@ -269,11 +264,6 @@ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *,
size_t paylen);
void sctp_chunk_assign_tsn(struct sctp_chunk *);
void sctp_chunk_assign_ssn(struct sctp_chunk *);
int sctp_datachunks_from_user(struct sctp_association *,
const struct sctp_sndrcvinfo *,
struct msghdr *, int len,
struct sk_buff_head *);
/* Prototypes for statetable processing. */
......
......@@ -70,7 +70,6 @@ union sctp_addr {
struct sockaddr sa;
};
/* Forward declarations for data structures. */
struct sctp_protocol;
struct sctp_endpoint;
......@@ -448,6 +447,29 @@ static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id)
return stream->ssn[id]++;
}
/* Structure to track chunk fragments that have been acked, but peer
* fragments of the same message have not.
*/
struct sctp_datamsg {
/* Chunks waiting to be submitted to lower layer. */
struct list_head chunks;
/* Chunks that have been transmitted. */
struct list_head track;
/* Reference counting. */
atomic_t refcnt;
/* Have the SEND_FAILED notifications been done. */
__u8 notify_done;
};
struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *,
struct sctp_sndrcvinfo *,
struct msghdr *, int len);
struct sctp_datamsg *sctp_datamsg_new(int gfp);
void sctp_datamsg_put(struct sctp_datamsg *);
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 *);
/* RFC2960 1.4 Key Terms
*
......@@ -465,6 +487,7 @@ struct sctp_chunk {
struct sctp_chunk *next;
struct sctp_chunk *prev;
struct sk_buff_head *list;
atomic_t refcnt;
/* This is our link to the per-transport transmitted list. */
struct list_head transmitted_list;
......@@ -536,6 +559,9 @@ struct sctp_chunk {
/* Destination address for this chunk. */
union sctp_addr dest;
/* For outbound message, track all fragments for SEND_FAILED. */
struct sctp_datamsg *msg;
/* For an inbound chunk, this tells us where it came from.
* For an outbound chunk, it tells us where we'd like it to
* go. It is NULL if we have no preference.
......@@ -543,9 +569,13 @@ struct sctp_chunk {
struct sctp_transport *transport;
};
void sctp_chunk_hold(struct sctp_chunk *);
void sctp_chunk_put(struct sctp_chunk *);
int sctp_user_addto_chunk(struct sctp_chunk *chunk, int off, int len,
struct iovec *data);
struct sctp_chunk *sctp_make_chunk(const struct sctp_association *, __u8 type,
__u8 flags, int size);
void sctp_free_chunk(struct sctp_chunk *);
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 *,
const struct sctp_association *,
......
......@@ -6,7 +6,7 @@ obj-$(CONFIG_IP_SCTP) += sctp.o
sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \
protocol.o endpointola.o associola.o \
transport.o sm_make_chunk.o ulpevent.o \
transport.o chunk.o sm_make_chunk.o ulpevent.o \
inqueue.o outqueue.o ulpqueue.o command.o \
tsnmap.o bind_addr.o socket.o primitive.o \
output.o input.o debug.o ssnmap.o proc.o
......
/* SCTP kernel reference Implementation
* Copyright (c) 2003 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* This file contains the code relating the the chunk abstraction.
*
* The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* The SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <lksctp-developers@lists.sourceforge.net>
*
* Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp
*
* Written or modified by:
* Jon Grimm <jgrimm@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.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/net.h>
#include <linux/inet.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
/* This file is mostly in anticipation of future work, but initially
* populate with fragment tracking for an outbound message.
*/
/* Initialize datamsg from memory. */
void sctp_datamsg_init(struct sctp_datamsg *msg)
{
atomic_set(&msg->refcnt, 1);
msg->notify_done = 0;
INIT_LIST_HEAD(&msg->chunks);
INIT_LIST_HEAD(&msg->track);
}
/* Allocate and initialize datamsg. */
struct sctp_datamsg *sctp_datamsg_new(int gfp)
{
struct sctp_datamsg *msg;
msg = kmalloc(sizeof(struct sctp_datamsg), gfp);
if (msg)
sctp_datamsg_init(msg);
SCTP_DBG_OBJCNT_INC(datamsg);
return msg;
}
/* Final destructruction of datamsg memory. */
static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
{
struct list_head *pos, *temp;
struct sctp_chunk *chunk;
/* Release all references, if there are any left. */
list_for_each_safe(pos, temp, &msg->track) {
list_del(pos);
chunk = list_entry(pos, struct sctp_chunk, frag_list);
sctp_chunk_put(chunk);
}
SCTP_DBG_OBJCNT_DEC(datamsg);
kfree(msg);
}
/* Hold a reference. */
void sctp_datamsg_hold(struct sctp_datamsg *msg)
{
atomic_inc(&msg->refcnt);
}
/* Release a reference. */
void sctp_datamsg_put(struct sctp_datamsg *msg)
{
if (atomic_dec_and_test(&msg->refcnt))
sctp_datamsg_destroy(msg);
}
/* Free a message. Really just give up a reference, the
* really free happens in sctp_datamsg_destroy().
*/
void sctp_datamsg_free(struct sctp_datamsg *msg)
{
sctp_datamsg_put(msg);
}
/* Hold on to all the fragments until all chunks have been sent. */
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. */
void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chunk)
{
sctp_datamsg_hold(msg);
chunk->msg = msg;
}
/* A data chunk can have a maximum payload of (2^16 - 20). Break
* down any such message into smaller chunks. Opportunistically, fragment
* the chunks down to the current MTU constraints. We may get refragmented
* later if the PMTU changes, but it is _much better_ to fragment immediately
* with a reasonable guess than always doing our fragmentation on the
* soft-interrupt.
*/
struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
struct sctp_sndrcvinfo *sinfo,
struct msghdr *msgh, int msg_len)
{
int max, whole, i, offset, over, err;
int len, first_len;
struct sctp_chunk *chunk;
struct sctp_datamsg *msg;
struct list_head *pos, *temp;
__u8 frag;
msg = sctp_datamsg_new(GFP_KERNEL);
if (!msg)
return NULL;
/* What is a reasonable fragmentation point right now? */
max = asoc->pmtu;
if (max < SCTP_MIN_PMTU)
max = SCTP_MIN_PMTU;
max -= SCTP_IP_OVERHEAD;
/* Make sure not beyond maximum chunk size. */
if (max > SCTP_MAX_CHUNK_LEN)
max = SCTP_MAX_CHUNK_LEN;
/* Subtract out the overhead of a data chunk header. */
max -= sizeof(struct sctp_data_chunk);
whole = 0;
first_len = max;
/* Encourage Cookie-ECHO bundling. */
if (asoc->state < SCTP_STATE_COOKIE_ECHOED) {
whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN);
/* Account for the DATA to be bundled with the COOKIE-ECHO. */
if (whole) {
first_len = max - SCTP_ARBITRARY_COOKIE_ECHO_LEN;
msg_len -= first_len;
whole = 1;
}
}
/* How many full sized? How many bytes leftover? */
whole += msg_len / max;
over = msg_len % max;
offset = 0;
if (whole && over)
SCTP_INC_STATS_USER(SctpFragUsrMsgs);
/* Create chunks for all the full sized DATA chunks. */
for (i=0, len=first_len; i < whole; i++) {
frag = SCTP_DATA_MIDDLE_FRAG;
if (0 == i)
frag |= SCTP_DATA_FIRST_FRAG;
if ((i == (whole - 1)) && !over)
frag |= SCTP_DATA_LAST_FRAG;
chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0);
if (!chunk)
goto errout;
err = sctp_user_addto_chunk(chunk, offset, len, msgh->msg_iov);
if (err < 0)
goto errout;
offset += len;
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
sctp_datamsg_assign(msg, chunk);
list_add_tail(&chunk->frag_list, &msg->chunks);
/* The first chunk, the first chunk was likely short
* to allow bundling, so reset to full size.
*/
if (0 == i)
len = max;
}
/* .. now the leftover bytes. */
if (over) {
if (!whole)
frag = SCTP_DATA_NOT_FRAG;
else
frag = SCTP_DATA_LAST_FRAG;
chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0);
if (!chunk)
goto errout;
err = sctp_user_addto_chunk(chunk, offset, over,msgh->msg_iov);
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
if (err < 0)
goto errout;
sctp_datamsg_assign(msg, chunk);
list_add_tail(&chunk->frag_list, &msg->chunks);
}
return msg;
errout:
list_for_each_safe(pos, temp, &msg->chunks) {
list_del(pos);
chunk = list_entry(pos, struct sctp_chunk, frag_list);
sctp_chunk_free(chunk);
}
sctp_datamsg_free(msg);
return NULL;
}
......@@ -79,13 +79,13 @@ void sctp_inq_free(struct sctp_inq *queue)
/* Empty the queue. */
while ((chunk = (struct sctp_chunk *) skb_dequeue(&queue->in)))
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
/* If there is a packet which is currently being worked on,
* free it as well.
*/
if (queue->in_progress)
sctp_free_chunk(queue->in_progress);
sctp_chunk_free(queue->in_progress);
if (queue->malloced) {
/* Dump the master memory segment. */
......@@ -130,7 +130,7 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
if (chunk->singleton ||
chunk->end_of_packet ||
chunk->pdiscard) {
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
chunk = queue->in_progress = NULL;
} else {
/* Nothing to do. Next chunk in the packet, please. */
......
......@@ -55,6 +55,7 @@ SCTP_DBG_OBJCNT(bind_addr);
SCTP_DBG_OBJCNT(chunk);
SCTP_DBG_OBJCNT(addr);
SCTP_DBG_OBJCNT(ssnmap);
SCTP_DBG_OBJCNT(datamsg);
/* An array to make it easy to pretty print the debug information
* to the proc fs.
......@@ -68,6 +69,7 @@ sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = {
SCTP_DBG_OBJCNT_ENTRY(bind_addr),
SCTP_DBG_OBJCNT_ENTRY(addr),
SCTP_DBG_OBJCNT_ENTRY(ssnmap),
SCTP_DBG_OBJCNT_ENTRY(datamsg),
};
/* Callback from procfs to read out objcount information.
......
......@@ -114,7 +114,7 @@ void sctp_packet_free(struct sctp_packet *packet)
struct sctp_chunk *chunk;
while ((chunk = (struct sctp_chunk *)__skb_dequeue(&packet->chunks)))
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
if (packet->malloced)
kfree(packet);
......@@ -374,6 +374,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
chunk->rtt_in_progress = 1;
tp->rto_pending = 1;
}
sctp_datamsg_track(chunk);
} else
chunk->resent = 1;
......@@ -406,7 +407,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
* acknowledged or have failed.
*/
if (!sctp_chunk_is_data(chunk))
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
}
/* Perform final transformation on checksum. */
......
......@@ -160,7 +160,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
if (ev)
sctp_ulpq_tail_event(&q->asoc->ulpq, ev);
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
}
}
......@@ -169,7 +169,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
list_del(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
}
/* Throw away any chunks in the retransmit queue. */
......@@ -177,7 +177,7 @@ void sctp_outq_teardown(struct sctp_outq *q)
list_del(lchunk);
chunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
}
/* Throw away any leftover data chunks. */
......@@ -190,14 +190,14 @@ void sctp_outq_teardown(struct sctp_outq *q)
if (ev)
sctp_ulpq_tail_event(&q->asoc->ulpq, ev);
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
}
q->error = 0;
/* Throw away any leftover control chunks. */
while ((chunk = (struct sctp_chunk *) skb_dequeue(&q->control)))
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
}
/* Free the outqueue structure and any related pending chunks. */
......@@ -653,6 +653,7 @@ struct sctp_chunk *sctp_fragment_chunk(struct sctp_chunk *chunk,
if (!first_frag)
goto err;
sctp_datamsg_assign(chunk->msg, first_frag);
first_frag->has_ssn = 1;
/* All the fragments are added to the frag_list of the first chunk. */
frag_list = &first_frag->frag_list;
......@@ -667,6 +668,7 @@ struct sctp_chunk *sctp_fragment_chunk(struct sctp_chunk *chunk,
ssn);
if (!frag)
goto err;
sctp_datamsg_assign(chunk->msg, frag);
frag->has_ssn = 1;
/* Add the middle fragment to the first fragment's
* frag_list.
......@@ -687,13 +689,14 @@ struct sctp_chunk *sctp_fragment_chunk(struct sctp_chunk *chunk,
flags, ssn);
if (!frag)
goto err;
sctp_datamsg_assign(chunk->msg, frag);
frag->has_ssn = 1;
/* Add the last fragment to the first fragment's frag_list. */
list_add_tail(&frag->frag_list, frag_list);
/* Free the original chunk. */
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
return first_frag;
......@@ -706,11 +709,10 @@ struct sctp_chunk *sctp_fragment_chunk(struct sctp_chunk *chunk,
flist = &first_frag->frag_list;
while (NULL != (lfrag = sctp_list_dequeue(flist))) {
frag = list_entry(lfrag, struct sctp_chunk, frag_list);
sctp_free_chunk(frag);
sctp_chunk_free(frag);
}
/* Free the first fragment. */
sctp_free_chunk(first_frag);
sctp_chunk_free(first_frag);
}
return NULL;
......@@ -917,7 +919,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
sctp_ulpq_tail_event(&asoc->ulpq, ev);
/* Free the chunk. */
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
continue;
}
......@@ -994,7 +996,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
* memory condition. Free the original
* chunk and return ENOMEM.
*/
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
error = -ENOMEM;
return error;
}
......@@ -1201,7 +1203,7 @@ int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack)
tsn = ntohl(tchunk->subh.data_hdr->tsn);
if (TSN_lte(tsn, ctsn)) {
lchunk = lchunk->prev;
sctp_free_chunk(tchunk);
sctp_chunk_free(tchunk);
}
}
......@@ -1300,7 +1302,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
/* The while loop will skip empty transmitted queues. */
while (NULL != (lchunk = sctp_list_dequeue(transmitted_queue))) {
tchunk = list_entry(lchunk, struct sctp_chunk,
tchunk = list_entry(lchunk, struct sctp_chunk,
transmitted_list);
tsn = ntohl(tchunk->subh.data_hdr->tsn);
......@@ -1531,8 +1533,8 @@ static void sctp_check_transmitted(struct sctp_outq *q,
* receiver's advertised window is zero, and there is
* only one data chunk in flight to the receiver.
*/
if ((0 == q->asoc->peer.rwnd) &&
(!list_empty(&tlist)) &&
if (!q->asoc->peer.rwnd &&
!list_empty(&tlist) &&
(sack_ctsn+2 == q->asoc->next_tsn)) {
SCTP_DEBUG_PRINTK("%s: SACK received for zero "
"window probe: %u\n",
......
......@@ -505,7 +505,7 @@ struct sctp_chunk *sctp_make_datafrag_empty(struct sctp_association *asoc,
int data_len, __u8 flags, __u16 ssn)
{
struct sctp_chunk *retval;
sctp_datahdr_t dp;
struct sctp_datahdr dp;
int chunk_len;
/* We assign the TSN as LATE as possible, not here when
......@@ -519,7 +519,7 @@ struct sctp_chunk *sctp_make_datafrag_empty(struct sctp_association *asoc,
if (sinfo->sinfo_flags & MSG_UNORDERED) {
flags |= SCTP_DATA_UNORDERED;
dp.ssn = 0;
} else
} else
dp.ssn = htons(ssn);
chunk_len = sizeof(dp) + data_len;
......@@ -857,7 +857,7 @@ struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc,
err_copy:
kfree(payload);
err_payload:
sctp_free_chunk(retval);
sctp_chunk_free(retval);
retval = NULL;
err_chunk:
return retval;
......@@ -1003,10 +1003,17 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb,
retval->tsn_gap_acked = 0;
retval->fast_retransmit = 0;
/* If this is a fragmented message, track all fragments
* of the message (for SEND_FAILED).
*/
retval->msg = NULL;
/* Polish the bead hole. */
INIT_LIST_HEAD(&retval->transmitted_list);
INIT_LIST_HEAD(&retval->frag_list);
SCTP_DBG_OBJCNT_INC(chunk);
atomic_set(&retval->refcnt, 1);
nodata:
return retval;
......@@ -1074,12 +1081,10 @@ struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc,
return NULL;
}
/* Release the memory occupied by a chunk. */
void sctp_free_chunk(struct sctp_chunk *chunk)
static void sctp_chunk_destroy(struct sctp_chunk *chunk)
{
/* Make sure that we are not on any list. */
skb_unlink((struct sk_buff *) chunk);
list_del(&chunk->transmitted_list);
/* Free the chunk skb data and the SCTP_chunk stub itself. */
dev_kfree_skb(chunk->skb);
......@@ -1088,6 +1093,32 @@ void sctp_free_chunk(struct sctp_chunk *chunk)
SCTP_DBG_OBJCNT_DEC(chunk);
}
/* Possibly, free the chunk. */
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);
/* Release our reference on the message tracker. */
if (chunk->msg)
sctp_datamsg_put(chunk->msg);
sctp_chunk_put(chunk);
}
/* Grab a reference to the chunk. */
void sctp_chunk_hold(struct sctp_chunk *ch)
{
atomic_inc(&ch->refcnt);
}
/* Release a reference to the chunk. */
void sctp_chunk_put(struct sctp_chunk *ch)
{
if (atomic_dec_and_test(&ch->refcnt))
sctp_chunk_destroy(ch);
}
/* Append bytes to the end of a chunk. Will panic if chunk is not big
* enough.
......@@ -1116,8 +1147,8 @@ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data)
* chunk is not big enough.
* Returns a kernel err value.
*/
static int sctp_user_addto_chunk(struct sctp_chunk *chunk, int off, int len,
struct iovec *data)
int sctp_user_addto_chunk(struct sctp_chunk *chunk, int off, int len,
struct iovec *data)
{
__u8 *target;
int err = 0;
......@@ -1138,126 +1169,6 @@ static int sctp_user_addto_chunk(struct sctp_chunk *chunk, int off, int len,
return err;
}
/* A data chunk can have a maximum payload of (2^16 - 20). Break
* down any such message into smaller chunks. Opportunistically, fragment
* the chunks down to the current MTU constraints. We may get refragmented
* later if the PMTU changes, but it is _much better_ to fragment immediately
* with a reasonable guess than always doing our fragmentation on the
* soft-interrupt.
*/
int sctp_datachunks_from_user(struct sctp_association *asoc,
const struct sctp_sndrcvinfo *sinfo,
struct msghdr *msg, int msg_len,
struct sk_buff_head *chunks)
{
int max, whole, i, offset, over, err;
int len, first_len;
struct sctp_chunk *chunk;
__u8 frag;
/* What is a reasonable fragmentation point right now? */
max = asoc->pmtu;
if (max < SCTP_MIN_PMTU)
max = SCTP_MIN_PMTU;
max -= SCTP_IP_OVERHEAD;
/* Make sure not beyond maximum chunk size. */
if (max > SCTP_MAX_CHUNK_LEN)
max = SCTP_MAX_CHUNK_LEN;
/* Subtract out the overhead of a data chunk header. */
max -= sizeof(struct sctp_data_chunk);
whole = 0;
first_len = max;
/* Encourage Cookie-ECHO bundling. */
if (asoc->state < SCTP_STATE_COOKIE_ECHOED) {
whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN);
/* Account for the DATA to be bundled with the COOKIE-ECHO. */
if (whole) {
first_len = max - SCTP_ARBITRARY_COOKIE_ECHO_LEN;
msg_len -= first_len;
whole = 1;
}
}
/* How many full sized? How many bytes leftover? */
whole += msg_len / max;
over = msg_len % max;
offset = 0;
if (whole && over)
SCTP_INC_STATS_USER(SctpFragUsrMsgs);
/* Create chunks for all the full sized DATA chunks. */
for (i=0, len=first_len; i < whole; i++) {
frag = SCTP_DATA_MIDDLE_FRAG;
if (0 == i)
frag |= SCTP_DATA_FIRST_FRAG;
if ((i == (whole - 1)) && !over)
frag |= SCTP_DATA_LAST_FRAG;
chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0);
if (!chunk)
goto nomem;
err = sctp_user_addto_chunk(chunk, offset, len, msg->msg_iov);
if (err < 0)
goto errout;
offset += len;
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
__skb_queue_tail(chunks, (struct sk_buff *)chunk);
/* The first chunk, the first chunk was likely short
* to allow bundling, so reset to full size.
*/
if (0 == i)
len = max;
}
/* .. now the leftover bytes. */
if (over) {
if (!whole)
frag = SCTP_DATA_NOT_FRAG;
else
frag = SCTP_DATA_LAST_FRAG;
chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0);
if (!chunk)
goto nomem;
err = sctp_user_addto_chunk(chunk, offset, over, msg->msg_iov);
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
if (err < 0)
goto errout;
__skb_queue_tail(chunks, (struct sk_buff *)chunk);
}
err = 0;
goto out;
nomem:
err = -ENOMEM;
errout:
while ((chunk = (struct sctp_chunk *)__skb_dequeue(chunks)))
sctp_free_chunk(chunk);
out:
return err;
}
/* Helper function to assign a TSN if needed. This assumes that both
* the data_hdr and association have already been assigned.
*/
......
......@@ -910,7 +910,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
new_obj = sctp_make_cookie_echo(asoc, chunk);
if (!new_obj) {
if (cmd->obj.ptr)
sctp_free_chunk(cmd->obj.ptr);
sctp_chunk_free(cmd->obj.ptr);
goto nomem;
}
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
......
......@@ -232,7 +232,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t));
sctp_free_chunk(err_chunk);
sctp_chunk_free(err_chunk);
if (packet) {
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
......@@ -303,7 +303,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
* parameter type.
*/
sctp_addto_chunk(repl, len, unk_param);
sctp_free_chunk(err_chunk);
sctp_chunk_free(err_chunk);
}
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
......@@ -320,7 +320,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep,
nomem_ack:
if (err_chunk)
sctp_free_chunk(err_chunk);
sctp_chunk_free(err_chunk);
nomem_init:
sctp_association_free(new_asoc);
nomem:
......@@ -416,7 +416,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep,
ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t));
sctp_free_chunk(err_chunk);
sctp_chunk_free(err_chunk);
if (packet) {
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
......@@ -623,7 +623,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep,
return SCTP_DISPOSITION_CONSUME;
nomem_ev:
sctp_free_chunk(repl);
sctp_chunk_free(repl);
nomem_repl:
nomem_init:
sctp_association_free(new_asoc);
......@@ -1238,7 +1238,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
* parameter type.
*/
sctp_addto_chunk(repl, len, unk_param);
sctp_free_chunk(err_chunk);
sctp_chunk_free(err_chunk);
}
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
......@@ -1254,7 +1254,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
cleanup:
if (err_chunk)
sctp_free_chunk(err_chunk);
sctp_chunk_free(err_chunk);
return retval;
nomem:
retval = SCTP_DISPOSITION_NOMEM;
......@@ -1429,7 +1429,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep,
return SCTP_DISPOSITION_CONSUME;
nomem_ev:
sctp_free_chunk(repl);
sctp_chunk_free(repl);
nomem:
return SCTP_DISPOSITION_NOMEM;
}
......@@ -1492,7 +1492,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep,
return SCTP_DISPOSITION_CONSUME;
nomem_ev:
sctp_free_chunk(repl);
sctp_chunk_free(repl);
nomem:
return SCTP_DISPOSITION_NOMEM;
}
......@@ -4540,6 +4540,6 @@ void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep,
SCTP_PACKET(packet));
SCTP_INC_STATS(SctpOutCtrlChunks);
} else
sctp_free_chunk (err_chunk);
sctp_chunk_free (err_chunk);
}
}
......@@ -749,7 +749,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
struct sctp_opt *sp;
struct sctp_endpoint *ep;
struct sctp_association *new_asoc=NULL, *asoc=NULL;
struct sctp_transport *transport;
struct sctp_transport *transport, *chunk_tp;
struct sctp_chunk *chunk = NULL;
union sctp_addr to;
struct sockaddr *msg_name = NULL;
......@@ -762,7 +762,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
sctp_scope_t scope;
long timeo;
__u16 sinfo_flags = 0;
struct sk_buff_head chunks;
struct sctp_datamsg *datamsg;
struct list_head *pos, *temp;
SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %d)\n",
sk, msg, msg_len);
......@@ -828,12 +829,20 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
goto out_nounlock;
}
sctp_lock_sock(sk);
/* If MSG_ADDR_OVER is set, there must be an address
* specified in msg_name.
*/
if ((sinfo_flags & MSG_ADDR_OVER) && (!msg->msg_name)) {
err = -EINVAL;
goto out_nounlock;
}
transport = NULL;
SCTP_DEBUG_PRINTK("About to look up association.\n");
sctp_lock_sock(sk);
/* If a msg_name has been specified, assume this is to be used. */
if (msg_name) {
/* Look for a matching association on the endpoint. */
......@@ -1005,11 +1014,25 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
goto out_free;
}
/* This flag, in the UDP model, requests the SCTP stack to
* override the primary destination address with the
* address found with the sendto/sendmsg call.
*/
if (sinfo_flags & MSG_ADDR_OVER) {
chunk_tp = sctp_assoc_lookup_paddr(asoc, &to);
if (!chunk_tp) {
err = -EINVAL;
goto out_free;
}
} else
chunk_tp = NULL;
/* Break the message into multiple chunks of maximum size. */
skb_queue_head_init(&chunks);
err = sctp_datachunks_from_user(asoc, sinfo, msg, msg_len, &chunks);
if (err)
datamsg = sctp_datamsg_from_user(asoc, sinfo, msg, msg_len);
if (!datamsg) {
err = -ENOMEM;
goto out_free;
}
/* Auto-connect, if we aren't connected already. */
if (SCTP_STATE_CLOSED == asoc->state) {
......@@ -1020,31 +1043,20 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
}
/* Now send the (possibly) fragmented message. */
while ((chunk = (struct sctp_chunk *)__skb_dequeue(&chunks))) {
list_for_each_safe(pos, temp, &datamsg->chunks) {
chunk = list_entry(pos, struct sctp_chunk, frag_list);
list_del_init(pos);
/* Do accounting for the write space. */
sctp_set_owner_w(chunk);
/* This flag, in the UDP model, requests the SCTP stack to
* override the primary destination address with the
* address found with the sendto/sendmsg call.
*/
if (sinfo_flags & MSG_ADDR_OVER) {
if (!msg->msg_name) {
err = -EINVAL;
goto out_free;
}
chunk->transport = sctp_assoc_lookup_paddr(asoc, &to);
if (!chunk->transport) {
err = -EINVAL;
goto out_free;
}
}
chunk->transport = chunk_tp;
/* Send it to the lower layers. */
sctp_primitive_SEND(asoc, chunk);
SCTP_DEBUG_PRINTK("We sent primitively.\n");
}
sctp_datamsg_free(datamsg);
if (!err) {
err = msg_len;
......@@ -1060,8 +1072,9 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
sctp_association_free(asoc);
out_free_chunk:
/* The datamsg struct will auto-destruct via ref counting. */
if (chunk)
sctp_free_chunk(chunk);
sctp_chunk_free(chunk);
out_unlock:
sctp_release_sock(sk);
......
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