forked from mirrors/linux
		
	sctp: factor out sctp_sendmsg_to_asoc from sctp_sendmsg
This patch is to move the codes for checking and sending on one asoc after this asoc has been found or created into sctp_sendmsg_to_asoc. Note that 'err != -ESRCH' check is for the case that asoc is freed when waiting for tx buffer in sctp_sendmsg_to_asoc. Signed-off-by: Xin Long <lucien.xin@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									e4e31cf07d
								
							
						
					
					
						commit
						f84af33138
					
				
					 1 changed files with 99 additions and 131 deletions
				
			
		| 
						 | 
					@ -1606,6 +1606,100 @@ static int sctp_error(struct sock *sk, int flags, int err)
 | 
				
			||||||
static int sctp_msghdr_parse(const struct msghdr *msg,
 | 
					static int sctp_msghdr_parse(const struct msghdr *msg,
 | 
				
			||||||
			     struct sctp_cmsgs *cmsgs);
 | 
								     struct sctp_cmsgs *cmsgs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
 | 
				
			||||||
 | 
									struct msghdr *msg, size_t msg_len,
 | 
				
			||||||
 | 
									struct sctp_transport *transport,
 | 
				
			||||||
 | 
									struct sctp_sndrcvinfo *sinfo)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sock *sk = asoc->base.sk;
 | 
				
			||||||
 | 
						struct net *net = sock_net(sk);
 | 
				
			||||||
 | 
						struct sctp_datamsg *datamsg;
 | 
				
			||||||
 | 
						bool wait_connect = false;
 | 
				
			||||||
 | 
						struct sctp_chunk *chunk;
 | 
				
			||||||
 | 
						long timeo;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sinfo->sinfo_stream >= asoc->stream.outcnt) {
 | 
				
			||||||
 | 
							err = -EINVAL;
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlikely(!asoc->stream.out[sinfo->sinfo_stream].ext)) {
 | 
				
			||||||
 | 
							err = sctp_stream_init_ext(&asoc->stream, sinfo->sinfo_stream);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sctp_sk(sk)->disable_fragments && msg_len > asoc->frag_point) {
 | 
				
			||||||
 | 
							err = -EMSGSIZE;
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sctp_state(asoc, CLOSED)) {
 | 
				
			||||||
 | 
							err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (sctp_sk(sk)->strm_interleave) {
 | 
				
			||||||
 | 
								timeo = sock_sndtimeo(sk, 0);
 | 
				
			||||||
 | 
								err = sctp_wait_for_connect(asoc, &timeo);
 | 
				
			||||||
 | 
								if (err)
 | 
				
			||||||
 | 
									goto err;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								wait_connect = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pr_debug("%s: we associated primitively\n", __func__);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (asoc->pmtu_pending)
 | 
				
			||||||
 | 
							sctp_assoc_pending_pmtu(asoc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sctp_wspace(asoc) < msg_len)
 | 
				
			||||||
 | 
							sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!sctp_wspace(asoc)) {
 | 
				
			||||||
 | 
							timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
 | 
				
			||||||
 | 
							err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						datamsg = sctp_datamsg_from_user(asoc, sinfo, &msg->msg_iter);
 | 
				
			||||||
 | 
						if (IS_ERR(datamsg)) {
 | 
				
			||||||
 | 
							err = PTR_ERR(datamsg);
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						asoc->force_delay = !!(msg->msg_flags & MSG_MORE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_for_each_entry(chunk, &datamsg->chunks, frag_list) {
 | 
				
			||||||
 | 
							sctp_chunk_hold(chunk);
 | 
				
			||||||
 | 
							sctp_set_owner_w(chunk);
 | 
				
			||||||
 | 
							chunk->transport = transport;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = sctp_primitive_SEND(net, asoc, datamsg);
 | 
				
			||||||
 | 
						if (err) {
 | 
				
			||||||
 | 
							sctp_datamsg_free(datamsg);
 | 
				
			||||||
 | 
							goto err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pr_debug("%s: we sent primitively\n", __func__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sctp_datamsg_put(datamsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlikely(wait_connect)) {
 | 
				
			||||||
 | 
							timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
 | 
				
			||||||
 | 
							sctp_wait_for_connect(asoc, &timeo);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = msg_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err:
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
 | 
					static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net *net = sock_net(sk);
 | 
						struct net *net = sock_net(sk);
 | 
				
			||||||
| 
						 | 
					@ -1622,11 +1716,8 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
 | 
				
			||||||
	sctp_assoc_t associd = 0;
 | 
						sctp_assoc_t associd = 0;
 | 
				
			||||||
	struct sctp_cmsgs cmsgs = { NULL };
 | 
						struct sctp_cmsgs cmsgs = { NULL };
 | 
				
			||||||
	enum sctp_scope scope;
 | 
						enum sctp_scope scope;
 | 
				
			||||||
	bool fill_sinfo_ttl = false, wait_connect = false;
 | 
						bool fill_sinfo_ttl = false;
 | 
				
			||||||
	struct sctp_datamsg *datamsg;
 | 
					 | 
				
			||||||
	int msg_flags = msg->msg_flags;
 | 
					 | 
				
			||||||
	__u16 sinfo_flags = 0;
 | 
						__u16 sinfo_flags = 0;
 | 
				
			||||||
	long timeo;
 | 
					 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = 0;
 | 
						err = 0;
 | 
				
			||||||
| 
						 | 
					@ -1923,49 +2014,6 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
 | 
				
			||||||
		goto out_free;
 | 
							goto out_free;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (asoc->pmtu_pending)
 | 
					 | 
				
			||||||
		sctp_assoc_pending_pmtu(asoc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If fragmentation is disabled and the message length exceeds the
 | 
					 | 
				
			||||||
	 * association fragmentation point, return EMSGSIZE.  The I-D
 | 
					 | 
				
			||||||
	 * does not specify what this error is, but this looks like
 | 
					 | 
				
			||||||
	 * a great fit.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (sctp_sk(sk)->disable_fragments && (msg_len > asoc->frag_point)) {
 | 
					 | 
				
			||||||
		err = -EMSGSIZE;
 | 
					 | 
				
			||||||
		goto out_free;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Check for invalid stream. */
 | 
					 | 
				
			||||||
	if (sinfo->sinfo_stream >= asoc->stream.outcnt) {
 | 
					 | 
				
			||||||
		err = -EINVAL;
 | 
					 | 
				
			||||||
		goto out_free;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Allocate sctp_stream_out_ext if not already done */
 | 
					 | 
				
			||||||
	if (unlikely(!asoc->stream.out[sinfo->sinfo_stream].ext)) {
 | 
					 | 
				
			||||||
		err = sctp_stream_init_ext(&asoc->stream, sinfo->sinfo_stream);
 | 
					 | 
				
			||||||
		if (err)
 | 
					 | 
				
			||||||
			goto out_free;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (sctp_wspace(asoc) < msg_len)
 | 
					 | 
				
			||||||
		sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
 | 
					 | 
				
			||||||
	if (!sctp_wspace(asoc)) {
 | 
					 | 
				
			||||||
		/* sk can be changed by peel off when waiting for buf. */
 | 
					 | 
				
			||||||
		err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
 | 
					 | 
				
			||||||
		if (err) {
 | 
					 | 
				
			||||||
			if (err == -ESRCH) {
 | 
					 | 
				
			||||||
				/* asoc is already dead. */
 | 
					 | 
				
			||||||
				new_asoc = NULL;
 | 
					 | 
				
			||||||
				err = -EPIPE;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			goto out_free;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If an address is passed with the sendto/sendmsg call, it is used
 | 
						/* If an address is passed with the sendto/sendmsg call, it is used
 | 
				
			||||||
	 * to override the primary destination address in the TCP model, or
 | 
						 * to override the primary destination address in the TCP model, or
 | 
				
			||||||
	 * when SCTP_ADDR_OVER flag is set in the UDP model.
 | 
						 * when SCTP_ADDR_OVER flag is set in the UDP model.
 | 
				
			||||||
| 
						 | 
					@ -1980,96 +2028,16 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
 | 
				
			||||||
	} else
 | 
						} else
 | 
				
			||||||
		chunk_tp = NULL;
 | 
							chunk_tp = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Auto-connect, if we aren't connected already. */
 | 
						/* Send msg to the asoc */
 | 
				
			||||||
	if (sctp_state(asoc, CLOSED)) {
 | 
						err = sctp_sendmsg_to_asoc(asoc, msg, msg_len, chunk_tp, sinfo);
 | 
				
			||||||
		err = sctp_primitive_ASSOCIATE(net, asoc, NULL);
 | 
					 | 
				
			||||||
		if (err < 0)
 | 
					 | 
				
			||||||
			goto out_free;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* If stream interleave is enabled, wait_connect has to be
 | 
					 | 
				
			||||||
		 * done earlier than data enqueue, as it needs to make data
 | 
					 | 
				
			||||||
		 * or idata according to asoc->intl_enable which is set
 | 
					 | 
				
			||||||
		 * after connection is done.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		if (sctp_sk(asoc->base.sk)->strm_interleave) {
 | 
					 | 
				
			||||||
			timeo = sock_sndtimeo(sk, 0);
 | 
					 | 
				
			||||||
			err = sctp_wait_for_connect(asoc, &timeo);
 | 
					 | 
				
			||||||
			if (err)
 | 
					 | 
				
			||||||
				goto out_unlock;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			wait_connect = true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		pr_debug("%s: we associated primitively\n", __func__);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Break the message into multiple chunks of maximum size. */
 | 
					 | 
				
			||||||
	datamsg = sctp_datamsg_from_user(asoc, sinfo, &msg->msg_iter);
 | 
					 | 
				
			||||||
	if (IS_ERR(datamsg)) {
 | 
					 | 
				
			||||||
		err = PTR_ERR(datamsg);
 | 
					 | 
				
			||||||
		goto out_free;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	asoc->force_delay = !!(msg->msg_flags & MSG_MORE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Now send the (possibly) fragmented message. */
 | 
					 | 
				
			||||||
	list_for_each_entry(chunk, &datamsg->chunks, frag_list) {
 | 
					 | 
				
			||||||
		sctp_chunk_hold(chunk);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* Do accounting for the write space.  */
 | 
					 | 
				
			||||||
		sctp_set_owner_w(chunk);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		chunk->transport = chunk_tp;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Send it to the lower layers.  Note:  all chunks
 | 
					 | 
				
			||||||
	 * must either fail or succeed.   The lower layer
 | 
					 | 
				
			||||||
	 * works that way today.  Keep it that way or this
 | 
					 | 
				
			||||||
	 * breaks.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	err = sctp_primitive_SEND(net, asoc, datamsg);
 | 
					 | 
				
			||||||
	/* Did the lower layer accept the chunk? */
 | 
					 | 
				
			||||||
	if (err) {
 | 
					 | 
				
			||||||
		sctp_datamsg_free(datamsg);
 | 
					 | 
				
			||||||
		goto out_free;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pr_debug("%s: we sent primitively\n", __func__);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sctp_datamsg_put(datamsg);
 | 
					 | 
				
			||||||
	err = msg_len;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (unlikely(wait_connect)) {
 | 
					 | 
				
			||||||
		timeo = sock_sndtimeo(sk, msg_flags & MSG_DONTWAIT);
 | 
					 | 
				
			||||||
		sctp_wait_for_connect(asoc, &timeo);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If we are already past ASSOCIATE, the lower
 | 
					 | 
				
			||||||
	 * layers are responsible for association cleanup.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	goto out_unlock;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
out_free:
 | 
					out_free:
 | 
				
			||||||
	if (new_asoc)
 | 
						if (err < 0 && err != -ESRCH && new_asoc)
 | 
				
			||||||
		sctp_association_free(asoc);
 | 
							sctp_association_free(asoc);
 | 
				
			||||||
out_unlock:
 | 
					out_unlock:
 | 
				
			||||||
	release_sock(sk);
 | 
						release_sock(sk);
 | 
				
			||||||
 | 
					 | 
				
			||||||
out_nounlock:
 | 
					out_nounlock:
 | 
				
			||||||
	return sctp_error(sk, msg_flags, err);
 | 
						return sctp_error(sk, msg->msg_flags, err);
 | 
				
			||||||
 | 
					 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
do_sock_err:
 | 
					 | 
				
			||||||
	if (msg_len)
 | 
					 | 
				
			||||||
		err = msg_len;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		err = sock_error(sk);
 | 
					 | 
				
			||||||
	goto out;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
do_interrupted:
 | 
					 | 
				
			||||||
	if (msg_len)
 | 
					 | 
				
			||||||
		err = msg_len;
 | 
					 | 
				
			||||||
	goto out;
 | 
					 | 
				
			||||||
#endif /* 0 */
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* This is an extended version of skb_pull() that removes the data from the
 | 
					/* This is an extended version of skb_pull() that removes the data from the
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue