Commit 829ff348 authored by David S. Miller's avatar David S. Miller

Merge branch 'sctp-transmit-errs'

Xin Long says:

====================
sctp: fix the transmit err process

This patchset is to improve the transmit err process and also fix some
issues.

After this patchset, once the chunks are enqueued successfully, even
if the chunks fail to send out, no matter because of nodst or nomem,
no err retruns back to users any more. Instead, they are taken care
of by retransmit.

v1->v2:
  - add more details to the changelog in patch 1/6
  - add Fixes: tag in patch 2/6, 3/6
  - also revert 69b5777f in patch 3/6
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents fd9527f4 41001faf
...@@ -537,6 +537,7 @@ struct sctp_datamsg { ...@@ -537,6 +537,7 @@ struct sctp_datamsg {
struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *, struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *,
struct sctp_sndrcvinfo *, struct sctp_sndrcvinfo *,
struct iov_iter *); struct iov_iter *);
void sctp_datamsg_free(struct sctp_datamsg *);
void sctp_datamsg_put(struct sctp_datamsg *); void sctp_datamsg_put(struct sctp_datamsg *);
void sctp_chunk_fail(struct sctp_chunk *, int error); void sctp_chunk_fail(struct sctp_chunk *, int error);
int sctp_chunk_abandoned(struct sctp_chunk *); int sctp_chunk_abandoned(struct sctp_chunk *);
...@@ -1076,7 +1077,7 @@ struct sctp_outq { ...@@ -1076,7 +1077,7 @@ struct sctp_outq {
void sctp_outq_init(struct sctp_association *, struct sctp_outq *); void sctp_outq_init(struct sctp_association *, struct sctp_outq *);
void sctp_outq_teardown(struct sctp_outq *); void sctp_outq_teardown(struct sctp_outq *);
void sctp_outq_free(struct sctp_outq*); void sctp_outq_free(struct sctp_outq*);
int sctp_outq_tail(struct sctp_outq *, struct sctp_chunk *chunk, gfp_t); void sctp_outq_tail(struct sctp_outq *, struct sctp_chunk *chunk, gfp_t);
int sctp_outq_sack(struct sctp_outq *, struct sctp_chunk *); int sctp_outq_sack(struct sctp_outq *, struct sctp_chunk *);
int sctp_outq_is_empty(const struct sctp_outq *); int sctp_outq_is_empty(const struct sctp_outq *);
void sctp_outq_restart(struct sctp_outq *); void sctp_outq_restart(struct sctp_outq *);
...@@ -1084,7 +1085,7 @@ void sctp_outq_restart(struct sctp_outq *); ...@@ -1084,7 +1085,7 @@ void sctp_outq_restart(struct sctp_outq *);
void sctp_retransmit(struct sctp_outq *, struct sctp_transport *, void sctp_retransmit(struct sctp_outq *, struct sctp_transport *,
sctp_retransmit_reason_t); sctp_retransmit_reason_t);
void sctp_retransmit_mark(struct sctp_outq *, struct sctp_transport *, __u8); void sctp_retransmit_mark(struct sctp_outq *, struct sctp_transport *, __u8);
int sctp_outq_uncork(struct sctp_outq *, gfp_t gfp); void sctp_outq_uncork(struct sctp_outq *, gfp_t gfp);
void sctp_prsctp_prune(struct sctp_association *asoc, void sctp_prsctp_prune(struct sctp_association *asoc,
struct sctp_sndrcvinfo *sinfo, int msg_len); struct sctp_sndrcvinfo *sinfo, int msg_len);
/* Uncork and flush an outqueue. */ /* Uncork and flush an outqueue. */
......
...@@ -70,6 +70,19 @@ static struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp) ...@@ -70,6 +70,19 @@ static struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp)
return msg; return msg;
} }
void sctp_datamsg_free(struct sctp_datamsg *msg)
{
struct sctp_chunk *chunk;
/* This doesn't have to be a _safe vairant because
* sctp_chunk_free() only drops the refs.
*/
list_for_each_entry(chunk, &msg->chunks, frag_list)
sctp_chunk_free(chunk);
sctp_datamsg_put(msg);
}
/* Final destructruction of datamsg memory. */ /* Final destructruction of datamsg memory. */
static void sctp_datamsg_destroy(struct sctp_datamsg *msg) static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
{ {
......
...@@ -180,7 +180,6 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, ...@@ -180,7 +180,6 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
int one_packet, gfp_t gfp) int one_packet, gfp_t gfp)
{ {
sctp_xmit_t retval; sctp_xmit_t retval;
int error = 0;
pr_debug("%s: packet:%p size:%Zu chunk:%p size:%d\n", __func__, pr_debug("%s: packet:%p size:%Zu chunk:%p size:%d\n", __func__,
packet, packet->size, chunk, chunk->skb ? chunk->skb->len : -1); packet, packet->size, chunk, chunk->skb ? chunk->skb->len : -1);
...@@ -188,6 +187,8 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, ...@@ -188,6 +187,8 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) { switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) {
case SCTP_XMIT_PMTU_FULL: case SCTP_XMIT_PMTU_FULL:
if (!packet->has_cookie_echo) { if (!packet->has_cookie_echo) {
int error = 0;
error = sctp_packet_transmit(packet, gfp); error = sctp_packet_transmit(packet, gfp);
if (error < 0) if (error < 0)
chunk->skb->sk->sk_err = -error; chunk->skb->sk->sk_err = -error;
...@@ -441,14 +442,14 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) ...@@ -441,14 +442,14 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
* time. Application may notice this error. * time. Application may notice this error.
*/ */
pr_err_once("Trying to GSO but underlying device doesn't support it."); pr_err_once("Trying to GSO but underlying device doesn't support it.");
goto nomem; goto err;
} }
} else { } else {
pkt_size = packet->size; pkt_size = packet->size;
} }
head = alloc_skb(pkt_size + MAX_HEADER, gfp); head = alloc_skb(pkt_size + MAX_HEADER, gfp);
if (!head) if (!head)
goto nomem; goto err;
if (gso) { if (gso) {
NAPI_GRO_CB(head)->last = head; NAPI_GRO_CB(head)->last = head;
skb_shinfo(head)->gso_type = sk->sk_gso_type; skb_shinfo(head)->gso_type = sk->sk_gso_type;
...@@ -469,8 +470,12 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) ...@@ -469,8 +470,12 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
} }
} }
dst = dst_clone(tp->dst); dst = dst_clone(tp->dst);
if (!dst) if (!dst) {
goto no_route; if (asoc)
IP_INC_STATS(sock_net(asoc->base.sk),
IPSTATS_MIB_OUTNOROUTES);
goto nodst;
}
skb_dst_set(head, dst); skb_dst_set(head, dst);
/* Build the SCTP header. */ /* Build the SCTP header. */
...@@ -621,8 +626,10 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) ...@@ -621,8 +626,10 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
if (!gso) if (!gso)
break; break;
if (skb_gro_receive(&head, nskb)) if (skb_gro_receive(&head, nskb)) {
kfree_skb(nskb);
goto nomem; goto nomem;
}
nskb = NULL; nskb = NULL;
if (WARN_ON_ONCE(skb_shinfo(head)->gso_segs >= if (WARN_ON_ONCE(skb_shinfo(head)->gso_segs >=
sk->sk_gso_max_segs)) sk->sk_gso_max_segs))
...@@ -716,18 +723,13 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) ...@@ -716,18 +723,13 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
} }
head->ignore_df = packet->ipfragok; head->ignore_df = packet->ipfragok;
tp->af_specific->sctp_xmit(head, tp); tp->af_specific->sctp_xmit(head, tp);
goto out;
out: nomem:
sctp_packet_reset(packet); if (packet->auth && list_empty(&packet->auth->list))
return err; sctp_chunk_free(packet->auth);
no_route:
kfree_skb(head);
if (nskb != head)
kfree_skb(nskb);
if (asoc)
IP_INC_STATS(sock_net(asoc->base.sk), IPSTATS_MIB_OUTNOROUTES);
nodst:
/* FIXME: Returning the 'err' will effect all the associations /* FIXME: Returning the 'err' will effect all the associations
* associated with a socket, although only one of the paths of the * associated with a socket, although only one of the paths of the
* association is unreachable. * association is unreachable.
...@@ -736,22 +738,18 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) ...@@ -736,22 +738,18 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
* required. * required.
*/ */
/* err = -EHOSTUNREACH; */ /* err = -EHOSTUNREACH; */
err: kfree_skb(head);
/* Control chunks are unreliable so just drop them. DATA chunks
* will get resent or dropped later.
*/
err:
list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) { list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) {
list_del_init(&chunk->list); list_del_init(&chunk->list);
if (!sctp_chunk_is_data(chunk)) if (!sctp_chunk_is_data(chunk))
sctp_chunk_free(chunk); sctp_chunk_free(chunk);
} }
goto out;
nomem: out:
if (packet->auth && list_empty(&packet->auth->list)) sctp_packet_reset(packet);
sctp_chunk_free(packet->auth); return err;
err = -ENOMEM;
goto err;
} }
/******************************************************************** /********************************************************************
......
...@@ -68,7 +68,7 @@ static void sctp_mark_missing(struct sctp_outq *q, ...@@ -68,7 +68,7 @@ static void sctp_mark_missing(struct sctp_outq *q,
static void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 sack_ctsn); static void sctp_generate_fwdtsn(struct sctp_outq *q, __u32 sack_ctsn);
static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp); static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp);
/* Add data to the front of the queue. */ /* Add data to the front of the queue. */
static inline void sctp_outq_head_data(struct sctp_outq *q, static inline void sctp_outq_head_data(struct sctp_outq *q,
...@@ -285,10 +285,9 @@ void sctp_outq_free(struct sctp_outq *q) ...@@ -285,10 +285,9 @@ void sctp_outq_free(struct sctp_outq *q)
} }
/* Put a new chunk in an sctp_outq. */ /* Put a new chunk in an sctp_outq. */
int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp) void sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp)
{ {
struct net *net = sock_net(q->asoc->base.sk); struct net *net = sock_net(q->asoc->base.sk);
int error = 0;
pr_debug("%s: outq:%p, chunk:%p[%s]\n", __func__, q, chunk, pr_debug("%s: outq:%p, chunk:%p[%s]\n", __func__, q, chunk,
chunk && chunk->chunk_hdr ? chunk && chunk->chunk_hdr ?
...@@ -299,54 +298,26 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp) ...@@ -299,54 +298,26 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp)
* immediately. * immediately.
*/ */
if (sctp_chunk_is_data(chunk)) { if (sctp_chunk_is_data(chunk)) {
/* Is it OK to queue data chunks? */ pr_debug("%s: outqueueing: outq:%p, chunk:%p[%s])\n",
/* From 9. Termination of Association __func__, q, chunk, chunk && chunk->chunk_hdr ?
* sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
* When either endpoint performs a shutdown, the "illegal chunk");
* association on each peer will stop accepting new
* data from its user and only deliver data in queue sctp_outq_tail_data(q, chunk);
* at the time of sending or receiving the SHUTDOWN if (chunk->asoc->prsctp_enable &&
* chunk. SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags))
*/ chunk->asoc->sent_cnt_removable++;
switch (q->asoc->state) { if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
case SCTP_STATE_CLOSED: SCTP_INC_STATS(net, SCTP_MIB_OUTUNORDERCHUNKS);
case SCTP_STATE_SHUTDOWN_PENDING: else
case SCTP_STATE_SHUTDOWN_SENT: SCTP_INC_STATS(net, SCTP_MIB_OUTORDERCHUNKS);
case SCTP_STATE_SHUTDOWN_RECEIVED:
case SCTP_STATE_SHUTDOWN_ACK_SENT:
/* Cannot send after transport endpoint shutdown */
error = -ESHUTDOWN;
break;
default:
pr_debug("%s: outqueueing: outq:%p, chunk:%p[%s])\n",
__func__, q, chunk, chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) :
"illegal chunk");
sctp_chunk_hold(chunk);
sctp_outq_tail_data(q, chunk);
if (chunk->asoc->prsctp_enable &&
SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags))
chunk->asoc->sent_cnt_removable++;
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
SCTP_INC_STATS(net, SCTP_MIB_OUTUNORDERCHUNKS);
else
SCTP_INC_STATS(net, SCTP_MIB_OUTORDERCHUNKS);
break;
}
} else { } else {
list_add_tail(&chunk->list, &q->control_chunk_list); list_add_tail(&chunk->list, &q->control_chunk_list);
SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS); SCTP_INC_STATS(net, SCTP_MIB_OUTCTRLCHUNKS);
} }
if (error < 0)
return error;
if (!q->cork) if (!q->cork)
error = sctp_outq_flush(q, 0, gfp); sctp_outq_flush(q, 0, gfp);
return error;
} }
/* Insert a chunk into the sorted list based on the TSNs. The retransmit list /* Insert a chunk into the sorted list based on the TSNs. The retransmit list
...@@ -559,7 +530,6 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, ...@@ -559,7 +530,6 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
sctp_retransmit_reason_t reason) sctp_retransmit_reason_t reason)
{ {
struct net *net = sock_net(q->asoc->base.sk); struct net *net = sock_net(q->asoc->base.sk);
int error = 0;
switch (reason) { switch (reason) {
case SCTP_RTXR_T3_RTX: case SCTP_RTXR_T3_RTX:
...@@ -603,10 +573,7 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, ...@@ -603,10 +573,7 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
* will be flushed at the end. * will be flushed at the end.
*/ */
if (reason != SCTP_RTXR_FAST_RTX) if (reason != SCTP_RTXR_FAST_RTX)
error = sctp_outq_flush(q, /* rtx_timeout */ 1, GFP_ATOMIC); sctp_outq_flush(q, /* rtx_timeout */ 1, GFP_ATOMIC);
if (error)
q->asoc->base.sk->sk_err = -error;
} }
/* /*
...@@ -778,12 +745,12 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, ...@@ -778,12 +745,12 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt,
} }
/* Cork the outqueue so queued chunks are really queued. */ /* Cork the outqueue so queued chunks are really queued. */
int sctp_outq_uncork(struct sctp_outq *q, gfp_t gfp) void sctp_outq_uncork(struct sctp_outq *q, gfp_t gfp)
{ {
if (q->cork) if (q->cork)
q->cork = 0; q->cork = 0;
return sctp_outq_flush(q, 0, gfp); sctp_outq_flush(q, 0, gfp);
} }
...@@ -796,7 +763,7 @@ int sctp_outq_uncork(struct sctp_outq *q, gfp_t gfp) ...@@ -796,7 +763,7 @@ int sctp_outq_uncork(struct sctp_outq *q, gfp_t gfp)
* locking concerns must be made. Today we use the sock lock to protect * locking concerns must be made. Today we use the sock lock to protect
* this function. * this function.
*/ */
static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
{ {
struct sctp_packet *packet; struct sctp_packet *packet;
struct sctp_packet singleton; struct sctp_packet singleton;
...@@ -919,8 +886,10 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) ...@@ -919,8 +886,10 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
sctp_packet_config(&singleton, vtag, 0); sctp_packet_config(&singleton, vtag, 0);
sctp_packet_append_chunk(&singleton, chunk); sctp_packet_append_chunk(&singleton, chunk);
error = sctp_packet_transmit(&singleton, gfp); error = sctp_packet_transmit(&singleton, gfp);
if (error < 0) if (error < 0) {
return error; asoc->base.sk->sk_err = -error;
return;
}
break; break;
case SCTP_CID_ABORT: case SCTP_CID_ABORT:
...@@ -1018,6 +987,8 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) ...@@ -1018,6 +987,8 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
retran: retran:
error = sctp_outq_flush_rtx(q, packet, error = sctp_outq_flush_rtx(q, packet,
rtx_timeout, &start_timer); rtx_timeout, &start_timer);
if (error < 0)
asoc->base.sk->sk_err = -error;
if (start_timer) { if (start_timer) {
sctp_transport_reset_t3_rtx(transport); sctp_transport_reset_t3_rtx(transport);
...@@ -1192,14 +1163,15 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) ...@@ -1192,14 +1163,15 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)
struct sctp_transport, struct sctp_transport,
send_ready); send_ready);
packet = &t->packet; packet = &t->packet;
if (!sctp_packet_empty(packet)) if (!sctp_packet_empty(packet)) {
error = sctp_packet_transmit(packet, gfp); error = sctp_packet_transmit(packet, gfp);
if (error < 0)
asoc->base.sk->sk_err = -error;
}
/* Clear the burst limited state, if any */ /* Clear the burst limited state, if any */
sctp_transport_burst_reset(t); sctp_transport_burst_reset(t);
} }
return error;
} }
/* Update unack_data based on the incoming SACK chunk */ /* Update unack_data based on the incoming SACK chunk */
......
...@@ -1020,19 +1020,13 @@ static void sctp_cmd_t1_timer_update(struct sctp_association *asoc, ...@@ -1020,19 +1020,13 @@ static void sctp_cmd_t1_timer_update(struct sctp_association *asoc,
* This way the whole message is queued up and bundling if * This way the whole message is queued up and bundling if
* encouraged for small fragments. * encouraged for small fragments.
*/ */
static int sctp_cmd_send_msg(struct sctp_association *asoc, static void sctp_cmd_send_msg(struct sctp_association *asoc,
struct sctp_datamsg *msg, gfp_t gfp) struct sctp_datamsg *msg, gfp_t gfp)
{ {
struct sctp_chunk *chunk; struct sctp_chunk *chunk;
int error = 0;
list_for_each_entry(chunk, &msg->chunks, frag_list) {
error = sctp_outq_tail(&asoc->outqueue, chunk, gfp);
if (error)
break;
}
return error; list_for_each_entry(chunk, &msg->chunks, frag_list)
sctp_outq_tail(&asoc->outqueue, chunk, gfp);
} }
...@@ -1427,8 +1421,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1427,8 +1421,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
local_cork = 1; local_cork = 1;
} }
/* Send a chunk to our peer. */ /* Send a chunk to our peer. */
error = sctp_outq_tail(&asoc->outqueue, cmd->obj.chunk, sctp_outq_tail(&asoc->outqueue, cmd->obj.chunk, gfp);
gfp);
break; break;
case SCTP_CMD_SEND_PKT: case SCTP_CMD_SEND_PKT:
...@@ -1682,7 +1675,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1682,7 +1675,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
case SCTP_CMD_FORCE_PRIM_RETRAN: case SCTP_CMD_FORCE_PRIM_RETRAN:
t = asoc->peer.retran_path; t = asoc->peer.retran_path;
asoc->peer.retran_path = asoc->peer.primary_path; asoc->peer.retran_path = asoc->peer.primary_path;
error = sctp_outq_uncork(&asoc->outqueue, gfp); sctp_outq_uncork(&asoc->outqueue, gfp);
local_cork = 0; local_cork = 0;
asoc->peer.retran_path = t; asoc->peer.retran_path = t;
break; break;
...@@ -1709,7 +1702,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1709,7 +1702,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
sctp_outq_cork(&asoc->outqueue); sctp_outq_cork(&asoc->outqueue);
local_cork = 1; local_cork = 1;
} }
error = sctp_cmd_send_msg(asoc, cmd->obj.msg, gfp); sctp_cmd_send_msg(asoc, cmd->obj.msg, gfp);
break; break;
case SCTP_CMD_SEND_NEXT_ASCONF: case SCTP_CMD_SEND_NEXT_ASCONF:
sctp_cmd_send_asconf(asoc); sctp_cmd_send_asconf(asoc);
...@@ -1739,9 +1732,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, ...@@ -1739,9 +1732,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
*/ */
if (asoc && SCTP_EVENT_T_CHUNK == event_type && chunk) { if (asoc && SCTP_EVENT_T_CHUNK == event_type && chunk) {
if (chunk->end_of_packet || chunk->singleton) if (chunk->end_of_packet || chunk->singleton)
error = sctp_outq_uncork(&asoc->outqueue, gfp); sctp_outq_uncork(&asoc->outqueue, gfp);
} else if (local_cork) } else if (local_cork)
error = sctp_outq_uncork(&asoc->outqueue, gfp); sctp_outq_uncork(&asoc->outqueue, gfp);
if (sp->data_ready_signalled) if (sp->data_ready_signalled)
sp->data_ready_signalled = 0; sp->data_ready_signalled = 0;
......
...@@ -1958,6 +1958,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) ...@@ -1958,6 +1958,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
/* Now send the (possibly) fragmented message. */ /* Now send the (possibly) fragmented message. */
list_for_each_entry(chunk, &datamsg->chunks, frag_list) { list_for_each_entry(chunk, &datamsg->chunks, frag_list) {
sctp_chunk_hold(chunk);
/* Do accounting for the write space. */ /* Do accounting for the write space. */
sctp_set_owner_w(chunk); sctp_set_owner_w(chunk);
...@@ -1970,13 +1972,15 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) ...@@ -1970,13 +1972,15 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
* breaks. * breaks.
*/ */
err = sctp_primitive_SEND(net, asoc, datamsg); err = sctp_primitive_SEND(net, asoc, datamsg);
sctp_datamsg_put(datamsg);
/* Did the lower layer accept the chunk? */ /* Did the lower layer accept the chunk? */
if (err) if (err) {
sctp_datamsg_free(datamsg);
goto out_free; goto out_free;
}
pr_debug("%s: we sent primitively\n", __func__); pr_debug("%s: we sent primitively\n", __func__);
sctp_datamsg_put(datamsg);
err = msg_len; err = msg_len;
if (unlikely(wait_connect)) { if (unlikely(wait_connect)) {
......
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