| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /* In-kernel rxperf server for testing purposes.
- *
- * Copyright (C) 2022 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- */
- #define pr_fmt(fmt) "rxperf: " fmt
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <net/sock.h>
- #include <net/af_rxrpc.h>
- #define RXRPC_TRACE_ONLY_DEFINE_ENUMS
- #include <trace/events/rxrpc.h>
- MODULE_DESCRIPTION("rxperf test server (afs)");
- MODULE_AUTHOR("Red Hat, Inc.");
- MODULE_LICENSE("GPL");
- #define RXPERF_PORT 7009
- #define RX_PERF_SERVICE 147
- #define RX_PERF_VERSION 3
- #define RX_PERF_SEND 0
- #define RX_PERF_RECV 1
- #define RX_PERF_RPC 3
- #define RX_PERF_FILE 4
- #define RX_PERF_MAGIC_COOKIE 0x4711
- struct rxperf_proto_params {
- __be32 version;
- __be32 type;
- __be32 rsize;
- __be32 wsize;
- } __packed;
- static const u8 rxperf_magic_cookie[] = { 0x00, 0x00, 0x47, 0x11 };
- static const u8 secret[8] = { 0xa7, 0x83, 0x8a, 0xcb, 0xc7, 0x83, 0xec, 0x94 };
- enum rxperf_call_state {
- RXPERF_CALL_SV_AWAIT_PARAMS, /* Server: Awaiting parameter block */
- RXPERF_CALL_SV_AWAIT_REQUEST, /* Server: Awaiting request data */
- RXPERF_CALL_SV_REPLYING, /* Server: Replying */
- RXPERF_CALL_SV_AWAIT_ACK, /* Server: Awaiting final ACK */
- RXPERF_CALL_COMPLETE, /* Completed or failed */
- };
- struct rxperf_call {
- struct rxrpc_call *rxcall;
- struct iov_iter iter;
- struct kvec kvec[1];
- struct work_struct work;
- const char *type;
- size_t iov_len;
- size_t req_len; /* Size of request blob */
- size_t reply_len; /* Size of reply blob */
- unsigned int debug_id;
- unsigned int operation_id;
- struct rxperf_proto_params params;
- __be32 tmp[2];
- s32 abort_code;
- enum rxperf_call_state state;
- short error;
- unsigned short unmarshal;
- u16 service_id;
- int (*deliver)(struct rxperf_call *call);
- void (*processor)(struct work_struct *work);
- };
- static struct socket *rxperf_socket;
- static struct key *rxperf_sec_keyring; /* Ring of security/crypto keys */
- static struct workqueue_struct *rxperf_workqueue;
- static void rxperf_deliver_to_call(struct work_struct *work);
- static int rxperf_deliver_param_block(struct rxperf_call *call);
- static int rxperf_deliver_request(struct rxperf_call *call);
- static int rxperf_process_call(struct rxperf_call *call);
- static void rxperf_charge_preallocation(struct work_struct *work);
- static DECLARE_WORK(rxperf_charge_preallocation_work,
- rxperf_charge_preallocation);
- static inline void rxperf_set_call_state(struct rxperf_call *call,
- enum rxperf_call_state to)
- {
- call->state = to;
- }
- static inline void rxperf_set_call_complete(struct rxperf_call *call,
- int error, s32 remote_abort)
- {
- if (call->state != RXPERF_CALL_COMPLETE) {
- call->abort_code = remote_abort;
- call->error = error;
- call->state = RXPERF_CALL_COMPLETE;
- }
- }
- static void rxperf_rx_discard_new_call(struct rxrpc_call *rxcall,
- unsigned long user_call_ID)
- {
- kfree((struct rxperf_call *)user_call_ID);
- }
- static void rxperf_rx_new_call(struct sock *sk, struct rxrpc_call *rxcall,
- unsigned long user_call_ID)
- {
- queue_work(rxperf_workqueue, &rxperf_charge_preallocation_work);
- }
- static void rxperf_queue_call_work(struct rxperf_call *call)
- {
- queue_work(rxperf_workqueue, &call->work);
- }
- static void rxperf_notify_rx(struct sock *sk, struct rxrpc_call *rxcall,
- unsigned long call_user_ID)
- {
- struct rxperf_call *call = (struct rxperf_call *)call_user_ID;
- if (call->state != RXPERF_CALL_COMPLETE)
- rxperf_queue_call_work(call);
- }
- static void rxperf_rx_attach(struct rxrpc_call *rxcall, unsigned long user_call_ID)
- {
- struct rxperf_call *call = (struct rxperf_call *)user_call_ID;
- call->rxcall = rxcall;
- }
- static void rxperf_notify_end_reply_tx(struct sock *sock,
- struct rxrpc_call *rxcall,
- unsigned long call_user_ID)
- {
- rxperf_set_call_state((struct rxperf_call *)call_user_ID,
- RXPERF_CALL_SV_AWAIT_ACK);
- }
- /*
- * Charge the incoming call preallocation.
- */
- static void rxperf_charge_preallocation(struct work_struct *work)
- {
- struct rxperf_call *call;
- for (;;) {
- call = kzalloc(sizeof(*call), GFP_KERNEL);
- if (!call)
- break;
- call->type = "unset";
- call->debug_id = atomic_inc_return(&rxrpc_debug_id);
- call->deliver = rxperf_deliver_param_block;
- call->state = RXPERF_CALL_SV_AWAIT_PARAMS;
- call->service_id = RX_PERF_SERVICE;
- call->iov_len = sizeof(call->params);
- call->kvec[0].iov_len = sizeof(call->params);
- call->kvec[0].iov_base = &call->params;
- iov_iter_kvec(&call->iter, READ, call->kvec, 1, call->iov_len);
- INIT_WORK(&call->work, rxperf_deliver_to_call);
- if (rxrpc_kernel_charge_accept(rxperf_socket,
- rxperf_notify_rx,
- rxperf_rx_attach,
- (unsigned long)call,
- GFP_KERNEL,
- call->debug_id) < 0)
- break;
- call = NULL;
- }
- kfree(call);
- }
- /*
- * Open an rxrpc socket and bind it to be a server for callback notifications
- * - the socket is left in blocking mode and non-blocking ops use MSG_DONTWAIT
- */
- static int rxperf_open_socket(void)
- {
- struct sockaddr_rxrpc srx;
- struct socket *socket;
- int ret;
- ret = sock_create_kern(&init_net, AF_RXRPC, SOCK_DGRAM, PF_INET6,
- &socket);
- if (ret < 0)
- goto error_1;
- socket->sk->sk_allocation = GFP_NOFS;
- /* bind the callback manager's address to make this a server socket */
- memset(&srx, 0, sizeof(srx));
- srx.srx_family = AF_RXRPC;
- srx.srx_service = RX_PERF_SERVICE;
- srx.transport_type = SOCK_DGRAM;
- srx.transport_len = sizeof(srx.transport.sin6);
- srx.transport.sin6.sin6_family = AF_INET6;
- srx.transport.sin6.sin6_port = htons(RXPERF_PORT);
- ret = rxrpc_sock_set_min_security_level(socket->sk,
- RXRPC_SECURITY_ENCRYPT);
- if (ret < 0)
- goto error_2;
- ret = rxrpc_sock_set_security_keyring(socket->sk, rxperf_sec_keyring);
- ret = kernel_bind(socket, (struct sockaddr *)&srx, sizeof(srx));
- if (ret < 0)
- goto error_2;
- rxrpc_kernel_new_call_notification(socket, rxperf_rx_new_call,
- rxperf_rx_discard_new_call);
- ret = kernel_listen(socket, INT_MAX);
- if (ret < 0)
- goto error_2;
- rxperf_socket = socket;
- rxperf_charge_preallocation(&rxperf_charge_preallocation_work);
- return 0;
- error_2:
- sock_release(socket);
- error_1:
- pr_err("Can't set up rxperf socket: %d\n", ret);
- return ret;
- }
- /*
- * close the rxrpc socket rxperf was using
- */
- static void rxperf_close_socket(void)
- {
- kernel_listen(rxperf_socket, 0);
- kernel_sock_shutdown(rxperf_socket, SHUT_RDWR);
- flush_workqueue(rxperf_workqueue);
- sock_release(rxperf_socket);
- }
- /*
- * Log remote abort codes that indicate that we have a protocol disagreement
- * with the server.
- */
- static void rxperf_log_error(struct rxperf_call *call, s32 remote_abort)
- {
- static int max = 0;
- const char *msg;
- int m;
- switch (remote_abort) {
- case RX_EOF: msg = "unexpected EOF"; break;
- case RXGEN_CC_MARSHAL: msg = "client marshalling"; break;
- case RXGEN_CC_UNMARSHAL: msg = "client unmarshalling"; break;
- case RXGEN_SS_MARSHAL: msg = "server marshalling"; break;
- case RXGEN_SS_UNMARSHAL: msg = "server unmarshalling"; break;
- case RXGEN_DECODE: msg = "opcode decode"; break;
- case RXGEN_SS_XDRFREE: msg = "server XDR cleanup"; break;
- case RXGEN_CC_XDRFREE: msg = "client XDR cleanup"; break;
- case -32: msg = "insufficient data"; break;
- default:
- return;
- }
- m = max;
- if (m < 3) {
- max = m + 1;
- pr_info("Peer reported %s failure on %s\n", msg, call->type);
- }
- }
- /*
- * deliver messages to a call
- */
- static void rxperf_deliver_to_call(struct work_struct *work)
- {
- struct rxperf_call *call = container_of(work, struct rxperf_call, work);
- enum rxperf_call_state state;
- u32 abort_code, remote_abort = 0;
- int ret = 0;
- if (call->state == RXPERF_CALL_COMPLETE)
- return;
- while (state = call->state,
- state == RXPERF_CALL_SV_AWAIT_PARAMS ||
- state == RXPERF_CALL_SV_AWAIT_REQUEST ||
- state == RXPERF_CALL_SV_AWAIT_ACK
- ) {
- if (state == RXPERF_CALL_SV_AWAIT_ACK) {
- if (!rxrpc_kernel_check_life(rxperf_socket, call->rxcall))
- goto call_complete;
- return;
- }
- ret = call->deliver(call);
- if (ret == 0)
- ret = rxperf_process_call(call);
- switch (ret) {
- case 0:
- continue;
- case -EINPROGRESS:
- case -EAGAIN:
- return;
- case -ECONNABORTED:
- rxperf_log_error(call, call->abort_code);
- goto call_complete;
- case -EOPNOTSUPP:
- abort_code = RXGEN_OPCODE;
- rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
- abort_code, ret,
- rxperf_abort_op_not_supported);
- goto call_complete;
- case -ENOTSUPP:
- abort_code = RX_USER_ABORT;
- rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
- abort_code, ret,
- rxperf_abort_op_not_supported);
- goto call_complete;
- case -EIO:
- pr_err("Call %u in bad state %u\n",
- call->debug_id, call->state);
- fallthrough;
- case -ENODATA:
- case -EBADMSG:
- case -EMSGSIZE:
- case -ENOMEM:
- case -EFAULT:
- rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
- RXGEN_SS_UNMARSHAL, ret,
- rxperf_abort_unmarshal_error);
- goto call_complete;
- default:
- rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
- RX_CALL_DEAD, ret,
- rxperf_abort_general_error);
- goto call_complete;
- }
- }
- call_complete:
- rxperf_set_call_complete(call, ret, remote_abort);
- /* The call may have been requeued */
- rxrpc_kernel_shutdown_call(rxperf_socket, call->rxcall);
- rxrpc_kernel_put_call(rxperf_socket, call->rxcall);
- cancel_work(&call->work);
- kfree(call);
- }
- /*
- * Extract a piece of data from the received data socket buffers.
- */
- static int rxperf_extract_data(struct rxperf_call *call, bool want_more)
- {
- u32 remote_abort = 0;
- int ret;
- ret = rxrpc_kernel_recv_data(rxperf_socket, call->rxcall, &call->iter,
- &call->iov_len, want_more, &remote_abort,
- &call->service_id);
- pr_debug("Extract i=%zu l=%zu m=%u ret=%d\n",
- iov_iter_count(&call->iter), call->iov_len, want_more, ret);
- if (ret == 0 || ret == -EAGAIN)
- return ret;
- if (ret == 1) {
- switch (call->state) {
- case RXPERF_CALL_SV_AWAIT_REQUEST:
- rxperf_set_call_state(call, RXPERF_CALL_SV_REPLYING);
- break;
- case RXPERF_CALL_COMPLETE:
- pr_debug("premature completion %d", call->error);
- return call->error;
- default:
- break;
- }
- return 0;
- }
- rxperf_set_call_complete(call, ret, remote_abort);
- return ret;
- }
- /*
- * Grab the operation ID from an incoming manager call.
- */
- static int rxperf_deliver_param_block(struct rxperf_call *call)
- {
- u32 version;
- int ret;
- /* Extract the parameter block */
- ret = rxperf_extract_data(call, true);
- if (ret < 0)
- return ret;
- version = ntohl(call->params.version);
- call->operation_id = ntohl(call->params.type);
- call->deliver = rxperf_deliver_request;
- if (version != RX_PERF_VERSION) {
- pr_info("Version mismatch %x\n", version);
- return -ENOTSUPP;
- }
- switch (call->operation_id) {
- case RX_PERF_SEND:
- call->type = "send";
- call->reply_len = 0;
- call->iov_len = 4; /* Expect req size */
- break;
- case RX_PERF_RECV:
- call->type = "recv";
- call->req_len = 0;
- call->iov_len = 4; /* Expect reply size */
- break;
- case RX_PERF_RPC:
- call->type = "rpc";
- call->iov_len = 8; /* Expect req size and reply size */
- break;
- case RX_PERF_FILE:
- call->type = "file";
- fallthrough;
- default:
- return -EOPNOTSUPP;
- }
- rxperf_set_call_state(call, RXPERF_CALL_SV_AWAIT_REQUEST);
- return call->deliver(call);
- }
- /*
- * Deliver the request data.
- */
- static int rxperf_deliver_request(struct rxperf_call *call)
- {
- int ret;
- switch (call->unmarshal) {
- case 0:
- call->kvec[0].iov_len = call->iov_len;
- call->kvec[0].iov_base = call->tmp;
- iov_iter_kvec(&call->iter, READ, call->kvec, 1, call->iov_len);
- call->unmarshal++;
- fallthrough;
- case 1:
- ret = rxperf_extract_data(call, true);
- if (ret < 0)
- return ret;
- switch (call->operation_id) {
- case RX_PERF_SEND:
- call->type = "send";
- call->req_len = ntohl(call->tmp[0]);
- call->reply_len = 0;
- break;
- case RX_PERF_RECV:
- call->type = "recv";
- call->req_len = 0;
- call->reply_len = ntohl(call->tmp[0]);
- break;
- case RX_PERF_RPC:
- call->type = "rpc";
- call->req_len = ntohl(call->tmp[0]);
- call->reply_len = ntohl(call->tmp[1]);
- break;
- default:
- pr_info("Can't parse extra params\n");
- return -EIO;
- }
- pr_debug("CALL op=%s rq=%zx rp=%zx\n",
- call->type, call->req_len, call->reply_len);
- call->iov_len = call->req_len;
- iov_iter_discard(&call->iter, READ, call->req_len);
- call->unmarshal++;
- fallthrough;
- case 2:
- ret = rxperf_extract_data(call, true);
- if (ret < 0)
- return ret;
- /* Deal with the terminal magic cookie. */
- call->iov_len = 4;
- call->kvec[0].iov_len = call->iov_len;
- call->kvec[0].iov_base = call->tmp;
- iov_iter_kvec(&call->iter, READ, call->kvec, 1, call->iov_len);
- call->unmarshal++;
- fallthrough;
- case 3:
- ret = rxperf_extract_data(call, false);
- if (ret < 0)
- return ret;
- call->unmarshal++;
- fallthrough;
- default:
- return 0;
- }
- }
- /*
- * Process a call for which we've received the request.
- */
- static int rxperf_process_call(struct rxperf_call *call)
- {
- struct msghdr msg = {};
- struct bio_vec bv;
- struct kvec iov[1];
- ssize_t n;
- size_t reply_len = call->reply_len, len;
- rxrpc_kernel_set_tx_length(rxperf_socket, call->rxcall,
- reply_len + sizeof(rxperf_magic_cookie));
- while (reply_len > 0) {
- len = min_t(size_t, reply_len, PAGE_SIZE);
- bvec_set_page(&bv, ZERO_PAGE(0), len, 0);
- iov_iter_bvec(&msg.msg_iter, WRITE, &bv, 1, len);
- msg.msg_flags = MSG_MORE;
- n = rxrpc_kernel_send_data(rxperf_socket, call->rxcall, &msg,
- len, rxperf_notify_end_reply_tx);
- if (n < 0)
- return n;
- if (n == 0)
- return -EIO;
- reply_len -= n;
- }
- len = sizeof(rxperf_magic_cookie);
- iov[0].iov_base = (void *)rxperf_magic_cookie;
- iov[0].iov_len = len;
- iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, len);
- msg.msg_flags = 0;
- n = rxrpc_kernel_send_data(rxperf_socket, call->rxcall, &msg, len,
- rxperf_notify_end_reply_tx);
- if (n >= 0)
- return 0; /* Success */
- if (n == -ENOMEM)
- rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
- RXGEN_SS_MARSHAL, -ENOMEM,
- rxperf_abort_oom);
- return n;
- }
- /*
- * Add a key to the security keyring.
- */
- static int rxperf_add_key(struct key *keyring)
- {
- key_ref_t kref;
- int ret;
- kref = key_create_or_update(make_key_ref(keyring, true),
- "rxrpc_s",
- __stringify(RX_PERF_SERVICE) ":2",
- secret,
- sizeof(secret),
- KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH
- | KEY_USR_VIEW,
- KEY_ALLOC_NOT_IN_QUOTA);
- if (IS_ERR(kref)) {
- pr_err("Can't allocate rxperf server key: %ld\n", PTR_ERR(kref));
- return PTR_ERR(kref);
- }
- ret = key_link(keyring, key_ref_to_ptr(kref));
- if (ret < 0)
- pr_err("Can't link rxperf server key: %d\n", ret);
- key_ref_put(kref);
- return ret;
- }
- /*
- * Initialise the rxperf server.
- */
- static int __init rxperf_init(void)
- {
- struct key *keyring;
- int ret = -ENOMEM;
- pr_info("Server registering\n");
- rxperf_workqueue = alloc_workqueue("rxperf", 0, 0);
- if (!rxperf_workqueue)
- goto error_workqueue;
- keyring = keyring_alloc("rxperf_server",
- GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
- KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
- KEY_POS_WRITE |
- KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH |
- KEY_USR_WRITE |
- KEY_OTH_VIEW | KEY_OTH_READ | KEY_OTH_SEARCH,
- KEY_ALLOC_NOT_IN_QUOTA,
- NULL, NULL);
- if (IS_ERR(keyring)) {
- pr_err("Can't allocate rxperf server keyring: %ld\n",
- PTR_ERR(keyring));
- goto error_keyring;
- }
- rxperf_sec_keyring = keyring;
- ret = rxperf_add_key(keyring);
- if (ret < 0)
- goto error_key;
- ret = rxperf_open_socket();
- if (ret < 0)
- goto error_socket;
- return 0;
- error_socket:
- error_key:
- key_put(rxperf_sec_keyring);
- error_keyring:
- destroy_workqueue(rxperf_workqueue);
- rcu_barrier();
- error_workqueue:
- pr_err("Failed to register: %d\n", ret);
- return ret;
- }
- late_initcall(rxperf_init); /* Must be called after net/ to create socket */
- static void __exit rxperf_exit(void)
- {
- pr_info("Server unregistering.\n");
- rxperf_close_socket();
- key_put(rxperf_sec_keyring);
- destroy_workqueue(rxperf_workqueue);
- rcu_barrier();
- }
- module_exit(rxperf_exit);
|