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

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

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

David Howells says:

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

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

To this end, the following significant changes are made:

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

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

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

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

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

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

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

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

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