mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02: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 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
 | 
			
		|||
	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 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
 | 
			
		|||
	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…
	
		Reference in a new issue