socket: keep fd the same across reconnects

There is no guarantee that we get the same fd again when
reestablishing a session. But if the fd changes during a
reconnect we might up with a client application busy polling
on the old fd.

Qemu registers a read handler on the current fd, but is
not realizing fd changes. So we busy poll on the old fd for good.
Things are working (except for the busy polling) until
a drain all is issued. At this point Qemu deadlocks.

Signed-off-by: Peter Lieven <pl@kamp.de>
libnfs-4.0.0-vitalif
Peter Lieven 2015-09-22 14:54:48 +02:00
parent 7b7aef6b6d
commit ddd9e2f7e9
2 changed files with 15 additions and 1 deletions

View File

@ -79,6 +79,7 @@ struct rpc_queue {
struct rpc_context {
uint32_t magic;
int fd;
int old_fd;
int is_connected;
char *error_string;

View File

@ -132,6 +132,10 @@ int rpc_get_fd(struct rpc_context *rpc)
{
assert(rpc->magic == RPC_CONTEXT_MAGIC);
if (rpc->old_fd) {
return rpc->old_fd;
}
return rpc->fd;
}
@ -418,6 +422,14 @@ static int rpc_connect_sockaddr_async(struct rpc_context *rpc, struct sockaddr_s
return -1;
}
if (rpc->old_fd) {
if (dup2(rpc->fd, rpc->old_fd) == -1) {
return -1;
}
close(rpc->fd);
rpc->fd = rpc->old_fd;
}
/* Some systems allow you to set capabilities on an executable
* to allow the file to be executed with privilege to bind to
* privileged system ports, even if the user is not root.
@ -579,6 +591,7 @@ static void reconnect_cb(struct rpc_context *rpc, int status, void *data _U_, vo
rpc->is_connected = 1;
rpc->connect_cb = NULL;
rpc->old_fd = 0;
}
/* disconnect but do not error all PDUs, just move pdus in-flight back to the outqueue and reconnect */
@ -590,7 +603,7 @@ static int rpc_reconnect_requeue(struct rpc_context *rpc)
assert(rpc->magic == RPC_CONTEXT_MAGIC);
if (rpc->fd != -1) {
close(rpc->fd);
rpc->old_fd = rpc->fd;
}
rpc->fd = -1;
rpc->is_connected = 0;