diff options
Diffstat (limited to 'net/rxrpc/call_object.c')
| -rw-r--r-- | net/rxrpc/call_object.c | 26 | 
1 files changed, 21 insertions, 5 deletions
diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index a31c18c09894..c9f34b0a11df 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -493,7 +493,7 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)  	_debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn); -	if (conn) +	if (conn && !test_bit(RXRPC_CALL_DISCONNECTED, &call->flags))  		rxrpc_disconnect_call(call);  	if (call->security)  		call->security->free_call_crypto(call); @@ -562,13 +562,14 @@ void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace op)  }  /* - * Final call destruction under RCU. + * Final call destruction - but must be done in process context.   */ -static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) +static void rxrpc_destroy_call(struct work_struct *work)  { -	struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu); +	struct rxrpc_call *call = container_of(work, struct rxrpc_call, processor);  	struct rxrpc_net *rxnet = call->rxnet; +	rxrpc_put_connection(call->conn);  	rxrpc_put_peer(call->peer);  	kfree(call->rxtx_buffer);  	kfree(call->rxtx_annotations); @@ -578,6 +579,22 @@ static void rxrpc_rcu_destroy_call(struct rcu_head *rcu)  }  /* + * Final call destruction under RCU. + */ +static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) +{ +	struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu); + +	if (in_softirq()) { +		INIT_WORK(&call->processor, rxrpc_destroy_call); +		if (!rxrpc_queue_work(&call->processor)) +			BUG(); +	} else { +		rxrpc_destroy_call(&call->processor); +	} +} + +/*   * clean up a call   */  void rxrpc_cleanup_call(struct rxrpc_call *call) @@ -590,7 +607,6 @@ void rxrpc_cleanup_call(struct rxrpc_call *call)  	ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE);  	ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags)); -	ASSERTCMP(call->conn, ==, NULL);  	rxrpc_cleanup_ring(call);  	rxrpc_free_skb(call->tx_pending, rxrpc_skb_cleaned);  |