Commit 2b7c4f7a authored by David S. Miller's avatar David S. Miller

Merge tag 'rxrpc-rewrite-20160622-2' of...

Merge tag 'rxrpc-rewrite-20160622-2' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

David Howells says:

====================
rxrpc: Get rid of conn bundle and transport structs

Here's the next part of the AF_RXRPC rewrite.  The primary purpose of this
set is to get rid of the rxrpc_conn_bundle and rxrpc_transport structs.
This simplifies things for future development of the connection handling.

To this end, the following significant changes are made:

 (1) The rxrpc_connection struct is given pointers to the local and peer
     endpoints, inside the rxrpc_conn_parameters struct.  Pointers to the
     transport's copy of these pointers are then redirected to the
     connection struct.

 (2) Exclusive connection handling is fixed.  Exclusive connections should
     do just one call and then be retired.  They are used in security
     negotiations and, I believe, the idea is to avoid reuse of negotiated
     security contexts.

     The current code is doing a single connection per socket and doing all
     the calls over that.  With this change it gets a new connection for
     each call made.

 (3) A new sendmsg() control message marker is added to make individual
     calls operate over exclusive connections.  This should be used in
     future in preference to the sockopt that marks a socket as "exclusive
     connection".

 (4) IDs for client connections initiated by a machine are now allocated
     from a global pool using the IDR facility and are unique across all
     client connections, no matter their destination.  The IDR facility is
     then used to look up a connection on the connection ID alone.  Other
     parameters are then verified afterwards.

     Note that the IDR facility may use a lot of memory if the IDs it holds
     are widely scattered.  Given this, in a future commit, client
     connections will be retired if they are more than a certain distance
     from the last ID allocated.

     The client epoch is advanced by 1 each time the client ID counter
     wraps.  Connections outside the current epoch will also be retired in
     a future commit.

 (5) The connection bundle concept is removed and the client connection
     tree is moved into the local endpoint.  The queue for waiting for a
     call channel is moved to the rxrpc_connection struct as there can only
     be one connection for any particular key going to any particular peer
     now.

 (6) The rxrpc_transport struct is removed and the service connection tree
     is moved into the peer struct.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents e7ffd812 aa390bbe
......@@ -35,7 +35,7 @@ struct sockaddr_rxrpc {
*/
#define RXRPC_SECURITY_KEY 1 /* [clnt] set client security key */
#define RXRPC_SECURITY_KEYRING 2 /* [srvr] set ring of server security keys */
#define RXRPC_EXCLUSIVE_CONNECTION 3 /* [clnt] use exclusive RxRPC connection */
#define RXRPC_EXCLUSIVE_CONNECTION 3 /* Deprecated; use RXRPC_EXCLUSIVE_CALL instead */
#define RXRPC_MIN_SECURITY_LEVEL 4 /* minimum security level */
/*
......@@ -52,6 +52,7 @@ struct sockaddr_rxrpc {
#define RXRPC_LOCAL_ERROR 7 /* -r: local error generated [terminal] */
#define RXRPC_NEW_CALL 8 /* -r: [Service] new incoming call notification */
#define RXRPC_ACCEPT 9 /* s-: [Service] accept request */
#define RXRPC_EXCLUSIVE_CALL 10 /* s-: Call should be on exclusive connection */
/*
* RxRPC security levels
......
......@@ -7,6 +7,7 @@ af-rxrpc-y := \
call_accept.o \
call_event.o \
call_object.o \
conn_client.o \
conn_event.o \
conn_object.o \
input.o \
......@@ -21,7 +22,6 @@ af-rxrpc-y := \
recvmsg.o \
security.o \
skbuff.o \
transport.o \
utils.o
af-rxrpc-$(CONFIG_PROC_FS) += proc.o
......
......@@ -97,7 +97,7 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx,
srx->transport_len > len)
return -EINVAL;
if (srx->transport.family != rx->proto)
if (srx->transport.family != rx->family)
return -EAFNOSUPPORT;
switch (srx->transport.family) {
......@@ -224,39 +224,6 @@ static int rxrpc_listen(struct socket *sock, int backlog)
return ret;
}
/*
* find a transport by address
*/
struct rxrpc_transport *rxrpc_name_to_transport(struct rxrpc_sock *rx,
struct sockaddr *addr,
int addr_len, int flags,
gfp_t gfp)
{
struct sockaddr_rxrpc *srx = (struct sockaddr_rxrpc *) addr;
struct rxrpc_transport *trans;
struct rxrpc_peer *peer;
_enter("%p,%p,%d,%d", rx, addr, addr_len, flags);
ASSERT(rx->local != NULL);
if (rx->srx.transport_type != srx->transport_type)
return ERR_PTR(-ESOCKTNOSUPPORT);
if (rx->srx.transport.family != srx->transport.family)
return ERR_PTR(-EAFNOSUPPORT);
/* find a remote transport endpoint from the local one */
peer = rxrpc_lookup_peer(rx->local, srx, gfp);
if (IS_ERR(peer))
return ERR_CAST(peer);
/* find a transport */
trans = rxrpc_get_transport(rx->local, peer, gfp);
rxrpc_put_peer(peer);
_leave(" = %p", trans);
return trans;
}
/**
* rxrpc_kernel_begin_call - Allow a kernel service to begin a call
* @sock: The socket on which to make the call
......@@ -277,39 +244,32 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
unsigned long user_call_ID,
gfp_t gfp)
{
struct rxrpc_conn_bundle *bundle;
struct rxrpc_transport *trans;
struct rxrpc_conn_parameters cp;
struct rxrpc_call *call;
struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
int ret;
_enter(",,%x,%lx", key_serial(key), user_call_ID);
lock_sock(&rx->sk);
ret = rxrpc_validate_address(rx, srx, sizeof(*srx));
if (ret < 0)
return ERR_PTR(ret);
trans = rxrpc_name_to_transport(rx, (struct sockaddr *)srx,
sizeof(*srx), 0, gfp);
if (IS_ERR(trans)) {
call = ERR_CAST(trans);
trans = NULL;
goto out_notrans;
}
lock_sock(&rx->sk);
if (!key)
key = rx->key;
if (key && !key->payload.data[0])
key = NULL; /* a no-security key */
bundle = rxrpc_get_bundle(rx, trans, key, srx->srx_service, gfp);
if (IS_ERR(bundle)) {
call = ERR_CAST(bundle);
goto out;
}
memset(&cp, 0, sizeof(cp));
cp.local = rx->local;
cp.key = key;
cp.security_level = 0;
cp.exclusive = false;
cp.service_id = srx->srx_service;
call = rxrpc_new_client_call(rx, &cp, srx, user_call_ID, gfp);
call = rxrpc_new_client_call(rx, trans, bundle, user_call_ID, gfp);
rxrpc_put_bundle(trans, bundle);
out:
rxrpc_put_transport(trans);
out_notrans:
release_sock(&rx->sk);
_leave(" = %p", call);
return call;
......@@ -487,7 +447,7 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname,
ret = -EISCONN;
if (rx->sk.sk_state != RXRPC_UNBOUND)
goto error;
set_bit(RXRPC_SOCK_EXCLUSIVE_CONN, &rx->flags);
rx->exclusive = true;
goto success;
case RXRPC_SECURITY_KEY:
......@@ -600,7 +560,7 @@ static int rxrpc_create(struct net *net, struct socket *sock, int protocol,
sk->sk_destruct = rxrpc_sock_destructor;
rx = rxrpc_sk(sk);
rx->proto = protocol;
rx->family = protocol;
rx->calls = RB_ROOT;
INIT_LIST_HEAD(&rx->listen_link);
......@@ -662,16 +622,8 @@ static int rxrpc_release_sock(struct sock *sk)
flush_workqueue(rxrpc_workqueue);
rxrpc_purge_queue(&sk->sk_receive_queue);
if (rx->conn) {
rxrpc_put_connection(rx->conn);
rx->conn = NULL;
}
if (rx->local) {
rxrpc_put_local(rx->local);
rx->local = NULL;
}
rxrpc_put_local(rx->local);
rx->local = NULL;
key_put(rx->key);
rx->key = NULL;
key_put(rx->securities);
......@@ -836,7 +788,6 @@ static void __exit af_rxrpc_exit(void)
proto_unregister(&rxrpc_proto);
rxrpc_destroy_all_calls();
rxrpc_destroy_all_connections();
rxrpc_destroy_all_transports();
ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0);
......@@ -856,6 +807,8 @@ static void __exit af_rxrpc_exit(void)
_debug("synchronise RCU");
rcu_barrier();
_debug("destroy locals");
ASSERT(idr_is_empty(&rxrpc_client_conn_ids));
idr_destroy(&rxrpc_client_conn_ids);
rxrpc_destroy_all_locals();
remove_proc_entry("rxrpc_conns", init_net.proc_net);
......
This diff is collapsed.
......@@ -74,7 +74,6 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
struct sockaddr_rxrpc *srx)
{
struct rxrpc_connection *conn;
struct rxrpc_transport *trans;
struct rxrpc_skb_priv *sp, *nsp;
struct rxrpc_peer *peer;
struct rxrpc_call *call;
......@@ -96,29 +95,21 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
notification->mark = RXRPC_SKB_MARK_NEW_CALL;
peer = rxrpc_lookup_peer(local, srx, GFP_NOIO);
if (IS_ERR(peer)) {
if (!peer) {
_debug("no peer");
ret = -EBUSY;
goto error;
}
trans = rxrpc_get_transport(local, peer, GFP_NOIO);
conn = rxrpc_incoming_connection(local, peer, skb);
rxrpc_put_peer(peer);
if (IS_ERR(trans)) {
_debug("no trans");
ret = -EBUSY;
goto error;
}
conn = rxrpc_incoming_connection(trans, &sp->hdr);
rxrpc_put_transport(trans);
if (IS_ERR(conn)) {
_debug("no conn");
ret = PTR_ERR(conn);
goto error;
}
call = rxrpc_incoming_call(rx, conn, &sp->hdr);
call = rxrpc_incoming_call(rx, conn, skb);
rxrpc_put_connection(conn);
if (IS_ERR(call)) {
_debug("no call");
......@@ -141,7 +132,7 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
_debug("await conn sec");
list_add_tail(&call->accept_link, &rx->secureq);
call->conn->state = RXRPC_CONN_SERVER_CHALLENGING;
atomic_inc(&call->conn->usage);
rxrpc_get_connection(call->conn);
set_bit(RXRPC_CONN_CHALLENGE, &call->conn->events);
rxrpc_queue_conn(call->conn);
} else {
......
......@@ -187,7 +187,7 @@ static void rxrpc_resend(struct rxrpc_call *call)
_proto("Tx DATA %%%u { #%d }",
sp->hdr.serial, sp->hdr.seq);
if (rxrpc_send_packet(call->conn->trans, txb) < 0) {
if (rxrpc_send_data_packet(call->conn, txb) < 0) {
stop = true;
sp->resend_at = jiffies + 3;
} else {
......@@ -545,7 +545,7 @@ static void rxrpc_extract_ackinfo(struct rxrpc_call *call, struct sk_buff *skb,
mtu = min(ntohl(ackinfo.rxMTU), ntohl(ackinfo.maxMTU));
peer = call->conn->trans->peer;
peer = call->conn->params.peer;
if (mtu < peer->maxdata) {
spin_lock_bh(&peer->lock);
peer->maxdata = mtu;
......@@ -836,13 +836,13 @@ void rxrpc_process_call(struct work_struct *work)
/* there's a good chance we're going to have to send a message, so set
* one up in advance */
msg.msg_name = &call->conn->trans->peer->srx.transport;
msg.msg_namelen = call->conn->trans->peer->srx.transport_len;
msg.msg_name = &call->conn->params.peer->srx.transport;
msg.msg_namelen = call->conn->params.peer->srx.transport_len;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
whdr.epoch = htonl(call->conn->epoch);
whdr.epoch = htonl(call->conn->proto.epoch);
whdr.cid = htonl(call->cid);
whdr.callNumber = htonl(call->call_id);
whdr.seq = 0;
......@@ -1151,8 +1151,8 @@ void rxrpc_process_call(struct work_struct *work)
ack.maxSkew = htons(atomic_read(&call->conn->hi_serial) -
ntohl(ack.serial));
send_ACK:
mtu = call->conn->trans->peer->if_mtu;
mtu -= call->conn->trans->peer->hdrsize;
mtu = call->conn->params.peer->if_mtu;
mtu -= call->conn->params.peer->hdrsize;
ackinfo.maxMTU = htonl(mtu);
ackinfo.rwind = htonl(rxrpc_rx_window_size);
......@@ -1206,7 +1206,7 @@ void rxrpc_process_call(struct work_struct *work)
len += iov[1].iov_len;
}
ret = kernel_sendmsg(call->conn->trans->local->socket,
ret = kernel_sendmsg(call->conn->params.local->socket,
&msg, iov, ioc, len);
if (ret < 0) {
_debug("sendmsg failed: %d", ret);
......@@ -1264,7 +1264,7 @@ void rxrpc_process_call(struct work_struct *work)
if (call->state >= RXRPC_CALL_COMPLETE &&
!list_empty(&call->accept_link)) {
_debug("X unlinking once-pending call %p { e=%lx f=%lx c=%x }",
call, call->events, call->flags, call->conn->cid);
call, call->events, call->flags, call->conn->proto.cid);
read_lock_bh(&call->state_lock);
if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) &&
......@@ -1282,7 +1282,7 @@ void rxrpc_process_call(struct work_struct *work)
* this means there's a race between clearing the flag and setting the
* work pending bit and the work item being processed again */
if (call->events && !work_pending(&call->processor)) {
_debug("jumpstart %x", call->conn->cid);
_debug("jumpstart %x", call->conn->proto.cid);
rxrpc_queue_call(call);
}
......
This diff is collapsed.
/* Client connection-specific management code.
*
* Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include <linux/idr.h>
#include <linux/timer.h>
#include "ar-internal.h"
/*
* We use machine-unique IDs for our client connections.
*/
DEFINE_IDR(rxrpc_client_conn_ids);
static DEFINE_SPINLOCK(rxrpc_conn_id_lock);
/*
* Get a connection ID and epoch for a client connection from the global pool.
* The connection struct pointer is then recorded in the idr radix tree. The
* epoch is changed if this wraps.
*
* TODO: The IDR tree gets very expensive on memory if the connection IDs are
* widely scattered throughout the number space, so we shall need to retire
* connections that have, say, an ID more than four times the maximum number of
* client conns away from the current allocation point to try and keep the IDs
* concentrated. We will also need to retire connections from an old epoch.
*/
int rxrpc_get_client_connection_id(struct rxrpc_connection *conn, gfp_t gfp)
{
u32 epoch;
int id;
_enter("");
idr_preload(gfp);
spin_lock(&rxrpc_conn_id_lock);
epoch = rxrpc_epoch;
/* We could use idr_alloc_cyclic() here, but we really need to know
* when the thing wraps so that we can advance the epoch.
*/
if (rxrpc_client_conn_ids.cur == 0)
rxrpc_client_conn_ids.cur = 1;
id = idr_alloc(&rxrpc_client_conn_ids, conn,
rxrpc_client_conn_ids.cur, 0x40000000, GFP_NOWAIT);
if (id < 0) {
if (id != -ENOSPC)
goto error;
id = idr_alloc(&rxrpc_client_conn_ids, conn,
1, 0x40000000, GFP_NOWAIT);
if (id < 0)
goto error;
epoch++;
rxrpc_epoch = epoch;
}
rxrpc_client_conn_ids.cur = id + 1;
spin_unlock(&rxrpc_conn_id_lock);
idr_preload_end();
conn->proto.epoch = epoch;
conn->proto.cid = id << RXRPC_CIDSHIFT;
set_bit(RXRPC_CONN_HAS_IDR, &conn->flags);
_leave(" [CID %x:%x]", epoch, conn->proto.cid);
return 0;
error:
spin_unlock(&rxrpc_conn_id_lock);
idr_preload_end();
_leave(" = %d", id);
return id;
}
/*
* Release a connection ID for a client connection from the global pool.
*/
void rxrpc_put_client_connection_id(struct rxrpc_connection *conn)
{
if (test_bit(RXRPC_CONN_HAS_IDR, &conn->flags)) {
spin_lock(&rxrpc_conn_id_lock);
idr_remove(&rxrpc_client_conn_ids,
conn->proto.cid >> RXRPC_CIDSHIFT);
spin_unlock(&rxrpc_conn_id_lock);
}
}
......@@ -88,14 +88,14 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn,
rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, abort_code);
msg.msg_name = &conn->trans->peer->srx.transport;
msg.msg_namelen = conn->trans->peer->srx.transport_len;
msg.msg_name = &conn->params.peer->srx.transport;
msg.msg_namelen = conn->params.peer->srx.transport_len;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
whdr.epoch = htonl(conn->epoch);
whdr.cid = htonl(conn->cid);
whdr.epoch = htonl(conn->proto.epoch);
whdr.cid = htonl(conn->proto.cid);
whdr.callNumber = 0;
whdr.seq = 0;
whdr.type = RXRPC_PACKET_TYPE_ABORT;
......@@ -103,7 +103,7 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn,
whdr.userStatus = 0;
whdr.securityIndex = conn->security_ix;
whdr._rsvd = 0;
whdr.serviceId = htons(conn->service_id);
whdr.serviceId = htons(conn->params.service_id);
word = htonl(conn->local_abort);
......@@ -118,7 +118,7 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn,
whdr.serial = htonl(serial);
_proto("Tx CONN ABORT %%%u { %d }", serial, conn->local_abort);
ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len);
ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len);
if (ret < 0) {
_debug("sendmsg failed: %d", ret);
return -EAGAIN;
......@@ -220,7 +220,7 @@ static void rxrpc_secure_connection(struct rxrpc_connection *conn)
ASSERT(conn->security_ix != 0);
if (!conn->key) {
if (!conn->params.key) {
_debug("set up security");
ret = rxrpc_init_server_conn_security(conn);
switch (ret) {
......@@ -263,7 +263,7 @@ void rxrpc_process_connection(struct work_struct *work)
_enter("{%d}", conn->debug_id);
atomic_inc(&conn->usage);
rxrpc_get_connection(conn);
if (test_and_clear_bit(RXRPC_CONN_CHALLENGE, &conn->events)) {
rxrpc_secure_connection(conn);
......
This diff is collapsed.
......@@ -360,7 +360,7 @@ void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb)
case RXRPC_PACKET_TYPE_BUSY:
_proto("Rx BUSY %%%u", sp->hdr.serial);
if (call->conn->out_clientflag)
if (rxrpc_conn_is_service(call->conn))
goto protocol_error;
write_lock_bh(&call->state_lock);
......@@ -533,7 +533,7 @@ static void rxrpc_post_packet_to_call(struct rxrpc_call *call,
case RXRPC_CALL_COMPLETE:
case RXRPC_CALL_CLIENT_FINAL_ACK:
/* complete server call */
if (call->conn->in_clientflag)
if (rxrpc_conn_is_service(call->conn))
goto dead_call;
/* resend last packet of a completed call */
_debug("final ack again");
......@@ -560,7 +560,7 @@ static void rxrpc_post_packet_to_call(struct rxrpc_call *call,
dead_call:
if (sp->hdr.type != RXRPC_PACKET_TYPE_ABORT) {
skb->priority = RX_CALL_DEAD;
rxrpc_reject_packet(call->conn->trans->local, skb);
rxrpc_reject_packet(call->conn->params.local, skb);
goto unlock;
}
free_unlock:
......@@ -580,7 +580,7 @@ static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn,
{
_enter("%p,%p", conn, skb);
atomic_inc(&conn->usage);
rxrpc_get_connection(conn);
skb_queue_tail(&conn->rx_queue, skb);
rxrpc_queue_conn(conn);
}
......@@ -628,27 +628,20 @@ int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb)
}
static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local,
struct sk_buff *skb,
struct rxrpc_skb_priv *sp)
struct sk_buff *skb)
{
struct rxrpc_peer *peer;
struct rxrpc_transport *trans;
struct rxrpc_connection *conn;
struct sockaddr_rxrpc srx;
rxrpc_get_addr_from_skb(local, skb, &srx);
rcu_read_lock();
peer = rxrpc_lookup_peer_rcu(local, &srx);
if (IS_ERR(peer))
if (!peer)
goto cant_find_peer;
trans = rxrpc_find_transport(local, peer);
conn = rxrpc_find_connection(local, peer, skb);
rcu_read_unlock();
if (!trans)
goto cant_find_conn;
conn = rxrpc_find_connection(trans, &sp->hdr);
rxrpc_put_transport(trans);
if (!conn)
goto cant_find_conn;
......@@ -739,7 +732,7 @@ void rxrpc_data_ready(struct sock *sk)
* old-fashioned way doesn't really hurt */
struct rxrpc_connection *conn;
conn = rxrpc_conn_from_local(local, skb, sp);
conn = rxrpc_conn_from_local(local, skb);
if (!conn)
goto cant_route_call;
......
......@@ -987,7 +987,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
if (ret < 0)
goto error;
conn->key = key;
conn->params.key = key;
_leave(" = 0 [%d]", key_serial(key));
return 0;
......
......@@ -80,7 +80,8 @@ static struct rxrpc_local *rxrpc_alloc_local(const struct sockaddr_rxrpc *srx)
skb_queue_head_init(&local->accept_queue);
skb_queue_head_init(&local->reject_queue);
skb_queue_head_init(&local->event_queue);
mutex_init(&local->conn_lock);
local->client_conns = RB_ROOT;
spin_lock_init(&local->client_conns_lock);
spin_lock_init(&local->lock);
rwlock_init(&local->services_lock);
local->debug_id = atomic_inc_return(&rxrpc_debug_id);
......@@ -209,7 +210,7 @@ struct rxrpc_local *rxrpc_lookup_local(const struct sockaddr_rxrpc *srx)
* bind the transport socket may still fail if we're attempting
* to use a local address that the dying object is still using.
*/
if (!atomic_inc_not_zero(&local->usage)) {
if (!rxrpc_get_local_maybe(local)) {
cursor = cursor->next;
list_del_init(&local->link);
break;
......@@ -294,6 +295,7 @@ static void rxrpc_local_destroyer(struct rxrpc_local *local)
list_del_init(&local->link);
mutex_unlock(&rxrpc_local_mutex);
ASSERT(RB_EMPTY_ROOT(&local->client_conns));
ASSERT(list_empty(&local->services));
if (socket) {
......
......@@ -35,7 +35,8 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
static int rxrpc_sendmsg_cmsg(struct msghdr *msg,
unsigned long *user_call_ID,
enum rxrpc_command *command,
u32 *abort_code)
u32 *abort_code,
bool *_exclusive)
{
struct cmsghdr *cmsg;
bool got_user_ID = false;
......@@ -93,6 +94,11 @@ static int rxrpc_sendmsg_cmsg(struct msghdr *msg,
return -EINVAL;
break;
case RXRPC_EXCLUSIVE_CALL:
*_exclusive = true;
if (len != 0)
return -EINVAL;
break;
default:
return -EINVAL;
}
......@@ -131,13 +137,11 @@ static void rxrpc_send_abort(struct rxrpc_call *call, u32 abort_code)
*/
static struct rxrpc_call *
rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg,
unsigned long user_call_ID)
unsigned long user_call_ID, bool exclusive)
{
struct rxrpc_conn_bundle *bundle;
struct rxrpc_transport *trans;
struct rxrpc_conn_parameters cp;
struct rxrpc_call *call;
struct key *key;
long ret;
DECLARE_SOCKADDR(struct sockaddr_rxrpc *, srx, msg->msg_name);
......@@ -146,39 +150,20 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg,
if (!msg->msg_name)
return ERR_PTR(-EDESTADDRREQ);
trans = rxrpc_name_to_transport(rx, msg->msg_name, msg->msg_namelen, 0,
GFP_KERNEL);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
goto out;
}
key = rx->key;
if (key && !rx->key->payload.data[0])
key = NULL;
bundle = rxrpc_get_bundle(rx, trans, key, srx->srx_service, GFP_KERNEL);
if (IS_ERR(bundle)) {
ret = PTR_ERR(bundle);
goto out_trans;
}
call = rxrpc_new_client_call(rx, trans, bundle, user_call_ID,
GFP_KERNEL);
rxrpc_put_bundle(trans, bundle);
rxrpc_put_transport(trans);
if (IS_ERR(call)) {
ret = PTR_ERR(call);
goto out_trans;
}
memset(&cp, 0, sizeof(cp));
cp.local = rx->local;
cp.key = rx->key;
cp.security_level = rx->min_sec_level;
cp.exclusive = rx->exclusive | exclusive;
cp.service_id = srx->srx_service;
call = rxrpc_new_client_call(rx, &cp, srx, user_call_ID, GFP_KERNEL);
_leave(" = %p\n", call);
return call;
out_trans:
rxrpc_put_transport(trans);
out:
_leave(" = %ld", ret);
return ERR_PTR(ret);
}
/*
......@@ -191,12 +176,14 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
enum rxrpc_command cmd;
struct rxrpc_call *call;
unsigned long user_call_ID = 0;
bool exclusive = false;
u32 abort_code = 0;
int ret;
_enter("");
ret = rxrpc_sendmsg_cmsg(msg, &user_call_ID, &cmd, &abort_code);
ret = rxrpc_sendmsg_cmsg(msg, &user_call_ID, &cmd, &abort_code,
&exclusive);
if (ret < 0)
return ret;
......@@ -214,7 +201,8 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
if (!call) {
if (cmd != RXRPC_CMD_SEND_DATA)
return -EBADSLT;
call = rxrpc_new_client_call_for_sendmsg(rx, msg, user_call_ID);
call = rxrpc_new_client_call_for_sendmsg(rx, msg, user_call_ID,
exclusive);
if (IS_ERR(call))
return PTR_ERR(call);
}
......@@ -319,7 +307,7 @@ EXPORT_SYMBOL(rxrpc_kernel_abort_call);
/*
* send a packet through the transport endpoint
*/
int rxrpc_send_packet(struct rxrpc_transport *trans, struct sk_buff *skb)
int rxrpc_send_data_packet(struct rxrpc_connection *conn, struct sk_buff *skb)
{
struct kvec iov[1];
struct msghdr msg;
......@@ -330,30 +318,30 @@ int rxrpc_send_packet(struct rxrpc_transport *trans, struct sk_buff *skb)
iov[0].iov_base = skb->head;
iov[0].iov_len = skb->len;
msg.msg_name = &trans->peer->srx.transport.sin;
msg.msg_namelen = sizeof(trans->peer->srx.transport.sin);
msg.msg_name = &conn->params.peer->srx.transport;
msg.msg_namelen = conn->params.peer->srx.transport_len;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
/* send the packet with the don't fragment bit set if we currently
* think it's small enough */
if (skb->len - sizeof(struct rxrpc_wire_header) < trans->peer->maxdata) {
down_read(&trans->local->defrag_sem);
if (skb->len - sizeof(struct rxrpc_wire_header) < conn->params.peer->maxdata) {
down_read(&conn->params.local->defrag_sem);
/* send the packet by UDP
* - returns -EMSGSIZE if UDP would have to fragment the packet
* to go out of the interface
* - in which case, we'll have processed the ICMP error
* message and update the peer record
*/
ret = kernel_sendmsg(trans->local->socket, &msg, iov, 1,
ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 1,
iov[0].iov_len);
up_read(&trans->local->defrag_sem);
up_read(&conn->params.local->defrag_sem);
if (ret == -EMSGSIZE)
goto send_fragmentable;
_leave(" = %d [%u]", ret, trans->peer->maxdata);
_leave(" = %d [%u]", ret, conn->params.peer->maxdata);
return ret;
}
......@@ -361,21 +349,28 @@ int rxrpc_send_packet(struct rxrpc_transport *trans, struct sk_buff *skb)
/* attempt to send this message with fragmentation enabled */
_debug("send fragment");
down_write(&trans->local->defrag_sem);
opt = IP_PMTUDISC_DONT;
ret = kernel_setsockopt(trans->local->socket, SOL_IP, IP_MTU_DISCOVER,
(char *) &opt, sizeof(opt));
if (ret == 0) {
ret = kernel_sendmsg(trans->local->socket, &msg, iov, 1,
iov[0].iov_len);
opt = IP_PMTUDISC_DO;
kernel_setsockopt(trans->local->socket, SOL_IP,
IP_MTU_DISCOVER, (char *) &opt, sizeof(opt));
down_write(&conn->params.local->defrag_sem);
switch (conn->params.local->srx.transport.family) {
case AF_INET:
opt = IP_PMTUDISC_DONT;
ret = kernel_setsockopt(conn->params.local->socket,
SOL_IP, IP_MTU_DISCOVER,
(char *)&opt, sizeof(opt));
if (ret == 0) {
ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 1,
iov[0].iov_len);
opt = IP_PMTUDISC_DO;
kernel_setsockopt(conn->params.local->socket, SOL_IP,
IP_MTU_DISCOVER,
(char *)&opt, sizeof(opt));
}
break;
}
up_write(&trans->local->defrag_sem);
_leave(" = %d [frag %u]", ret, trans->peer->maxdata);
up_write(&conn->params.local->defrag_sem);
_leave(" = %d [frag %u]", ret, conn->params.peer->maxdata);
return ret;
}
......@@ -487,7 +482,7 @@ static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb,
if (try_to_del_timer_sync(&call->ack_timer) >= 0) {
/* the packet may be freed by rxrpc_process_call() before this
* returns */
ret = rxrpc_send_packet(call->conn->trans, skb);
ret = rxrpc_send_data_packet(call->conn, skb);
_net("sent skb %p", skb);
} else {
_debug("failed to delete ACK timer");
......@@ -573,7 +568,7 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
goto maybe_error;
}
max = call->conn->trans->peer->maxdata;
max = call->conn->params.peer->maxdata;
max -= call->conn->security_size;
max &= ~(call->conn->size_align - 1UL);
......@@ -664,7 +659,7 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
seq = atomic_inc_return(&call->sequence);
sp->hdr.epoch = conn->epoch;
sp->hdr.epoch = conn->proto.epoch;
sp->hdr.cid = call->cid;
sp->hdr.callNumber = call->call_id;
sp->hdr.seq = seq;
......
......@@ -50,6 +50,9 @@ static unsigned long rxrpc_peer_hash_key(struct rxrpc_local *local,
size = sizeof(srx->transport.sin.sin_addr);
p = (u16 *)&srx->transport.sin.sin_addr;
break;
default:
WARN(1, "AF_RXRPC: Unsupported transport address family\n");
return 0;
}
/* Step through the peer address in 16-bit portions for speed */
......@@ -185,6 +188,8 @@ struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp)
INIT_HLIST_HEAD(&peer->error_targets);
INIT_WORK(&peer->error_distributor,
&rxrpc_peer_error_distributor);
peer->service_conns = RB_ROOT;
rwlock_init(&peer->conn_lock);
spin_lock_init(&peer->lock);
peer->debug_id = atomic_inc_return(&rxrpc_debug_id);
}
......
......@@ -46,7 +46,7 @@ static void rxrpc_call_seq_stop(struct seq_file *seq, void *v)
static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
{
struct rxrpc_transport *trans;
struct rxrpc_connection *conn;
struct rxrpc_call *call;
char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1];
......@@ -59,25 +59,28 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
}
call = list_entry(v, struct rxrpc_call, link);
trans = call->conn->trans;
sprintf(lbuff, "%pI4:%u",
&trans->local->srx.transport.sin.sin_addr,
ntohs(trans->local->srx.transport.sin.sin_port));
&call->local->srx.transport.sin.sin_addr,
ntohs(call->local->srx.transport.sin.sin_port));
sprintf(rbuff, "%pI4:%u",
&trans->peer->srx.transport.sin.sin_addr,
ntohs(trans->peer->srx.transport.sin.sin_port));
conn = call->conn;
if (conn)
sprintf(rbuff, "%pI4:%u",
&conn->params.peer->srx.transport.sin.sin_addr,
ntohs(conn->params.peer->srx.transport.sin.sin_port));
else
strcpy(rbuff, "no_connection");
seq_printf(seq,
"UDP %-22.22s %-22.22s %4x %08x %08x %s %3u"
" %-8.8s %08x %lx\n",
lbuff,
rbuff,
call->conn->service_id,
call->service_id,
call->cid,
call->call_id,
call->conn->in_clientflag ? "Svc" : "Clt",
call->in_clientflag ? "Svc" : "Clt",
atomic_read(&call->usage),
rxrpc_call_states[call->state],
call->remote_abort ?: call->local_abort,
......@@ -129,7 +132,6 @@ static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v)
static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
{
struct rxrpc_connection *conn;
struct rxrpc_transport *trans;
char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1];
if (v == &rxrpc_connections) {
......@@ -142,28 +144,27 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
}
conn = list_entry(v, struct rxrpc_connection, link);
trans = conn->trans;
sprintf(lbuff, "%pI4:%u",
&trans->local->srx.transport.sin.sin_addr,
ntohs(trans->local->srx.transport.sin.sin_port));
&conn->params.local->srx.transport.sin.sin_addr,
ntohs(conn->params.local->srx.transport.sin.sin_port));
sprintf(rbuff, "%pI4:%u",
&trans->peer->srx.transport.sin.sin_addr,
ntohs(trans->peer->srx.transport.sin.sin_port));
&conn->params.peer->srx.transport.sin.sin_addr,
ntohs(conn->params.peer->srx.transport.sin.sin_port));
seq_printf(seq,
"UDP %-22.22s %-22.22s %4x %08x %08x %s %3u"
" %s %08x %08x %08x\n",
lbuff,
rbuff,
conn->service_id,
conn->cid,
conn->params.service_id,
conn->proto.cid,
conn->call_counter,
conn->in_clientflag ? "Svc" : "Clt",
rxrpc_conn_is_service(conn) ? "Svc" : "Clt",
atomic_read(&conn->usage),
rxrpc_conn_states[conn->state],
key_serial(conn->key),
key_serial(conn->params.key),
atomic_read(&conn->serial),
atomic_read(&conn->hi_serial));
......
......@@ -147,9 +147,9 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
if (!continue_call) {
if (msg->msg_name) {
size_t len =
sizeof(call->conn->trans->peer->srx);
sizeof(call->conn->params.peer->srx);
memcpy(msg->msg_name,
&call->conn->trans->peer->srx, len);
&call->conn->params.peer->srx, len);
msg->msg_namelen = len;
}
sock_recv_timestamp(msg, &rx->sk, skb);
......@@ -205,7 +205,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
/* we transferred the whole data packet */
if (sp->hdr.flags & RXRPC_LAST_PACKET) {
_debug("last");
if (call->conn->out_clientflag) {
if (rxrpc_conn_is_client(call->conn)) {
/* last byte of reply received */
ret = copied;
goto terminal_message;
......
......@@ -58,9 +58,9 @@ static int rxkad_init_connection_security(struct rxrpc_connection *conn)
struct rxrpc_key_token *token;
int ret;
_enter("{%d},{%x}", conn->debug_id, key_serial(conn->key));
_enter("{%d},{%x}", conn->debug_id, key_serial(conn->params.key));
token = conn->key->payload.data[0];
token = conn->params.key->payload.data[0];
conn->security_ix = token->security_index;
ci = crypto_alloc_skcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC);
......@@ -74,7 +74,7 @@ static int rxkad_init_connection_security(struct rxrpc_connection *conn)
sizeof(token->kad->session_key)) < 0)
BUG();
switch (conn->security_level) {
switch (conn->params.security_level) {
case RXRPC_SECURITY_PLAIN:
break;
case RXRPC_SECURITY_AUTH:
......@@ -115,14 +115,14 @@ static void rxkad_prime_packet_security(struct rxrpc_connection *conn)
_enter("");
if (!conn->key)
if (!conn->params.key)
return;
token = conn->key->payload.data[0];
token = conn->params.key->payload.data[0];
memcpy(&iv, token->kad->session_key, sizeof(iv));
tmpbuf.x[0] = htonl(conn->epoch);
tmpbuf.x[1] = htonl(conn->cid);
tmpbuf.x[0] = htonl(conn->proto.epoch);
tmpbuf.x[1] = htonl(conn->proto.cid);
tmpbuf.x[2] = 0;
tmpbuf.x[3] = htonl(conn->security_ix);
......@@ -220,7 +220,7 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call,
rxkhdr.checksum = 0;
/* encrypt from the session key */
token = call->conn->key->payload.data[0];
token = call->conn->params.key->payload.data[0];
memcpy(&iv, token->kad->session_key, sizeof(iv));
sg_init_one(&sg[0], sechdr, sizeof(rxkhdr));
......@@ -277,13 +277,13 @@ static int rxkad_secure_packet(const struct rxrpc_call *call,
sp = rxrpc_skb(skb);
_enter("{%d{%x}},{#%u},%zu,",
call->debug_id, key_serial(call->conn->key), sp->hdr.seq,
data_size);
call->debug_id, key_serial(call->conn->params.key),
sp->hdr.seq, data_size);
if (!call->conn->cipher)
return 0;
ret = key_validate(call->conn->key);
ret = key_validate(call->conn->params.key);
if (ret < 0)
return ret;
......@@ -312,7 +312,7 @@ static int rxkad_secure_packet(const struct rxrpc_call *call,
y = 1; /* zero checksums are not permitted */
sp->hdr.cksum = y;
switch (call->conn->security_level) {
switch (call->conn->params.security_level) {
case RXRPC_SECURITY_PLAIN:
ret = 0;
break;
......@@ -446,7 +446,7 @@ static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call,
skb_to_sgvec(skb, sg, 0, skb->len);
/* decrypt from the session key */
token = call->conn->key->payload.data[0];
token = call->conn->params.key->payload.data[0];
memcpy(&iv, token->kad->session_key, sizeof(iv));
skcipher_request_set_tfm(req, call->conn->cipher);
......@@ -516,7 +516,7 @@ static int rxkad_verify_packet(const struct rxrpc_call *call,
sp = rxrpc_skb(skb);
_enter("{%d{%x}},{#%u}",
call->debug_id, key_serial(call->conn->key), sp->hdr.seq);
call->debug_id, key_serial(call->conn->params.key), sp->hdr.seq);
if (!call->conn->cipher)
return 0;
......@@ -557,7 +557,7 @@ static int rxkad_verify_packet(const struct rxrpc_call *call,
return -EPROTO;
}
switch (call->conn->security_level) {
switch (call->conn->params.security_level) {
case RXRPC_SECURITY_PLAIN:
ret = 0;
break;
......@@ -589,9 +589,9 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
u32 serial;
int ret;
_enter("{%d,%x}", conn->debug_id, key_serial(conn->key));
_enter("{%d,%x}", conn->debug_id, key_serial(conn->params.key));
ret = key_validate(conn->key);
ret = key_validate(conn->params.key);
if (ret < 0)
return ret;
......@@ -602,14 +602,14 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
challenge.min_level = htonl(0);
challenge.__padding = 0;
msg.msg_name = &conn->trans->peer->srx.transport.sin;
msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin);
msg.msg_name = &conn->params.peer->srx.transport.sin;
msg.msg_namelen = sizeof(conn->params.peer->srx.transport.sin);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
whdr.epoch = htonl(conn->epoch);
whdr.cid = htonl(conn->cid);
whdr.epoch = htonl(conn->proto.epoch);
whdr.cid = htonl(conn->proto.cid);
whdr.callNumber = 0;
whdr.seq = 0;
whdr.type = RXRPC_PACKET_TYPE_CHALLENGE;
......@@ -617,7 +617,7 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
whdr.userStatus = 0;
whdr.securityIndex = conn->security_ix;
whdr._rsvd = 0;
whdr.serviceId = htons(conn->service_id);
whdr.serviceId = htons(conn->params.service_id);
iov[0].iov_base = &whdr;
iov[0].iov_len = sizeof(whdr);
......@@ -630,7 +630,7 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
whdr.serial = htonl(serial);
_proto("Tx CHALLENGE %%%u", serial);
ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len);
ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len);
if (ret < 0) {
_debug("sendmsg failed: %d", ret);
return -EAGAIN;
......@@ -657,8 +657,8 @@ static int rxkad_send_response(struct rxrpc_connection *conn,
_enter("");
msg.msg_name = &conn->trans->peer->srx.transport.sin;
msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin);
msg.msg_name = &conn->params.peer->srx.transport.sin;
msg.msg_namelen = sizeof(conn->params.peer->srx.transport.sin);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
......@@ -684,7 +684,7 @@ static int rxkad_send_response(struct rxrpc_connection *conn,
whdr.serial = htonl(serial);
_proto("Tx RESPONSE %%%u", serial);
ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 3, len);
ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 3, len);
if (ret < 0) {
_debug("sendmsg failed: %d", ret);
return -EAGAIN;
......@@ -771,14 +771,14 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
u32 version, nonce, min_level, abort_code;
int ret;
_enter("{%d,%x}", conn->debug_id, key_serial(conn->key));
_enter("{%d,%x}", conn->debug_id, key_serial(conn->params.key));
if (!conn->key) {
if (!conn->params.key) {
_leave(" = -EPROTO [no key]");
return -EPROTO;
}
ret = key_validate(conn->key);
ret = key_validate(conn->params.key);
if (ret < 0) {
*_abort_code = RXKADEXPIRED;
return ret;
......@@ -801,20 +801,20 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
goto protocol_error;
abort_code = RXKADLEVELFAIL;
if (conn->security_level < min_level)
if (conn->params.security_level < min_level)
goto protocol_error;
token = conn->key->payload.data[0];
token = conn->params.key->payload.data[0];
/* build the response packet */
memset(&resp, 0, sizeof(resp));
resp.version = htonl(RXKAD_VERSION);
resp.encrypted.epoch = htonl(conn->epoch);
resp.encrypted.cid = htonl(conn->cid);
resp.encrypted.epoch = htonl(conn->proto.epoch);
resp.encrypted.cid = htonl(conn->proto.cid);
resp.encrypted.securityIndex = htonl(conn->security_ix);
resp.encrypted.inc_nonce = htonl(nonce + 1);
resp.encrypted.level = htonl(conn->security_level);
resp.encrypted.level = htonl(conn->params.security_level);
resp.kvno = htonl(token->kad->kvno);
resp.ticket_len = htonl(token->kad->ticket_len);
......@@ -1096,9 +1096,9 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
rxkad_decrypt_response(conn, &response, &session_key);
abort_code = RXKADSEALEDINCON;
if (ntohl(response.encrypted.epoch) != conn->epoch)
if (ntohl(response.encrypted.epoch) != conn->proto.epoch)
goto protocol_error_free;
if (ntohl(response.encrypted.cid) != conn->cid)
if (ntohl(response.encrypted.cid) != conn->proto.cid)
goto protocol_error_free;
if (ntohl(response.encrypted.securityIndex) != conn->security_ix)
goto protocol_error_free;
......@@ -1122,7 +1122,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
level = ntohl(response.encrypted.level);
if (level > RXRPC_SECURITY_ENCRYPT)
goto protocol_error_free;
conn->security_level = level;
conn->params.security_level = level;
/* create a key to hold the security data and expiration time - after
* this the connection security can be handled in exactly the same way
......
......@@ -76,7 +76,7 @@ int rxrpc_init_client_conn_security(struct rxrpc_connection *conn)
{
const struct rxrpc_security *sec;
struct rxrpc_key_token *token;
struct key *key = conn->key;
struct key *key = conn->params.key;
int ret;
_enter("{%d},{%x}", conn->debug_id, key_serial(key));
......@@ -113,7 +113,7 @@ int rxrpc_init_client_conn_security(struct rxrpc_connection *conn)
int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
{
const struct rxrpc_security *sec;
struct rxrpc_local *local = conn->trans->local;
struct rxrpc_local *local = conn->params.local;
struct rxrpc_sock *rx;
struct key *key;
key_ref_t kref;
......@@ -121,7 +121,7 @@ int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
_enter("");
sprintf(kdesc, "%u:%u", conn->service_id, conn->security_ix);
sprintf(kdesc, "%u:%u", conn->params.service_id, conn->security_ix);
sec = rxrpc_security_lookup(conn->security_ix);
if (!sec) {
......@@ -132,7 +132,7 @@ int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
/* find the service */
read_lock_bh(&local->services_lock);
list_for_each_entry(rx, &local->services, listen_link) {
if (rx->srx.srx_service == conn->service_id)
if (rx->srx.srx_service == conn->params.service_id)
goto found_service;
}
......
......@@ -90,14 +90,6 @@ static struct ctl_table rxrpc_sysctl_table[] = {
.proc_handler = proc_dointvec_minmax,
.extra1 = (void *)&one,
},
{
.procname = "transport_expiry",
.data = &rxrpc_transport_expiry,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = (void *)&one,
},
/* Non-time values */
{
......
/* RxRPC point-to-point transport session management
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/net.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
#include "ar-internal.h"
/*
* Time after last use at which transport record is cleaned up.
*/
unsigned int rxrpc_transport_expiry = 3600 * 24;
static void rxrpc_transport_reaper(struct work_struct *work);
static LIST_HEAD(rxrpc_transports);
static DEFINE_RWLOCK(rxrpc_transport_lock);
static DECLARE_DELAYED_WORK(rxrpc_transport_reap, rxrpc_transport_reaper);
/*
* allocate a new transport session manager
*/
static struct rxrpc_transport *rxrpc_alloc_transport(struct rxrpc_local *local,
struct rxrpc_peer *peer,
gfp_t gfp)
{
struct rxrpc_transport *trans;
_enter("");
trans = kzalloc(sizeof(struct rxrpc_transport), gfp);
if (trans) {
trans->local = local;
trans->peer = peer;
INIT_LIST_HEAD(&trans->link);
trans->bundles = RB_ROOT;
trans->client_conns = RB_ROOT;
trans->server_conns = RB_ROOT;
spin_lock_init(&trans->client_lock);
rwlock_init(&trans->conn_lock);
atomic_set(&trans->usage, 1);
trans->conn_idcounter = peer->srx.srx_service << 16;
trans->debug_id = atomic_inc_return(&rxrpc_debug_id);
}
_leave(" = %p", trans);
return trans;
}
/*
* obtain a transport session for the nominated endpoints
*/
struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *local,
struct rxrpc_peer *peer,
gfp_t gfp)
{
struct rxrpc_transport *trans, *candidate;
const char *new = "old";
int usage;
_enter("{%pI4+%hu},{%pI4+%hu},",
&local->srx.transport.sin.sin_addr,
ntohs(local->srx.transport.sin.sin_port),
&peer->srx.transport.sin.sin_addr,
ntohs(peer->srx.transport.sin.sin_port));
/* search the transport list first */
read_lock_bh(&rxrpc_transport_lock);
list_for_each_entry(trans, &rxrpc_transports, link) {
if (trans->local == local && trans->peer == peer)
goto found_extant_transport;
}
read_unlock_bh(&rxrpc_transport_lock);
/* not yet present - create a candidate for a new record and then
* redo the search */
candidate = rxrpc_alloc_transport(local, peer, gfp);
if (!candidate) {
_leave(" = -ENOMEM");
return ERR_PTR(-ENOMEM);
}
write_lock_bh(&rxrpc_transport_lock);
list_for_each_entry(trans, &rxrpc_transports, link) {
if (trans->local == local && trans->peer == peer)
goto found_extant_second;
}
/* we can now add the new candidate to the list */
trans = candidate;
candidate = NULL;
usage = atomic_read(&trans->usage);
rxrpc_get_local(trans->local);
rxrpc_get_peer(trans->peer);
list_add_tail(&trans->link, &rxrpc_transports);
write_unlock_bh(&rxrpc_transport_lock);
new = "new";
success:
_net("TRANSPORT %s %d local %d -> peer %d",
new,
trans->debug_id,
trans->local->debug_id,
trans->peer->debug_id);
_leave(" = %p {u=%d}", trans, usage);
return trans;
/* we found the transport in the list immediately */
found_extant_transport:
usage = atomic_inc_return(&trans->usage);
read_unlock_bh(&rxrpc_transport_lock);
goto success;
/* we found the transport on the second time through the list */
found_extant_second:
usage = atomic_inc_return(&trans->usage);
write_unlock_bh(&rxrpc_transport_lock);
kfree(candidate);
goto success;
}
/*
* find the transport connecting two endpoints
*/
struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *local,
struct rxrpc_peer *peer)
{
struct rxrpc_transport *trans;
_enter("{%pI4+%hu},{%pI4+%hu},",
&local->srx.transport.sin.sin_addr,
ntohs(local->srx.transport.sin.sin_port),
&peer->srx.transport.sin.sin_addr,
ntohs(peer->srx.transport.sin.sin_port));
/* search the transport list */
read_lock_bh(&rxrpc_transport_lock);
list_for_each_entry(trans, &rxrpc_transports, link) {
if (trans->local == local && trans->peer == peer)
goto found_extant_transport;
}
read_unlock_bh(&rxrpc_transport_lock);
_leave(" = NULL");
return NULL;
found_extant_transport:
atomic_inc(&trans->usage);
read_unlock_bh(&rxrpc_transport_lock);
_leave(" = %p", trans);
return trans;
}
/*
* release a transport session
*/
void rxrpc_put_transport(struct rxrpc_transport *trans)
{
_enter("%p{u=%d}", trans, atomic_read(&trans->usage));
ASSERTCMP(atomic_read(&trans->usage), >, 0);
trans->put_time = ktime_get_seconds();
if (unlikely(atomic_dec_and_test(&trans->usage))) {
_debug("zombie");
/* let the reaper determine the timeout to avoid a race with
* overextending the timeout if the reaper is running at the
* same time */
rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0);
}
_leave("");
}
/*
* clean up a transport session
*/
static void rxrpc_cleanup_transport(struct rxrpc_transport *trans)
{
_net("DESTROY TRANS %d", trans->debug_id);
rxrpc_put_local(trans->local);
rxrpc_put_peer(trans->peer);
kfree(trans);
}
/*
* reap dead transports that have passed their expiry date
*/
static void rxrpc_transport_reaper(struct work_struct *work)
{
struct rxrpc_transport *trans, *_p;
unsigned long now, earliest, reap_time;
LIST_HEAD(graveyard);
_enter("");
now = ktime_get_seconds();
earliest = ULONG_MAX;
/* extract all the transports that have been dead too long */
write_lock_bh(&rxrpc_transport_lock);
list_for_each_entry_safe(trans, _p, &rxrpc_transports, link) {
_debug("reap TRANS %d { u=%d t=%ld }",
trans->debug_id, atomic_read(&trans->usage),
(long) now - (long) trans->put_time);
if (likely(atomic_read(&trans->usage) > 0))
continue;
reap_time = trans->put_time + rxrpc_transport_expiry;
if (reap_time <= now)
list_move_tail(&trans->link, &graveyard);
else if (reap_time < earliest)
earliest = reap_time;
}
write_unlock_bh(&rxrpc_transport_lock);
if (earliest != ULONG_MAX) {
_debug("reschedule reaper %ld", (long) earliest - now);
ASSERTCMP(earliest, >, now);
rxrpc_queue_delayed_work(&rxrpc_transport_reap,
(earliest - now) * HZ);
}
/* then destroy all those pulled out */
while (!list_empty(&graveyard)) {
trans = list_entry(graveyard.next, struct rxrpc_transport,
link);
list_del_init(&trans->link);
ASSERTCMP(atomic_read(&trans->usage), ==, 0);
rxrpc_cleanup_transport(trans);
}
_leave("");
}
/*
* preemptively destroy all the transport session records rather than waiting
* for them to time out
*/
void __exit rxrpc_destroy_all_transports(void)
{
_enter("");
rxrpc_transport_expiry = 0;
cancel_delayed_work(&rxrpc_transport_reap);
rxrpc_queue_delayed_work(&rxrpc_transport_reap, 0);
_leave("");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment