162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* In-kernel rxperf server for testing purposes.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2022 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) "rxperf: " fmt
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <net/sock.h>
1262306a36Sopenharmony_ci#include <net/af_rxrpc.h>
1362306a36Sopenharmony_ci#define RXRPC_TRACE_ONLY_DEFINE_ENUMS
1462306a36Sopenharmony_ci#include <trace/events/rxrpc.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciMODULE_DESCRIPTION("rxperf test server (afs)");
1762306a36Sopenharmony_ciMODULE_AUTHOR("Red Hat, Inc.");
1862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define RXPERF_PORT		7009
2162306a36Sopenharmony_ci#define RX_PERF_SERVICE		147
2262306a36Sopenharmony_ci#define RX_PERF_VERSION		3
2362306a36Sopenharmony_ci#define RX_PERF_SEND		0
2462306a36Sopenharmony_ci#define RX_PERF_RECV		1
2562306a36Sopenharmony_ci#define RX_PERF_RPC		3
2662306a36Sopenharmony_ci#define RX_PERF_FILE		4
2762306a36Sopenharmony_ci#define RX_PERF_MAGIC_COOKIE	0x4711
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct rxperf_proto_params {
3062306a36Sopenharmony_ci	__be32		version;
3162306a36Sopenharmony_ci	__be32		type;
3262306a36Sopenharmony_ci	__be32		rsize;
3362306a36Sopenharmony_ci	__be32		wsize;
3462306a36Sopenharmony_ci} __packed;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic const u8 rxperf_magic_cookie[] = { 0x00, 0x00, 0x47, 0x11 };
3762306a36Sopenharmony_cistatic const u8 secret[8] = { 0xa7, 0x83, 0x8a, 0xcb, 0xc7, 0x83, 0xec, 0x94 };
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cienum rxperf_call_state {
4062306a36Sopenharmony_ci	RXPERF_CALL_SV_AWAIT_PARAMS,	/* Server: Awaiting parameter block */
4162306a36Sopenharmony_ci	RXPERF_CALL_SV_AWAIT_REQUEST,	/* Server: Awaiting request data */
4262306a36Sopenharmony_ci	RXPERF_CALL_SV_REPLYING,	/* Server: Replying */
4362306a36Sopenharmony_ci	RXPERF_CALL_SV_AWAIT_ACK,	/* Server: Awaiting final ACK */
4462306a36Sopenharmony_ci	RXPERF_CALL_COMPLETE,		/* Completed or failed */
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct rxperf_call {
4862306a36Sopenharmony_ci	struct rxrpc_call	*rxcall;
4962306a36Sopenharmony_ci	struct iov_iter		iter;
5062306a36Sopenharmony_ci	struct kvec		kvec[1];
5162306a36Sopenharmony_ci	struct work_struct	work;
5262306a36Sopenharmony_ci	const char		*type;
5362306a36Sopenharmony_ci	size_t			iov_len;
5462306a36Sopenharmony_ci	size_t			req_len;	/* Size of request blob */
5562306a36Sopenharmony_ci	size_t			reply_len;	/* Size of reply blob */
5662306a36Sopenharmony_ci	unsigned int		debug_id;
5762306a36Sopenharmony_ci	unsigned int		operation_id;
5862306a36Sopenharmony_ci	struct rxperf_proto_params params;
5962306a36Sopenharmony_ci	__be32			tmp[2];
6062306a36Sopenharmony_ci	s32			abort_code;
6162306a36Sopenharmony_ci	enum rxperf_call_state	state;
6262306a36Sopenharmony_ci	short			error;
6362306a36Sopenharmony_ci	unsigned short		unmarshal;
6462306a36Sopenharmony_ci	u16			service_id;
6562306a36Sopenharmony_ci	int (*deliver)(struct rxperf_call *call);
6662306a36Sopenharmony_ci	void (*processor)(struct work_struct *work);
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic struct socket *rxperf_socket;
7062306a36Sopenharmony_cistatic struct key *rxperf_sec_keyring;	/* Ring of security/crypto keys */
7162306a36Sopenharmony_cistatic struct workqueue_struct *rxperf_workqueue;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void rxperf_deliver_to_call(struct work_struct *work);
7462306a36Sopenharmony_cistatic int rxperf_deliver_param_block(struct rxperf_call *call);
7562306a36Sopenharmony_cistatic int rxperf_deliver_request(struct rxperf_call *call);
7662306a36Sopenharmony_cistatic int rxperf_process_call(struct rxperf_call *call);
7762306a36Sopenharmony_cistatic void rxperf_charge_preallocation(struct work_struct *work);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic DECLARE_WORK(rxperf_charge_preallocation_work,
8062306a36Sopenharmony_ci		    rxperf_charge_preallocation);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline void rxperf_set_call_state(struct rxperf_call *call,
8362306a36Sopenharmony_ci					 enum rxperf_call_state to)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	call->state = to;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic inline void rxperf_set_call_complete(struct rxperf_call *call,
8962306a36Sopenharmony_ci					    int error, s32 remote_abort)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	if (call->state != RXPERF_CALL_COMPLETE) {
9262306a36Sopenharmony_ci		call->abort_code = remote_abort;
9362306a36Sopenharmony_ci		call->error = error;
9462306a36Sopenharmony_ci		call->state = RXPERF_CALL_COMPLETE;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void rxperf_rx_discard_new_call(struct rxrpc_call *rxcall,
9962306a36Sopenharmony_ci				       unsigned long user_call_ID)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	kfree((struct rxperf_call *)user_call_ID);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void rxperf_rx_new_call(struct sock *sk, struct rxrpc_call *rxcall,
10562306a36Sopenharmony_ci			       unsigned long user_call_ID)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	queue_work(rxperf_workqueue, &rxperf_charge_preallocation_work);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void rxperf_queue_call_work(struct rxperf_call *call)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	queue_work(rxperf_workqueue, &call->work);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic void rxperf_notify_rx(struct sock *sk, struct rxrpc_call *rxcall,
11662306a36Sopenharmony_ci			     unsigned long call_user_ID)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct rxperf_call *call = (struct rxperf_call *)call_user_ID;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (call->state != RXPERF_CALL_COMPLETE)
12162306a36Sopenharmony_ci		rxperf_queue_call_work(call);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void rxperf_rx_attach(struct rxrpc_call *rxcall, unsigned long user_call_ID)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct rxperf_call *call = (struct rxperf_call *)user_call_ID;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	call->rxcall = rxcall;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void rxperf_notify_end_reply_tx(struct sock *sock,
13262306a36Sopenharmony_ci				       struct rxrpc_call *rxcall,
13362306a36Sopenharmony_ci				       unsigned long call_user_ID)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	rxperf_set_call_state((struct rxperf_call *)call_user_ID,
13662306a36Sopenharmony_ci			      RXPERF_CALL_SV_AWAIT_ACK);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/*
14062306a36Sopenharmony_ci * Charge the incoming call preallocation.
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_cistatic void rxperf_charge_preallocation(struct work_struct *work)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct rxperf_call *call;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	for (;;) {
14762306a36Sopenharmony_ci		call = kzalloc(sizeof(*call), GFP_KERNEL);
14862306a36Sopenharmony_ci		if (!call)
14962306a36Sopenharmony_ci			break;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		call->type		= "unset";
15262306a36Sopenharmony_ci		call->debug_id		= atomic_inc_return(&rxrpc_debug_id);
15362306a36Sopenharmony_ci		call->deliver		= rxperf_deliver_param_block;
15462306a36Sopenharmony_ci		call->state		= RXPERF_CALL_SV_AWAIT_PARAMS;
15562306a36Sopenharmony_ci		call->service_id	= RX_PERF_SERVICE;
15662306a36Sopenharmony_ci		call->iov_len		= sizeof(call->params);
15762306a36Sopenharmony_ci		call->kvec[0].iov_len	= sizeof(call->params);
15862306a36Sopenharmony_ci		call->kvec[0].iov_base	= &call->params;
15962306a36Sopenharmony_ci		iov_iter_kvec(&call->iter, READ, call->kvec, 1, call->iov_len);
16062306a36Sopenharmony_ci		INIT_WORK(&call->work, rxperf_deliver_to_call);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		if (rxrpc_kernel_charge_accept(rxperf_socket,
16362306a36Sopenharmony_ci					       rxperf_notify_rx,
16462306a36Sopenharmony_ci					       rxperf_rx_attach,
16562306a36Sopenharmony_ci					       (unsigned long)call,
16662306a36Sopenharmony_ci					       GFP_KERNEL,
16762306a36Sopenharmony_ci					       call->debug_id) < 0)
16862306a36Sopenharmony_ci			break;
16962306a36Sopenharmony_ci		call = NULL;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	kfree(call);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/*
17662306a36Sopenharmony_ci * Open an rxrpc socket and bind it to be a server for callback notifications
17762306a36Sopenharmony_ci * - the socket is left in blocking mode and non-blocking ops use MSG_DONTWAIT
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_cistatic int rxperf_open_socket(void)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct sockaddr_rxrpc srx;
18262306a36Sopenharmony_ci	struct socket *socket;
18362306a36Sopenharmony_ci	int ret;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	ret = sock_create_kern(&init_net, AF_RXRPC, SOCK_DGRAM, PF_INET6,
18662306a36Sopenharmony_ci			       &socket);
18762306a36Sopenharmony_ci	if (ret < 0)
18862306a36Sopenharmony_ci		goto error_1;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	socket->sk->sk_allocation = GFP_NOFS;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/* bind the callback manager's address to make this a server socket */
19362306a36Sopenharmony_ci	memset(&srx, 0, sizeof(srx));
19462306a36Sopenharmony_ci	srx.srx_family			= AF_RXRPC;
19562306a36Sopenharmony_ci	srx.srx_service			= RX_PERF_SERVICE;
19662306a36Sopenharmony_ci	srx.transport_type		= SOCK_DGRAM;
19762306a36Sopenharmony_ci	srx.transport_len		= sizeof(srx.transport.sin6);
19862306a36Sopenharmony_ci	srx.transport.sin6.sin6_family	= AF_INET6;
19962306a36Sopenharmony_ci	srx.transport.sin6.sin6_port	= htons(RXPERF_PORT);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	ret = rxrpc_sock_set_min_security_level(socket->sk,
20262306a36Sopenharmony_ci						RXRPC_SECURITY_ENCRYPT);
20362306a36Sopenharmony_ci	if (ret < 0)
20462306a36Sopenharmony_ci		goto error_2;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	ret = rxrpc_sock_set_security_keyring(socket->sk, rxperf_sec_keyring);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	ret = kernel_bind(socket, (struct sockaddr *)&srx, sizeof(srx));
20962306a36Sopenharmony_ci	if (ret < 0)
21062306a36Sopenharmony_ci		goto error_2;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	rxrpc_kernel_new_call_notification(socket, rxperf_rx_new_call,
21362306a36Sopenharmony_ci					   rxperf_rx_discard_new_call);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ret = kernel_listen(socket, INT_MAX);
21662306a36Sopenharmony_ci	if (ret < 0)
21762306a36Sopenharmony_ci		goto error_2;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	rxperf_socket = socket;
22062306a36Sopenharmony_ci	rxperf_charge_preallocation(&rxperf_charge_preallocation_work);
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cierror_2:
22462306a36Sopenharmony_ci	sock_release(socket);
22562306a36Sopenharmony_cierror_1:
22662306a36Sopenharmony_ci	pr_err("Can't set up rxperf socket: %d\n", ret);
22762306a36Sopenharmony_ci	return ret;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/*
23162306a36Sopenharmony_ci * close the rxrpc socket rxperf was using
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cistatic void rxperf_close_socket(void)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	kernel_listen(rxperf_socket, 0);
23662306a36Sopenharmony_ci	kernel_sock_shutdown(rxperf_socket, SHUT_RDWR);
23762306a36Sopenharmony_ci	flush_workqueue(rxperf_workqueue);
23862306a36Sopenharmony_ci	sock_release(rxperf_socket);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/*
24262306a36Sopenharmony_ci * Log remote abort codes that indicate that we have a protocol disagreement
24362306a36Sopenharmony_ci * with the server.
24462306a36Sopenharmony_ci */
24562306a36Sopenharmony_cistatic void rxperf_log_error(struct rxperf_call *call, s32 remote_abort)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	static int max = 0;
24862306a36Sopenharmony_ci	const char *msg;
24962306a36Sopenharmony_ci	int m;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	switch (remote_abort) {
25262306a36Sopenharmony_ci	case RX_EOF:		 msg = "unexpected EOF";	break;
25362306a36Sopenharmony_ci	case RXGEN_CC_MARSHAL:	 msg = "client marshalling";	break;
25462306a36Sopenharmony_ci	case RXGEN_CC_UNMARSHAL: msg = "client unmarshalling";	break;
25562306a36Sopenharmony_ci	case RXGEN_SS_MARSHAL:	 msg = "server marshalling";	break;
25662306a36Sopenharmony_ci	case RXGEN_SS_UNMARSHAL: msg = "server unmarshalling";	break;
25762306a36Sopenharmony_ci	case RXGEN_DECODE:	 msg = "opcode decode";		break;
25862306a36Sopenharmony_ci	case RXGEN_SS_XDRFREE:	 msg = "server XDR cleanup";	break;
25962306a36Sopenharmony_ci	case RXGEN_CC_XDRFREE:	 msg = "client XDR cleanup";	break;
26062306a36Sopenharmony_ci	case -32:		 msg = "insufficient data";	break;
26162306a36Sopenharmony_ci	default:
26262306a36Sopenharmony_ci		return;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	m = max;
26662306a36Sopenharmony_ci	if (m < 3) {
26762306a36Sopenharmony_ci		max = m + 1;
26862306a36Sopenharmony_ci		pr_info("Peer reported %s failure on %s\n", msg, call->type);
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/*
27362306a36Sopenharmony_ci * deliver messages to a call
27462306a36Sopenharmony_ci */
27562306a36Sopenharmony_cistatic void rxperf_deliver_to_call(struct work_struct *work)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct rxperf_call *call = container_of(work, struct rxperf_call, work);
27862306a36Sopenharmony_ci	enum rxperf_call_state state;
27962306a36Sopenharmony_ci	u32 abort_code, remote_abort = 0;
28062306a36Sopenharmony_ci	int ret = 0;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (call->state == RXPERF_CALL_COMPLETE)
28362306a36Sopenharmony_ci		return;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	while (state = call->state,
28662306a36Sopenharmony_ci	       state == RXPERF_CALL_SV_AWAIT_PARAMS ||
28762306a36Sopenharmony_ci	       state == RXPERF_CALL_SV_AWAIT_REQUEST ||
28862306a36Sopenharmony_ci	       state == RXPERF_CALL_SV_AWAIT_ACK
28962306a36Sopenharmony_ci	       ) {
29062306a36Sopenharmony_ci		if (state == RXPERF_CALL_SV_AWAIT_ACK) {
29162306a36Sopenharmony_ci			if (!rxrpc_kernel_check_life(rxperf_socket, call->rxcall))
29262306a36Sopenharmony_ci				goto call_complete;
29362306a36Sopenharmony_ci			return;
29462306a36Sopenharmony_ci		}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		ret = call->deliver(call);
29762306a36Sopenharmony_ci		if (ret == 0)
29862306a36Sopenharmony_ci			ret = rxperf_process_call(call);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		switch (ret) {
30162306a36Sopenharmony_ci		case 0:
30262306a36Sopenharmony_ci			continue;
30362306a36Sopenharmony_ci		case -EINPROGRESS:
30462306a36Sopenharmony_ci		case -EAGAIN:
30562306a36Sopenharmony_ci			return;
30662306a36Sopenharmony_ci		case -ECONNABORTED:
30762306a36Sopenharmony_ci			rxperf_log_error(call, call->abort_code);
30862306a36Sopenharmony_ci			goto call_complete;
30962306a36Sopenharmony_ci		case -EOPNOTSUPP:
31062306a36Sopenharmony_ci			abort_code = RXGEN_OPCODE;
31162306a36Sopenharmony_ci			rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
31262306a36Sopenharmony_ci						abort_code, ret,
31362306a36Sopenharmony_ci						rxperf_abort_op_not_supported);
31462306a36Sopenharmony_ci			goto call_complete;
31562306a36Sopenharmony_ci		case -ENOTSUPP:
31662306a36Sopenharmony_ci			abort_code = RX_USER_ABORT;
31762306a36Sopenharmony_ci			rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
31862306a36Sopenharmony_ci						abort_code, ret,
31962306a36Sopenharmony_ci						rxperf_abort_op_not_supported);
32062306a36Sopenharmony_ci			goto call_complete;
32162306a36Sopenharmony_ci		case -EIO:
32262306a36Sopenharmony_ci			pr_err("Call %u in bad state %u\n",
32362306a36Sopenharmony_ci			       call->debug_id, call->state);
32462306a36Sopenharmony_ci			fallthrough;
32562306a36Sopenharmony_ci		case -ENODATA:
32662306a36Sopenharmony_ci		case -EBADMSG:
32762306a36Sopenharmony_ci		case -EMSGSIZE:
32862306a36Sopenharmony_ci		case -ENOMEM:
32962306a36Sopenharmony_ci		case -EFAULT:
33062306a36Sopenharmony_ci			rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
33162306a36Sopenharmony_ci						RXGEN_SS_UNMARSHAL, ret,
33262306a36Sopenharmony_ci						rxperf_abort_unmarshal_error);
33362306a36Sopenharmony_ci			goto call_complete;
33462306a36Sopenharmony_ci		default:
33562306a36Sopenharmony_ci			rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
33662306a36Sopenharmony_ci						RX_CALL_DEAD, ret,
33762306a36Sopenharmony_ci						rxperf_abort_general_error);
33862306a36Sopenharmony_ci			goto call_complete;
33962306a36Sopenharmony_ci		}
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cicall_complete:
34362306a36Sopenharmony_ci	rxperf_set_call_complete(call, ret, remote_abort);
34462306a36Sopenharmony_ci	/* The call may have been requeued */
34562306a36Sopenharmony_ci	rxrpc_kernel_shutdown_call(rxperf_socket, call->rxcall);
34662306a36Sopenharmony_ci	rxrpc_kernel_put_call(rxperf_socket, call->rxcall);
34762306a36Sopenharmony_ci	cancel_work(&call->work);
34862306a36Sopenharmony_ci	kfree(call);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci/*
35262306a36Sopenharmony_ci * Extract a piece of data from the received data socket buffers.
35362306a36Sopenharmony_ci */
35462306a36Sopenharmony_cistatic int rxperf_extract_data(struct rxperf_call *call, bool want_more)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	u32 remote_abort = 0;
35762306a36Sopenharmony_ci	int ret;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	ret = rxrpc_kernel_recv_data(rxperf_socket, call->rxcall, &call->iter,
36062306a36Sopenharmony_ci				     &call->iov_len, want_more, &remote_abort,
36162306a36Sopenharmony_ci				     &call->service_id);
36262306a36Sopenharmony_ci	pr_debug("Extract i=%zu l=%zu m=%u ret=%d\n",
36362306a36Sopenharmony_ci		 iov_iter_count(&call->iter), call->iov_len, want_more, ret);
36462306a36Sopenharmony_ci	if (ret == 0 || ret == -EAGAIN)
36562306a36Sopenharmony_ci		return ret;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (ret == 1) {
36862306a36Sopenharmony_ci		switch (call->state) {
36962306a36Sopenharmony_ci		case RXPERF_CALL_SV_AWAIT_REQUEST:
37062306a36Sopenharmony_ci			rxperf_set_call_state(call, RXPERF_CALL_SV_REPLYING);
37162306a36Sopenharmony_ci			break;
37262306a36Sopenharmony_ci		case RXPERF_CALL_COMPLETE:
37362306a36Sopenharmony_ci			pr_debug("premature completion %d", call->error);
37462306a36Sopenharmony_ci			return call->error;
37562306a36Sopenharmony_ci		default:
37662306a36Sopenharmony_ci			break;
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci		return 0;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	rxperf_set_call_complete(call, ret, remote_abort);
38262306a36Sopenharmony_ci	return ret;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci/*
38662306a36Sopenharmony_ci * Grab the operation ID from an incoming manager call.
38762306a36Sopenharmony_ci */
38862306a36Sopenharmony_cistatic int rxperf_deliver_param_block(struct rxperf_call *call)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	u32 version;
39162306a36Sopenharmony_ci	int ret;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* Extract the parameter block */
39462306a36Sopenharmony_ci	ret = rxperf_extract_data(call, true);
39562306a36Sopenharmony_ci	if (ret < 0)
39662306a36Sopenharmony_ci		return ret;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	version			= ntohl(call->params.version);
39962306a36Sopenharmony_ci	call->operation_id	= ntohl(call->params.type);
40062306a36Sopenharmony_ci	call->deliver		= rxperf_deliver_request;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (version != RX_PERF_VERSION) {
40362306a36Sopenharmony_ci		pr_info("Version mismatch %x\n", version);
40462306a36Sopenharmony_ci		return -ENOTSUPP;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	switch (call->operation_id) {
40862306a36Sopenharmony_ci	case RX_PERF_SEND:
40962306a36Sopenharmony_ci		call->type = "send";
41062306a36Sopenharmony_ci		call->reply_len = 0;
41162306a36Sopenharmony_ci		call->iov_len = 4;	/* Expect req size */
41262306a36Sopenharmony_ci		break;
41362306a36Sopenharmony_ci	case RX_PERF_RECV:
41462306a36Sopenharmony_ci		call->type = "recv";
41562306a36Sopenharmony_ci		call->req_len = 0;
41662306a36Sopenharmony_ci		call->iov_len = 4;	/* Expect reply size */
41762306a36Sopenharmony_ci		break;
41862306a36Sopenharmony_ci	case RX_PERF_RPC:
41962306a36Sopenharmony_ci		call->type = "rpc";
42062306a36Sopenharmony_ci		call->iov_len = 8;	/* Expect req size and reply size */
42162306a36Sopenharmony_ci		break;
42262306a36Sopenharmony_ci	case RX_PERF_FILE:
42362306a36Sopenharmony_ci		call->type = "file";
42462306a36Sopenharmony_ci		fallthrough;
42562306a36Sopenharmony_ci	default:
42662306a36Sopenharmony_ci		return -EOPNOTSUPP;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	rxperf_set_call_state(call, RXPERF_CALL_SV_AWAIT_REQUEST);
43062306a36Sopenharmony_ci	return call->deliver(call);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci/*
43462306a36Sopenharmony_ci * Deliver the request data.
43562306a36Sopenharmony_ci */
43662306a36Sopenharmony_cistatic int rxperf_deliver_request(struct rxperf_call *call)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	int ret;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	switch (call->unmarshal) {
44162306a36Sopenharmony_ci	case 0:
44262306a36Sopenharmony_ci		call->kvec[0].iov_len	= call->iov_len;
44362306a36Sopenharmony_ci		call->kvec[0].iov_base	= call->tmp;
44462306a36Sopenharmony_ci		iov_iter_kvec(&call->iter, READ, call->kvec, 1, call->iov_len);
44562306a36Sopenharmony_ci		call->unmarshal++;
44662306a36Sopenharmony_ci		fallthrough;
44762306a36Sopenharmony_ci	case 1:
44862306a36Sopenharmony_ci		ret = rxperf_extract_data(call, true);
44962306a36Sopenharmony_ci		if (ret < 0)
45062306a36Sopenharmony_ci			return ret;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		switch (call->operation_id) {
45362306a36Sopenharmony_ci		case RX_PERF_SEND:
45462306a36Sopenharmony_ci			call->type = "send";
45562306a36Sopenharmony_ci			call->req_len	= ntohl(call->tmp[0]);
45662306a36Sopenharmony_ci			call->reply_len	= 0;
45762306a36Sopenharmony_ci			break;
45862306a36Sopenharmony_ci		case RX_PERF_RECV:
45962306a36Sopenharmony_ci			call->type = "recv";
46062306a36Sopenharmony_ci			call->req_len = 0;
46162306a36Sopenharmony_ci			call->reply_len	= ntohl(call->tmp[0]);
46262306a36Sopenharmony_ci			break;
46362306a36Sopenharmony_ci		case RX_PERF_RPC:
46462306a36Sopenharmony_ci			call->type = "rpc";
46562306a36Sopenharmony_ci			call->req_len	= ntohl(call->tmp[0]);
46662306a36Sopenharmony_ci			call->reply_len	= ntohl(call->tmp[1]);
46762306a36Sopenharmony_ci			break;
46862306a36Sopenharmony_ci		default:
46962306a36Sopenharmony_ci			pr_info("Can't parse extra params\n");
47062306a36Sopenharmony_ci			return -EIO;
47162306a36Sopenharmony_ci		}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		pr_debug("CALL op=%s rq=%zx rp=%zx\n",
47462306a36Sopenharmony_ci			 call->type, call->req_len, call->reply_len);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		call->iov_len = call->req_len;
47762306a36Sopenharmony_ci		iov_iter_discard(&call->iter, READ, call->req_len);
47862306a36Sopenharmony_ci		call->unmarshal++;
47962306a36Sopenharmony_ci		fallthrough;
48062306a36Sopenharmony_ci	case 2:
48162306a36Sopenharmony_ci		ret = rxperf_extract_data(call, false);
48262306a36Sopenharmony_ci		if (ret < 0)
48362306a36Sopenharmony_ci			return ret;
48462306a36Sopenharmony_ci		call->unmarshal++;
48562306a36Sopenharmony_ci		fallthrough;
48662306a36Sopenharmony_ci	default:
48762306a36Sopenharmony_ci		return 0;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci/*
49262306a36Sopenharmony_ci * Process a call for which we've received the request.
49362306a36Sopenharmony_ci */
49462306a36Sopenharmony_cistatic int rxperf_process_call(struct rxperf_call *call)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct msghdr msg = {};
49762306a36Sopenharmony_ci	struct bio_vec bv;
49862306a36Sopenharmony_ci	struct kvec iov[1];
49962306a36Sopenharmony_ci	ssize_t n;
50062306a36Sopenharmony_ci	size_t reply_len = call->reply_len, len;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	rxrpc_kernel_set_tx_length(rxperf_socket, call->rxcall,
50362306a36Sopenharmony_ci				   reply_len + sizeof(rxperf_magic_cookie));
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	while (reply_len > 0) {
50662306a36Sopenharmony_ci		len = min_t(size_t, reply_len, PAGE_SIZE);
50762306a36Sopenharmony_ci		bvec_set_page(&bv, ZERO_PAGE(0), len, 0);
50862306a36Sopenharmony_ci		iov_iter_bvec(&msg.msg_iter, WRITE, &bv, 1, len);
50962306a36Sopenharmony_ci		msg.msg_flags = MSG_MORE;
51062306a36Sopenharmony_ci		n = rxrpc_kernel_send_data(rxperf_socket, call->rxcall, &msg,
51162306a36Sopenharmony_ci					   len, rxperf_notify_end_reply_tx);
51262306a36Sopenharmony_ci		if (n < 0)
51362306a36Sopenharmony_ci			return n;
51462306a36Sopenharmony_ci		if (n == 0)
51562306a36Sopenharmony_ci			return -EIO;
51662306a36Sopenharmony_ci		reply_len -= n;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	len = sizeof(rxperf_magic_cookie);
52062306a36Sopenharmony_ci	iov[0].iov_base	= (void *)rxperf_magic_cookie;
52162306a36Sopenharmony_ci	iov[0].iov_len	= len;
52262306a36Sopenharmony_ci	iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, len);
52362306a36Sopenharmony_ci	msg.msg_flags = 0;
52462306a36Sopenharmony_ci	n = rxrpc_kernel_send_data(rxperf_socket, call->rxcall, &msg, len,
52562306a36Sopenharmony_ci				   rxperf_notify_end_reply_tx);
52662306a36Sopenharmony_ci	if (n >= 0)
52762306a36Sopenharmony_ci		return 0; /* Success */
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (n == -ENOMEM)
53062306a36Sopenharmony_ci		rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
53162306a36Sopenharmony_ci					RXGEN_SS_MARSHAL, -ENOMEM,
53262306a36Sopenharmony_ci					rxperf_abort_oom);
53362306a36Sopenharmony_ci	return n;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci/*
53762306a36Sopenharmony_ci * Add a key to the security keyring.
53862306a36Sopenharmony_ci */
53962306a36Sopenharmony_cistatic int rxperf_add_key(struct key *keyring)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	key_ref_t kref;
54262306a36Sopenharmony_ci	int ret;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	kref = key_create_or_update(make_key_ref(keyring, true),
54562306a36Sopenharmony_ci				    "rxrpc_s",
54662306a36Sopenharmony_ci				    __stringify(RX_PERF_SERVICE) ":2",
54762306a36Sopenharmony_ci				    secret,
54862306a36Sopenharmony_ci				    sizeof(secret),
54962306a36Sopenharmony_ci				    KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH
55062306a36Sopenharmony_ci				    | KEY_USR_VIEW,
55162306a36Sopenharmony_ci				    KEY_ALLOC_NOT_IN_QUOTA);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (IS_ERR(kref)) {
55462306a36Sopenharmony_ci		pr_err("Can't allocate rxperf server key: %ld\n", PTR_ERR(kref));
55562306a36Sopenharmony_ci		return PTR_ERR(kref);
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	ret = key_link(keyring, key_ref_to_ptr(kref));
55962306a36Sopenharmony_ci	if (ret < 0)
56062306a36Sopenharmony_ci		pr_err("Can't link rxperf server key: %d\n", ret);
56162306a36Sopenharmony_ci	key_ref_put(kref);
56262306a36Sopenharmony_ci	return ret;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci/*
56662306a36Sopenharmony_ci * Initialise the rxperf server.
56762306a36Sopenharmony_ci */
56862306a36Sopenharmony_cistatic int __init rxperf_init(void)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct key *keyring;
57162306a36Sopenharmony_ci	int ret = -ENOMEM;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	pr_info("Server registering\n");
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	rxperf_workqueue = alloc_workqueue("rxperf", 0, 0);
57662306a36Sopenharmony_ci	if (!rxperf_workqueue)
57762306a36Sopenharmony_ci		goto error_workqueue;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	keyring = keyring_alloc("rxperf_server",
58062306a36Sopenharmony_ci				GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
58162306a36Sopenharmony_ci				KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
58262306a36Sopenharmony_ci				KEY_POS_WRITE |
58362306a36Sopenharmony_ci				KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH |
58462306a36Sopenharmony_ci				KEY_USR_WRITE |
58562306a36Sopenharmony_ci				KEY_OTH_VIEW | KEY_OTH_READ | KEY_OTH_SEARCH,
58662306a36Sopenharmony_ci				KEY_ALLOC_NOT_IN_QUOTA,
58762306a36Sopenharmony_ci				NULL, NULL);
58862306a36Sopenharmony_ci	if (IS_ERR(keyring)) {
58962306a36Sopenharmony_ci		pr_err("Can't allocate rxperf server keyring: %ld\n",
59062306a36Sopenharmony_ci		       PTR_ERR(keyring));
59162306a36Sopenharmony_ci		goto error_keyring;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci	rxperf_sec_keyring = keyring;
59462306a36Sopenharmony_ci	ret = rxperf_add_key(keyring);
59562306a36Sopenharmony_ci	if (ret < 0)
59662306a36Sopenharmony_ci		goto error_key;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	ret = rxperf_open_socket();
59962306a36Sopenharmony_ci	if (ret < 0)
60062306a36Sopenharmony_ci		goto error_socket;
60162306a36Sopenharmony_ci	return 0;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cierror_socket:
60462306a36Sopenharmony_cierror_key:
60562306a36Sopenharmony_ci	key_put(rxperf_sec_keyring);
60662306a36Sopenharmony_cierror_keyring:
60762306a36Sopenharmony_ci	destroy_workqueue(rxperf_workqueue);
60862306a36Sopenharmony_ci	rcu_barrier();
60962306a36Sopenharmony_cierror_workqueue:
61062306a36Sopenharmony_ci	pr_err("Failed to register: %d\n", ret);
61162306a36Sopenharmony_ci	return ret;
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_cilate_initcall(rxperf_init); /* Must be called after net/ to create socket */
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic void __exit rxperf_exit(void)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	pr_info("Server unregistering.\n");
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	rxperf_close_socket();
62062306a36Sopenharmony_ci	key_put(rxperf_sec_keyring);
62162306a36Sopenharmony_ci	destroy_workqueue(rxperf_workqueue);
62262306a36Sopenharmony_ci	rcu_barrier();
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_cimodule_exit(rxperf_exit);
62562306a36Sopenharmony_ci
626