diff mbox series

[RFC,5/8] rxrpc: Implement sendfile() support

Message ID 165599096629.1827880.6190981681205501847.stgit@warthog.procyon.org.uk (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series rxrpc: Multiqueue, sendfile, splice and call security | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net, async
netdev/fixes_present fail Series targets non-next tree, but doesn't contain any Fixes tags
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 8 this patch: 8
netdev/cc_maintainers warning 3 maintainers not CCed: pabeni@redhat.com kuba@kernel.org edumazet@google.com
netdev/build_clang success Errors and warnings before: 11 this patch: 11
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch warning WARNING: function definition argument 'int' should also have an identifier name WARNING: function definition argument 'size_t' should also have an identifier name WARNING: function definition argument 'struct page *' should also have an identifier name WARNING: function definition argument 'struct rxrpc_call *' should also have an identifier name WARNING: function definition argument 'struct rxrpc_sock *' should also have an identifier name WARNING: line length of 81 exceeds 80 columns WARNING: networking block comments don't use an empty /* line, use /* Comment...
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

David Howells June 23, 2022, 1:29 p.m. UTC
Implement the sendpage protocol operation so that sendfile() will work
directly with AF_RXRPC calls.  To use sendfile() to communicate with a call
requires the call to be specified beforehand with setsockopt():

	setsockopt(client, SOL_RXRPC, RXRPC_SELECT_CALL_FOR_SEND,
		   &call_id, sizeof(call_id));
	sendfile(client, source, &pos, st.st_size);

The specified call ID can be cleared:

	call_id = 0;
	setsockopt(client, SOL_RXRPC, RXRPC_SELECT_CALL_FOR_SEND,
		   &call_id, sizeof(call_id));

or changed.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 net/rxrpc/af_rxrpc.c    |   40 +++++++++++++++++++++++++++++++++++++++-
 net/rxrpc/ar-internal.h |    2 ++
 net/rxrpc/sendmsg.c     |   39 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 8ac014aff7a2..41420c456e77 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -798,6 +798,44 @@  static int rxrpc_bind_channel(struct rxrpc_sock *rx2, int fd)
 	return ret;
 }
 
+/*
+ * Splice into a call.  The call to send as part of must have been set with
+ * setsockopt(RXRPC_SELECT_CALL_FOR_SEND).
+ */
+static ssize_t rxrpc_sendpage(struct socket *sock, struct page *page, int offset,
+			      size_t size, int flags)
+{
+	struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
+	struct rxrpc_call *call;
+	ssize_t ret;
+
+	_enter("{%d},,%u,%zu,%x", rx->sk.sk_state, offset, size, flags);
+
+	lock_sock(&rx->sk);
+
+	read_lock_bh(&rx->recvmsg_lock);
+	call = rx->selected_send_call;
+	if (!call) {
+		read_unlock_bh(&rx->recvmsg_lock);
+		release_sock(&rx->sk);
+		return -EBADSLT;
+	}
+
+	rxrpc_get_call(call, rxrpc_call_got);
+	read_unlock_bh(&rx->recvmsg_lock);
+
+	ret = mutex_lock_interruptible(&call->user_mutex);
+	release_sock(&rx->sk);
+	if (ret == 0) {
+		ret = rxrpc_do_sendpage(rx, call, page, offset, size, flags);
+		mutex_unlock(&call->user_mutex);
+	}
+
+	rxrpc_put_call(call, rxrpc_call_put);
+	_leave(" = %zd", ret);
+	return ret;
+}
+
 /*
  * Set the default call for 'targetless' operations such as splice(), SIOCINQ
  * and SIOCOUTQ and also as a filter for recvmsg().  Calling this function
@@ -1279,9 +1317,9 @@  static const struct proto_ops rxrpc_rpc_ops = {
 	.setsockopt	= rxrpc_setsockopt,
 	.getsockopt	= rxrpc_getsockopt,
 	.sendmsg	= rxrpc_sendmsg,
+	.sendpage	= rxrpc_sendpage,
 	.recvmsg	= rxrpc_recvmsg,
 	.mmap		= sock_no_mmap,
-	.sendpage	= sock_no_sendpage,
 };
 
 static struct proto rxrpc_proto = {
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index ec2c614082b9..bec398c66341 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -1107,6 +1107,8 @@  struct key *rxrpc_look_up_server_security(struct rxrpc_connection *,
  * sendmsg.c
  */
 int rxrpc_do_sendmsg(struct rxrpc_sock *, struct msghdr *, size_t);
+ssize_t rxrpc_do_sendpage(struct rxrpc_sock *, struct rxrpc_call *,
+			  struct page *, int, size_t, int);
 
 /*
  * server_key.c
diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c
index 1d38e279e2ef..77699008c428 100644
--- a/net/rxrpc/sendmsg.c
+++ b/net/rxrpc/sendmsg.c
@@ -762,6 +762,45 @@  int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
 	return ret;
 }
 
+/*
+ * Handle data input from a splice.  The call to send as part of must have been
+ * set with setsockopt(RXRPC_SELECT_CALL_FOR_SEND).
+ */
+ssize_t rxrpc_do_sendpage(struct rxrpc_sock *rx, struct rxrpc_call *call,
+			  struct page *page, int offset, size_t size, int flags)
+{
+	struct bio_vec bv = {
+		.bv_page	= page,
+		.bv_offset	= offset,
+		.bv_len		= size,
+	};
+	struct msghdr msg = {
+		.msg_flags	= flags,
+	};
+
+	_enter(",,%lx,%u,%zu,%x", page->index, offset, size, flags);
+
+	switch (READ_ONCE(call->state)) {
+	case RXRPC_CALL_COMPLETE:
+		return -ESHUTDOWN;
+	default:
+		return -EPROTO;
+	case RXRPC_CALL_CLIENT_SEND_REQUEST:
+	case RXRPC_CALL_SERVER_ACK_REQUEST:
+	case RXRPC_CALL_SERVER_SEND_REPLY:
+		break;
+	}
+
+	/* Ideally, we'd allow sendfile() to end the Tx phase - but there's no
+	 * way for userspace to communicate this option through that syscall.
+	 */
+	//if (flags & MSG_SENDPAGE_NOTLAST)
+	msg.msg_flags |= MSG_MORE;
+
+	iov_iter_bvec(&msg.msg_iter, WRITE, &bv, 1, size);
+	return rxrpc_send_data(rx, call, &msg, size, NULL);
+}
+
 /**
  * rxrpc_kernel_send_data - Allow a kernel service to send data on a call
  * @sock: The socket the call is on