From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-08 09:31:56
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.
Xin Long (6):
sctp: remove the unnecessary state check in sctp_outq_tail
sctp: do not return the transmit err back to sctp_sendmsg
sctp: free msg->chunks when sctp_primitive_SEND return err
sctp: save transmit error to sk_err in sctp_outq_flush
sctp: make sctp_outq_flush/tail/uncork return void
sctp: not return ENOMEM err back in sctp_packet_transmit
include/net/sctp/structs.h | 5 +--
net/sctp/chunk.c | 13 +++++++
net/sctp/output.c | 50 +++++++++++++-------------
net/sctp/outqueue.c | 89 ++++++++++++++++------------------------------
net/sctp/sm_sideeffect.c | 25 +++++--------
net/sctp/socket.c | 6 ++--
6 files changed, 84 insertions(+), 104 deletions(-)
--
2.1.0
From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-08 09:32:02
Data Chunks are only sent by sctp_primitive_SEND, in which sctp checks
the asoc's state through statetable before calling sctp_outq_tail. So
there's no need to do it again in sctp_outq_tail.
This patch is to remove it from sctp_outq_tail.
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/outqueue.c | 53 ++++++++++++++---------------------------------------
1 file changed, 14 insertions(+), 39 deletions(-)
@@ -299,50 +299,25 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp)*immediately.*/if(sctp_chunk_is_data(chunk)){-/* Is it OK to queue data chunks? */-/* From 9. Termination of Association-*-*Wheneitherendpointperformsashutdown,the-*associationoneachpeerwillstopacceptingnew-*datafromitsuserandonlydeliverdatainqueue-*atthetimeofsendingorreceivingtheSHUTDOWN-*chunk.-*/-switch(q->asoc->state){-caseSCTP_STATE_CLOSED:-caseSCTP_STATE_SHUTDOWN_PENDING:-caseSCTP_STATE_SHUTDOWN_SENT:-caseSCTP_STATE_SHUTDOWN_RECEIVED:-caseSCTP_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;-}+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);}else{list_add_tail(&chunk->list,&q->control_chunk_list);SCTP_INC_STATS(net,SCTP_MIB_OUTCTRLCHUNKS);}-if(error<0)-returnerror;-if(!q->cork)error=sctp_outq_flush(q,0,gfp);
From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-08 09:32:06
Once a chunk is enqueued successfully, sctp queues can take care of it.
Even if it is failed to transmit (like because of nomem), it should be
put into retransmit queue.
If sctp report this error to users, it confuses them, they may resend
that msg, but actually in kernel sctp stack is in charge of retransmit
it already.
Besides, this error probably is not from the failure of transmitting
current msg, but transmitting or retransmitting another msg's chunks,
as sctp_outq_flush just tries to send out all transports' chunks.
This patch is to make sctp_cmd_send_msg return avoid, and not return the
transmit err back to sctp_sendmsg
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/sm_sideeffect.c | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-08 09:32:09
Last patch "sctp: do not return the transmit err back to sctp_sendmsg"
made sctp_primitive_SEND return err only when asoc state is unavailable.
In this case, chunks are not enqueued, they have no chance to be freed if
we don't take care of them later.
This Patch is actually to revert commit 1cd4d5c4326a ("sctp: remove the
unused sctp_datamsg_free()") and commit 8b570dc9f7b6 ("sctp: only drop the
reference on the datamsg after sending a msg"), to use sctp_datamsg_free to
free the chunks of current msg.
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
include/net/sctp/structs.h | 1 +
net/sctp/chunk.c | 13 +++++++++++++
net/sctp/socket.c | 6 ++++--
3 files changed, 18 insertions(+), 2 deletions(-)
@@ -70,6 +70,19 @@ static struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp)returnmsg;}+voidsctp_datamsg_free(structsctp_datamsg*msg)+{+structsctp_chunk*chunk;++/* This doesn't have to be a _safe vairant because+*sctp_chunk_free()onlydropstherefs.+*/+list_for_each_entry(chunk,&msg->chunks,frag_list)+sctp_chunk_free(chunk);++sctp_datamsg_put(msg);+}+/* Final destructruction of datamsg memory. */staticvoidsctp_datamsg_destroy(structsctp_datamsg*msg){
@@ -1970,13 +1970,15 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)*breaks.*/err=sctp_primitive_SEND(net,asoc,datamsg);-sctp_datamsg_put(datamsg);/* Did the lower layer accept the chunk? */-if(err)+if(err){+sctp_datamsg_free(datamsg);gotoout_free;+}pr_debug("%s: we sent primitively\n",__func__);+sctp_datamsg_put(datamsg);err=msg_len;if(unlikely(wait_connect)){
From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-08 09:43:17
Every time when sctp calls sctp_outq_flush, it sends out the chunks of
control queue, retransmit queue and data queue. Even if some trunks are
failed to transmit, it still has to flush all the transports, as it's
the only chance to clean that transmit_list.
So the latest transmit error here should be returned back. This transmit
error is an internal error of sctp stack.
I checked all the places where it uses the transmit error (the return
value of sctp_outq_flush), most of them are actually just save it to
sk_err.
Except for sctp_assoc/endpoint_bh_rcv, they will drop the chunk if
it's failed to send a REPLY, which is actually incorrect, as we can't
be sure the error that sctp_outq_flush returns is from sending that
REPLY.
So it's meaningless for sctp_outq_flush to return error back.
This patch is to save transmit error to sk_err in sctp_outq_flush, the
new error can update the old value. Eventually, sctp_wait_for_* would
check for it.
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/output.c | 3 ++-
net/sctp/outqueue.c | 21 ++++++++++++---------
2 files changed, 14 insertions(+), 10 deletions(-)
@@ -894,8 +890,10 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)sctp_packet_config(&singleton,vtag,0);sctp_packet_append_chunk(&singleton,chunk);error=sctp_packet_transmit(&singleton,gfp);-if(error<0)-returnerror;+if(error<0){+asoc->base.sk->sk_err=-error;+return0;+}break;caseSCTP_CID_ABORT:
@@ -993,6 +991,8 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)retran:error=sctp_outq_flush_rtx(q,packet,rtx_timeout,&start_timer);+if(error<0)+asoc->base.sk->sk_err=-error;if(start_timer){sctp_transport_reset_t3_rtx(transport);
@@ -1167,14 +1167,17 @@ sctp_flush_out:structsctp_transport,send_ready);packet=&t->packet;-if(!sctp_packet_empty(packet))+if(!sctp_packet_empty(packet)){error=sctp_packet_transmit(packet,gfp);+if(error<0)+asoc->base.sk->sk_err=-error;+}/* Clear the burst limited state, if any */sctp_transport_burst_reset(t);}-returnerror;+return0;}/* Update unack_data based on the incoming SACK chunk */
From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-08 09:44:09
sctp_outq_flush return value is meaningless now, this patch is
to make sctp_outq_flush return void, as well as sctp_outq_fail
and sctp_outq_uncork.
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
include/net/sctp/structs.h | 4 ++--
net/sctp/outqueue.c | 19 +++++++------------
net/sctp/sm_sideeffect.c | 9 ++++-----
3 files changed, 13 insertions(+), 19 deletions(-)
@@ -68,7 +68,7 @@ static void sctp_mark_missing(struct sctp_outq *q,staticvoidsctp_generate_fwdtsn(structsctp_outq*q,__u32sack_ctsn);-staticintsctp_outq_flush(structsctp_outq*q,intrtx_timeout,gfp_tgfp);+staticvoidsctp_outq_flush(structsctp_outq*q,intrtx_timeout,gfp_tgfp);/* Add data to the front of the queue. */staticinlinevoidsctp_outq_head_data(structsctp_outq*q,
@@ -285,10 +285,9 @@ void sctp_outq_free(struct sctp_outq *q)}/* Put a new chunk in an sctp_outq. */-intsctp_outq_tail(structsctp_outq*q,structsctp_chunk*chunk,gfp_tgfp)+voidsctp_outq_tail(structsctp_outq*q,structsctp_chunk*chunk,gfp_tgfp){structnet*net=sock_net(q->asoc->base.sk);-interror=0;pr_debug("%s: outq:%p, chunk:%p[%s]\n",__func__,q,chunk,chunk&&chunk->chunk_hdr?
@@ -319,9 +318,7 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp)}if(!q->cork)-error=sctp_outq_flush(q,0,gfp);--returnerror;+sctp_outq_flush(q,0,gfp);}/* Insert a chunk into the sorted list based on the TSNs. The retransmit list
@@ -749,12 +746,12 @@ redo:}/* Cork the outqueue so queued chunks are really queued. */-intsctp_outq_uncork(structsctp_outq*q,gfp_tgfp)+voidsctp_outq_uncork(structsctp_outq*q,gfp_tgfp){if(q->cork)q->cork=0;-returnsctp_outq_flush(q,0,gfp);+sctp_outq_flush(q,0,gfp);}
@@ -767,7 +764,7 @@ int sctp_outq_uncork(struct sctp_outq *q, gfp_t gfp)*lockingconcernsmustbemade.Todayweusethesocklocktoprotect*thisfunction.*/-staticintsctp_outq_flush(structsctp_outq*q,intrtx_timeout,gfp_tgfp)+staticvoidsctp_outq_flush(structsctp_outq*q,intrtx_timeout,gfp_tgfp){structsctp_packet*packet;structsctp_packetsingleton;
@@ -892,7 +889,7 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp)error=sctp_packet_transmit(&singleton,gfp);if(error<0){asoc->base.sk->sk_err=-error;-return0;+return;}break;
@@ -1176,8 +1173,6 @@ sctp_flush_out:/* Clear the burst limited state, if any */sctp_transport_burst_reset(t);}--return0;}/* Update unack_data based on the incoming SACK chunk */
@@ -1421,8 +1421,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,local_cork=1;}/* Send a chunk to our peer. */-error=sctp_outq_tail(&asoc->outqueue,cmd->obj.chunk,-gfp);+sctp_outq_tail(&asoc->outqueue,cmd->obj.chunk,gfp);break;caseSCTP_CMD_SEND_PKT:
@@ -1676,7 +1675,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,caseSCTP_CMD_FORCE_PRIM_RETRAN:t=asoc->peer.retran_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;asoc->peer.retran_path=t;break;
From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-08 09:44:48
As David and Marcelo's suggestion, ENOMEM err shouldn't return back to
user in transmit path. Instead, sctp's retransmit would take care of
the chunks that fail to send because of ENOMEM.
This patch is only to do some release job when alloc_skb fails, not to
return ENOMEM back any more.
Besides, it also cleans up sctp_packet_transmit's err path, and fixes
some issues in err path:
- It didn't free the head skb in nomem: path.
- No need to check nskb in no_route: path.
- It should goto err: path if alloc_skb fails for head.
- Not all the NOMEMs should free nskb.
Signed-off-by: Xin Long <lucien.xin@gmail.com>
---
net/sctp/output.c | 47 ++++++++++++++++++++++-------------------------
1 file changed, 22 insertions(+), 25 deletions(-)
@@ -442,14 +442,14 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)*time.Applicationmaynoticethiserror.*/pr_err_once("Trying to GSO but underlying device doesn't support it.");-gotonomem;+gotoerr;}}else{pkt_size=packet->size;}head=alloc_skb(pkt_size+MAX_HEADER,gfp);if(!head)-gotonomem;+gotoerr;if(gso){NAPI_GRO_CB(head)->last=head;skb_shinfo(head)->gso_type=sk->sk_gso_type;
@@ -470,8 +470,12 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)}}dst=dst_clone(tp->dst);-if(!dst)-gotono_route;+if(!dst){+if(asoc)+IP_INC_STATS(sock_net(asoc->base.sk),+IPSTATS_MIB_OUTNOROUTES);+gotonodst;+}skb_dst_set(head,dst);/* Build the SCTP header. */
@@ -622,8 +626,10 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)if(!gso)break;-if(skb_gro_receive(&head,nskb))+if(skb_gro_receive(&head,nskb)){+kfree_skb(nskb);gotonomem;+}nskb=NULL;if(WARN_ON_ONCE(skb_shinfo(head)->gso_segs>=sk->sk_gso_max_segs))
@@ -717,18 +723,13 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)}head->ignore_df=packet->ipfragok;tp->af_specific->sctp_xmit(head,tp);+gotoout;-out:-sctp_packet_reset(packet);-returnerr;-no_route:-kfree_skb(head);-if(nskb!=head)-kfree_skb(nskb);--if(asoc)-IP_INC_STATS(sock_net(asoc->base.sk),IPSTATS_MIB_OUTNOROUTES);+nomem:+if(packet->auth&&list_empty(&packet->auth->list))+sctp_chunk_free(packet->auth);+nodst:/* FIXME: Returning the 'err' will effect all the associations*associatedwithasocket,althoughonlyoneofthepathsofthe*associationisunreachable.
@@ -737,22 +738,18 @@ no_route:*required.*//* err = -EHOSTUNREACH; */-err:-/* Control chunks are unreliable so just drop them. DATA chunks-*willgetresentordroppedlater.-*/+kfree_skb(head);+err:list_for_each_entry_safe(chunk,tmp,&packet->chunk_list,list){list_del_init(&chunk->list);if(!sctp_chunk_is_data(chunk))sctp_chunk_free(chunk);}-gotoout;-nomem:-if(packet->auth&&list_empty(&packet->auth->list))-sctp_chunk_free(packet->auth);-err=-ENOMEM;-gotoerr;++out:+sctp_packet_reset(packet);+returnerr;}/********************************************************************
From: Neil Horman <nhorman@tuxdriver.com> Date: 2016-09-08 14:27:21
On Thu, Sep 08, 2016 at 05:31:45PM +0800, Xin Long wrote:
Data Chunks are only sent by sctp_primitive_SEND, in which sctp checks
the asoc's state through statetable before calling sctp_outq_tail. So
there's no need to do it again in sctp_outq_tail.
This patch is to remove it from sctp_outq_tail.
Signed-off-by: Xin Long <lucien.xin@gmail.com>
This doesn't seem safe to me. The send operation is handled in side effect
processing off a queue that might have operations queued ahead of it, which
affect the associations state. I think you need to keep this check in place
Neil
@@ -299,50 +299,25 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk, gfp_t gfp)*immediately.*/if(sctp_chunk_is_data(chunk)){-/* Is it OK to queue data chunks? */-/* From 9. Termination of Association-*-*Wheneitherendpointperformsashutdown,the-*associationoneachpeerwillstopacceptingnew-*datafromitsuserandonlydeliverdatainqueue-*atthetimeofsendingorreceivingtheSHUTDOWN-*chunk.-*/-switch(q->asoc->state){-caseSCTP_STATE_CLOSED:-caseSCTP_STATE_SHUTDOWN_PENDING:-caseSCTP_STATE_SHUTDOWN_SENT:-caseSCTP_STATE_SHUTDOWN_RECEIVED:-caseSCTP_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;-}+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);}else{list_add_tail(&chunk->list,&q->control_chunk_list);SCTP_INC_STATS(net,SCTP_MIB_OUTCTRLCHUNKS);}-if(error<0)-returnerror;-if(!q->cork)error=sctp_outq_flush(q,0,gfp);
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-08 17:34:06
quoted
Data Chunks are only sent by sctp_primitive_SEND, in which sctp checks
the asoc's state through statetable before calling sctp_outq_tail. So
there's no need to do it again in sctp_outq_tail.
This patch is to remove it from sctp_outq_tail.
Signed-off-by: Xin Long <lucien.xin@gmail.com>
This doesn't seem safe to me. The send operation is handled in side effect
processing off a queue that might have operations queued ahead of it, which
affect the associations state. I think you need to keep this check in place
But not really for Data Chunk.
There are 3 places calling sctp_outq_tail.
1. in sctp_assoc_rwnd_increase():
it's for sending sack chunk, so this patch is safe for here
2. sctp_cmd_interpreter(), SCTP_CMD_REPLY:
still not Data Chunk, so safe.
3.SCTP_CMD_SEND_MSG: -> sctp_cmd_send_msg()
it's for Data Chunk, but it's only called by sctp_sf_do_prm_send().
#define TYPE_SCTP_PRIMITIVE_SEND { \
...
/* SCTP_STATE_COOKIE_WAIT */ \
TYPE_SCTP_FUNC(sctp_sf_do_prm_send), \
/* SCTP_STATE_COOKIE_ECHOED */ \
TYPE_SCTP_FUNC(sctp_sf_do_prm_send), \
/* SCTP_STATE_ESTABLISHED */ \
TYPE_SCTP_FUNC(sctp_sf_do_prm_send), \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
in sctp_sf_do_prm_send():
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_MSG, SCTP_DATAMSG(msg));
return SCTP_DISPOSITION_CONSUME;
This function is called by PRIMITIVE_SEND, and every time after
calling sctp_do_sm(), there must be no operations inside.
so it's impossible that other operations queued ahead of it.
I'm not sure if I miss some special case, pls correct me if I'm wrong.
Thanks.
From: Neil Horman <nhorman@tuxdriver.com> Date: 2016-09-08 19:23:33
On Fri, Sep 09, 2016 at 01:34:05AM +0800, Xin Long wrote:
quoted
quoted
Data Chunks are only sent by sctp_primitive_SEND, in which sctp checks
the asoc's state through statetable before calling sctp_outq_tail. So
there's no need to do it again in sctp_outq_tail.
This patch is to remove it from sctp_outq_tail.
Signed-off-by: Xin Long <lucien.xin@gmail.com>
This doesn't seem safe to me. The send operation is handled in side effect
processing off a queue that might have operations queued ahead of it, which
affect the associations state. I think you need to keep this check in place
But not really for Data Chunk.
There are 3 places calling sctp_outq_tail.
1. in sctp_assoc_rwnd_increase():
it's for sending sack chunk, so this patch is safe for here
2. sctp_cmd_interpreter(), SCTP_CMD_REPLY:
still not Data Chunk, so safe.
3.SCTP_CMD_SEND_MSG: -> sctp_cmd_send_msg()
it's for Data Chunk, but it's only called by sctp_sf_do_prm_send().
Its not enough to just look at the paths where outq_tail is called, because
the outq_tail function is checking a state variable that can update
asynchronously in the side effect processing queue. Look at any one of the
timer functions. Those all fire asynchronous to the outq list, but they all
enqueue to the side effect list. Its entirely possible that the state can be
ESTABLISHED in the primitive send path, but before you enqueue the SEND
sideffect, a timer will pop and enqueue a NEW_STATE side effect. The end result
is you need to check the state again.
Neil
#define TYPE_SCTP_PRIMITIVE_SEND { \
...
/* SCTP_STATE_COOKIE_WAIT */ \
TYPE_SCTP_FUNC(sctp_sf_do_prm_send), \
/* SCTP_STATE_COOKIE_ECHOED */ \
TYPE_SCTP_FUNC(sctp_sf_do_prm_send), \
/* SCTP_STATE_ESTABLISHED */ \
TYPE_SCTP_FUNC(sctp_sf_do_prm_send), \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
in sctp_sf_do_prm_send():
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_MSG, SCTP_DATAMSG(msg));
return SCTP_DISPOSITION_CONSUME;
This function is called by PRIMITIVE_SEND, and every time after
calling sctp_do_sm(), there must be no operations inside.
so it's impossible that other operations queued ahead of it.
I'm not sure if I miss some special case, pls correct me if I'm wrong.
Thanks.
--
To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-09 07:11:58
quoted
Its not enough to just look at the paths where outq_tail is called, because
the outq_tail function is checking a state variable that can update
asynchronously in the side effect processing queue. Look at any one of the
timer functions. Those all fire asynchronous to the outq list, but they all
Fortunately, sctp_do_sm is protected by lock_sock.
I just checked all the sctp_do_sm calling in timer functions.
------------
bh_lock_sock(sk);
if (sock_owned_by_user(sk)) { <----------- [1]
pr_debug("%s: sock is busy\n", __func__);
/* Try again later. */
mod_timer()
goto out_unlock;
}
error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT,
out_unlock:
bh_unlock_sock(sk);
which means sctp_do_sm is *non-reenterable* because of
sock lock's protection. even if it's interrupted by something
like timers, they have to delay their real process until sock
lock is released.
So It's still safe here. pls double-check.
From: Neil Horman <nhorman@tuxdriver.com> Date: 2016-09-09 14:28:56
On Fri, Sep 09, 2016 at 03:11:57PM +0800, Xin Long wrote:
quoted
quoted
Its not enough to just look at the paths where outq_tail is called, because
the outq_tail function is checking a state variable that can update
asynchronously in the side effect processing queue. Look at any one of the
timer functions. Those all fire asynchronous to the outq list, but they all
Fortunately, sctp_do_sm is protected by lock_sock.
I just checked all the sctp_do_sm calling in timer functions.
------------
bh_lock_sock(sk);
if (sock_owned_by_user(sk)) { <----------- [1]
pr_debug("%s: sock is busy\n", __func__);
/* Try again later. */
mod_timer()
goto out_unlock;
}
error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT,
out_unlock:
bh_unlock_sock(sk);
which means sctp_do_sm is *non-reenterable* because of
sock lock's protection. even if it's interrupted by something
like timers, they have to delay their real process until sock
lock is released.
So It's still safe here. pls double-check.
I don't know, I still don't feel safe about it. I agree the socket lock keeps
the state from changing during a single transmission, which makes the use case
you are focused on correct.
That said, have you considered the retransmit case? That is to say, if you
queue and flush the outq, and some packets fail delivery, and in the time
between the intial send and the expiration of the RTX timer (during which the
socket lock will have been released), an event may occur which changes the
transport state, which will then be ignored with your patch.
Neil
From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-09 16:03:55
I don't know, I still don't feel safe about it. I agree the socket lock keeps
the state from changing during a single transmission, which makes the use case
you are focused on correct.
ok, :-)
That said, have you considered the retransmit case? That is to say, if you
queue and flush the outq, and some packets fail delivery, and in the time
between the intial send and the expiration of the RTX timer (during which the
socket lock will have been released), an event may occur which changes the
transport state, which will then be ignored with your patch.
Sorry, I'm not sure if I got it.
You mean "during which changes q->asoc->state", right ?
This patch removes the check of q->asoc->state in sctp_outq_tail().
sctp_outq_tail() is called for data only in:
sctp_primitive_SEND -> sctp_do_sm -> sctp_cmd_send_msg ->
sctp_cmd_interpreter -> sctp_cmd_send_msg() -> sctp_outq_tail()
before calling sctp_primitive_SEND, hold sock lock first.
then sctp_primitive_SEND choose FUNC according:
#define TYPE_SCTP_PRIMITIVE_SEND {
....
if asoc->state is unavailable, FUNC can't be sctp_cmd_send_msg,
but sctp_sf_error_closed/sctp_sf_error_shutdown, sctp_outq_tail
can't be called, either.
I mean sctp_primitive_SEND do the same check for asoc->state
already actually.
so the code in sctp_outq_tail is redundant actually.
On Thu, Sep 08, 2016 at 05:31:47PM +0800, Xin Long wrote:
Last patch "sctp: do not return the transmit err back to sctp_sendmsg"
made sctp_primitive_SEND return err only when asoc state is unavailable.
In this case, chunks are not enqueued, they have no chance to be freed if
we don't take care of them later.
This Patch is actually to revert commit 1cd4d5c4326a ("sctp: remove the
unused sctp_datamsg_free()") and commit 8b570dc9f7b6 ("sctp: only drop the
reference on the datamsg after sending a msg"), to use sctp_datamsg_free to
free the chunks of current msg.
Considering the subsequent patches in this series are improving error
return and the first is a cleanup/optimization, Xin, this patch and the
previous one both deserve a Fixes tag too, for 8b570dc9f7b6. Thanks
@@ -70,6 +70,19 @@ static struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp)returnmsg;}+voidsctp_datamsg_free(structsctp_datamsg*msg)+{+structsctp_chunk*chunk;++/* This doesn't have to be a _safe vairant because+*sctp_chunk_free()onlydropstherefs.+*/+list_for_each_entry(chunk,&msg->chunks,frag_list)+sctp_chunk_free(chunk);++sctp_datamsg_put(msg);+}+/* Final destructruction of datamsg memory. */staticvoidsctp_datamsg_destroy(structsctp_datamsg*msg){
@@ -1970,13 +1970,15 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)*breaks.*/err=sctp_primitive_SEND(net,asoc,datamsg);-sctp_datamsg_put(datamsg);/* Did the lower layer accept the chunk? */-if(err)+if(err){+sctp_datamsg_free(datamsg);gotoout_free;+}pr_debug("%s: we sent primitively\n",__func__);+sctp_datamsg_put(datamsg);err=msg_len;if(unlikely(wait_connect)){
--
2.1.0
--
To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
On Sat, Sep 10, 2016 at 12:03:53AM +0800, Xin Long wrote:
quoted
That said, have you considered the retransmit case? That is to say, if you
queue and flush the outq, and some packets fail delivery, and in the time
between the intial send and the expiration of the RTX timer (during which the
socket lock will have been released), an event may occur which changes the
transport state, which will then be ignored with your patch.
Sorry, I'm not sure if I got it.
You mean "during which changes q->asoc->state", right ?
This patch removes the check of q->asoc->state in sctp_outq_tail().
sctp_outq_tail() is called for data only in:
sctp_primitive_SEND -> sctp_do_sm -> sctp_cmd_send_msg ->
sctp_cmd_interpreter -> sctp_cmd_send_msg() -> sctp_outq_tail()
before calling sctp_primitive_SEND, hold sock lock first.
then sctp_primitive_SEND choose FUNC according:
#define TYPE_SCTP_PRIMITIVE_SEND {
....
if asoc->state is unavailable, FUNC can't be sctp_cmd_send_msg,
but sctp_sf_error_closed/sctp_sf_error_shutdown, sctp_outq_tail
can't be called, either.
I mean sctp_primitive_SEND do the same check for asoc->state
already actually.
so the code in sctp_outq_tail is redundant actually.
I also don't see an issue with this patch, btw.
Xin, you may want to add more/such details to the changelog, specially
about the timer versus primitive handling.
Marcelo
From: Neil Horman <nhorman@tuxdriver.com> Date: 2016-09-13 11:57:38
On Sat, Sep 10, 2016 at 12:03:53AM +0800, Xin Long wrote:
quoted
I don't know, I still don't feel safe about it. I agree the socket lock keeps
the state from changing during a single transmission, which makes the use case
you are focused on correct.
ok, :-)
quoted
That said, have you considered the retransmit case? That is to say, if you
queue and flush the outq, and some packets fail delivery, and in the time
between the intial send and the expiration of the RTX timer (during which the
socket lock will have been released), an event may occur which changes the
transport state, which will then be ignored with your patch.
Sorry, I'm not sure if I got it.
You mean "during which changes q->asoc->state", right ?
This patch removes the check of q->asoc->state in sctp_outq_tail().
sctp_outq_tail() is called for data only in:
sctp_primitive_SEND -> sctp_do_sm -> sctp_cmd_send_msg ->
sctp_cmd_interpreter -> sctp_cmd_send_msg() -> sctp_outq_tail()
before calling sctp_primitive_SEND, hold sock lock first.
then sctp_primitive_SEND choose FUNC according:
#define TYPE_SCTP_PRIMITIVE_SEND {
....
if asoc->state is unavailable, FUNC can't be sctp_cmd_send_msg,
but sctp_sf_error_closed/sctp_sf_error_shutdown, sctp_outq_tail
can't be called, either.
I mean sctp_primitive_SEND do the same check for asoc->state
already actually.
so the code in sctp_outq_tail is redundant actually.
quoted
Neil
Ok, you've convinced me, thanks for taking the time to go through it
Acked-by: Neil Horman <nhorman@tuxdriver.com>
From: Xin Long <lucien.xin@gmail.com> Date: 2016-09-13 17:58:04
I also don't see an issue with this patch, btw.
Xin, you may want to add more/such details to the changelog, specially
about the timer versus primitive handling.