From 03877bf6a30cca7d4bc3ffabd3c3e9464a7a1a19 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 30 Mar 2018 21:04:43 +0100 Subject: rxrpc: Fix Tx ring annotation after initial Tx failure rxrpc calls have a ring of packets that are awaiting ACK or retransmission and a parallel ring of annotations that tracks the state of those packets. If the initial transmission of a packet on the underlying UDP socket fails then the packet annotation is marked for resend - but the setting of this mark accidentally erases the last-packet mark also stored in the same annotation slot. If this happens, a call won't switch out of the Tx phase when all the packets have been transmitted. Fix this by retaining the last-packet mark and only altering the packet state. Fixes: 248f219cb8bc ("rxrpc: Rewrite the data and ack handling code") Signed-off-by: David Howells --- net/rxrpc/sendmsg.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net/rxrpc/sendmsg.c') diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 8503f279b467..783c777fc6e7 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -130,7 +130,9 @@ static inline void rxrpc_instant_resend(struct rxrpc_call *call, int ix) spin_lock_bh(&call->lock); if (call->state < RXRPC_CALL_COMPLETE) { - call->rxtx_annotations[ix] = RXRPC_TX_ANNO_RETRANS; + call->rxtx_annotations[ix] = + (call->rxtx_annotations[ix] & RXRPC_TX_ANNO_LAST) | + RXRPC_TX_ANNO_RETRANS; if (!test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events)) rxrpc_queue_call(call); } -- cgit v1.2.3 From 88f2a8257c9aa7df957b1a79a104f348d60d8027 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 30 Mar 2018 21:05:17 +0100 Subject: rxrpc: Fix checker warnings and errors Fix various issues detected by checker. Errors: (*) rxrpc_discard_prealloc() should be using rcu_assign_pointer to set call->socket. Warnings: (*) rxrpc_service_connection_reaper() should be passing NULL rather than 0 to trace_rxrpc_conn() as the where argument. (*) rxrpc_disconnect_client_call() should get its net pointer via the call->conn rather than call->sock to avoid a warning about accessing an RCU pointer without protection. (*) Proc seq start/stop functions need annotation as they pass locks between the functions. False positives: (*) Checker doesn't correctly handle of seq-retry lock context balance in rxrpc_find_service_conn_rcu(). (*) Checker thinks execution may proceed past the BUG() in rxrpc_publish_service_conn(). (*) Variable length array warnings from SKCIPHER_REQUEST_ON_STACK() in rxkad.c. Signed-off-by: David Howells --- net/rxrpc/call_accept.c | 3 ++- net/rxrpc/call_object.c | 1 + net/rxrpc/conn_client.c | 2 +- net/rxrpc/conn_object.c | 2 +- net/rxrpc/proc.c | 6 ++++++ net/rxrpc/sendmsg.c | 2 ++ 6 files changed, 13 insertions(+), 3 deletions(-) (limited to 'net/rxrpc/sendmsg.c') diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 92ebd1d7e0bb..4ce24c000653 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -225,7 +225,7 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx) tail = b->call_backlog_tail; while (CIRC_CNT(head, tail, size) > 0) { struct rxrpc_call *call = b->call_backlog[tail]; - call->socket = rx; + rcu_assign_pointer(call->socket, rx); if (rx->discard_new_call) { _debug("discard %lx", call->user_call_ID); rx->discard_new_call(call, call->user_call_ID); @@ -456,6 +456,7 @@ struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *rx, unsigned long user_call_ID, rxrpc_notify_rx_t notify_rx) __releases(&rx->sk.sk_lock.slock) + __acquires(call->user_mutex) { struct rxrpc_call *call; struct rb_node *parent, **pp; diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 147657dfe757..85b12c472522 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -219,6 +219,7 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, gfp_t gfp, unsigned int debug_id) __releases(&rx->sk.sk_lock.slock) + __acquires(&call->user_mutex) { struct rxrpc_call *call, *xcall; struct rxrpc_net *rxnet = rxrpc_net(sock_net(&rx->sk)); diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 064175068059..041da40dbf93 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -776,7 +776,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call) unsigned int channel = call->cid & RXRPC_CHANNELMASK; struct rxrpc_connection *conn = call->conn; struct rxrpc_channel *chan = &conn->channels[channel]; - struct rxrpc_net *rxnet = rxrpc_net(sock_net(&call->socket->sk)); + struct rxrpc_net *rxnet = conn->params.local->rxnet; trace_rxrpc_client(conn, channel, rxrpc_client_chan_disconnect); call->conn = NULL; diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index ccbac190add1..bfc46fd69a62 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -418,7 +418,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work) */ if (atomic_cmpxchg(&conn->usage, 1, 0) != 1) continue; - trace_rxrpc_conn(conn, rxrpc_conn_reap_service, 0, 0); + trace_rxrpc_conn(conn, rxrpc_conn_reap_service, 0, NULL); if (rxrpc_conn_is_client(conn)) BUG(); diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c index f79f260c6ddc..7e45db058823 100644 --- a/net/rxrpc/proc.c +++ b/net/rxrpc/proc.c @@ -29,6 +29,8 @@ static const char *const rxrpc_conn_states[RXRPC_CONN__NR_STATES] = { * generate a list of extant and dead calls in /proc/net/rxrpc_calls */ static void *rxrpc_call_seq_start(struct seq_file *seq, loff_t *_pos) + __acquires(rcu) + __acquires(rxnet->call_lock) { struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); @@ -45,6 +47,8 @@ static void *rxrpc_call_seq_next(struct seq_file *seq, void *v, loff_t *pos) } static void rxrpc_call_seq_stop(struct seq_file *seq, void *v) + __releases(rxnet->call_lock) + __releases(rcu) { struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); @@ -135,6 +139,7 @@ const struct file_operations rxrpc_call_seq_fops = { * generate a list of extant virtual connections in /proc/net/rxrpc_conns */ static void *rxrpc_connection_seq_start(struct seq_file *seq, loff_t *_pos) + __acquires(rxnet->conn_lock) { struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); @@ -151,6 +156,7 @@ static void *rxrpc_connection_seq_next(struct seq_file *seq, void *v, } static void rxrpc_connection_seq_stop(struct seq_file *seq, void *v) + __releases(rxnet->conn_lock) { struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq)); diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 783c777fc6e7..a62980a80151 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -556,6 +556,7 @@ static struct rxrpc_call * rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, struct rxrpc_send_params *p) __releases(&rx->sk.sk_lock.slock) + __acquires(&call->user_mutex) { struct rxrpc_conn_parameters cp; struct rxrpc_call *call; @@ -596,6 +597,7 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, */ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len) __releases(&rx->sk.sk_lock.slock) + __releases(&call->user_mutex) { enum rxrpc_call_state state; struct rxrpc_call *call; -- cgit v1.2.3 From 17226f1240381812c3a4927dc9da2814fb71c8ac Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 30 Mar 2018 21:05:44 +0100 Subject: rxrpc: Fix leak of rxrpc_peer objects When a new client call is requested, an rxrpc_conn_parameters struct object is passed in with a bunch of parameters set, such as the local endpoint to use. A pointer to the target peer record is also placed in there by rxrpc_get_client_conn() - and this is removed if and only if a new connection object is allocated. Thus it leaks if a new connection object isn't allocated. Fix this by putting any peer object attached to the rxrpc_conn_parameters object in the function that allocated it. Fixes: 19ffa01c9c45 ("rxrpc: Use structs to hold connection params and protocol info") Signed-off-by: David Howells --- net/rxrpc/af_rxrpc.c | 2 ++ net/rxrpc/ar-internal.h | 1 + net/rxrpc/net_ns.c | 1 + net/rxrpc/peer_object.c | 21 +++++++++++++++++++++ net/rxrpc/sendmsg.c | 1 + 5 files changed, 26 insertions(+) (limited to 'net/rxrpc/sendmsg.c') diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 0b3026b8fa40..9a2c8e7c000e 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -324,6 +324,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, mutex_unlock(&call->user_mutex); } + rxrpc_put_peer(cp.peer); _leave(" = %p", call); return call; } @@ -447,6 +448,7 @@ int rxrpc_kernel_retry_call(struct socket *sock, struct rxrpc_call *call, ret = rxrpc_retry_client_call(rx, call, &cp, srx, GFP_KERNEL); mutex_unlock(&call->user_mutex); + rxrpc_put_peer(cp.peer); _leave(" = %d", ret); return ret; } diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index c46583bc255d..90d7079e0aa9 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -1041,6 +1041,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *, struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t); struct rxrpc_peer *rxrpc_lookup_incoming_peer(struct rxrpc_local *, struct rxrpc_peer *); +void rxrpc_destroy_all_peers(struct rxrpc_net *); struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *); struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *); void rxrpc_put_peer(struct rxrpc_peer *); diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c index fa9ce60e7bfa..c7a023fb22d0 100644 --- a/net/rxrpc/net_ns.c +++ b/net/rxrpc/net_ns.c @@ -118,6 +118,7 @@ static __net_exit void rxrpc_exit_net(struct net *net) cancel_work_sync(&rxnet->peer_keepalive_work); rxrpc_destroy_all_calls(rxnet); rxrpc_destroy_all_connections(rxnet); + rxrpc_destroy_all_peers(rxnet); rxrpc_destroy_all_locals(rxnet); proc_remove(rxnet->proc_net); } diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index a4a750aea1e5..1b7e8107b3ae 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c @@ -463,6 +463,27 @@ void rxrpc_put_peer(struct rxrpc_peer *peer) } } +/* + * Make sure all peer records have been discarded. + */ +void rxrpc_destroy_all_peers(struct rxrpc_net *rxnet) +{ + struct rxrpc_peer *peer; + int i; + + for (i = 0; i < HASH_SIZE(rxnet->peer_hash); i++) { + if (hlist_empty(&rxnet->peer_hash[i])) + continue; + + hlist_for_each_entry(peer, &rxnet->peer_hash[i], hash_link) { + pr_err("Leaked peer %u {%u} %pISp\n", + peer->debug_id, + atomic_read(&peer->usage), + &peer->srx.transport); + } + } +} + /** * rxrpc_kernel_get_peer - Get the peer address of a call * @sock: The socket on which the call is in progress. diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index a62980a80151..206e802ccbdc 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -586,6 +586,7 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, atomic_inc_return(&rxrpc_debug_id)); /* The socket is now unlocked */ + rxrpc_put_peer(cp.peer); _leave(" = %p\n", call); return call; } -- cgit v1.2.3