From aff03ba25e41c8cb1d2cc8b206c4f046ea8d0c61 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Sun, 3 Jun 2007 08:30:42 +0000 Subject: libulockmgr: Work around a kernel bug in recv() --- ChangeLog | 5 +++++ lib/ulockmgr.c | 19 +++++++++++++++++-- util/ulockmgr_server.c | 8 ++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 570a36f..9e8a830 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2007-06-03 Miklos Szeredi + + * libulockmgr: Work around a kernel bug in recv(), causing it to + sometimes return zero even if data was available on the socket. + 2007-05-29 Miklos Szeredi * lib: optimization: store parent pointer in node instead of diff --git a/lib/ulockmgr.c b/lib/ulockmgr.c index bf27b36..05b940f 100644 --- a/lib/ulockmgr.c +++ b/lib/ulockmgr.c @@ -69,6 +69,21 @@ static void list_add_owner(struct owner *owner, struct owner *next) next->prev = owner; } +/* + * There's a bug in the linux kernel (< 2.6.22) recv() implementation + * on AF_UNIX, SOCK_STREAM sockets, that could cause it to return + * zero, even if data was available. Retrying the recv will return + * the data in this case. +*/ +static int do_recv(int sock, void *buf, size_t len, int flags) +{ + int res = recv(sock, buf, len, flags); + if (res == 0) + res = recv(sock, buf, len, flags); + + return res; +} + static int ulockmgr_send_message(int sock, void *buf, size_t buflen, int *fdp, int numfds) { @@ -252,7 +267,7 @@ static int ulockmgr_send_request(struct message *msg, const void *id, if (f) f->inuse++; - res = recv(cfd, msg, sizeof(struct message), MSG_WAITALL); + res = do_recv(cfd, msg, sizeof(struct message), MSG_WAITALL); if (res == -1) { perror("libulockmgr: recv"); msg->error = EIO; @@ -269,7 +284,7 @@ static int ulockmgr_send_request(struct message *msg, const void *id, sigemptyset(&unblock); sigaddset(&unblock, SIGUSR1); pthread_sigmask(SIG_UNBLOCK, &unblock, &old); - res = recv(cfd, msg, sizeof(struct message), MSG_WAITALL); + res = do_recv(cfd, msg, sizeof(struct message), MSG_WAITALL); errno_save = errno; pthread_sigmask(SIG_SETMASK, &old, NULL); if (res == sizeof(struct message)) diff --git a/util/ulockmgr_server.c b/util/ulockmgr_server.c index 211d74a..4f831b0 100644 --- a/util/ulockmgr_server.c +++ b/util/ulockmgr_server.c @@ -75,8 +75,12 @@ static int receive_message(int sock, void *buf, size_t buflen, int *fdp, msg.msg_controllen = sizeof(ccmsg); res = recvmsg(sock, &msg, MSG_WAITALL); - if (!res) - return 0; + if (!res) { + /* retry on zero return, see do_recv() in ulockmgr.c */ + res = recvmsg(sock, &msg, MSG_WAITALL); + if (!res) + return 0; + } if (res == -1) { perror("ulockmgr_server: recvmsg"); return -1; -- cgit v1.2.3