mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-10-31 08:44:41 +00:00 
			
		
		
		
	SUNRPC: Teach server to use xprt_sock_sendmsg for socket sends
xprt_sock_sendmsg uses the more efficient iov_iter-enabled kernel socket API, and is a pre-requisite for server send-side support for TLS. Note that svc_process no longer needs to reserve a word for the stream record marker, since the TCP transport now provides the record marker automatically in a separate buffer. The dprintk() in svc_send_common is also removed. It didn't seem crucial for field troubleshooting. If more is needed there, a trace point could be added in xprt_sock_sendmsg(). Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
		
							parent
							
								
									9e55eef4ab
								
							
						
					
					
						commit
						da1661b93b
					
				
					 4 changed files with 71 additions and 175 deletions
				
			
		|  | @ -50,10 +50,6 @@ static inline int sock_is_loopback(struct sock *sk) | |||
| 	return loopback; | ||||
| } | ||||
| 
 | ||||
| int svc_send_common(struct socket *sock, struct xdr_buf *xdr, | ||||
| 		    struct page *headpage, unsigned long headoffset, | ||||
| 		    struct page *tailpage, unsigned long tailoffset); | ||||
| 
 | ||||
| int rpc_clients_notifier_register(void); | ||||
| void rpc_clients_notifier_unregister(void); | ||||
| #endif /* _NET_SUNRPC_SUNRPC_H */ | ||||
|  |  | |||
|  | @ -1529,10 +1529,6 @@ svc_process(struct svc_rqst *rqstp) | |||
| 		goto out_drop; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Reserve space for the record marker */ | ||||
| 	if (rqstp->rq_prot == IPPROTO_TCP) | ||||
| 		svc_putnl(resv, 0); | ||||
| 
 | ||||
| 	/* Returns 1 for send, 0 for drop */ | ||||
| 	if (likely(svc_process_common(rqstp, argv, resv))) | ||||
| 		return svc_send(rqstp); | ||||
|  |  | |||
|  | @ -175,111 +175,6 @@ static void svc_set_cmsg_data(struct svc_rqst *rqstp, struct cmsghdr *cmh) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * send routine intended to be shared by the fore- and back-channel | ||||
|  */ | ||||
| int svc_send_common(struct socket *sock, struct xdr_buf *xdr, | ||||
| 		    struct page *headpage, unsigned long headoffset, | ||||
| 		    struct page *tailpage, unsigned long tailoffset) | ||||
| { | ||||
| 	int		result; | ||||
| 	int		size; | ||||
| 	struct page	**ppage = xdr->pages; | ||||
| 	size_t		base = xdr->page_base; | ||||
| 	unsigned int	pglen = xdr->page_len; | ||||
| 	unsigned int	flags = MSG_MORE | MSG_SENDPAGE_NOTLAST; | ||||
| 	int		slen; | ||||
| 	int		len = 0; | ||||
| 
 | ||||
| 	slen = xdr->len; | ||||
| 
 | ||||
| 	/* send head */ | ||||
| 	if (slen == xdr->head[0].iov_len) | ||||
| 		flags = 0; | ||||
| 	len = kernel_sendpage(sock, headpage, headoffset, | ||||
| 				  xdr->head[0].iov_len, flags); | ||||
| 	if (len != xdr->head[0].iov_len) | ||||
| 		goto out; | ||||
| 	slen -= xdr->head[0].iov_len; | ||||
| 	if (slen == 0) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* send page data */ | ||||
| 	size = PAGE_SIZE - base < pglen ? PAGE_SIZE - base : pglen; | ||||
| 	while (pglen > 0) { | ||||
| 		if (slen == size) | ||||
| 			flags = 0; | ||||
| 		result = kernel_sendpage(sock, *ppage, base, size, flags); | ||||
| 		if (result > 0) | ||||
| 			len += result; | ||||
| 		if (result != size) | ||||
| 			goto out; | ||||
| 		slen -= size; | ||||
| 		pglen -= size; | ||||
| 		size = PAGE_SIZE < pglen ? PAGE_SIZE : pglen; | ||||
| 		base = 0; | ||||
| 		ppage++; | ||||
| 	} | ||||
| 
 | ||||
| 	/* send tail */ | ||||
| 	if (xdr->tail[0].iov_len) { | ||||
| 		result = kernel_sendpage(sock, tailpage, tailoffset, | ||||
| 				   xdr->tail[0].iov_len, 0); | ||||
| 		if (result > 0) | ||||
| 			len += result; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	return len; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Generic sendto routine | ||||
|  */ | ||||
| static int svc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr) | ||||
| { | ||||
| 	struct svc_sock	*svsk = | ||||
| 		container_of(rqstp->rq_xprt, struct svc_sock, sk_xprt); | ||||
| 	struct socket	*sock = svsk->sk_sock; | ||||
| 	union { | ||||
| 		struct cmsghdr	hdr; | ||||
| 		long		all[SVC_PKTINFO_SPACE / sizeof(long)]; | ||||
| 	} buffer; | ||||
| 	struct cmsghdr *cmh = &buffer.hdr; | ||||
| 	int		len = 0; | ||||
| 	unsigned long tailoff; | ||||
| 	unsigned long headoff; | ||||
| 	RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); | ||||
| 
 | ||||
| 	if (rqstp->rq_prot == IPPROTO_UDP) { | ||||
| 		struct msghdr msg = { | ||||
| 			.msg_name	= &rqstp->rq_addr, | ||||
| 			.msg_namelen	= rqstp->rq_addrlen, | ||||
| 			.msg_control	= cmh, | ||||
| 			.msg_controllen	= sizeof(buffer), | ||||
| 			.msg_flags	= MSG_MORE, | ||||
| 		}; | ||||
| 
 | ||||
| 		svc_set_cmsg_data(rqstp, cmh); | ||||
| 
 | ||||
| 		if (sock_sendmsg(sock, &msg) < 0) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	tailoff = ((unsigned long)xdr->tail[0].iov_base) & (PAGE_SIZE-1); | ||||
| 	headoff = 0; | ||||
| 	len = svc_send_common(sock, xdr, rqstp->rq_respages[0], headoff, | ||||
| 			       rqstp->rq_respages[0], tailoff); | ||||
| 
 | ||||
| out: | ||||
| 	dprintk("svc: socket %p sendto([%p %zu... ], %d) = %d (addr %s)\n", | ||||
| 		svsk, xdr->head[0].iov_base, xdr->head[0].iov_len, | ||||
| 		xdr->len, len, svc_print_addr(rqstp, buf, sizeof(buf))); | ||||
| 
 | ||||
| 	return len; | ||||
| } | ||||
| 
 | ||||
| static int svc_sock_read_payload(struct svc_rqst *rqstp, unsigned int offset, | ||||
| 				 unsigned int length) | ||||
| { | ||||
|  | @ -607,17 +502,43 @@ out_free: | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| svc_udp_sendto(struct svc_rqst *rqstp) | ||||
| /**
 | ||||
|  * svc_udp_sendto - Send out a reply on a UDP socket | ||||
|  * @rqstp: completed svc_rqst | ||||
|  * | ||||
|  * Returns the number of bytes sent, or a negative errno. | ||||
|  */ | ||||
| static int svc_udp_sendto(struct svc_rqst *rqstp) | ||||
| { | ||||
| 	int		error; | ||||
| 	struct svc_xprt *xprt = rqstp->rq_xprt; | ||||
| 	struct svc_sock	*svsk = container_of(xprt, struct svc_sock, sk_xprt); | ||||
| 	struct xdr_buf *xdr = &rqstp->rq_res; | ||||
| 	union { | ||||
| 		struct cmsghdr	hdr; | ||||
| 		long		all[SVC_PKTINFO_SPACE / sizeof(long)]; | ||||
| 	} buffer; | ||||
| 	struct cmsghdr *cmh = &buffer.hdr; | ||||
| 	struct msghdr msg = { | ||||
| 		.msg_name	= &rqstp->rq_addr, | ||||
| 		.msg_namelen	= rqstp->rq_addrlen, | ||||
| 		.msg_control	= cmh, | ||||
| 		.msg_controllen	= sizeof(buffer), | ||||
| 	}; | ||||
| 	unsigned int uninitialized_var(sent); | ||||
| 	int err; | ||||
| 
 | ||||
| 	error = svc_sendto(rqstp, &rqstp->rq_res); | ||||
| 	if (error == -ECONNREFUSED) | ||||
| 	svc_set_cmsg_data(rqstp, cmh); | ||||
| 
 | ||||
| 	err = xprt_sock_sendmsg(svsk->sk_sock, &msg, xdr, 0, 0, &sent); | ||||
| 	xdr_free_bvec(xdr); | ||||
| 	if (err == -ECONNREFUSED) { | ||||
| 		/* ICMP error on earlier request. */ | ||||
| 		error = svc_sendto(rqstp, &rqstp->rq_res); | ||||
| 
 | ||||
| 	return error; | ||||
| 		err = xprt_sock_sendmsg(svsk->sk_sock, &msg, xdr, 0, 0, &sent); | ||||
| 		xdr_free_bvec(xdr); | ||||
| 	} | ||||
| 	if (err < 0) | ||||
| 		return err; | ||||
| 	return sent; | ||||
| } | ||||
| 
 | ||||
| static int svc_udp_has_wspace(struct svc_xprt *xprt) | ||||
|  | @ -1136,35 +1057,39 @@ err_noclose: | |||
| 	return 0;	/* record not complete */ | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Send out data on TCP socket. | ||||
| /**
 | ||||
|  * svc_tcp_sendto - Send out a reply on a TCP socket | ||||
|  * @rqstp: completed svc_rqst | ||||
|  * | ||||
|  * Returns the number of bytes sent, or a negative errno. | ||||
|  */ | ||||
| static int svc_tcp_sendto(struct svc_rqst *rqstp) | ||||
| { | ||||
| 	struct xdr_buf	*xbufp = &rqstp->rq_res; | ||||
| 	int sent; | ||||
| 	__be32 reclen; | ||||
| 	struct svc_xprt *xprt = rqstp->rq_xprt; | ||||
| 	struct svc_sock	*svsk = container_of(xprt, struct svc_sock, sk_xprt); | ||||
| 	struct xdr_buf *xdr = &rqstp->rq_res; | ||||
| 	rpc_fraghdr marker = cpu_to_be32(RPC_LAST_STREAM_FRAGMENT | | ||||
| 					 (u32)xdr->len); | ||||
| 	struct msghdr msg = { | ||||
| 		.msg_flags	= 0, | ||||
| 	}; | ||||
| 	unsigned int uninitialized_var(sent); | ||||
| 	int err; | ||||
| 
 | ||||
| 	/* Set up the first element of the reply kvec.
 | ||||
| 	 * Any other kvecs that may be in use have been taken | ||||
| 	 * care of by the server implementation itself. | ||||
| 	 */ | ||||
| 	reclen = htonl(0x80000000|((xbufp->len ) - 4)); | ||||
| 	memcpy(xbufp->head[0].iov_base, &reclen, 4); | ||||
| 
 | ||||
| 	sent = svc_sendto(rqstp, &rqstp->rq_res); | ||||
| 	if (sent != xbufp->len) { | ||||
| 		printk(KERN_NOTICE | ||||
| 		       "rpc-srv/tcp: %s: %s %d when sending %d bytes " | ||||
| 		       "- shutting down socket\n", | ||||
| 		       rqstp->rq_xprt->xpt_server->sv_name, | ||||
| 		       (sent<0)?"got error":"sent only", | ||||
| 		       sent, xbufp->len); | ||||
| 		set_bit(XPT_CLOSE, &rqstp->rq_xprt->xpt_flags); | ||||
| 		svc_xprt_enqueue(rqstp->rq_xprt); | ||||
| 		sent = -EAGAIN; | ||||
| 	} | ||||
| 	err = xprt_sock_sendmsg(svsk->sk_sock, &msg, xdr, 0, marker, &sent); | ||||
| 	xdr_free_bvec(xdr); | ||||
| 	if (err < 0 || sent != (xdr->len + sizeof(marker))) | ||||
| 		goto out_close; | ||||
| 	return sent; | ||||
| 
 | ||||
| out_close: | ||||
| 	pr_notice("rpc-srv/tcp: %s: %s %d when sending %d bytes - shutting down socket\n", | ||||
| 		  xprt->xpt_server->sv_name, | ||||
| 		  (err < 0) ? "got error" : "sent", | ||||
| 		  (err < 0) ? err : sent, xdr->len); | ||||
| 	set_bit(XPT_CLOSE, &xprt->xpt_flags); | ||||
| 	svc_xprt_enqueue(xprt); | ||||
| 	return -EAGAIN; | ||||
| } | ||||
| 
 | ||||
| static struct svc_xprt *svc_tcp_create(struct svc_serv *serv, | ||||
|  |  | |||
|  | @ -2527,46 +2527,25 @@ static void bc_free(struct rpc_task *task) | |||
| 	free_page((unsigned long)buf); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Use the svc_sock to send the callback. Must be called with svsk->sk_mutex | ||||
|  * held. Borrows heavily from svc_tcp_sendto and xs_tcp_send_request. | ||||
|  */ | ||||
| static int bc_sendto(struct rpc_rqst *req) | ||||
| { | ||||
| 	int len; | ||||
| 	struct xdr_buf *xbufp = &req->rq_snd_buf; | ||||
| 	struct xdr_buf *xdr = &req->rq_snd_buf; | ||||
| 	struct sock_xprt *transport = | ||||
| 			container_of(req->rq_xprt, struct sock_xprt, xprt); | ||||
| 	unsigned long headoff; | ||||
| 	unsigned long tailoff; | ||||
| 	struct page *tailpage; | ||||
| 	struct msghdr msg = { | ||||
| 		.msg_flags	= MSG_MORE | ||||
| 		.msg_flags	= 0, | ||||
| 	}; | ||||
| 	rpc_fraghdr marker = cpu_to_be32(RPC_LAST_STREAM_FRAGMENT | | ||||
| 					 (u32)xbufp->len); | ||||
| 	struct kvec iov = { | ||||
| 		.iov_base	= &marker, | ||||
| 		.iov_len	= sizeof(marker), | ||||
| 	}; | ||||
| 					 (u32)xdr->len); | ||||
| 	unsigned int sent = 0; | ||||
| 	int err; | ||||
| 
 | ||||
| 	req->rq_xtime = ktime_get(); | ||||
| 
 | ||||
| 	len = kernel_sendmsg(transport->sock, &msg, &iov, 1, iov.iov_len); | ||||
| 	if (len != iov.iov_len) | ||||
| 	err = xprt_sock_sendmsg(transport->sock, &msg, xdr, 0, marker, &sent); | ||||
| 	xdr_free_bvec(xdr); | ||||
| 	if (err < 0 || sent != (xdr->len + sizeof(marker))) | ||||
| 		return -EAGAIN; | ||||
| 
 | ||||
| 	tailpage = NULL; | ||||
| 	if (xbufp->tail[0].iov_len) | ||||
| 		tailpage = virt_to_page(xbufp->tail[0].iov_base); | ||||
| 	tailoff = (unsigned long)xbufp->tail[0].iov_base & ~PAGE_MASK; | ||||
| 	headoff = (unsigned long)xbufp->head[0].iov_base & ~PAGE_MASK; | ||||
| 	len = svc_send_common(transport->sock, xbufp, | ||||
| 			      virt_to_page(xbufp->head[0].iov_base), headoff, | ||||
| 			      tailpage, tailoff); | ||||
| 	if (len != xbufp->len) | ||||
| 		return -EAGAIN; | ||||
| 	return len; | ||||
| 	return sent; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Chuck Lever
						Chuck Lever