Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 37 additions & 10 deletions net/vmw_vsock/af_vsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ EXPORT_SYMBOL_GPL(vsock_enqueue_accept);

static bool vsock_use_local_transport(unsigned int remote_cid)
{
lockdep_assert_held(&vsock_register_mutex);

if (!transport_local)
return false;

Expand Down Expand Up @@ -450,6 +452,8 @@ int vsock_assign_transport(struct vsock_sock *vsk, struct vsock_sock *psk)

remote_flags = vsk->remote_addr.svm_flags;

mutex_lock(&vsock_register_mutex);

switch (sk->sk_type) {
case SOCK_DGRAM:
new_transport = transport_dgram;
Expand All @@ -464,13 +468,30 @@ int vsock_assign_transport(struct vsock_sock *vsk, struct vsock_sock *psk)
new_transport = transport_h2g;
break;
default:
return -ESOCKTNOSUPPORT;
ret = -ESOCKTNOSUPPORT;
goto err;
}

if (vsk->transport) {
if (vsk->transport == new_transport)
return 0;
if (vsk->transport && vsk->transport == new_transport) {
ret = 0;
goto err;
}

/* We increase the module refcnt to prevent the transport unloading
* while there are open sockets assigned to it.
*/
if (!new_transport || !try_module_get(new_transport->module)) {
ret = -ENODEV;
goto err;
}

/* It's safe to release the mutex after a successful try_module_get().
* Whichever transport `new_transport` points at, it won't go away until
* the last module_put() below or in vsock_deassign_transport().
*/
mutex_unlock(&vsock_register_mutex);

if (vsk->transport) {
/* transport->release() must be called with sock lock acquired.
* This path can only be taken during vsock_stream_connect(),
* where we have already held the sock lock.
Expand All @@ -479,13 +500,16 @@ int vsock_assign_transport(struct vsock_sock *vsk, struct vsock_sock *psk)
*/
vsk->transport->release(vsk);
vsock_deassign_transport(vsk);
}

/* We increase the module refcnt to prevent the transport unloading
* while there are open sockets assigned to it.
*/
if (!new_transport || !try_module_get(new_transport->module))
return -ENODEV;
/* transport's release() and destruct() can touch some socket
* state, since we are reassigning the socket to a new transport
* during vsock_connect(), let's reset these fields to have a
* clean state.
*/
sock_reset_flag(sk, SOCK_DONE);
sk->sk_state = TCP_CLOSE;
vsk->peer_shutdown = 0;
}

ret = new_transport->init(vsk, psk);
if (ret) {
Expand All @@ -496,6 +520,9 @@ int vsock_assign_transport(struct vsock_sock *vsk, struct vsock_sock *psk)
vsk->transport = new_transport;

return 0;
err:
mutex_unlock(&vsock_register_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(vsock_assign_transport);

Expand Down