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 { ...@@ -35,7 +35,7 @@ struct sockaddr_rxrpc {
*/ */
#define RXRPC_SECURITY_KEY 1 /* [clnt] set client security key */ #define RXRPC_SECURITY_KEY 1 /* [clnt] set client security key */
#define RXRPC_SECURITY_KEYRING 2 /* [srvr] set ring of server security keys */ #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 */ #define RXRPC_MIN_SECURITY_LEVEL 4 /* minimum security level */
/* /*
...@@ -52,6 +52,7 @@ struct sockaddr_rxrpc { ...@@ -52,6 +52,7 @@ struct sockaddr_rxrpc {
#define RXRPC_LOCAL_ERROR 7 /* -r: local error generated [terminal] */ #define RXRPC_LOCAL_ERROR 7 /* -r: local error generated [terminal] */
#define RXRPC_NEW_CALL 8 /* -r: [Service] new incoming call notification */ #define RXRPC_NEW_CALL 8 /* -r: [Service] new incoming call notification */
#define RXRPC_ACCEPT 9 /* s-: [Service] accept request */ #define RXRPC_ACCEPT 9 /* s-: [Service] accept request */
#define RXRPC_EXCLUSIVE_CALL 10 /* s-: Call should be on exclusive connection */
/* /*
* RxRPC security levels * RxRPC security levels
......
...@@ -7,6 +7,7 @@ af-rxrpc-y := \ ...@@ -7,6 +7,7 @@ af-rxrpc-y := \
call_accept.o \ call_accept.o \
call_event.o \ call_event.o \
call_object.o \ call_object.o \
conn_client.o \
conn_event.o \ conn_event.o \
conn_object.o \ conn_object.o \
input.o \ input.o \
...@@ -21,7 +22,6 @@ af-rxrpc-y := \ ...@@ -21,7 +22,6 @@ af-rxrpc-y := \
recvmsg.o \ recvmsg.o \
security.o \ security.o \
skbuff.o \ skbuff.o \
transport.o \
utils.o utils.o
af-rxrpc-$(CONFIG_PROC_FS) += proc.o af-rxrpc-$(CONFIG_PROC_FS) += proc.o
......
...@@ -97,7 +97,7 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx, ...@@ -97,7 +97,7 @@ static int rxrpc_validate_address(struct rxrpc_sock *rx,
srx->transport_len > len) srx->transport_len > len)
return -EINVAL; return -EINVAL;
if (srx->transport.family != rx->proto) if (srx->transport.family != rx->family)
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
switch (srx->transport.family) { switch (srx->transport.family) {
...@@ -224,39 +224,6 @@ static int rxrpc_listen(struct socket *sock, int backlog) ...@@ -224,39 +224,6 @@ static int rxrpc_listen(struct socket *sock, int backlog)
return ret; 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 * rxrpc_kernel_begin_call - Allow a kernel service to begin a call
* @sock: The socket on which to make the call * @sock: The socket on which to make the call
...@@ -277,39 +244,32 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, ...@@ -277,39 +244,32 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
unsigned long user_call_ID, unsigned long user_call_ID,
gfp_t gfp) gfp_t gfp)
{ {
struct rxrpc_conn_bundle *bundle; struct rxrpc_conn_parameters cp;
struct rxrpc_transport *trans;
struct rxrpc_call *call; struct rxrpc_call *call;
struct rxrpc_sock *rx = rxrpc_sk(sock->sk); struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
int ret;
_enter(",,%x,%lx", key_serial(key), user_call_ID); _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, lock_sock(&rx->sk);
sizeof(*srx), 0, gfp);
if (IS_ERR(trans)) {
call = ERR_CAST(trans);
trans = NULL;
goto out_notrans;
}
if (!key) if (!key)
key = rx->key; key = rx->key;
if (key && !key->payload.data[0]) if (key && !key->payload.data[0])
key = NULL; /* a no-security key */ key = NULL; /* a no-security key */
bundle = rxrpc_get_bundle(rx, trans, key, srx->srx_service, gfp); memset(&cp, 0, sizeof(cp));
if (IS_ERR(bundle)) { cp.local = rx->local;
call = ERR_CAST(bundle); cp.key = key;
goto out; 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); release_sock(&rx->sk);
_leave(" = %p", call); _leave(" = %p", call);
return call; return call;
...@@ -487,7 +447,7 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname, ...@@ -487,7 +447,7 @@ static int rxrpc_setsockopt(struct socket *sock, int level, int optname,
ret = -EISCONN; ret = -EISCONN;
if (rx->sk.sk_state != RXRPC_UNBOUND) if (rx->sk.sk_state != RXRPC_UNBOUND)
goto error; goto error;
set_bit(RXRPC_SOCK_EXCLUSIVE_CONN, &rx->flags); rx->exclusive = true;
goto success; goto success;
case RXRPC_SECURITY_KEY: case RXRPC_SECURITY_KEY:
...@@ -600,7 +560,7 @@ static int rxrpc_create(struct net *net, struct socket *sock, int protocol, ...@@ -600,7 +560,7 @@ static int rxrpc_create(struct net *net, struct socket *sock, int protocol,
sk->sk_destruct = rxrpc_sock_destructor; sk->sk_destruct = rxrpc_sock_destructor;
rx = rxrpc_sk(sk); rx = rxrpc_sk(sk);
rx->proto = protocol; rx->family = protocol;
rx->calls = RB_ROOT; rx->calls = RB_ROOT;
INIT_LIST_HEAD(&rx->listen_link); INIT_LIST_HEAD(&rx->listen_link);
...@@ -662,16 +622,8 @@ static int rxrpc_release_sock(struct sock *sk) ...@@ -662,16 +622,8 @@ static int rxrpc_release_sock(struct sock *sk)
flush_workqueue(rxrpc_workqueue); flush_workqueue(rxrpc_workqueue);
rxrpc_purge_queue(&sk->sk_receive_queue); 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); rxrpc_put_local(rx->local);
rx->local = NULL; rx->local = NULL;
}
key_put(rx->key); key_put(rx->key);
rx->key = NULL; rx->key = NULL;
key_put(rx->securities); key_put(rx->securities);
...@@ -836,7 +788,6 @@ static void __exit af_rxrpc_exit(void) ...@@ -836,7 +788,6 @@ static void __exit af_rxrpc_exit(void)
proto_unregister(&rxrpc_proto); proto_unregister(&rxrpc_proto);
rxrpc_destroy_all_calls(); rxrpc_destroy_all_calls();
rxrpc_destroy_all_connections(); rxrpc_destroy_all_connections();
rxrpc_destroy_all_transports();
ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0); ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0);
...@@ -856,6 +807,8 @@ static void __exit af_rxrpc_exit(void) ...@@ -856,6 +807,8 @@ static void __exit af_rxrpc_exit(void)
_debug("synchronise RCU"); _debug("synchronise RCU");
rcu_barrier(); rcu_barrier();
_debug("destroy locals"); _debug("destroy locals");
ASSERT(idr_is_empty(&rxrpc_client_conn_ids));
idr_destroy(&rxrpc_client_conn_ids);
rxrpc_destroy_all_locals(); rxrpc_destroy_all_locals();
remove_proc_entry("rxrpc_conns", init_net.proc_net); remove_proc_entry("rxrpc_conns", init_net.proc_net);
......
...@@ -37,6 +37,8 @@ struct rxrpc_crypt { ...@@ -37,6 +37,8 @@ struct rxrpc_crypt {
#define rxrpc_queue_call(CALL) rxrpc_queue_work(&(CALL)->processor) #define rxrpc_queue_call(CALL) rxrpc_queue_work(&(CALL)->processor)
#define rxrpc_queue_conn(CONN) rxrpc_queue_work(&(CONN)->processor) #define rxrpc_queue_conn(CONN) rxrpc_queue_work(&(CONN)->processor)
struct rxrpc_connection;
/* /*
* sk_state for RxRPC sockets * sk_state for RxRPC sockets
*/ */
...@@ -57,7 +59,6 @@ struct rxrpc_sock { ...@@ -57,7 +59,6 @@ struct rxrpc_sock {
struct sock sk; struct sock sk;
rxrpc_interceptor_t interceptor; /* kernel service Rx interceptor function */ rxrpc_interceptor_t interceptor; /* kernel service Rx interceptor function */
struct rxrpc_local *local; /* local endpoint */ struct rxrpc_local *local; /* local endpoint */
struct rxrpc_connection *conn; /* exclusive virtual connection */
struct list_head listen_link; /* link in the local endpoint's listen list */ struct list_head listen_link; /* link in the local endpoint's listen list */
struct list_head secureq; /* calls awaiting connection security clearance */ struct list_head secureq; /* calls awaiting connection security clearance */
struct list_head acceptq; /* calls awaiting acceptance */ struct list_head acceptq; /* calls awaiting acceptance */
...@@ -66,13 +67,13 @@ struct rxrpc_sock { ...@@ -66,13 +67,13 @@ struct rxrpc_sock {
struct rb_root calls; /* outstanding calls on this socket */ struct rb_root calls; /* outstanding calls on this socket */
unsigned long flags; unsigned long flags;
#define RXRPC_SOCK_CONNECTED 0 /* connect_srx is set */ #define RXRPC_SOCK_CONNECTED 0 /* connect_srx is set */
#define RXRPC_SOCK_EXCLUSIVE_CONN 1 /* exclusive connection for a client socket */
rwlock_t call_lock; /* lock for calls */ rwlock_t call_lock; /* lock for calls */
u32 min_sec_level; /* minimum security level */ u32 min_sec_level; /* minimum security level */
#define RXRPC_SECURITY_MAX RXRPC_SECURITY_ENCRYPT #define RXRPC_SECURITY_MAX RXRPC_SECURITY_ENCRYPT
bool exclusive; /* Exclusive connection for a client socket */
sa_family_t family; /* Protocol family created with */
struct sockaddr_rxrpc srx; /* local address */ struct sockaddr_rxrpc srx; /* local address */
struct sockaddr_rxrpc connect_srx; /* Default client address from connect() */ struct sockaddr_rxrpc connect_srx; /* Default client address from connect() */
sa_family_t proto; /* protocol created with */
}; };
#define rxrpc_sk(__sk) container_of((__sk), struct rxrpc_sock, sk) #define rxrpc_sk(__sk) container_of((__sk), struct rxrpc_sock, sk)
...@@ -185,7 +186,8 @@ struct rxrpc_local { ...@@ -185,7 +186,8 @@ struct rxrpc_local {
struct sk_buff_head accept_queue; /* incoming calls awaiting acceptance */ struct sk_buff_head accept_queue; /* incoming calls awaiting acceptance */
struct sk_buff_head reject_queue; /* packets awaiting rejection */ struct sk_buff_head reject_queue; /* packets awaiting rejection */
struct sk_buff_head event_queue; /* endpoint event packets awaiting processing */ struct sk_buff_head event_queue; /* endpoint event packets awaiting processing */
struct mutex conn_lock; /* Client connection creation lock */ struct rb_root client_conns; /* Client connections by socket params */
spinlock_t client_conns_lock; /* Lock for client_conns */
spinlock_t lock; /* access lock */ spinlock_t lock; /* access lock */
rwlock_t services_lock; /* lock for services list */ rwlock_t services_lock; /* lock for services list */
int debug_id; /* debug ID for printks */ int debug_id; /* debug ID for printks */
...@@ -205,6 +207,8 @@ struct rxrpc_peer { ...@@ -205,6 +207,8 @@ struct rxrpc_peer {
struct rxrpc_local *local; struct rxrpc_local *local;
struct hlist_head error_targets; /* targets for net error distribution */ struct hlist_head error_targets; /* targets for net error distribution */
struct work_struct error_distributor; struct work_struct error_distributor;
struct rb_root service_conns; /* Service connections */
rwlock_t conn_lock;
spinlock_t lock; /* access lock */ spinlock_t lock; /* access lock */
unsigned int if_mtu; /* interface MTU for this peer */ unsigned int if_mtu; /* interface MTU for this peer */
unsigned int mtu; /* network MTU for this peer */ unsigned int mtu; /* network MTU for this peer */
...@@ -224,66 +228,63 @@ struct rxrpc_peer { ...@@ -224,66 +228,63 @@ struct rxrpc_peer {
}; };
/* /*
* RxRPC point-to-point transport / connection manager definition * Keys for matching a connection.
* - handles a bundle of connections between two endpoints */
* - matched by { local, peer } struct rxrpc_conn_proto {
*/ unsigned long hash_key;
struct rxrpc_transport { struct rxrpc_local *local; /* Representation of local endpoint */
struct rxrpc_local *local; /* local transport endpoint */ u32 epoch; /* epoch of this connection */
struct rxrpc_peer *peer; /* remote transport endpoint */ u32 cid; /* connection ID */
struct rb_root bundles; /* client connection bundles on this transport */ u8 in_clientflag; /* RXRPC_CLIENT_INITIATED if we are server */
struct rb_root client_conns; /* client connections on this transport */ u8 addr_size; /* Size of the address */
struct rb_root server_conns; /* server connections on this transport */ sa_family_t family; /* Transport protocol */
struct list_head link; /* link in master session list */ __be16 port; /* Peer UDP/UDP6 port */
unsigned long put_time; /* time at which to reap */ union { /* Peer address */
spinlock_t client_lock; /* client connection allocation lock */ struct in_addr ipv4_addr;
rwlock_t conn_lock; /* lock for active/dead connections */ struct in6_addr ipv6_addr;
atomic_t usage; u32 raw_addr[0];
int debug_id; /* debug ID for printks */ };
unsigned int conn_idcounter; /* connection ID counter (client) */
}; };
/* struct rxrpc_conn_parameters {
* RxRPC client connection bundle struct rxrpc_local *local; /* Representation of local endpoint */
* - matched by { transport, service_id, key } struct rxrpc_peer *peer; /* Remote endpoint */
*/ struct key *key; /* Security details */
struct rxrpc_conn_bundle { bool exclusive; /* T if conn is exclusive */
struct rb_node node; /* node in transport's lookup tree */ u16 service_id; /* Service ID for this connection */
struct list_head unused_conns; /* unused connections in this bundle */ u32 security_level; /* Security level selected */
struct list_head avail_conns; /* available connections in this bundle */
struct list_head busy_conns; /* busy connections in this bundle */
struct key *key; /* security for this bundle */
wait_queue_head_t chanwait; /* wait for channel to become available */
atomic_t usage;
int debug_id; /* debug ID for printks */
unsigned short num_conns; /* number of connections in this bundle */
u16 service_id; /* Service ID for this bundle */
u8 security_ix; /* security type */
}; };
/* /*
* RxRPC connection definition * RxRPC connection definition
* - matched by { transport, service_id, conn_id, direction, key } * - matched by { local, peer, epoch, conn_id, direction }
* - each connection can only handle four simultaneous calls * - each connection can only handle four simultaneous calls
*/ */
struct rxrpc_connection { struct rxrpc_connection {
struct rxrpc_transport *trans; /* transport session */ struct rxrpc_conn_proto proto;
struct rxrpc_conn_bundle *bundle; /* connection bundle (client) */ struct rxrpc_conn_parameters params;
spinlock_t channel_lock;
struct rxrpc_call *channels[RXRPC_MAXCALLS]; /* active calls */
wait_queue_head_t channel_wq; /* queue to wait for channel to become available */
struct work_struct processor; /* connection event processor */ struct work_struct processor; /* connection event processor */
struct rb_node node; /* node in transport's lookup tree */ union {
struct rb_node client_node; /* Node in local->client_conns */
struct rb_node service_node; /* Node in peer->service_conns */
};
struct list_head link; /* link in master connection list */ struct list_head link; /* link in master connection list */
struct list_head bundle_link; /* link in bundle */
struct rb_root calls; /* calls on this connection */ struct rb_root calls; /* calls on this connection */
struct sk_buff_head rx_queue; /* received conn-level packets */ struct sk_buff_head rx_queue; /* received conn-level packets */
struct rxrpc_call *channels[RXRPC_MAXCALLS]; /* channels (active calls) */
const struct rxrpc_security *security; /* applied security module */ const struct rxrpc_security *security; /* applied security module */
struct key *key; /* security for this connection (client) */
struct key *server_key; /* security for this service */ struct key *server_key; /* security for this service */
struct crypto_skcipher *cipher; /* encryption handle */ struct crypto_skcipher *cipher; /* encryption handle */
struct rxrpc_crypt csum_iv; /* packet checksum base */ struct rxrpc_crypt csum_iv; /* packet checksum base */
unsigned long flags;
#define RXRPC_CONN_HAS_IDR 0 /* - Has a client conn ID assigned */
unsigned long events; unsigned long events;
#define RXRPC_CONN_CHALLENGE 0 /* send challenge packet */ #define RXRPC_CONN_CHALLENGE 0 /* send challenge packet */
unsigned long put_time; /* time at which to reap */ unsigned long put_time; /* Time at which last put */
rwlock_t lock; /* access lock */ rwlock_t lock; /* access lock */
spinlock_t state_lock; /* state-change lock */ spinlock_t state_lock; /* state-change lock */
atomic_t usage; atomic_t usage;
...@@ -304,17 +305,12 @@ struct rxrpc_connection { ...@@ -304,17 +305,12 @@ struct rxrpc_connection {
unsigned int call_counter; /* call ID counter */ unsigned int call_counter; /* call ID counter */
atomic_t serial; /* packet serial number counter */ atomic_t serial; /* packet serial number counter */
atomic_t hi_serial; /* highest serial number received */ atomic_t hi_serial; /* highest serial number received */
u8 avail_calls; /* number of calls available */ atomic_t avail_chans; /* number of channels available */
u8 size_align; /* data size alignment (for security) */ u8 size_align; /* data size alignment (for security) */
u8 header_size; /* rxrpc + security header size */ u8 header_size; /* rxrpc + security header size */
u8 security_size; /* security header size */ u8 security_size; /* security header size */
u32 security_level; /* security level negotiated */
u32 security_nonce; /* response re-use preventer */ u32 security_nonce; /* response re-use preventer */
u32 epoch; /* epoch of this connection */
u32 cid; /* connection ID */
u16 service_id; /* service ID for this connection */
u8 security_ix; /* security type */ u8 security_ix; /* security type */
u8 in_clientflag; /* RXRPC_CLIENT_INITIATED if we are server */
u8 out_clientflag; /* RXRPC_CLIENT_INITIATED if we are client */ u8 out_clientflag; /* RXRPC_CLIENT_INITIATED if we are client */
}; };
...@@ -360,6 +356,8 @@ enum rxrpc_call_event { ...@@ -360,6 +356,8 @@ enum rxrpc_call_event {
* The states that a call can be in. * The states that a call can be in.
*/ */
enum rxrpc_call_state { enum rxrpc_call_state {
RXRPC_CALL_UNINITIALISED,
RXRPC_CALL_CLIENT_AWAIT_CONN, /* - client waiting for connection to become available */
RXRPC_CALL_CLIENT_SEND_REQUEST, /* - client sending request phase */ RXRPC_CALL_CLIENT_SEND_REQUEST, /* - client sending request phase */
RXRPC_CALL_CLIENT_AWAIT_REPLY, /* - client awaiting reply */ RXRPC_CALL_CLIENT_AWAIT_REPLY, /* - client awaiting reply */
RXRPC_CALL_CLIENT_RECV_REPLY, /* - client receiving reply phase */ RXRPC_CALL_CLIENT_RECV_REPLY, /* - client receiving reply phase */
...@@ -448,7 +446,7 @@ struct rxrpc_call { ...@@ -448,7 +446,7 @@ struct rxrpc_call {
unsigned long hash_key; /* Full hash key */ unsigned long hash_key; /* Full hash key */
u8 in_clientflag; /* Copy of conn->in_clientflag for hashing */ u8 in_clientflag; /* Copy of conn->in_clientflag for hashing */
struct rxrpc_local *local; /* Local endpoint. Used for hashing. */ struct rxrpc_local *local; /* Local endpoint. Used for hashing. */
sa_family_t proto; /* Frame protocol */ sa_family_t family; /* Frame protocol */
u32 call_id; /* call ID on connection */ u32 call_id; /* call ID on connection */
u32 cid; /* connection ID plus channel index */ u32 cid; /* connection ID plus channel index */
u32 epoch; /* epoch of this connection */ u32 epoch; /* epoch of this connection */
...@@ -481,10 +479,6 @@ extern u32 rxrpc_epoch; ...@@ -481,10 +479,6 @@ extern u32 rxrpc_epoch;
extern atomic_t rxrpc_debug_id; extern atomic_t rxrpc_debug_id;
extern struct workqueue_struct *rxrpc_workqueue; extern struct workqueue_struct *rxrpc_workqueue;
extern struct rxrpc_transport *rxrpc_name_to_transport(struct rxrpc_sock *,
struct sockaddr *,
int, int, gfp_t);
/* /*
* call_accept.c * call_accept.c
*/ */
...@@ -512,17 +506,25 @@ struct rxrpc_call *rxrpc_find_call_hash(struct rxrpc_host_header *, ...@@ -512,17 +506,25 @@ struct rxrpc_call *rxrpc_find_call_hash(struct rxrpc_host_header *,
void *, sa_family_t, const void *); void *, sa_family_t, const void *);
struct rxrpc_call *rxrpc_find_call_by_user_ID(struct rxrpc_sock *, unsigned long); struct rxrpc_call *rxrpc_find_call_by_user_ID(struct rxrpc_sock *, unsigned long);
struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *, struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *,
struct rxrpc_transport *, struct rxrpc_conn_parameters *,
struct rxrpc_conn_bundle *, struct sockaddr_rxrpc *,
unsigned long, gfp_t); unsigned long, gfp_t);
struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *, struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *,
struct rxrpc_connection *, struct rxrpc_connection *,
struct rxrpc_host_header *); struct sk_buff *);
void rxrpc_release_call(struct rxrpc_call *); void rxrpc_release_call(struct rxrpc_call *);
void rxrpc_release_calls_on_socket(struct rxrpc_sock *); void rxrpc_release_calls_on_socket(struct rxrpc_sock *);
void __rxrpc_put_call(struct rxrpc_call *); void __rxrpc_put_call(struct rxrpc_call *);
void __exit rxrpc_destroy_all_calls(void); void __exit rxrpc_destroy_all_calls(void);
/*
* conn_client.c
*/
extern struct idr rxrpc_client_conn_ids;
int rxrpc_get_client_connection_id(struct rxrpc_connection *, gfp_t);
void rxrpc_put_client_connection_id(struct rxrpc_connection *);
/* /*
* conn_event.c * conn_event.c
*/ */
...@@ -537,18 +539,32 @@ extern unsigned int rxrpc_connection_expiry; ...@@ -537,18 +539,32 @@ extern unsigned int rxrpc_connection_expiry;
extern struct list_head rxrpc_connections; extern struct list_head rxrpc_connections;
extern rwlock_t rxrpc_connection_lock; extern rwlock_t rxrpc_connection_lock;
struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *, int rxrpc_connect_call(struct rxrpc_call *, struct rxrpc_conn_parameters *,
struct rxrpc_transport *, struct sockaddr_rxrpc *, gfp_t);
struct key *, u16, gfp_t); struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *,
void rxrpc_put_bundle(struct rxrpc_transport *, struct rxrpc_conn_bundle *); struct rxrpc_peer *,
int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_transport *, struct sk_buff *);
struct rxrpc_conn_bundle *, struct rxrpc_call *, gfp_t); void rxrpc_disconnect_call(struct rxrpc_call *);
void rxrpc_put_connection(struct rxrpc_connection *); void rxrpc_put_connection(struct rxrpc_connection *);
void __exit rxrpc_destroy_all_connections(void); void __exit rxrpc_destroy_all_connections(void);
struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *, struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *,
struct rxrpc_host_header *); struct rxrpc_peer *,
extern struct rxrpc_connection * struct sk_buff *);
rxrpc_incoming_connection(struct rxrpc_transport *, struct rxrpc_host_header *);
static inline bool rxrpc_conn_is_client(const struct rxrpc_connection *conn)
{
return conn->out_clientflag;
}
static inline bool rxrpc_conn_is_service(const struct rxrpc_connection *conn)
{
return conn->proto.in_clientflag;
}
static inline void rxrpc_get_connection(struct rxrpc_connection *conn)
{
atomic_inc(&conn->usage);
}
/* /*
* input.c * input.c
...@@ -598,7 +614,7 @@ struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local) ...@@ -598,7 +614,7 @@ struct rxrpc_local *rxrpc_get_local_maybe(struct rxrpc_local *local)
static inline void rxrpc_put_local(struct rxrpc_local *local) static inline void rxrpc_put_local(struct rxrpc_local *local)
{ {
if (atomic_dec_and_test(&local->usage)) if (local && atomic_dec_and_test(&local->usage))
__rxrpc_put_local(local); __rxrpc_put_local(local);
} }
...@@ -623,7 +639,7 @@ extern const char *rxrpc_acks(u8 reason); ...@@ -623,7 +639,7 @@ extern const char *rxrpc_acks(u8 reason);
*/ */
extern unsigned int rxrpc_resend_timeout; extern unsigned int rxrpc_resend_timeout;
int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *); int rxrpc_send_data_packet(struct rxrpc_connection *, struct sk_buff *);
int rxrpc_do_sendmsg(struct rxrpc_sock *, struct msghdr *, size_t); int rxrpc_do_sendmsg(struct rxrpc_sock *, struct msghdr *, size_t);
/* /*
...@@ -655,7 +671,7 @@ struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *peer) ...@@ -655,7 +671,7 @@ struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *peer)
extern void __rxrpc_put_peer(struct rxrpc_peer *peer); extern void __rxrpc_put_peer(struct rxrpc_peer *peer);
static inline void rxrpc_put_peer(struct rxrpc_peer *peer) static inline void rxrpc_put_peer(struct rxrpc_peer *peer)
{ {
if (atomic_dec_and_test(&peer->usage)) if (peer && atomic_dec_and_test(&peer->usage))
__rxrpc_put_peer(peer); __rxrpc_put_peer(peer);
} }
...@@ -703,18 +719,6 @@ static inline int __init rxrpc_sysctl_init(void) { return 0; } ...@@ -703,18 +719,6 @@ static inline int __init rxrpc_sysctl_init(void) { return 0; }
static inline void rxrpc_sysctl_exit(void) {} static inline void rxrpc_sysctl_exit(void) {}
#endif #endif
/*
* transport.c
*/
extern unsigned int rxrpc_transport_expiry;
struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *,
struct rxrpc_peer *, gfp_t);
void rxrpc_put_transport(struct rxrpc_transport *);
void __exit rxrpc_destroy_all_transports(void);
struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *,
struct rxrpc_peer *);
/* /*
* utils.c * utils.c
*/ */
......
...@@ -74,7 +74,6 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local, ...@@ -74,7 +74,6 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
struct sockaddr_rxrpc *srx) struct sockaddr_rxrpc *srx)
{ {
struct rxrpc_connection *conn; struct rxrpc_connection *conn;
struct rxrpc_transport *trans;
struct rxrpc_skb_priv *sp, *nsp; struct rxrpc_skb_priv *sp, *nsp;
struct rxrpc_peer *peer; struct rxrpc_peer *peer;
struct rxrpc_call *call; struct rxrpc_call *call;
...@@ -96,29 +95,21 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local, ...@@ -96,29 +95,21 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
notification->mark = RXRPC_SKB_MARK_NEW_CALL; notification->mark = RXRPC_SKB_MARK_NEW_CALL;
peer = rxrpc_lookup_peer(local, srx, GFP_NOIO); peer = rxrpc_lookup_peer(local, srx, GFP_NOIO);
if (IS_ERR(peer)) { if (!peer) {
_debug("no peer"); _debug("no peer");
ret = -EBUSY; ret = -EBUSY;
goto error; goto error;
} }
trans = rxrpc_get_transport(local, peer, GFP_NOIO); conn = rxrpc_incoming_connection(local, peer, skb);
rxrpc_put_peer(peer); 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)) { if (IS_ERR(conn)) {
_debug("no conn"); _debug("no conn");
ret = PTR_ERR(conn); ret = PTR_ERR(conn);
goto error; goto error;
} }
call = rxrpc_incoming_call(rx, conn, &sp->hdr); call = rxrpc_incoming_call(rx, conn, skb);
rxrpc_put_connection(conn); rxrpc_put_connection(conn);
if (IS_ERR(call)) { if (IS_ERR(call)) {
_debug("no call"); _debug("no call");
...@@ -141,7 +132,7 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local, ...@@ -141,7 +132,7 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
_debug("await conn sec"); _debug("await conn sec");
list_add_tail(&call->accept_link, &rx->secureq); list_add_tail(&call->accept_link, &rx->secureq);
call->conn->state = RXRPC_CONN_SERVER_CHALLENGING; 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); set_bit(RXRPC_CONN_CHALLENGE, &call->conn->events);
rxrpc_queue_conn(call->conn); rxrpc_queue_conn(call->conn);
} else { } else {
......
...@@ -187,7 +187,7 @@ static void rxrpc_resend(struct rxrpc_call *call) ...@@ -187,7 +187,7 @@ static void rxrpc_resend(struct rxrpc_call *call)
_proto("Tx DATA %%%u { #%d }", _proto("Tx DATA %%%u { #%d }",
sp->hdr.serial, sp->hdr.seq); 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; stop = true;
sp->resend_at = jiffies + 3; sp->resend_at = jiffies + 3;
} else { } else {
...@@ -545,7 +545,7 @@ static void rxrpc_extract_ackinfo(struct rxrpc_call *call, struct sk_buff *skb, ...@@ -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)); mtu = min(ntohl(ackinfo.rxMTU), ntohl(ackinfo.maxMTU));
peer = call->conn->trans->peer; peer = call->conn->params.peer;
if (mtu < peer->maxdata) { if (mtu < peer->maxdata) {
spin_lock_bh(&peer->lock); spin_lock_bh(&peer->lock);
peer->maxdata = mtu; peer->maxdata = mtu;
...@@ -836,13 +836,13 @@ void rxrpc_process_call(struct work_struct *work) ...@@ -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 /* there's a good chance we're going to have to send a message, so set
* one up in advance */ * one up in advance */
msg.msg_name = &call->conn->trans->peer->srx.transport; msg.msg_name = &call->conn->params.peer->srx.transport;
msg.msg_namelen = call->conn->trans->peer->srx.transport_len; msg.msg_namelen = call->conn->params.peer->srx.transport_len;
msg.msg_control = NULL; msg.msg_control = NULL;
msg.msg_controllen = 0; msg.msg_controllen = 0;
msg.msg_flags = 0; msg.msg_flags = 0;
whdr.epoch = htonl(call->conn->epoch); whdr.epoch = htonl(call->conn->proto.epoch);
whdr.cid = htonl(call->cid); whdr.cid = htonl(call->cid);
whdr.callNumber = htonl(call->call_id); whdr.callNumber = htonl(call->call_id);
whdr.seq = 0; whdr.seq = 0;
...@@ -1151,8 +1151,8 @@ void rxrpc_process_call(struct work_struct *work) ...@@ -1151,8 +1151,8 @@ void rxrpc_process_call(struct work_struct *work)
ack.maxSkew = htons(atomic_read(&call->conn->hi_serial) - ack.maxSkew = htons(atomic_read(&call->conn->hi_serial) -
ntohl(ack.serial)); ntohl(ack.serial));
send_ACK: send_ACK:
mtu = call->conn->trans->peer->if_mtu; mtu = call->conn->params.peer->if_mtu;
mtu -= call->conn->trans->peer->hdrsize; mtu -= call->conn->params.peer->hdrsize;
ackinfo.maxMTU = htonl(mtu); ackinfo.maxMTU = htonl(mtu);
ackinfo.rwind = htonl(rxrpc_rx_window_size); ackinfo.rwind = htonl(rxrpc_rx_window_size);
...@@ -1206,7 +1206,7 @@ void rxrpc_process_call(struct work_struct *work) ...@@ -1206,7 +1206,7 @@ void rxrpc_process_call(struct work_struct *work)
len += iov[1].iov_len; 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); &msg, iov, ioc, len);
if (ret < 0) { if (ret < 0) {
_debug("sendmsg failed: %d", ret); _debug("sendmsg failed: %d", ret);
...@@ -1264,7 +1264,7 @@ void rxrpc_process_call(struct work_struct *work) ...@@ -1264,7 +1264,7 @@ void rxrpc_process_call(struct work_struct *work)
if (call->state >= RXRPC_CALL_COMPLETE && if (call->state >= RXRPC_CALL_COMPLETE &&
!list_empty(&call->accept_link)) { !list_empty(&call->accept_link)) {
_debug("X unlinking once-pending call %p { e=%lx f=%lx c=%x }", _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); read_lock_bh(&call->state_lock);
if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) && if (!test_bit(RXRPC_CALL_RELEASED, &call->flags) &&
...@@ -1282,7 +1282,7 @@ void rxrpc_process_call(struct work_struct *work) ...@@ -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 * this means there's a race between clearing the flag and setting the
* work pending bit and the work item being processed again */ * work pending bit and the work item being processed again */
if (call->events && !work_pending(&call->processor)) { if (call->events && !work_pending(&call->processor)) {
_debug("jumpstart %x", call->conn->cid); _debug("jumpstart %x", call->conn->proto.cid);
rxrpc_queue_call(call); rxrpc_queue_call(call);
} }
......
...@@ -31,6 +31,8 @@ unsigned int rxrpc_max_call_lifetime = 60 * HZ; ...@@ -31,6 +31,8 @@ unsigned int rxrpc_max_call_lifetime = 60 * HZ;
unsigned int rxrpc_dead_call_expiry = 2 * HZ; unsigned int rxrpc_dead_call_expiry = 2 * HZ;
const char *const rxrpc_call_states[NR__RXRPC_CALL_STATES] = { const char *const rxrpc_call_states[NR__RXRPC_CALL_STATES] = {
[RXRPC_CALL_UNINITIALISED] = "Uninit",
[RXRPC_CALL_CLIENT_AWAIT_CONN] = "ClWtConn",
[RXRPC_CALL_CLIENT_SEND_REQUEST] = "ClSndReq", [RXRPC_CALL_CLIENT_SEND_REQUEST] = "ClSndReq",
[RXRPC_CALL_CLIENT_AWAIT_REPLY] = "ClAwtRpl", [RXRPC_CALL_CLIENT_AWAIT_REPLY] = "ClAwtRpl",
[RXRPC_CALL_CLIENT_RECV_REPLY] = "ClRcvRpl", [RXRPC_CALL_CLIENT_RECV_REPLY] = "ClRcvRpl",
...@@ -71,7 +73,7 @@ static unsigned long rxrpc_call_hashfunc( ...@@ -71,7 +73,7 @@ static unsigned long rxrpc_call_hashfunc(
u32 call_id, u32 call_id,
u32 epoch, u32 epoch,
u16 service_id, u16 service_id,
sa_family_t proto, sa_family_t family,
void *localptr, void *localptr,
unsigned int addr_size, unsigned int addr_size,
const u8 *peer_addr) const u8 *peer_addr)
...@@ -92,7 +94,7 @@ static unsigned long rxrpc_call_hashfunc( ...@@ -92,7 +94,7 @@ static unsigned long rxrpc_call_hashfunc(
key += (cid & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT; key += (cid & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT;
key += cid & RXRPC_CHANNELMASK; key += cid & RXRPC_CHANNELMASK;
key += in_clientflag; key += in_clientflag;
key += proto; key += family;
/* Step through the peer address in 16-bit portions for speed */ /* Step through the peer address in 16-bit portions for speed */
for (i = 0, p = (const u16 *)peer_addr; i < addr_size >> 1; i++, p++) for (i = 0, p = (const u16 *)peer_addr; i < addr_size >> 1; i++, p++)
key += *p; key += *p;
...@@ -109,7 +111,7 @@ static void rxrpc_call_hash_add(struct rxrpc_call *call) ...@@ -109,7 +111,7 @@ static void rxrpc_call_hash_add(struct rxrpc_call *call)
unsigned int addr_size = 0; unsigned int addr_size = 0;
_enter(""); _enter("");
switch (call->proto) { switch (call->family) {
case AF_INET: case AF_INET:
addr_size = sizeof(call->peer_ip.ipv4_addr); addr_size = sizeof(call->peer_ip.ipv4_addr);
break; break;
...@@ -121,8 +123,8 @@ static void rxrpc_call_hash_add(struct rxrpc_call *call) ...@@ -121,8 +123,8 @@ static void rxrpc_call_hash_add(struct rxrpc_call *call)
} }
key = rxrpc_call_hashfunc(call->in_clientflag, call->cid, key = rxrpc_call_hashfunc(call->in_clientflag, call->cid,
call->call_id, call->epoch, call->call_id, call->epoch,
call->service_id, call->proto, call->service_id, call->family,
call->conn->trans->local, addr_size, call->conn->params.local, addr_size,
call->peer_ip.ipv6_addr); call->peer_ip.ipv6_addr);
/* Store the full key in the call */ /* Store the full key in the call */
call->hash_key = key; call->hash_key = key;
...@@ -151,7 +153,7 @@ static void rxrpc_call_hash_del(struct rxrpc_call *call) ...@@ -151,7 +153,7 @@ static void rxrpc_call_hash_del(struct rxrpc_call *call)
struct rxrpc_call *rxrpc_find_call_hash( struct rxrpc_call *rxrpc_find_call_hash(
struct rxrpc_host_header *hdr, struct rxrpc_host_header *hdr,
void *localptr, void *localptr,
sa_family_t proto, sa_family_t family,
const void *peer_addr) const void *peer_addr)
{ {
unsigned long key; unsigned long key;
...@@ -161,7 +163,7 @@ struct rxrpc_call *rxrpc_find_call_hash( ...@@ -161,7 +163,7 @@ struct rxrpc_call *rxrpc_find_call_hash(
u8 in_clientflag = hdr->flags & RXRPC_CLIENT_INITIATED; u8 in_clientflag = hdr->flags & RXRPC_CLIENT_INITIATED;
_enter(""); _enter("");
switch (proto) { switch (family) {
case AF_INET: case AF_INET:
addr_size = sizeof(call->peer_ip.ipv4_addr); addr_size = sizeof(call->peer_ip.ipv4_addr);
break; break;
...@@ -174,7 +176,7 @@ struct rxrpc_call *rxrpc_find_call_hash( ...@@ -174,7 +176,7 @@ struct rxrpc_call *rxrpc_find_call_hash(
key = rxrpc_call_hashfunc(in_clientflag, hdr->cid, hdr->callNumber, key = rxrpc_call_hashfunc(in_clientflag, hdr->cid, hdr->callNumber,
hdr->epoch, hdr->serviceId, hdr->epoch, hdr->serviceId,
proto, localptr, addr_size, family, localptr, addr_size,
peer_addr); peer_addr);
hash_for_each_possible_rcu(rxrpc_call_hash, call, hash_node, key) { hash_for_each_possible_rcu(rxrpc_call_hash, call, hash_node, key) {
if (call->hash_key == key && if (call->hash_key == key &&
...@@ -182,7 +184,7 @@ struct rxrpc_call *rxrpc_find_call_hash( ...@@ -182,7 +184,7 @@ struct rxrpc_call *rxrpc_find_call_hash(
call->cid == hdr->cid && call->cid == hdr->cid &&
call->in_clientflag == in_clientflag && call->in_clientflag == in_clientflag &&
call->service_id == hdr->serviceId && call->service_id == hdr->serviceId &&
call->proto == proto && call->family == family &&
call->local == localptr && call->local == localptr &&
memcmp(call->peer_ip.ipv6_addr, peer_addr, memcmp(call->peer_ip.ipv6_addr, peer_addr,
addr_size) == 0 && addr_size) == 0 &&
...@@ -261,6 +263,7 @@ static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp) ...@@ -261,6 +263,7 @@ static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp)
(unsigned long) call); (unsigned long) call);
INIT_WORK(&call->destroyer, &rxrpc_destroy_call); INIT_WORK(&call->destroyer, &rxrpc_destroy_call);
INIT_WORK(&call->processor, &rxrpc_process_call); INIT_WORK(&call->processor, &rxrpc_process_call);
INIT_LIST_HEAD(&call->link);
INIT_LIST_HEAD(&call->accept_link); INIT_LIST_HEAD(&call->accept_link);
skb_queue_head_init(&call->rx_queue); skb_queue_head_init(&call->rx_queue);
skb_queue_head_init(&call->rx_oos_queue); skb_queue_head_init(&call->rx_oos_queue);
...@@ -269,7 +272,6 @@ static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp) ...@@ -269,7 +272,6 @@ static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp)
rwlock_init(&call->state_lock); rwlock_init(&call->state_lock);
atomic_set(&call->usage, 1); atomic_set(&call->usage, 1);
call->debug_id = atomic_inc_return(&rxrpc_debug_id); call->debug_id = atomic_inc_return(&rxrpc_debug_id);
call->state = RXRPC_CALL_CLIENT_SEND_REQUEST;
memset(&call->sock_node, 0xed, sizeof(call->sock_node)); memset(&call->sock_node, 0xed, sizeof(call->sock_node));
...@@ -282,66 +284,77 @@ static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp) ...@@ -282,66 +284,77 @@ static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp)
} }
/* /*
* allocate a new client call and attempt to get a connection slot for it * Allocate a new client call.
*/ */
static struct rxrpc_call *rxrpc_alloc_client_call( static struct rxrpc_call *rxrpc_alloc_client_call(struct rxrpc_sock *rx,
struct rxrpc_sock *rx, struct sockaddr_rxrpc *srx,
struct rxrpc_transport *trans,
struct rxrpc_conn_bundle *bundle,
gfp_t gfp) gfp_t gfp)
{ {
struct rxrpc_call *call; struct rxrpc_call *call;
int ret;
_enter(""); _enter("");
ASSERT(rx != NULL); ASSERT(rx->local != NULL);
ASSERT(trans != NULL);
ASSERT(bundle != NULL);
call = rxrpc_alloc_call(gfp); call = rxrpc_alloc_call(gfp);
if (!call) if (!call)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
call->state = RXRPC_CALL_CLIENT_AWAIT_CONN;
sock_hold(&rx->sk); sock_hold(&rx->sk);
call->socket = rx; call->socket = rx;
call->rx_data_post = 1; call->rx_data_post = 1;
ret = rxrpc_connect_call(rx, trans, bundle, call, gfp);
if (ret < 0) {
kmem_cache_free(rxrpc_call_jar, call);
return ERR_PTR(ret);
}
/* Record copies of information for hashtable lookup */ /* Record copies of information for hashtable lookup */
call->proto = rx->proto; call->family = rx->family;
call->local = trans->local; call->local = rx->local;
switch (call->proto) { switch (call->family) {
case AF_INET: case AF_INET:
call->peer_ip.ipv4_addr = call->peer_ip.ipv4_addr = srx->transport.sin.sin_addr.s_addr;
trans->peer->srx.transport.sin.sin_addr.s_addr;
break; break;
case AF_INET6: case AF_INET6:
memcpy(call->peer_ip.ipv6_addr, memcpy(call->peer_ip.ipv6_addr,
trans->peer->srx.transport.sin6.sin6_addr.in6_u.u6_addr8, srx->transport.sin6.sin6_addr.in6_u.u6_addr8,
sizeof(call->peer_ip.ipv6_addr)); sizeof(call->peer_ip.ipv6_addr));
break; break;
} }
call->epoch = call->conn->epoch;
call->service_id = call->conn->service_id; call->service_id = srx->srx_service;
call->in_clientflag = call->conn->in_clientflag; call->in_clientflag = 0;
_leave(" = %p", call);
return call;
}
/*
* Begin client call.
*/
static int rxrpc_begin_client_call(struct rxrpc_call *call,
struct rxrpc_conn_parameters *cp,
struct sockaddr_rxrpc *srx,
gfp_t gfp)
{
int ret;
/* Set up or get a connection record and set the protocol parameters,
* including channel number and call ID.
*/
ret = rxrpc_connect_call(call, cp, srx, gfp);
if (ret < 0)
return ret;
call->state = RXRPC_CALL_CLIENT_SEND_REQUEST;
/* Add the new call to the hashtable */ /* Add the new call to the hashtable */
rxrpc_call_hash_add(call); rxrpc_call_hash_add(call);
spin_lock(&call->conn->trans->peer->lock); spin_lock(&call->conn->params.peer->lock);
hlist_add_head(&call->error_link, &call->conn->trans->peer->error_targets); hlist_add_head(&call->error_link, &call->conn->params.peer->error_targets);
spin_unlock(&call->conn->trans->peer->lock); spin_unlock(&call->conn->params.peer->lock);
call->lifetimer.expires = jiffies + rxrpc_max_call_lifetime; call->lifetimer.expires = jiffies + rxrpc_max_call_lifetime;
add_timer(&call->lifetimer); add_timer(&call->lifetimer);
return 0;
_leave(" = %p", call);
return call;
} }
/* /*
...@@ -349,24 +362,24 @@ static struct rxrpc_call *rxrpc_alloc_client_call( ...@@ -349,24 +362,24 @@ static struct rxrpc_call *rxrpc_alloc_client_call(
* - called in process context with IRQs enabled * - called in process context with IRQs enabled
*/ */
struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
struct rxrpc_transport *trans, struct rxrpc_conn_parameters *cp,
struct rxrpc_conn_bundle *bundle, struct sockaddr_rxrpc *srx,
unsigned long user_call_ID, unsigned long user_call_ID,
gfp_t gfp) gfp_t gfp)
{ {
struct rxrpc_call *call, *xcall; struct rxrpc_call *call, *xcall;
struct rb_node *parent, **pp; struct rb_node *parent, **pp;
int ret;
_enter("%p,%d,%d,%lx", _enter("%p,%lx", rx, user_call_ID);
rx, trans->debug_id, bundle ? bundle->debug_id : -1,
user_call_ID);
call = rxrpc_alloc_client_call(rx, trans, bundle, gfp); call = rxrpc_alloc_client_call(rx, srx, gfp);
if (IS_ERR(call)) { if (IS_ERR(call)) {
_leave(" = %ld", PTR_ERR(call)); _leave(" = %ld", PTR_ERR(call));
return call; return call;
} }
/* Publish the call, even though it is incompletely set up as yet */
call->user_call_ID = user_call_ID; call->user_call_ID = user_call_ID;
__set_bit(RXRPC_CALL_HAS_USERID, &call->flags); __set_bit(RXRPC_CALL_HAS_USERID, &call->flags);
...@@ -396,11 +409,29 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, ...@@ -396,11 +409,29 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
list_add_tail(&call->link, &rxrpc_calls); list_add_tail(&call->link, &rxrpc_calls);
write_unlock_bh(&rxrpc_call_lock); write_unlock_bh(&rxrpc_call_lock);
ret = rxrpc_begin_client_call(call, cp, srx, gfp);
if (ret < 0)
goto error;
_net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id); _net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id);
_leave(" = %p [new]", call); _leave(" = %p [new]", call);
return call; return call;
error:
write_lock(&rx->call_lock);
rb_erase(&call->sock_node, &rx->calls);
write_unlock(&rx->call_lock);
rxrpc_put_call(call);
write_lock_bh(&rxrpc_call_lock);
list_del(&call->link);
write_unlock_bh(&rxrpc_call_lock);
rxrpc_put_call(call);
_leave(" = %d", ret);
return ERR_PTR(ret);
/* We unexpectedly found the user ID in the list after taking /* We unexpectedly found the user ID in the list after taking
* the call_lock. This shouldn't happen unless the user races * the call_lock. This shouldn't happen unless the user races
* with itself and tries to add the same user ID twice at the * with itself and tries to add the same user ID twice at the
...@@ -419,8 +450,9 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, ...@@ -419,8 +450,9 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
*/ */
struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx,
struct rxrpc_connection *conn, struct rxrpc_connection *conn,
struct rxrpc_host_header *hdr) struct sk_buff *skb)
{ {
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rxrpc_call *call, *candidate; struct rxrpc_call *call, *candidate;
struct rb_node **p, *parent; struct rb_node **p, *parent;
u32 call_id; u32 call_id;
...@@ -435,9 +467,9 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, ...@@ -435,9 +467,9 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx,
candidate->socket = rx; candidate->socket = rx;
candidate->conn = conn; candidate->conn = conn;
candidate->cid = hdr->cid; candidate->cid = sp->hdr.cid;
candidate->call_id = hdr->callNumber; candidate->call_id = sp->hdr.callNumber;
candidate->channel = hdr->cid & RXRPC_CHANNELMASK; candidate->channel = sp->hdr.cid & RXRPC_CHANNELMASK;
candidate->rx_data_post = 0; candidate->rx_data_post = 0;
candidate->state = RXRPC_CALL_SERVER_ACCEPTING; candidate->state = RXRPC_CALL_SERVER_ACCEPTING;
if (conn->security_ix > 0) if (conn->security_ix > 0)
...@@ -448,7 +480,7 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, ...@@ -448,7 +480,7 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx,
/* set the channel for this call */ /* set the channel for this call */
call = conn->channels[candidate->channel]; call = conn->channels[candidate->channel];
_debug("channel[%u] is %p", candidate->channel, call); _debug("channel[%u] is %p", candidate->channel, call);
if (call && call->call_id == hdr->callNumber) { if (call && call->call_id == sp->hdr.callNumber) {
/* already set; must've been a duplicate packet */ /* already set; must've been a duplicate packet */
_debug("extant call [%d]", call->state); _debug("extant call [%d]", call->state);
ASSERTCMP(call->conn, ==, conn); ASSERTCMP(call->conn, ==, conn);
...@@ -486,7 +518,7 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, ...@@ -486,7 +518,7 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx,
/* check the call number isn't duplicate */ /* check the call number isn't duplicate */
_debug("check dup"); _debug("check dup");
call_id = hdr->callNumber; call_id = sp->hdr.callNumber;
p = &conn->calls.rb_node; p = &conn->calls.rb_node;
parent = NULL; parent = NULL;
while (*p) { while (*p) {
...@@ -512,36 +544,36 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, ...@@ -512,36 +544,36 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx,
rb_insert_color(&call->conn_node, &conn->calls); rb_insert_color(&call->conn_node, &conn->calls);
conn->channels[call->channel] = call; conn->channels[call->channel] = call;
sock_hold(&rx->sk); sock_hold(&rx->sk);
atomic_inc(&conn->usage); rxrpc_get_connection(conn);
write_unlock_bh(&conn->lock); write_unlock_bh(&conn->lock);
spin_lock(&conn->trans->peer->lock); spin_lock(&conn->params.peer->lock);
hlist_add_head(&call->error_link, &conn->trans->peer->error_targets); hlist_add_head(&call->error_link, &conn->params.peer->error_targets);
spin_unlock(&conn->trans->peer->lock); spin_unlock(&conn->params.peer->lock);
write_lock_bh(&rxrpc_call_lock); write_lock_bh(&rxrpc_call_lock);
list_add_tail(&call->link, &rxrpc_calls); list_add_tail(&call->link, &rxrpc_calls);
write_unlock_bh(&rxrpc_call_lock); write_unlock_bh(&rxrpc_call_lock);
/* Record copies of information for hashtable lookup */ /* Record copies of information for hashtable lookup */
call->proto = rx->proto; call->family = rx->family;
call->local = conn->trans->local; call->local = conn->params.local;
switch (call->proto) { switch (call->family) {
case AF_INET: case AF_INET:
call->peer_ip.ipv4_addr = call->peer_ip.ipv4_addr =
conn->trans->peer->srx.transport.sin.sin_addr.s_addr; conn->params.peer->srx.transport.sin.sin_addr.s_addr;
break; break;
case AF_INET6: case AF_INET6:
memcpy(call->peer_ip.ipv6_addr, memcpy(call->peer_ip.ipv6_addr,
conn->trans->peer->srx.transport.sin6.sin6_addr.in6_u.u6_addr8, conn->params.peer->srx.transport.sin6.sin6_addr.in6_u.u6_addr8,
sizeof(call->peer_ip.ipv6_addr)); sizeof(call->peer_ip.ipv6_addr));
break; break;
default: default:
break; break;
} }
call->epoch = conn->epoch; call->epoch = conn->proto.epoch;
call->service_id = conn->service_id; call->service_id = conn->params.service_id;
call->in_clientflag = conn->in_clientflag; call->in_clientflag = conn->proto.in_clientflag;
/* Add the new call to the hashtable */ /* Add the new call to the hashtable */
rxrpc_call_hash_add(call); rxrpc_call_hash_add(call);
...@@ -609,40 +641,13 @@ void rxrpc_release_call(struct rxrpc_call *call) ...@@ -609,40 +641,13 @@ void rxrpc_release_call(struct rxrpc_call *call)
write_unlock_bh(&rx->call_lock); write_unlock_bh(&rx->call_lock);
/* free up the channel for reuse */ /* free up the channel for reuse */
spin_lock(&conn->trans->client_lock); spin_lock(&conn->channel_lock);
write_lock_bh(&conn->lock); write_lock_bh(&conn->lock);
write_lock(&call->state_lock); write_lock(&call->state_lock);
if (conn->channels[call->channel] == call) rxrpc_disconnect_call(call);
conn->channels[call->channel] = NULL;
if (conn->out_clientflag && conn->bundle) {
conn->avail_calls++;
switch (conn->avail_calls) {
case 1:
list_move_tail(&conn->bundle_link,
&conn->bundle->avail_conns);
case 2 ... RXRPC_MAXCALLS - 1:
ASSERT(conn->channels[0] == NULL ||
conn->channels[1] == NULL ||
conn->channels[2] == NULL ||
conn->channels[3] == NULL);
break;
case RXRPC_MAXCALLS:
list_move_tail(&conn->bundle_link,
&conn->bundle->unused_conns);
ASSERT(conn->channels[0] == NULL &&
conn->channels[1] == NULL &&
conn->channels[2] == NULL &&
conn->channels[3] == NULL);
break;
default:
pr_err("conn->avail_calls=%d\n", conn->avail_calls);
BUG();
}
}
spin_unlock(&conn->trans->client_lock); spin_unlock(&conn->channel_lock);
if (call->state < RXRPC_CALL_COMPLETE && if (call->state < RXRPC_CALL_COMPLETE &&
call->state != RXRPC_CALL_CLIENT_FINAL_ACK) { call->state != RXRPC_CALL_CLIENT_FINAL_ACK) {
...@@ -811,9 +816,9 @@ static void rxrpc_cleanup_call(struct rxrpc_call *call) ...@@ -811,9 +816,9 @@ static void rxrpc_cleanup_call(struct rxrpc_call *call)
} }
if (call->conn) { if (call->conn) {
spin_lock(&call->conn->trans->peer->lock); spin_lock(&call->conn->params.peer->lock);
hlist_del_init(&call->error_link); hlist_del_init(&call->error_link);
spin_unlock(&call->conn->trans->peer->lock); spin_unlock(&call->conn->params.peer->lock);
write_lock_bh(&call->conn->lock); write_lock_bh(&call->conn->lock);
rb_erase(&call->conn_node, &call->conn->calls); rb_erase(&call->conn_node, &call->conn->calls);
......
/* 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, ...@@ -88,14 +88,14 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn,
rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, abort_code); rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, abort_code);
msg.msg_name = &conn->trans->peer->srx.transport; msg.msg_name = &conn->params.peer->srx.transport;
msg.msg_namelen = conn->trans->peer->srx.transport_len; msg.msg_namelen = conn->params.peer->srx.transport_len;
msg.msg_control = NULL; msg.msg_control = NULL;
msg.msg_controllen = 0; msg.msg_controllen = 0;
msg.msg_flags = 0; msg.msg_flags = 0;
whdr.epoch = htonl(conn->epoch); whdr.epoch = htonl(conn->proto.epoch);
whdr.cid = htonl(conn->cid); whdr.cid = htonl(conn->proto.cid);
whdr.callNumber = 0; whdr.callNumber = 0;
whdr.seq = 0; whdr.seq = 0;
whdr.type = RXRPC_PACKET_TYPE_ABORT; whdr.type = RXRPC_PACKET_TYPE_ABORT;
...@@ -103,7 +103,7 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn, ...@@ -103,7 +103,7 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn,
whdr.userStatus = 0; whdr.userStatus = 0;
whdr.securityIndex = conn->security_ix; whdr.securityIndex = conn->security_ix;
whdr._rsvd = 0; whdr._rsvd = 0;
whdr.serviceId = htons(conn->service_id); whdr.serviceId = htons(conn->params.service_id);
word = htonl(conn->local_abort); word = htonl(conn->local_abort);
...@@ -118,7 +118,7 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn, ...@@ -118,7 +118,7 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn,
whdr.serial = htonl(serial); whdr.serial = htonl(serial);
_proto("Tx CONN ABORT %%%u { %d }", serial, conn->local_abort); _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) { if (ret < 0) {
_debug("sendmsg failed: %d", ret); _debug("sendmsg failed: %d", ret);
return -EAGAIN; return -EAGAIN;
...@@ -220,7 +220,7 @@ static void rxrpc_secure_connection(struct rxrpc_connection *conn) ...@@ -220,7 +220,7 @@ static void rxrpc_secure_connection(struct rxrpc_connection *conn)
ASSERT(conn->security_ix != 0); ASSERT(conn->security_ix != 0);
if (!conn->key) { if (!conn->params.key) {
_debug("set up security"); _debug("set up security");
ret = rxrpc_init_server_conn_security(conn); ret = rxrpc_init_server_conn_security(conn);
switch (ret) { switch (ret) {
...@@ -263,7 +263,7 @@ void rxrpc_process_connection(struct work_struct *work) ...@@ -263,7 +263,7 @@ void rxrpc_process_connection(struct work_struct *work)
_enter("{%d}", conn->debug_id); _enter("{%d}", conn->debug_id);
atomic_inc(&conn->usage); rxrpc_get_connection(conn);
if (test_and_clear_bit(RXRPC_CONN_CHALLENGE, &conn->events)) { if (test_and_clear_bit(RXRPC_CONN_CHALLENGE, &conn->events)) {
rxrpc_secure_connection(conn); rxrpc_secure_connection(conn);
......
...@@ -31,152 +31,6 @@ LIST_HEAD(rxrpc_connections); ...@@ -31,152 +31,6 @@ LIST_HEAD(rxrpc_connections);
DEFINE_RWLOCK(rxrpc_connection_lock); DEFINE_RWLOCK(rxrpc_connection_lock);
static DECLARE_DELAYED_WORK(rxrpc_connection_reap, rxrpc_connection_reaper); static DECLARE_DELAYED_WORK(rxrpc_connection_reap, rxrpc_connection_reaper);
/*
* allocate a new client connection bundle
*/
static struct rxrpc_conn_bundle *rxrpc_alloc_bundle(gfp_t gfp)
{
struct rxrpc_conn_bundle *bundle;
_enter("");
bundle = kzalloc(sizeof(struct rxrpc_conn_bundle), gfp);
if (bundle) {
INIT_LIST_HEAD(&bundle->unused_conns);
INIT_LIST_HEAD(&bundle->avail_conns);
INIT_LIST_HEAD(&bundle->busy_conns);
init_waitqueue_head(&bundle->chanwait);
atomic_set(&bundle->usage, 1);
}
_leave(" = %p", bundle);
return bundle;
}
/*
* compare bundle parameters with what we're looking for
* - return -ve, 0 or +ve
*/
static inline
int rxrpc_cmp_bundle(const struct rxrpc_conn_bundle *bundle,
struct key *key, u16 service_id)
{
return (bundle->service_id - service_id) ?:
((unsigned long)bundle->key - (unsigned long)key);
}
/*
* get bundle of client connections that a client socket can make use of
*/
struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *rx,
struct rxrpc_transport *trans,
struct key *key,
u16 service_id,
gfp_t gfp)
{
struct rxrpc_conn_bundle *bundle, *candidate;
struct rb_node *p, *parent, **pp;
_enter("%p{%x},%x,%hx,",
rx, key_serial(key), trans->debug_id, service_id);
/* search the extant bundles first for one that matches the specified
* user ID */
spin_lock(&trans->client_lock);
p = trans->bundles.rb_node;
while (p) {
bundle = rb_entry(p, struct rxrpc_conn_bundle, node);
if (rxrpc_cmp_bundle(bundle, key, service_id) < 0)
p = p->rb_left;
else if (rxrpc_cmp_bundle(bundle, key, service_id) > 0)
p = p->rb_right;
else
goto found_extant_bundle;
}
spin_unlock(&trans->client_lock);
/* not yet present - create a candidate for a new record and then
* redo the search */
candidate = rxrpc_alloc_bundle(gfp);
if (!candidate) {
_leave(" = -ENOMEM");
return ERR_PTR(-ENOMEM);
}
candidate->key = key_get(key);
candidate->service_id = service_id;
spin_lock(&trans->client_lock);
pp = &trans->bundles.rb_node;
parent = NULL;
while (*pp) {
parent = *pp;
bundle = rb_entry(parent, struct rxrpc_conn_bundle, node);
if (rxrpc_cmp_bundle(bundle, key, service_id) < 0)
pp = &(*pp)->rb_left;
else if (rxrpc_cmp_bundle(bundle, key, service_id) > 0)
pp = &(*pp)->rb_right;
else
goto found_extant_second;
}
/* second search also failed; add the new bundle */
bundle = candidate;
candidate = NULL;
rb_link_node(&bundle->node, parent, pp);
rb_insert_color(&bundle->node, &trans->bundles);
spin_unlock(&trans->client_lock);
_net("BUNDLE new on trans %d", trans->debug_id);
_leave(" = %p [new]", bundle);
return bundle;
/* we found the bundle in the list immediately */
found_extant_bundle:
atomic_inc(&bundle->usage);
spin_unlock(&trans->client_lock);
_net("BUNDLE old on trans %d", trans->debug_id);
_leave(" = %p [extant %d]", bundle, atomic_read(&bundle->usage));
return bundle;
/* we found the bundle on the second time through the list */
found_extant_second:
atomic_inc(&bundle->usage);
spin_unlock(&trans->client_lock);
kfree(candidate);
_net("BUNDLE old2 on trans %d", trans->debug_id);
_leave(" = %p [second %d]", bundle, atomic_read(&bundle->usage));
return bundle;
}
/*
* release a bundle
*/
void rxrpc_put_bundle(struct rxrpc_transport *trans,
struct rxrpc_conn_bundle *bundle)
{
_enter("%p,%p{%d}",trans, bundle, atomic_read(&bundle->usage));
if (atomic_dec_and_lock(&bundle->usage, &trans->client_lock)) {
_debug("Destroy bundle");
rb_erase(&bundle->node, &trans->bundles);
spin_unlock(&trans->client_lock);
ASSERT(list_empty(&bundle->unused_conns));
ASSERT(list_empty(&bundle->avail_conns));
ASSERT(list_empty(&bundle->busy_conns));
ASSERTCMP(bundle->num_conns, ==, 0);
key_put(bundle->key);
kfree(bundle);
}
_leave("");
}
/* /*
* allocate a new connection * allocate a new connection
*/ */
...@@ -188,8 +42,10 @@ static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) ...@@ -188,8 +42,10 @@ static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp)
conn = kzalloc(sizeof(struct rxrpc_connection), gfp); conn = kzalloc(sizeof(struct rxrpc_connection), gfp);
if (conn) { if (conn) {
spin_lock_init(&conn->channel_lock);
init_waitqueue_head(&conn->channel_wq);
INIT_WORK(&conn->processor, &rxrpc_process_connection); INIT_WORK(&conn->processor, &rxrpc_process_connection);
INIT_LIST_HEAD(&conn->bundle_link); INIT_LIST_HEAD(&conn->link);
conn->calls = RB_ROOT; conn->calls = RB_ROOT;
skb_queue_head_init(&conn->rx_queue); skb_queue_head_init(&conn->rx_queue);
conn->security = &rxrpc_no_security; conn->security = &rxrpc_no_security;
...@@ -197,7 +53,7 @@ static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) ...@@ -197,7 +53,7 @@ static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp)
spin_lock_init(&conn->state_lock); spin_lock_init(&conn->state_lock);
atomic_set(&conn->usage, 1); atomic_set(&conn->usage, 1);
conn->debug_id = atomic_inc_return(&rxrpc_debug_id); conn->debug_id = atomic_inc_return(&rxrpc_debug_id);
conn->avail_calls = RXRPC_MAXCALLS; atomic_set(&conn->avail_chans, RXRPC_MAXCALLS);
conn->size_align = 4; conn->size_align = 4;
conn->header_size = sizeof(struct rxrpc_wire_header); conn->header_size = sizeof(struct rxrpc_wire_header);
} }
...@@ -206,81 +62,6 @@ static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) ...@@ -206,81 +62,6 @@ static struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp)
return conn; return conn;
} }
/*
* assign a connection ID to a connection and add it to the transport's
* connection lookup tree
* - called with transport client lock held
*/
static void rxrpc_assign_connection_id(struct rxrpc_connection *conn)
{
struct rxrpc_connection *xconn;
struct rb_node *parent, **p;
__be32 epoch;
u32 cid;
_enter("");
epoch = conn->epoch;
write_lock_bh(&conn->trans->conn_lock);
conn->trans->conn_idcounter += RXRPC_CID_INC;
if (conn->trans->conn_idcounter < RXRPC_CID_INC)
conn->trans->conn_idcounter = RXRPC_CID_INC;
cid = conn->trans->conn_idcounter;
attempt_insertion:
parent = NULL;
p = &conn->trans->client_conns.rb_node;
while (*p) {
parent = *p;
xconn = rb_entry(parent, struct rxrpc_connection, node);
if (epoch < xconn->epoch)
p = &(*p)->rb_left;
else if (epoch > xconn->epoch)
p = &(*p)->rb_right;
else if (cid < xconn->cid)
p = &(*p)->rb_left;
else if (cid > xconn->cid)
p = &(*p)->rb_right;
else
goto id_exists;
}
/* we've found a suitable hole - arrange for this connection to occupy
* it */
rb_link_node(&conn->node, parent, p);
rb_insert_color(&conn->node, &conn->trans->client_conns);
conn->cid = cid;
write_unlock_bh(&conn->trans->conn_lock);
_leave(" [CID %x]", cid);
return;
/* we found a connection with the proposed ID - walk the tree from that
* point looking for the next unused ID */
id_exists:
for (;;) {
cid += RXRPC_CID_INC;
if (cid < RXRPC_CID_INC) {
cid = RXRPC_CID_INC;
conn->trans->conn_idcounter = cid;
goto attempt_insertion;
}
parent = rb_next(parent);
if (!parent)
goto attempt_insertion;
xconn = rb_entry(parent, struct rxrpc_connection, node);
if (epoch < xconn->epoch ||
cid < xconn->cid)
goto attempt_insertion;
}
}
/* /*
* add a call to a connection's call-by-ID tree * add a call to a connection's call-by-ID tree
*/ */
...@@ -315,286 +96,242 @@ static void rxrpc_add_call_ID_to_conn(struct rxrpc_connection *conn, ...@@ -315,286 +96,242 @@ static void rxrpc_add_call_ID_to_conn(struct rxrpc_connection *conn,
} }
/* /*
* connect a call on an exclusive connection * Allocate a client connection. The caller must take care to clear any
* padding bytes in *cp.
*/ */
static int rxrpc_connect_exclusive(struct rxrpc_sock *rx, static struct rxrpc_connection *
struct rxrpc_transport *trans, rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp)
u16 service_id,
struct rxrpc_call *call,
gfp_t gfp)
{ {
struct rxrpc_connection *conn; struct rxrpc_connection *conn;
int chan, ret; int ret;
_enter(""); _enter("");
conn = rx->conn;
if (!conn) {
/* not yet present - create a candidate for a new connection
* and then redo the check */
conn = rxrpc_alloc_connection(gfp); conn = rxrpc_alloc_connection(gfp);
if (!conn) { if (!conn) {
_leave(" = -ENOMEM"); _leave(" = -ENOMEM");
return -ENOMEM; return ERR_PTR(-ENOMEM);
} }
conn->trans = trans; conn->params = *cp;
conn->bundle = NULL; conn->proto.local = cp->local;
conn->service_id = service_id; conn->proto.epoch = rxrpc_epoch;
conn->epoch = rxrpc_epoch; conn->proto.cid = 0;
conn->in_clientflag = 0; conn->proto.in_clientflag = 0;
conn->proto.family = cp->peer->srx.transport.family;
conn->out_clientflag = RXRPC_CLIENT_INITIATED; conn->out_clientflag = RXRPC_CLIENT_INITIATED;
conn->cid = 0;
conn->state = RXRPC_CONN_CLIENT; conn->state = RXRPC_CONN_CLIENT;
conn->avail_calls = RXRPC_MAXCALLS - 1;
conn->security_level = rx->min_sec_level;
conn->key = key_get(rx->key);
ret = rxrpc_init_client_conn_security(conn); switch (conn->proto.family) {
if (ret < 0) { case AF_INET:
key_put(conn->key); conn->proto.addr_size = sizeof(conn->proto.ipv4_addr);
kfree(conn); conn->proto.ipv4_addr = cp->peer->srx.transport.sin.sin_addr;
_leave(" = %d [key]", ret); conn->proto.port = cp->peer->srx.transport.sin.sin_port;
return ret; break;
} }
write_lock_bh(&rxrpc_connection_lock); ret = rxrpc_get_client_connection_id(conn, gfp);
list_add_tail(&conn->link, &rxrpc_connections); if (ret < 0)
write_unlock_bh(&rxrpc_connection_lock); goto error_0;
spin_lock(&trans->client_lock);
atomic_inc(&trans->usage);
_net("CONNECT EXCL new %d on TRANS %d",
conn->debug_id, conn->trans->debug_id);
rxrpc_assign_connection_id(conn);
rx->conn = conn;
} else {
spin_lock(&trans->client_lock);
}
/* we've got a connection with a free channel and we can now attach the ret = rxrpc_init_client_conn_security(conn);
* call to it if (ret < 0)
* - we're holding the transport's client lock goto error_1;
* - we're holding a reference on the connection
*/
for (chan = 0; chan < RXRPC_MAXCALLS; chan++)
if (!conn->channels[chan])
goto found_channel;
goto no_free_channels;
found_channel: conn->security->prime_packet_security(conn);
atomic_inc(&conn->usage);
conn->channels[chan] = call;
call->conn = conn;
call->channel = chan;
call->cid = conn->cid | chan;
call->call_id = ++conn->call_counter;
_net("CONNECT client on conn %d chan %d as call %x", write_lock(&rxrpc_connection_lock);
conn->debug_id, chan, call->call_id); list_add_tail(&conn->link, &rxrpc_connections);
write_unlock(&rxrpc_connection_lock);
spin_unlock(&trans->client_lock); /* We steal the caller's peer ref. */
cp->peer = NULL;
rxrpc_get_local(conn->params.local);
key_get(conn->params.key);
rxrpc_add_call_ID_to_conn(conn, call); _leave(" = %p", conn);
_leave(" = 0"); return conn;
return 0;
no_free_channels: error_1:
spin_unlock(&trans->client_lock); rxrpc_put_client_connection_id(conn);
_leave(" = -ENOSR"); error_0:
return -ENOSR; kfree(conn);
_leave(" = %d", ret);
return ERR_PTR(ret);
} }
/* /*
* find a connection for a call * find a connection for a call
* - called in process context with IRQs enabled * - called in process context with IRQs enabled
*/ */
int rxrpc_connect_call(struct rxrpc_sock *rx, int rxrpc_connect_call(struct rxrpc_call *call,
struct rxrpc_transport *trans, struct rxrpc_conn_parameters *cp,
struct rxrpc_conn_bundle *bundle, struct sockaddr_rxrpc *srx,
struct rxrpc_call *call,
gfp_t gfp) gfp_t gfp)
{ {
struct rxrpc_connection *conn, *candidate; struct rxrpc_connection *conn, *candidate = NULL;
int chan, ret; struct rxrpc_local *local = cp->local;
struct rb_node *p, **pp, *parent;
long diff;
int chan;
DECLARE_WAITQUEUE(myself, current); DECLARE_WAITQUEUE(myself, current);
_enter("%p,%lx,", rx, call->user_call_ID); _enter("{%d,%lx},", call->debug_id, call->user_call_ID);
if (test_bit(RXRPC_SOCK_EXCLUSIVE_CONN, &rx->flags)) cp->peer = rxrpc_lookup_peer(cp->local, srx, gfp);
return rxrpc_connect_exclusive(rx, trans, bundle->service_id, if (!cp->peer)
call, gfp); return -ENOMEM;
spin_lock(&trans->client_lock); if (!cp->exclusive) {
for (;;) { /* Search for a existing client connection unless this is going
/* see if the bundle has a call slot available */ * to be a connection that's used exclusively for a single call.
if (!list_empty(&bundle->avail_conns)) { */
_debug("avail"); _debug("search 1");
conn = list_entry(bundle->avail_conns.next, spin_lock(&local->client_conns_lock);
struct rxrpc_connection, p = local->client_conns.rb_node;
bundle_link); while (p) {
if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) { conn = rb_entry(p, struct rxrpc_connection, client_node);
list_del_init(&conn->bundle_link);
bundle->num_conns--; #define cmp(X) ((long)conn->params.X - (long)cp->X)
continue; diff = (cmp(peer) ?:
cmp(key) ?:
cmp(security_level));
if (diff < 0)
p = p->rb_left;
else if (diff > 0)
p = p->rb_right;
else
goto found_extant_conn;
} }
if (--conn->avail_calls == 0) spin_unlock(&local->client_conns_lock);
list_move(&conn->bundle_link,
&bundle->busy_conns);
ASSERTCMP(conn->avail_calls, <, RXRPC_MAXCALLS);
ASSERT(conn->channels[0] == NULL ||
conn->channels[1] == NULL ||
conn->channels[2] == NULL ||
conn->channels[3] == NULL);
atomic_inc(&conn->usage);
break;
} }
if (!list_empty(&bundle->unused_conns)) { /* We didn't find a connection or we want an exclusive one. */
_debug("unused"); _debug("get new conn");
conn = list_entry(bundle->unused_conns.next, candidate = rxrpc_alloc_client_connection(cp, gfp);
struct rxrpc_connection, if (!candidate) {
bundle_link); _leave(" = -ENOMEM");
if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) { return -ENOMEM;
list_del_init(&conn->bundle_link);
bundle->num_conns--;
continue;
} }
ASSERTCMP(conn->avail_calls, ==, RXRPC_MAXCALLS);
conn->avail_calls = RXRPC_MAXCALLS - 1; if (cp->exclusive) {
ASSERT(conn->channels[0] == NULL && /* Assign the call on an exclusive connection to channel 0 and
conn->channels[1] == NULL && * don't add the connection to the endpoint's shareable conn
conn->channels[2] == NULL && * lookup tree.
conn->channels[3] == NULL); */
atomic_inc(&conn->usage); _debug("exclusive chan 0");
list_move(&conn->bundle_link, &bundle->avail_conns); conn = candidate;
break; atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1);
spin_lock(&conn->channel_lock);
chan = 0;
goto found_channel;
} }
/* need to allocate a new connection */ /* We need to redo the search before attempting to add a new connection
_debug("get new conn [%d]", bundle->num_conns); * lest we race with someone else adding a conflicting instance.
*/
_debug("search 2");
spin_lock(&local->client_conns_lock);
spin_unlock(&trans->client_lock); pp = &local->client_conns.rb_node;
parent = NULL;
while (*pp) {
parent = *pp;
conn = rb_entry(parent, struct rxrpc_connection, client_node);
if (signal_pending(current)) diff = (cmp(peer) ?:
goto interrupted; cmp(key) ?:
cmp(security_level));
if (diff < 0)
pp = &(*pp)->rb_left;
else if (diff > 0)
pp = &(*pp)->rb_right;
else
goto found_extant_conn;
}
if (bundle->num_conns >= 20) { /* The second search also failed; simply add the new connection with
_debug("too many conns"); * the new call in channel 0. Note that we need to take the channel
* lock before dropping the client conn lock.
*/
_debug("new conn");
conn = candidate;
candidate = NULL;
rb_link_node(&conn->client_node, parent, pp);
rb_insert_color(&conn->client_node, &local->client_conns);
atomic_set(&conn->avail_chans, RXRPC_MAXCALLS - 1);
spin_lock(&conn->channel_lock);
spin_unlock(&local->client_conns_lock);
chan = 0;
found_channel:
_debug("found chan");
call->conn = conn;
call->channel = chan;
call->epoch = conn->proto.epoch;
call->cid = conn->proto.cid | chan;
call->call_id = ++conn->call_counter;
rcu_assign_pointer(conn->channels[chan], call);
_net("CONNECT call %d on conn %d", call->debug_id, conn->debug_id);
rxrpc_add_call_ID_to_conn(conn, call);
spin_unlock(&conn->channel_lock);
rxrpc_put_peer(cp->peer);
cp->peer = NULL;
_leave(" = %p {u=%d}", conn, atomic_read(&conn->usage));
return 0;
/* We found a suitable connection already in existence. Discard any
* candidate we may have allocated, and try to get a channel on this
* one.
*/
found_extant_conn:
_debug("found conn");
rxrpc_get_connection(conn);
spin_unlock(&local->client_conns_lock);
rxrpc_put_connection(candidate);
if (!atomic_add_unless(&conn->avail_chans, -1, 0)) {
if (!gfpflags_allow_blocking(gfp)) { if (!gfpflags_allow_blocking(gfp)) {
rxrpc_put_connection(conn);
_leave(" = -EAGAIN"); _leave(" = -EAGAIN");
return -EAGAIN; return -EAGAIN;
} }
add_wait_queue(&bundle->chanwait, &myself); add_wait_queue(&conn->channel_wq, &myself);
for (;;) { for (;;) {
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
if (bundle->num_conns < 20 || if (atomic_add_unless(&conn->avail_chans, -1, 0))
!list_empty(&bundle->unused_conns) ||
!list_empty(&bundle->avail_conns))
break; break;
if (signal_pending(current)) if (signal_pending(current))
goto interrupted_dequeue; goto interrupted;
schedule(); schedule();
} }
remove_wait_queue(&bundle->chanwait, &myself); remove_wait_queue(&conn->channel_wq, &myself);
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
spin_lock(&trans->client_lock);
continue;
} }
/* not yet present - create a candidate for a new connection and then /* The connection allegedly now has a free channel and we can now
* redo the check */ * attach the call to it.
candidate = rxrpc_alloc_connection(gfp);
if (!candidate) {
_leave(" = -ENOMEM");
return -ENOMEM;
}
candidate->trans = trans;
candidate->bundle = bundle;
candidate->service_id = bundle->service_id;
candidate->epoch = rxrpc_epoch;
candidate->in_clientflag = 0;
candidate->out_clientflag = RXRPC_CLIENT_INITIATED;
candidate->cid = 0;
candidate->state = RXRPC_CONN_CLIENT;
candidate->avail_calls = RXRPC_MAXCALLS;
candidate->security_level = rx->min_sec_level;
candidate->key = key_get(bundle->key);
ret = rxrpc_init_client_conn_security(candidate);
if (ret < 0) {
key_put(candidate->key);
kfree(candidate);
_leave(" = %d [key]", ret);
return ret;
}
write_lock_bh(&rxrpc_connection_lock);
list_add_tail(&candidate->link, &rxrpc_connections);
write_unlock_bh(&rxrpc_connection_lock);
spin_lock(&trans->client_lock);
list_add(&candidate->bundle_link, &bundle->unused_conns);
bundle->num_conns++;
atomic_inc(&bundle->usage);
atomic_inc(&trans->usage);
_net("CONNECT new %d on TRANS %d",
candidate->debug_id, candidate->trans->debug_id);
rxrpc_assign_connection_id(candidate);
candidate->security->prime_packet_security(candidate);
/* leave the candidate lurking in zombie mode attached to the
* bundle until we're ready for it */
rxrpc_put_connection(candidate);
candidate = NULL;
}
/* we've got a connection with a free channel and we can now attach the
* call to it
* - we're holding the transport's client lock
* - we're holding a reference on the connection
* - we're holding a reference on the bundle
*/ */
spin_lock(&conn->channel_lock);
for (chan = 0; chan < RXRPC_MAXCALLS; chan++) for (chan = 0; chan < RXRPC_MAXCALLS; chan++)
if (!conn->channels[chan]) if (!conn->channels[chan])
goto found_channel; goto found_channel;
ASSERT(conn->channels[0] == NULL ||
conn->channels[1] == NULL ||
conn->channels[2] == NULL ||
conn->channels[3] == NULL);
BUG(); BUG();
found_channel:
conn->channels[chan] = call;
call->conn = conn;
call->channel = chan;
call->cid = conn->cid | chan;
call->call_id = ++conn->call_counter;
_net("CONNECT client on conn %d chan %d as call %x",
conn->debug_id, chan, call->call_id);
ASSERTCMP(conn->avail_calls, <, RXRPC_MAXCALLS);
spin_unlock(&trans->client_lock);
rxrpc_add_call_ID_to_conn(conn, call);
_leave(" = 0");
return 0;
interrupted_dequeue:
remove_wait_queue(&bundle->chanwait, &myself);
__set_current_state(TASK_RUNNING);
interrupted: interrupted:
remove_wait_queue(&conn->channel_wq, &myself);
__set_current_state(TASK_RUNNING);
rxrpc_put_connection(conn);
rxrpc_put_peer(cp->peer);
cp->peer = NULL;
_leave(" = -ERESTARTSYS"); _leave(" = -ERESTARTSYS");
return -ERESTARTSYS; return -ERESTARTSYS;
} }
...@@ -602,11 +339,12 @@ int rxrpc_connect_call(struct rxrpc_sock *rx, ...@@ -602,11 +339,12 @@ int rxrpc_connect_call(struct rxrpc_sock *rx,
/* /*
* get a record of an incoming connection * get a record of an incoming connection
*/ */
struct rxrpc_connection * struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *local,
rxrpc_incoming_connection(struct rxrpc_transport *trans, struct rxrpc_peer *peer,
struct rxrpc_host_header *hdr) struct sk_buff *skb)
{ {
struct rxrpc_connection *conn, *candidate = NULL; struct rxrpc_connection *conn, *candidate = NULL;
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rb_node *p, **pp; struct rb_node *p, **pp;
const char *new = "old"; const char *new = "old";
__be32 epoch; __be32 epoch;
...@@ -614,32 +352,32 @@ rxrpc_incoming_connection(struct rxrpc_transport *trans, ...@@ -614,32 +352,32 @@ rxrpc_incoming_connection(struct rxrpc_transport *trans,
_enter(""); _enter("");
ASSERT(hdr->flags & RXRPC_CLIENT_INITIATED); ASSERT(sp->hdr.flags & RXRPC_CLIENT_INITIATED);
epoch = hdr->epoch; epoch = sp->hdr.epoch;
cid = hdr->cid & RXRPC_CIDMASK; cid = sp->hdr.cid & RXRPC_CIDMASK;
/* search the connection list first */ /* search the connection list first */
read_lock_bh(&trans->conn_lock); read_lock_bh(&peer->conn_lock);
p = trans->server_conns.rb_node; p = peer->service_conns.rb_node;
while (p) { while (p) {
conn = rb_entry(p, struct rxrpc_connection, node); conn = rb_entry(p, struct rxrpc_connection, service_node);
_debug("maybe %x", conn->cid); _debug("maybe %x", conn->proto.cid);
if (epoch < conn->epoch) if (epoch < conn->proto.epoch)
p = p->rb_left; p = p->rb_left;
else if (epoch > conn->epoch) else if (epoch > conn->proto.epoch)
p = p->rb_right; p = p->rb_right;
else if (cid < conn->cid) else if (cid < conn->proto.cid)
p = p->rb_left; p = p->rb_left;
else if (cid > conn->cid) else if (cid > conn->proto.cid)
p = p->rb_right; p = p->rb_right;
else else
goto found_extant_connection; goto found_extant_connection;
} }
read_unlock_bh(&trans->conn_lock); read_unlock_bh(&peer->conn_lock);
/* not yet present - create a candidate for a new record and then /* not yet present - create a candidate for a new record and then
* redo the search */ * redo the search */
...@@ -649,32 +387,34 @@ rxrpc_incoming_connection(struct rxrpc_transport *trans, ...@@ -649,32 +387,34 @@ rxrpc_incoming_connection(struct rxrpc_transport *trans,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
candidate->trans = trans; candidate->proto.local = local;
candidate->epoch = hdr->epoch; candidate->proto.epoch = sp->hdr.epoch;
candidate->cid = hdr->cid & RXRPC_CIDMASK; candidate->proto.cid = sp->hdr.cid & RXRPC_CIDMASK;
candidate->service_id = hdr->serviceId; candidate->proto.in_clientflag = RXRPC_CLIENT_INITIATED;
candidate->security_ix = hdr->securityIndex; candidate->params.local = local;
candidate->in_clientflag = RXRPC_CLIENT_INITIATED; candidate->params.peer = peer;
candidate->params.service_id = sp->hdr.serviceId;
candidate->security_ix = sp->hdr.securityIndex;
candidate->out_clientflag = 0; candidate->out_clientflag = 0;
candidate->state = RXRPC_CONN_SERVER; candidate->state = RXRPC_CONN_SERVER;
if (candidate->service_id) if (candidate->params.service_id)
candidate->state = RXRPC_CONN_SERVER_UNSECURED; candidate->state = RXRPC_CONN_SERVER_UNSECURED;
write_lock_bh(&trans->conn_lock); write_lock_bh(&peer->conn_lock);
pp = &trans->server_conns.rb_node; pp = &peer->service_conns.rb_node;
p = NULL; p = NULL;
while (*pp) { while (*pp) {
p = *pp; p = *pp;
conn = rb_entry(p, struct rxrpc_connection, node); conn = rb_entry(p, struct rxrpc_connection, service_node);
if (epoch < conn->epoch) if (epoch < conn->proto.epoch)
pp = &(*pp)->rb_left; pp = &(*pp)->rb_left;
else if (epoch > conn->epoch) else if (epoch > conn->proto.epoch)
pp = &(*pp)->rb_right; pp = &(*pp)->rb_right;
else if (cid < conn->cid) else if (cid < conn->proto.cid)
pp = &(*pp)->rb_left; pp = &(*pp)->rb_left;
else if (cid > conn->cid) else if (cid > conn->proto.cid)
pp = &(*pp)->rb_right; pp = &(*pp)->rb_right;
else else
goto found_extant_second; goto found_extant_second;
...@@ -683,42 +423,43 @@ rxrpc_incoming_connection(struct rxrpc_transport *trans, ...@@ -683,42 +423,43 @@ rxrpc_incoming_connection(struct rxrpc_transport *trans,
/* we can now add the new candidate to the list */ /* we can now add the new candidate to the list */
conn = candidate; conn = candidate;
candidate = NULL; candidate = NULL;
rb_link_node(&conn->node, p, pp); rb_link_node(&conn->service_node, p, pp);
rb_insert_color(&conn->node, &trans->server_conns); rb_insert_color(&conn->service_node, &peer->service_conns);
atomic_inc(&conn->trans->usage); rxrpc_get_peer(peer);
rxrpc_get_local(local);
write_unlock_bh(&trans->conn_lock); write_unlock_bh(&peer->conn_lock);
write_lock_bh(&rxrpc_connection_lock); write_lock(&rxrpc_connection_lock);
list_add_tail(&conn->link, &rxrpc_connections); list_add_tail(&conn->link, &rxrpc_connections);
write_unlock_bh(&rxrpc_connection_lock); write_unlock(&rxrpc_connection_lock);
new = "new"; new = "new";
success: success:
_net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->cid); _net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->proto.cid);
_leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage));
return conn; return conn;
/* we found the connection in the list immediately */ /* we found the connection in the list immediately */
found_extant_connection: found_extant_connection:
if (hdr->securityIndex != conn->security_ix) { if (sp->hdr.securityIndex != conn->security_ix) {
read_unlock_bh(&trans->conn_lock); read_unlock_bh(&peer->conn_lock);
goto security_mismatch; goto security_mismatch;
} }
atomic_inc(&conn->usage); rxrpc_get_connection(conn);
read_unlock_bh(&trans->conn_lock); read_unlock_bh(&peer->conn_lock);
goto success; goto success;
/* we found the connection on the second time through the list */ /* we found the connection on the second time through the list */
found_extant_second: found_extant_second:
if (hdr->securityIndex != conn->security_ix) { if (sp->hdr.securityIndex != conn->security_ix) {
write_unlock_bh(&trans->conn_lock); write_unlock_bh(&peer->conn_lock);
goto security_mismatch; goto security_mismatch;
} }
atomic_inc(&conn->usage); rxrpc_get_connection(conn);
write_unlock_bh(&trans->conn_lock); write_unlock_bh(&peer->conn_lock);
kfree(candidate); kfree(candidate);
goto success; goto success;
...@@ -732,58 +473,83 @@ rxrpc_incoming_connection(struct rxrpc_transport *trans, ...@@ -732,58 +473,83 @@ rxrpc_incoming_connection(struct rxrpc_transport *trans,
* find a connection based on transport and RxRPC connection ID for an incoming * find a connection based on transport and RxRPC connection ID for an incoming
* packet * packet
*/ */
struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *trans, struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local,
struct rxrpc_host_header *hdr) struct rxrpc_peer *peer,
struct sk_buff *skb)
{ {
struct rxrpc_connection *conn; struct rxrpc_connection *conn;
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rb_node *p; struct rb_node *p;
u32 epoch, cid; u32 epoch, cid;
_enter(",{%x,%x}", hdr->cid, hdr->flags); _enter(",{%x,%x}", sp->hdr.cid, sp->hdr.flags);
read_lock_bh(&trans->conn_lock); read_lock_bh(&peer->conn_lock);
cid = hdr->cid & RXRPC_CIDMASK; cid = sp->hdr.cid & RXRPC_CIDMASK;
epoch = hdr->epoch; epoch = sp->hdr.epoch;
if (hdr->flags & RXRPC_CLIENT_INITIATED)
p = trans->server_conns.rb_node;
else
p = trans->client_conns.rb_node;
if (sp->hdr.flags & RXRPC_CLIENT_INITIATED) {
p = peer->service_conns.rb_node;
while (p) { while (p) {
conn = rb_entry(p, struct rxrpc_connection, node); conn = rb_entry(p, struct rxrpc_connection, service_node);
_debug("maybe %x", conn->cid); _debug("maybe %x", conn->proto.cid);
if (epoch < conn->epoch) if (epoch < conn->proto.epoch)
p = p->rb_left; p = p->rb_left;
else if (epoch > conn->epoch) else if (epoch > conn->proto.epoch)
p = p->rb_right; p = p->rb_right;
else if (cid < conn->cid) else if (cid < conn->proto.cid)
p = p->rb_left; p = p->rb_left;
else if (cid > conn->cid) else if (cid > conn->proto.cid)
p = p->rb_right; p = p->rb_right;
else else
goto found; goto found;
} }
} else {
conn = idr_find(&rxrpc_client_conn_ids, cid >> RXRPC_CIDSHIFT);
if (conn && conn->proto.epoch == epoch)
goto found;
}
read_unlock_bh(&trans->conn_lock); read_unlock_bh(&peer->conn_lock);
_leave(" = NULL"); _leave(" = NULL");
return NULL; return NULL;
found: found:
atomic_inc(&conn->usage); rxrpc_get_connection(conn);
read_unlock_bh(&trans->conn_lock); read_unlock_bh(&peer->conn_lock);
_leave(" = %p", conn); _leave(" = %p", conn);
return conn; return conn;
} }
/*
* Disconnect a call and clear any channel it occupies when that call
* terminates.
*/
void rxrpc_disconnect_call(struct rxrpc_call *call)
{
struct rxrpc_connection *conn = call->conn;
unsigned chan = call->channel;
_enter("%d,%d", conn->debug_id, call->channel);
if (conn->channels[chan] == call) {
rcu_assign_pointer(conn->channels[chan], NULL);
atomic_inc(&conn->avail_chans);
wake_up(&conn->channel_wq);
}
}
/* /*
* release a virtual connection * release a virtual connection
*/ */
void rxrpc_put_connection(struct rxrpc_connection *conn) void rxrpc_put_connection(struct rxrpc_connection *conn)
{ {
if (!conn)
return;
_enter("%p{u=%d,d=%d}", _enter("%p{u=%d,d=%d}",
conn, atomic_read(&conn->usage), conn->debug_id); conn, atomic_read(&conn->usage), conn->debug_id);
...@@ -809,17 +575,15 @@ static void rxrpc_destroy_connection(struct rxrpc_connection *conn) ...@@ -809,17 +575,15 @@ static void rxrpc_destroy_connection(struct rxrpc_connection *conn)
_net("DESTROY CONN %d", conn->debug_id); _net("DESTROY CONN %d", conn->debug_id);
if (conn->bundle)
rxrpc_put_bundle(conn->trans, conn->bundle);
ASSERT(RB_EMPTY_ROOT(&conn->calls)); ASSERT(RB_EMPTY_ROOT(&conn->calls));
rxrpc_purge_queue(&conn->rx_queue); rxrpc_purge_queue(&conn->rx_queue);
conn->security->clear(conn); conn->security->clear(conn);
key_put(conn->key); key_put(conn->params.key);
key_put(conn->server_key); key_put(conn->server_key);
rxrpc_put_peer(conn->params.peer);
rxrpc_put_local(conn->params.local);
rxrpc_put_transport(conn->trans);
kfree(conn); kfree(conn);
_leave(""); _leave("");
} }
...@@ -830,6 +594,7 @@ static void rxrpc_destroy_connection(struct rxrpc_connection *conn) ...@@ -830,6 +594,7 @@ static void rxrpc_destroy_connection(struct rxrpc_connection *conn)
static void rxrpc_connection_reaper(struct work_struct *work) static void rxrpc_connection_reaper(struct work_struct *work)
{ {
struct rxrpc_connection *conn, *_p; struct rxrpc_connection *conn, *_p;
struct rxrpc_peer *peer;
unsigned long now, earliest, reap_time; unsigned long now, earliest, reap_time;
LIST_HEAD(graveyard); LIST_HEAD(graveyard);
...@@ -839,7 +604,7 @@ static void rxrpc_connection_reaper(struct work_struct *work) ...@@ -839,7 +604,7 @@ static void rxrpc_connection_reaper(struct work_struct *work)
now = ktime_get_seconds(); now = ktime_get_seconds();
earliest = ULONG_MAX; earliest = ULONG_MAX;
write_lock_bh(&rxrpc_connection_lock); write_lock(&rxrpc_connection_lock);
list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) {
_debug("reap CONN %d { u=%d,t=%ld }", _debug("reap CONN %d { u=%d,t=%ld }",
conn->debug_id, atomic_read(&conn->usage), conn->debug_id, atomic_read(&conn->usage),
...@@ -848,33 +613,42 @@ static void rxrpc_connection_reaper(struct work_struct *work) ...@@ -848,33 +613,42 @@ static void rxrpc_connection_reaper(struct work_struct *work)
if (likely(atomic_read(&conn->usage) > 0)) if (likely(atomic_read(&conn->usage) > 0))
continue; continue;
spin_lock(&conn->trans->client_lock); if (rxrpc_conn_is_client(conn)) {
write_lock(&conn->trans->conn_lock); struct rxrpc_local *local = conn->params.local;
spin_lock(&local->client_conns_lock);
reap_time = conn->put_time + rxrpc_connection_expiry; reap_time = conn->put_time + rxrpc_connection_expiry;
if (atomic_read(&conn->usage) > 0) { if (atomic_read(&conn->usage) > 0) {
; ;
} else if (reap_time <= now) { } else if (reap_time <= now) {
list_move_tail(&conn->link, &graveyard); list_move_tail(&conn->link, &graveyard);
if (conn->out_clientflag) rxrpc_put_client_connection_id(conn);
rb_erase(&conn->node, rb_erase(&conn->client_node,
&conn->trans->client_conns); &local->client_conns);
else } else if (reap_time < earliest) {
rb_erase(&conn->node, earliest = reap_time;
&conn->trans->server_conns);
if (conn->bundle) {
list_del_init(&conn->bundle_link);
conn->bundle->num_conns--;
} }
spin_unlock(&local->client_conns_lock);
} else {
peer = conn->params.peer;
write_lock_bh(&peer->conn_lock);
reap_time = conn->put_time + rxrpc_connection_expiry;
if (atomic_read(&conn->usage) > 0) {
;
} else if (reap_time <= now) {
list_move_tail(&conn->link, &graveyard);
rb_erase(&conn->service_node,
&peer->service_conns);
} else if (reap_time < earliest) { } else if (reap_time < earliest) {
earliest = reap_time; earliest = reap_time;
} }
write_unlock(&conn->trans->conn_lock); write_unlock_bh(&peer->conn_lock);
spin_unlock(&conn->trans->client_lock); }
} }
write_unlock_bh(&rxrpc_connection_lock); write_unlock(&rxrpc_connection_lock);
if (earliest != ULONG_MAX) { if (earliest != ULONG_MAX) {
_debug("reschedule reaper %ld", (long) earliest - now); _debug("reschedule reaper %ld", (long) earliest - now);
......
...@@ -360,7 +360,7 @@ void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb) ...@@ -360,7 +360,7 @@ void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb)
case RXRPC_PACKET_TYPE_BUSY: case RXRPC_PACKET_TYPE_BUSY:
_proto("Rx BUSY %%%u", sp->hdr.serial); _proto("Rx BUSY %%%u", sp->hdr.serial);
if (call->conn->out_clientflag) if (rxrpc_conn_is_service(call->conn))
goto protocol_error; goto protocol_error;
write_lock_bh(&call->state_lock); write_lock_bh(&call->state_lock);
...@@ -533,7 +533,7 @@ static void rxrpc_post_packet_to_call(struct rxrpc_call *call, ...@@ -533,7 +533,7 @@ static void rxrpc_post_packet_to_call(struct rxrpc_call *call,
case RXRPC_CALL_COMPLETE: case RXRPC_CALL_COMPLETE:
case RXRPC_CALL_CLIENT_FINAL_ACK: case RXRPC_CALL_CLIENT_FINAL_ACK:
/* complete server call */ /* complete server call */
if (call->conn->in_clientflag) if (rxrpc_conn_is_service(call->conn))
goto dead_call; goto dead_call;
/* resend last packet of a completed call */ /* resend last packet of a completed call */
_debug("final ack again"); _debug("final ack again");
...@@ -560,7 +560,7 @@ static void rxrpc_post_packet_to_call(struct rxrpc_call *call, ...@@ -560,7 +560,7 @@ static void rxrpc_post_packet_to_call(struct rxrpc_call *call,
dead_call: dead_call:
if (sp->hdr.type != RXRPC_PACKET_TYPE_ABORT) { if (sp->hdr.type != RXRPC_PACKET_TYPE_ABORT) {
skb->priority = RX_CALL_DEAD; skb->priority = RX_CALL_DEAD;
rxrpc_reject_packet(call->conn->trans->local, skb); rxrpc_reject_packet(call->conn->params.local, skb);
goto unlock; goto unlock;
} }
free_unlock: free_unlock:
...@@ -580,7 +580,7 @@ static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn, ...@@ -580,7 +580,7 @@ static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn,
{ {
_enter("%p,%p", conn, skb); _enter("%p,%p", conn, skb);
atomic_inc(&conn->usage); rxrpc_get_connection(conn);
skb_queue_tail(&conn->rx_queue, skb); skb_queue_tail(&conn->rx_queue, skb);
rxrpc_queue_conn(conn); rxrpc_queue_conn(conn);
} }
...@@ -628,27 +628,20 @@ int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb) ...@@ -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, static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local,
struct sk_buff *skb, struct sk_buff *skb)
struct rxrpc_skb_priv *sp)
{ {
struct rxrpc_peer *peer; struct rxrpc_peer *peer;
struct rxrpc_transport *trans;
struct rxrpc_connection *conn; struct rxrpc_connection *conn;
struct sockaddr_rxrpc srx; struct sockaddr_rxrpc srx;
rxrpc_get_addr_from_skb(local, skb, &srx); rxrpc_get_addr_from_skb(local, skb, &srx);
rcu_read_lock(); rcu_read_lock();
peer = rxrpc_lookup_peer_rcu(local, &srx); peer = rxrpc_lookup_peer_rcu(local, &srx);
if (IS_ERR(peer)) if (!peer)
goto cant_find_peer; goto cant_find_peer;
trans = rxrpc_find_transport(local, peer); conn = rxrpc_find_connection(local, peer, skb);
rcu_read_unlock(); rcu_read_unlock();
if (!trans)
goto cant_find_conn;
conn = rxrpc_find_connection(trans, &sp->hdr);
rxrpc_put_transport(trans);
if (!conn) if (!conn)
goto cant_find_conn; goto cant_find_conn;
...@@ -739,7 +732,7 @@ void rxrpc_data_ready(struct sock *sk) ...@@ -739,7 +732,7 @@ void rxrpc_data_ready(struct sock *sk)
* old-fashioned way doesn't really hurt */ * old-fashioned way doesn't really hurt */
struct rxrpc_connection *conn; struct rxrpc_connection *conn;
conn = rxrpc_conn_from_local(local, skb, sp); conn = rxrpc_conn_from_local(local, skb);
if (!conn) if (!conn)
goto cant_route_call; goto cant_route_call;
......
...@@ -987,7 +987,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn, ...@@ -987,7 +987,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn,
if (ret < 0) if (ret < 0)
goto error; goto error;
conn->key = key; conn->params.key = key;
_leave(" = 0 [%d]", key_serial(key)); _leave(" = 0 [%d]", key_serial(key));
return 0; return 0;
......
...@@ -80,7 +80,8 @@ static struct rxrpc_local *rxrpc_alloc_local(const struct sockaddr_rxrpc *srx) ...@@ -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->accept_queue);
skb_queue_head_init(&local->reject_queue); skb_queue_head_init(&local->reject_queue);
skb_queue_head_init(&local->event_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); spin_lock_init(&local->lock);
rwlock_init(&local->services_lock); rwlock_init(&local->services_lock);
local->debug_id = atomic_inc_return(&rxrpc_debug_id); local->debug_id = atomic_inc_return(&rxrpc_debug_id);
...@@ -209,7 +210,7 @@ struct rxrpc_local *rxrpc_lookup_local(const struct sockaddr_rxrpc *srx) ...@@ -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 * bind the transport socket may still fail if we're attempting
* to use a local address that the dying object is still using. * 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; cursor = cursor->next;
list_del_init(&local->link); list_del_init(&local->link);
break; break;
...@@ -294,6 +295,7 @@ static void rxrpc_local_destroyer(struct rxrpc_local *local) ...@@ -294,6 +295,7 @@ static void rxrpc_local_destroyer(struct rxrpc_local *local)
list_del_init(&local->link); list_del_init(&local->link);
mutex_unlock(&rxrpc_local_mutex); mutex_unlock(&rxrpc_local_mutex);
ASSERT(RB_EMPTY_ROOT(&local->client_conns));
ASSERT(list_empty(&local->services)); ASSERT(list_empty(&local->services));
if (socket) { if (socket) {
......
...@@ -35,7 +35,8 @@ static int rxrpc_send_data(struct rxrpc_sock *rx, ...@@ -35,7 +35,8 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
static int rxrpc_sendmsg_cmsg(struct msghdr *msg, static int rxrpc_sendmsg_cmsg(struct msghdr *msg,
unsigned long *user_call_ID, unsigned long *user_call_ID,
enum rxrpc_command *command, enum rxrpc_command *command,
u32 *abort_code) u32 *abort_code,
bool *_exclusive)
{ {
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
bool got_user_ID = false; bool got_user_ID = false;
...@@ -93,6 +94,11 @@ static int rxrpc_sendmsg_cmsg(struct msghdr *msg, ...@@ -93,6 +94,11 @@ static int rxrpc_sendmsg_cmsg(struct msghdr *msg,
return -EINVAL; return -EINVAL;
break; break;
case RXRPC_EXCLUSIVE_CALL:
*_exclusive = true;
if (len != 0)
return -EINVAL;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -131,13 +137,11 @@ static void rxrpc_send_abort(struct rxrpc_call *call, u32 abort_code) ...@@ -131,13 +137,11 @@ static void rxrpc_send_abort(struct rxrpc_call *call, u32 abort_code)
*/ */
static struct rxrpc_call * static struct rxrpc_call *
rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, 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_conn_parameters cp;
struct rxrpc_transport *trans;
struct rxrpc_call *call; struct rxrpc_call *call;
struct key *key; struct key *key;
long ret;
DECLARE_SOCKADDR(struct sockaddr_rxrpc *, srx, msg->msg_name); 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, ...@@ -146,39 +150,20 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg,
if (!msg->msg_name) if (!msg->msg_name)
return ERR_PTR(-EDESTADDRREQ); 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; key = rx->key;
if (key && !rx->key->payload.data[0]) if (key && !rx->key->payload.data[0])
key = NULL; 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, memset(&cp, 0, sizeof(cp));
GFP_KERNEL); cp.local = rx->local;
rxrpc_put_bundle(trans, bundle); cp.key = rx->key;
rxrpc_put_transport(trans); cp.security_level = rx->min_sec_level;
if (IS_ERR(call)) { cp.exclusive = rx->exclusive | exclusive;
ret = PTR_ERR(call); cp.service_id = srx->srx_service;
goto out_trans; call = rxrpc_new_client_call(rx, &cp, srx, user_call_ID, GFP_KERNEL);
}
_leave(" = %p\n", call); _leave(" = %p\n", call);
return 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) ...@@ -191,12 +176,14 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
enum rxrpc_command cmd; enum rxrpc_command cmd;
struct rxrpc_call *call; struct rxrpc_call *call;
unsigned long user_call_ID = 0; unsigned long user_call_ID = 0;
bool exclusive = false;
u32 abort_code = 0; u32 abort_code = 0;
int ret; int ret;
_enter(""); _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) if (ret < 0)
return ret; return ret;
...@@ -214,7 +201,8 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len) ...@@ -214,7 +201,8 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
if (!call) { if (!call) {
if (cmd != RXRPC_CMD_SEND_DATA) if (cmd != RXRPC_CMD_SEND_DATA)
return -EBADSLT; 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)) if (IS_ERR(call))
return PTR_ERR(call); return PTR_ERR(call);
} }
...@@ -319,7 +307,7 @@ EXPORT_SYMBOL(rxrpc_kernel_abort_call); ...@@ -319,7 +307,7 @@ EXPORT_SYMBOL(rxrpc_kernel_abort_call);
/* /*
* send a packet through the transport endpoint * 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 kvec iov[1];
struct msghdr msg; struct msghdr msg;
...@@ -330,30 +318,30 @@ int rxrpc_send_packet(struct rxrpc_transport *trans, struct sk_buff *skb) ...@@ -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_base = skb->head;
iov[0].iov_len = skb->len; iov[0].iov_len = skb->len;
msg.msg_name = &trans->peer->srx.transport.sin; msg.msg_name = &conn->params.peer->srx.transport;
msg.msg_namelen = sizeof(trans->peer->srx.transport.sin); msg.msg_namelen = conn->params.peer->srx.transport_len;
msg.msg_control = NULL; msg.msg_control = NULL;
msg.msg_controllen = 0; msg.msg_controllen = 0;
msg.msg_flags = 0; msg.msg_flags = 0;
/* send the packet with the don't fragment bit set if we currently /* send the packet with the don't fragment bit set if we currently
* think it's small enough */ * think it's small enough */
if (skb->len - sizeof(struct rxrpc_wire_header) < trans->peer->maxdata) { if (skb->len - sizeof(struct rxrpc_wire_header) < conn->params.peer->maxdata) {
down_read(&trans->local->defrag_sem); down_read(&conn->params.local->defrag_sem);
/* send the packet by UDP /* send the packet by UDP
* - returns -EMSGSIZE if UDP would have to fragment the packet * - returns -EMSGSIZE if UDP would have to fragment the packet
* to go out of the interface * to go out of the interface
* - in which case, we'll have processed the ICMP error * - in which case, we'll have processed the ICMP error
* message and update the peer record * 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); iov[0].iov_len);
up_read(&trans->local->defrag_sem); up_read(&conn->params.local->defrag_sem);
if (ret == -EMSGSIZE) if (ret == -EMSGSIZE)
goto send_fragmentable; goto send_fragmentable;
_leave(" = %d [%u]", ret, trans->peer->maxdata); _leave(" = %d [%u]", ret, conn->params.peer->maxdata);
return ret; return ret;
} }
...@@ -361,21 +349,28 @@ int rxrpc_send_packet(struct rxrpc_transport *trans, struct sk_buff *skb) ...@@ -361,21 +349,28 @@ int rxrpc_send_packet(struct rxrpc_transport *trans, struct sk_buff *skb)
/* attempt to send this message with fragmentation enabled */ /* attempt to send this message with fragmentation enabled */
_debug("send fragment"); _debug("send fragment");
down_write(&trans->local->defrag_sem); down_write(&conn->params.local->defrag_sem);
switch (conn->params.local->srx.transport.family) {
case AF_INET:
opt = IP_PMTUDISC_DONT; opt = IP_PMTUDISC_DONT;
ret = kernel_setsockopt(trans->local->socket, SOL_IP, IP_MTU_DISCOVER, ret = kernel_setsockopt(conn->params.local->socket,
(char *) &opt, sizeof(opt)); SOL_IP, IP_MTU_DISCOVER,
(char *)&opt, sizeof(opt));
if (ret == 0) { if (ret == 0) {
ret = kernel_sendmsg(trans->local->socket, &msg, iov, 1, ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 1,
iov[0].iov_len); iov[0].iov_len);
opt = IP_PMTUDISC_DO; opt = IP_PMTUDISC_DO;
kernel_setsockopt(trans->local->socket, SOL_IP, kernel_setsockopt(conn->params.local->socket, SOL_IP,
IP_MTU_DISCOVER, (char *) &opt, sizeof(opt)); IP_MTU_DISCOVER,
(char *)&opt, sizeof(opt));
}
break;
} }
up_write(&trans->local->defrag_sem); up_write(&conn->params.local->defrag_sem);
_leave(" = %d [frag %u]", ret, trans->peer->maxdata); _leave(" = %d [frag %u]", ret, conn->params.peer->maxdata);
return ret; return ret;
} }
...@@ -487,7 +482,7 @@ static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb, ...@@ -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) { if (try_to_del_timer_sync(&call->ack_timer) >= 0) {
/* the packet may be freed by rxrpc_process_call() before this /* the packet may be freed by rxrpc_process_call() before this
* returns */ * returns */
ret = rxrpc_send_packet(call->conn->trans, skb); ret = rxrpc_send_data_packet(call->conn, skb);
_net("sent skb %p", skb); _net("sent skb %p", skb);
} else { } else {
_debug("failed to delete ACK timer"); _debug("failed to delete ACK timer");
...@@ -573,7 +568,7 @@ static int rxrpc_send_data(struct rxrpc_sock *rx, ...@@ -573,7 +568,7 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
goto maybe_error; goto maybe_error;
} }
max = call->conn->trans->peer->maxdata; max = call->conn->params.peer->maxdata;
max -= call->conn->security_size; max -= call->conn->security_size;
max &= ~(call->conn->size_align - 1UL); max &= ~(call->conn->size_align - 1UL);
...@@ -664,7 +659,7 @@ static int rxrpc_send_data(struct rxrpc_sock *rx, ...@@ -664,7 +659,7 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
seq = atomic_inc_return(&call->sequence); seq = atomic_inc_return(&call->sequence);
sp->hdr.epoch = conn->epoch; sp->hdr.epoch = conn->proto.epoch;
sp->hdr.cid = call->cid; sp->hdr.cid = call->cid;
sp->hdr.callNumber = call->call_id; sp->hdr.callNumber = call->call_id;
sp->hdr.seq = seq; sp->hdr.seq = seq;
......
...@@ -50,6 +50,9 @@ static unsigned long rxrpc_peer_hash_key(struct rxrpc_local *local, ...@@ -50,6 +50,9 @@ static unsigned long rxrpc_peer_hash_key(struct rxrpc_local *local,
size = sizeof(srx->transport.sin.sin_addr); size = sizeof(srx->transport.sin.sin_addr);
p = (u16 *)&srx->transport.sin.sin_addr; p = (u16 *)&srx->transport.sin.sin_addr;
break; break;
default:
WARN(1, "AF_RXRPC: Unsupported transport address family\n");
return 0;
} }
/* Step through the peer address in 16-bit portions for speed */ /* 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) ...@@ -185,6 +188,8 @@ struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp)
INIT_HLIST_HEAD(&peer->error_targets); INIT_HLIST_HEAD(&peer->error_targets);
INIT_WORK(&peer->error_distributor, INIT_WORK(&peer->error_distributor,
&rxrpc_peer_error_distributor); &rxrpc_peer_error_distributor);
peer->service_conns = RB_ROOT;
rwlock_init(&peer->conn_lock);
spin_lock_init(&peer->lock); spin_lock_init(&peer->lock);
peer->debug_id = atomic_inc_return(&rxrpc_debug_id); 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) ...@@ -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) static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
{ {
struct rxrpc_transport *trans; struct rxrpc_connection *conn;
struct rxrpc_call *call; struct rxrpc_call *call;
char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1]; 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) ...@@ -59,25 +59,28 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
} }
call = list_entry(v, struct rxrpc_call, link); call = list_entry(v, struct rxrpc_call, link);
trans = call->conn->trans;
sprintf(lbuff, "%pI4:%u", sprintf(lbuff, "%pI4:%u",
&trans->local->srx.transport.sin.sin_addr, &call->local->srx.transport.sin.sin_addr,
ntohs(trans->local->srx.transport.sin.sin_port)); ntohs(call->local->srx.transport.sin.sin_port));
conn = call->conn;
if (conn)
sprintf(rbuff, "%pI4:%u", sprintf(rbuff, "%pI4:%u",
&trans->peer->srx.transport.sin.sin_addr, &conn->params.peer->srx.transport.sin.sin_addr,
ntohs(trans->peer->srx.transport.sin.sin_port)); ntohs(conn->params.peer->srx.transport.sin.sin_port));
else
strcpy(rbuff, "no_connection");
seq_printf(seq, seq_printf(seq,
"UDP %-22.22s %-22.22s %4x %08x %08x %s %3u" "UDP %-22.22s %-22.22s %4x %08x %08x %s %3u"
" %-8.8s %08x %lx\n", " %-8.8s %08x %lx\n",
lbuff, lbuff,
rbuff, rbuff,
call->conn->service_id, call->service_id,
call->cid, call->cid,
call->call_id, call->call_id,
call->conn->in_clientflag ? "Svc" : "Clt", call->in_clientflag ? "Svc" : "Clt",
atomic_read(&call->usage), atomic_read(&call->usage),
rxrpc_call_states[call->state], rxrpc_call_states[call->state],
call->remote_abort ?: call->local_abort, call->remote_abort ?: call->local_abort,
...@@ -129,7 +132,6 @@ static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v) ...@@ -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) static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
{ {
struct rxrpc_connection *conn; struct rxrpc_connection *conn;
struct rxrpc_transport *trans;
char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1]; char lbuff[4 + 4 + 4 + 4 + 5 + 1], rbuff[4 + 4 + 4 + 4 + 5 + 1];
if (v == &rxrpc_connections) { if (v == &rxrpc_connections) {
...@@ -142,28 +144,27 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v) ...@@ -142,28 +144,27 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
} }
conn = list_entry(v, struct rxrpc_connection, link); conn = list_entry(v, struct rxrpc_connection, link);
trans = conn->trans;
sprintf(lbuff, "%pI4:%u", sprintf(lbuff, "%pI4:%u",
&trans->local->srx.transport.sin.sin_addr, &conn->params.local->srx.transport.sin.sin_addr,
ntohs(trans->local->srx.transport.sin.sin_port)); ntohs(conn->params.local->srx.transport.sin.sin_port));
sprintf(rbuff, "%pI4:%u", sprintf(rbuff, "%pI4:%u",
&trans->peer->srx.transport.sin.sin_addr, &conn->params.peer->srx.transport.sin.sin_addr,
ntohs(trans->peer->srx.transport.sin.sin_port)); ntohs(conn->params.peer->srx.transport.sin.sin_port));
seq_printf(seq, seq_printf(seq,
"UDP %-22.22s %-22.22s %4x %08x %08x %s %3u" "UDP %-22.22s %-22.22s %4x %08x %08x %s %3u"
" %s %08x %08x %08x\n", " %s %08x %08x %08x\n",
lbuff, lbuff,
rbuff, rbuff,
conn->service_id, conn->params.service_id,
conn->cid, conn->proto.cid,
conn->call_counter, conn->call_counter,
conn->in_clientflag ? "Svc" : "Clt", rxrpc_conn_is_service(conn) ? "Svc" : "Clt",
atomic_read(&conn->usage), atomic_read(&conn->usage),
rxrpc_conn_states[conn->state], rxrpc_conn_states[conn->state],
key_serial(conn->key), key_serial(conn->params.key),
atomic_read(&conn->serial), atomic_read(&conn->serial),
atomic_read(&conn->hi_serial)); atomic_read(&conn->hi_serial));
......
...@@ -147,9 +147,9 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, ...@@ -147,9 +147,9 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
if (!continue_call) { if (!continue_call) {
if (msg->msg_name) { if (msg->msg_name) {
size_t len = size_t len =
sizeof(call->conn->trans->peer->srx); sizeof(call->conn->params.peer->srx);
memcpy(msg->msg_name, memcpy(msg->msg_name,
&call->conn->trans->peer->srx, len); &call->conn->params.peer->srx, len);
msg->msg_namelen = len; msg->msg_namelen = len;
} }
sock_recv_timestamp(msg, &rx->sk, skb); sock_recv_timestamp(msg, &rx->sk, skb);
...@@ -205,7 +205,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, ...@@ -205,7 +205,7 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
/* we transferred the whole data packet */ /* we transferred the whole data packet */
if (sp->hdr.flags & RXRPC_LAST_PACKET) { if (sp->hdr.flags & RXRPC_LAST_PACKET) {
_debug("last"); _debug("last");
if (call->conn->out_clientflag) { if (rxrpc_conn_is_client(call->conn)) {
/* last byte of reply received */ /* last byte of reply received */
ret = copied; ret = copied;
goto terminal_message; goto terminal_message;
......
...@@ -58,9 +58,9 @@ static int rxkad_init_connection_security(struct rxrpc_connection *conn) ...@@ -58,9 +58,9 @@ static int rxkad_init_connection_security(struct rxrpc_connection *conn)
struct rxrpc_key_token *token; struct rxrpc_key_token *token;
int ret; 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; conn->security_ix = token->security_index;
ci = crypto_alloc_skcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC); ci = crypto_alloc_skcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC);
...@@ -74,7 +74,7 @@ static int rxkad_init_connection_security(struct rxrpc_connection *conn) ...@@ -74,7 +74,7 @@ static int rxkad_init_connection_security(struct rxrpc_connection *conn)
sizeof(token->kad->session_key)) < 0) sizeof(token->kad->session_key)) < 0)
BUG(); BUG();
switch (conn->security_level) { switch (conn->params.security_level) {
case RXRPC_SECURITY_PLAIN: case RXRPC_SECURITY_PLAIN:
break; break;
case RXRPC_SECURITY_AUTH: case RXRPC_SECURITY_AUTH:
...@@ -115,14 +115,14 @@ static void rxkad_prime_packet_security(struct rxrpc_connection *conn) ...@@ -115,14 +115,14 @@ static void rxkad_prime_packet_security(struct rxrpc_connection *conn)
_enter(""); _enter("");
if (!conn->key) if (!conn->params.key)
return; return;
token = conn->key->payload.data[0]; token = conn->params.key->payload.data[0];
memcpy(&iv, token->kad->session_key, sizeof(iv)); memcpy(&iv, token->kad->session_key, sizeof(iv));
tmpbuf.x[0] = htonl(conn->epoch); tmpbuf.x[0] = htonl(conn->proto.epoch);
tmpbuf.x[1] = htonl(conn->cid); tmpbuf.x[1] = htonl(conn->proto.cid);
tmpbuf.x[2] = 0; tmpbuf.x[2] = 0;
tmpbuf.x[3] = htonl(conn->security_ix); tmpbuf.x[3] = htonl(conn->security_ix);
...@@ -220,7 +220,7 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, ...@@ -220,7 +220,7 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call,
rxkhdr.checksum = 0; rxkhdr.checksum = 0;
/* encrypt from the session key */ /* 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)); memcpy(&iv, token->kad->session_key, sizeof(iv));
sg_init_one(&sg[0], sechdr, sizeof(rxkhdr)); sg_init_one(&sg[0], sechdr, sizeof(rxkhdr));
...@@ -277,13 +277,13 @@ static int rxkad_secure_packet(const struct rxrpc_call *call, ...@@ -277,13 +277,13 @@ static int rxkad_secure_packet(const struct rxrpc_call *call,
sp = rxrpc_skb(skb); sp = rxrpc_skb(skb);
_enter("{%d{%x}},{#%u},%zu,", _enter("{%d{%x}},{#%u},%zu,",
call->debug_id, key_serial(call->conn->key), sp->hdr.seq, call->debug_id, key_serial(call->conn->params.key),
data_size); sp->hdr.seq, data_size);
if (!call->conn->cipher) if (!call->conn->cipher)
return 0; return 0;
ret = key_validate(call->conn->key); ret = key_validate(call->conn->params.key);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -312,7 +312,7 @@ static int rxkad_secure_packet(const struct rxrpc_call *call, ...@@ -312,7 +312,7 @@ static int rxkad_secure_packet(const struct rxrpc_call *call,
y = 1; /* zero checksums are not permitted */ y = 1; /* zero checksums are not permitted */
sp->hdr.cksum = y; sp->hdr.cksum = y;
switch (call->conn->security_level) { switch (call->conn->params.security_level) {
case RXRPC_SECURITY_PLAIN: case RXRPC_SECURITY_PLAIN:
ret = 0; ret = 0;
break; break;
...@@ -446,7 +446,7 @@ static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call, ...@@ -446,7 +446,7 @@ static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call,
skb_to_sgvec(skb, sg, 0, skb->len); skb_to_sgvec(skb, sg, 0, skb->len);
/* decrypt from the session key */ /* 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)); memcpy(&iv, token->kad->session_key, sizeof(iv));
skcipher_request_set_tfm(req, call->conn->cipher); skcipher_request_set_tfm(req, call->conn->cipher);
...@@ -516,7 +516,7 @@ static int rxkad_verify_packet(const struct rxrpc_call *call, ...@@ -516,7 +516,7 @@ static int rxkad_verify_packet(const struct rxrpc_call *call,
sp = rxrpc_skb(skb); sp = rxrpc_skb(skb);
_enter("{%d{%x}},{#%u}", _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) if (!call->conn->cipher)
return 0; return 0;
...@@ -557,7 +557,7 @@ static int rxkad_verify_packet(const struct rxrpc_call *call, ...@@ -557,7 +557,7 @@ static int rxkad_verify_packet(const struct rxrpc_call *call,
return -EPROTO; return -EPROTO;
} }
switch (call->conn->security_level) { switch (call->conn->params.security_level) {
case RXRPC_SECURITY_PLAIN: case RXRPC_SECURITY_PLAIN:
ret = 0; ret = 0;
break; break;
...@@ -589,9 +589,9 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn) ...@@ -589,9 +589,9 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
u32 serial; u32 serial;
int ret; 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) if (ret < 0)
return ret; return ret;
...@@ -602,14 +602,14 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn) ...@@ -602,14 +602,14 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
challenge.min_level = htonl(0); challenge.min_level = htonl(0);
challenge.__padding = 0; challenge.__padding = 0;
msg.msg_name = &conn->trans->peer->srx.transport.sin; msg.msg_name = &conn->params.peer->srx.transport.sin;
msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin); msg.msg_namelen = sizeof(conn->params.peer->srx.transport.sin);
msg.msg_control = NULL; msg.msg_control = NULL;
msg.msg_controllen = 0; msg.msg_controllen = 0;
msg.msg_flags = 0; msg.msg_flags = 0;
whdr.epoch = htonl(conn->epoch); whdr.epoch = htonl(conn->proto.epoch);
whdr.cid = htonl(conn->cid); whdr.cid = htonl(conn->proto.cid);
whdr.callNumber = 0; whdr.callNumber = 0;
whdr.seq = 0; whdr.seq = 0;
whdr.type = RXRPC_PACKET_TYPE_CHALLENGE; whdr.type = RXRPC_PACKET_TYPE_CHALLENGE;
...@@ -617,7 +617,7 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn) ...@@ -617,7 +617,7 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
whdr.userStatus = 0; whdr.userStatus = 0;
whdr.securityIndex = conn->security_ix; whdr.securityIndex = conn->security_ix;
whdr._rsvd = 0; whdr._rsvd = 0;
whdr.serviceId = htons(conn->service_id); whdr.serviceId = htons(conn->params.service_id);
iov[0].iov_base = &whdr; iov[0].iov_base = &whdr;
iov[0].iov_len = sizeof(whdr); iov[0].iov_len = sizeof(whdr);
...@@ -630,7 +630,7 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn) ...@@ -630,7 +630,7 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
whdr.serial = htonl(serial); whdr.serial = htonl(serial);
_proto("Tx CHALLENGE %%%u", 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) { if (ret < 0) {
_debug("sendmsg failed: %d", ret); _debug("sendmsg failed: %d", ret);
return -EAGAIN; return -EAGAIN;
...@@ -657,8 +657,8 @@ static int rxkad_send_response(struct rxrpc_connection *conn, ...@@ -657,8 +657,8 @@ static int rxkad_send_response(struct rxrpc_connection *conn,
_enter(""); _enter("");
msg.msg_name = &conn->trans->peer->srx.transport.sin; msg.msg_name = &conn->params.peer->srx.transport.sin;
msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin); msg.msg_namelen = sizeof(conn->params.peer->srx.transport.sin);
msg.msg_control = NULL; msg.msg_control = NULL;
msg.msg_controllen = 0; msg.msg_controllen = 0;
msg.msg_flags = 0; msg.msg_flags = 0;
...@@ -684,7 +684,7 @@ static int rxkad_send_response(struct rxrpc_connection *conn, ...@@ -684,7 +684,7 @@ static int rxkad_send_response(struct rxrpc_connection *conn,
whdr.serial = htonl(serial); whdr.serial = htonl(serial);
_proto("Tx RESPONSE %%%u", 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) { if (ret < 0) {
_debug("sendmsg failed: %d", ret); _debug("sendmsg failed: %d", ret);
return -EAGAIN; return -EAGAIN;
...@@ -771,14 +771,14 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, ...@@ -771,14 +771,14 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
u32 version, nonce, min_level, abort_code; u32 version, nonce, min_level, abort_code;
int ret; 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]"); _leave(" = -EPROTO [no key]");
return -EPROTO; return -EPROTO;
} }
ret = key_validate(conn->key); ret = key_validate(conn->params.key);
if (ret < 0) { if (ret < 0) {
*_abort_code = RXKADEXPIRED; *_abort_code = RXKADEXPIRED;
return ret; return ret;
...@@ -801,20 +801,20 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, ...@@ -801,20 +801,20 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
goto protocol_error; goto protocol_error;
abort_code = RXKADLEVELFAIL; abort_code = RXKADLEVELFAIL;
if (conn->security_level < min_level) if (conn->params.security_level < min_level)
goto protocol_error; goto protocol_error;
token = conn->key->payload.data[0]; token = conn->params.key->payload.data[0];
/* build the response packet */ /* build the response packet */
memset(&resp, 0, sizeof(resp)); memset(&resp, 0, sizeof(resp));
resp.version = htonl(RXKAD_VERSION); resp.version = htonl(RXKAD_VERSION);
resp.encrypted.epoch = htonl(conn->epoch); resp.encrypted.epoch = htonl(conn->proto.epoch);
resp.encrypted.cid = htonl(conn->cid); resp.encrypted.cid = htonl(conn->proto.cid);
resp.encrypted.securityIndex = htonl(conn->security_ix); resp.encrypted.securityIndex = htonl(conn->security_ix);
resp.encrypted.inc_nonce = htonl(nonce + 1); 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.kvno = htonl(token->kad->kvno);
resp.ticket_len = htonl(token->kad->ticket_len); resp.ticket_len = htonl(token->kad->ticket_len);
...@@ -1096,9 +1096,9 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, ...@@ -1096,9 +1096,9 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
rxkad_decrypt_response(conn, &response, &session_key); rxkad_decrypt_response(conn, &response, &session_key);
abort_code = RXKADSEALEDINCON; abort_code = RXKADSEALEDINCON;
if (ntohl(response.encrypted.epoch) != conn->epoch) if (ntohl(response.encrypted.epoch) != conn->proto.epoch)
goto protocol_error_free; goto protocol_error_free;
if (ntohl(response.encrypted.cid) != conn->cid) if (ntohl(response.encrypted.cid) != conn->proto.cid)
goto protocol_error_free; goto protocol_error_free;
if (ntohl(response.encrypted.securityIndex) != conn->security_ix) if (ntohl(response.encrypted.securityIndex) != conn->security_ix)
goto protocol_error_free; goto protocol_error_free;
...@@ -1122,7 +1122,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, ...@@ -1122,7 +1122,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
level = ntohl(response.encrypted.level); level = ntohl(response.encrypted.level);
if (level > RXRPC_SECURITY_ENCRYPT) if (level > RXRPC_SECURITY_ENCRYPT)
goto protocol_error_free; 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 /* create a key to hold the security data and expiration time - after
* this the connection security can be handled in exactly the same way * 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) ...@@ -76,7 +76,7 @@ int rxrpc_init_client_conn_security(struct rxrpc_connection *conn)
{ {
const struct rxrpc_security *sec; const struct rxrpc_security *sec;
struct rxrpc_key_token *token; struct rxrpc_key_token *token;
struct key *key = conn->key; struct key *key = conn->params.key;
int ret; int ret;
_enter("{%d},{%x}", conn->debug_id, key_serial(key)); _enter("{%d},{%x}", conn->debug_id, key_serial(key));
...@@ -113,7 +113,7 @@ int rxrpc_init_client_conn_security(struct rxrpc_connection *conn) ...@@ -113,7 +113,7 @@ int rxrpc_init_client_conn_security(struct rxrpc_connection *conn)
int rxrpc_init_server_conn_security(struct rxrpc_connection *conn) int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
{ {
const struct rxrpc_security *sec; const struct rxrpc_security *sec;
struct rxrpc_local *local = conn->trans->local; struct rxrpc_local *local = conn->params.local;
struct rxrpc_sock *rx; struct rxrpc_sock *rx;
struct key *key; struct key *key;
key_ref_t kref; key_ref_t kref;
...@@ -121,7 +121,7 @@ int rxrpc_init_server_conn_security(struct rxrpc_connection *conn) ...@@ -121,7 +121,7 @@ int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
_enter(""); _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); sec = rxrpc_security_lookup(conn->security_ix);
if (!sec) { if (!sec) {
...@@ -132,7 +132,7 @@ int rxrpc_init_server_conn_security(struct rxrpc_connection *conn) ...@@ -132,7 +132,7 @@ int rxrpc_init_server_conn_security(struct rxrpc_connection *conn)
/* find the service */ /* find the service */
read_lock_bh(&local->services_lock); read_lock_bh(&local->services_lock);
list_for_each_entry(rx, &local->services, listen_link) { 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; goto found_service;
} }
......
...@@ -90,14 +90,6 @@ static struct ctl_table rxrpc_sysctl_table[] = { ...@@ -90,14 +90,6 @@ static struct ctl_table rxrpc_sysctl_table[] = {
.proc_handler = proc_dointvec_minmax, .proc_handler = proc_dointvec_minmax,
.extra1 = (void *)&one, .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 */ /* 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