mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:04:41 +01:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
703 lines
18 KiB
C
703 lines
18 KiB
C
// 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 <crypto/krb5.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);
|
|
}
|
|
|
|
static const struct rxrpc_kernel_ops rxperf_rxrpc_callback_ops = {
|
|
.notify_new_call = rxperf_rx_new_call,
|
|
.discard_new_call = rxperf_rx_discard_new_call,
|
|
.user_attach_call = rxperf_rx_attach,
|
|
};
|
|
|
|
/*
|
|
* Charge the incoming call preallocation.
|
|
*/
|
|
static void rxperf_charge_preallocation(struct work_struct *work)
|
|
{
|
|
struct rxperf_call *call;
|
|
|
|
for (;;) {
|
|
call = kzalloc_obj(*call);
|
|
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,
|
|
(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_unsized *)&srx, sizeof(srx));
|
|
if (ret < 0)
|
|
goto error_2;
|
|
|
|
rxrpc_kernel_set_notifications(socket, &rxperf_rxrpc_callback_ops);
|
|
|
|
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 = umin(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 an rxkad key to the security keyring.
|
|
*/
|
|
static int rxperf_add_rxkad_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;
|
|
}
|
|
|
|
#ifdef CONFIG_RXGK
|
|
/*
|
|
* Add a yfs-rxgk key to the security keyring.
|
|
*/
|
|
static int rxperf_add_yfs_rxgk_key(struct key *keyring, u32 enctype)
|
|
{
|
|
const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(enctype);
|
|
key_ref_t kref;
|
|
char name[64];
|
|
int ret;
|
|
u8 key[32];
|
|
|
|
if (!krb5 || krb5->key_len > sizeof(key))
|
|
return 0;
|
|
|
|
/* The key is just { 0, 1, 2, 3, 4, ... } */
|
|
for (int i = 0; i < krb5->key_len; i++)
|
|
key[i] = i;
|
|
|
|
sprintf(name, "%u:6:1:%u", RX_PERF_SERVICE, enctype);
|
|
|
|
kref = key_create_or_update(make_key_ref(keyring, true),
|
|
"rxrpc_s", name,
|
|
key, krb5->key_len,
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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", WQ_PERCPU, 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_rxkad_key(keyring);
|
|
if (ret < 0)
|
|
goto error_key;
|
|
#ifdef CONFIG_RXGK
|
|
ret = rxperf_add_yfs_rxgk_key(keyring, KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96);
|
|
if (ret < 0)
|
|
goto error_key;
|
|
ret = rxperf_add_yfs_rxgk_key(keyring, KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96);
|
|
if (ret < 0)
|
|
goto error_key;
|
|
ret = rxperf_add_yfs_rxgk_key(keyring, KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128);
|
|
if (ret < 0)
|
|
goto error_key;
|
|
ret = rxperf_add_yfs_rxgk_key(keyring, KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192);
|
|
if (ret < 0)
|
|
goto error_key;
|
|
ret = rxperf_add_yfs_rxgk_key(keyring, KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC);
|
|
if (ret < 0)
|
|
goto error_key;
|
|
ret = rxperf_add_yfs_rxgk_key(keyring, KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC);
|
|
if (ret < 0)
|
|
goto error_key;
|
|
#endif
|
|
|
|
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);
|
|
|