Skip to content

Commit 77ee4ba

Browse files
author
Pierre Ducroquet
committed
Handle backlog of UNIX socket too
The cheaper subsystem specifies that "backlog is only available on Linux and only on TCP sockets (not UNIX domain sockets)." This commit specifically implement this: UNIX domain socket support for backlog on Linux, using Netlink to call the kernel and get the queue status.
1 parent 2fe305f commit 77ee4ba

File tree

1 file changed

+147
-5
lines changed

1 file changed

+147
-5
lines changed

core/master.c

+147-5
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,13 @@ static void get_tcp_info(struct uwsgi_socket *uwsgi_sock) {
259259

260260
#ifdef UNBIT
261261
#define SIOBKLGQ 0x8908
262+
#else
263+
264+
#include <linux/netlink.h>
265+
#include <linux/rtnetlink.h>
266+
#include <linux/unix_diag.h>
267+
#include <linux/sock_diag.h>
268+
262269
#endif
263270

264271
#ifdef SIOBKLGQ
@@ -272,7 +279,141 @@ static void get_linux_unbit_SIOBKLGQ(struct uwsgi_socket *uwsgi_sock) {
272279
uwsgi_sock->max_queue = (uint64_t) uwsgi.listen_queue;
273280
}
274281
}
282+
#else
283+
284+
static int get_socket_inode_from_fd(int fd) {
285+
int inode = -1;
286+
char source_link_path[32];
287+
char link_target[32];
288+
sprintf(source_link_path, "/proc/self/fd/%i", fd);
289+
if (readlink(source_link_path, link_target, 32) > 0)
290+
sscanf(link_target, "socket:[%i]", &inode);
291+
return inode;
292+
}
293+
294+
static void send_netlink_query_for_inode(int fd, int target_ino) {
295+
struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
296+
struct {
297+
struct nlmsghdr nlh;
298+
struct unix_diag_req udr;
299+
} req = {
300+
.nlh = {
301+
.nlmsg_len = sizeof(req),
302+
.nlmsg_type = SOCK_DIAG_BY_FAMILY,
303+
.nlmsg_flags = NLM_F_REQUEST
304+
},
305+
.udr = {
306+
.sdiag_family = AF_UNIX,
307+
.udiag_show = UDIAG_SHOW_RQLEN,
308+
.udiag_ino = target_ino,
309+
.udiag_cookie = {-1, -1}
310+
}
311+
};
312+
struct iovec iov = {.iov_base = &req, .iov_len = sizeof(req)};
313+
struct msghdr msg = {
314+
.msg_name = &nladdr,
315+
.msg_namelen = sizeof(nladdr),
316+
.msg_iov = &iov,
317+
.msg_iovlen = 1
318+
};
319+
320+
for (;;) {
321+
if (sendmsg(fd, &msg, 0) < 0) {
322+
if (errno == EINTR)
323+
continue;
324+
325+
perror("sendmsg");
326+
return;
327+
}
328+
329+
return;
330+
}
331+
}
332+
333+
static int receive_netlink_answers(int fd, uint64_t *current_queue, uint64_t *max_queue) {
334+
long buf[8192 / sizeof(long)];
335+
struct sockaddr_nl nladdr;
336+
struct iovec iov = {.iov_base = buf, .iov_len = sizeof(buf)};
337+
int flags = 0;
338+
339+
for (;;) {
340+
struct msghdr msg = {
341+
.msg_name = &nladdr,
342+
.msg_namelen = sizeof(nladdr),
343+
.msg_iov = &iov,
344+
.msg_iovlen = 1
345+
};
346+
347+
ssize_t ret = recvmsg(fd, &msg, flags);
348+
349+
if (ret < 0) {
350+
if (errno == EINTR)
351+
continue;
352+
353+
perror("recvmsg");
354+
return -1;
355+
}
356+
if (ret == 0)
357+
return 0;
358+
359+
if (nladdr.nl_family != AF_NETLINK) {
360+
fputs("!AF_NETLINK\n", stderr);
361+
return -1;
362+
}
363+
364+
const struct nlmsghdr *h = (struct nlmsghdr *)buf;
365+
366+
if (!NLMSG_OK(h, ret)) {
367+
fputs("!NLMSG_OK\n", stderr);
368+
return -1;
369+
}
370+
371+
for (; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) {
372+
if (h->nlmsg_type == NLMSG_DONE)
373+
return 0;
374+
if (h->nlmsg_type == NLMSG_ERROR) {
375+
const struct nlmsgerr *err = NLMSG_DATA(h);
376+
if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*err))) {
377+
fputs("NLMSG_ERROR\n", stderr);
378+
} else {
379+
errno = -err->error;
380+
perror("NLMSG_ERROR");
381+
}
382+
return -1;
383+
}
384+
if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY) {
385+
fprintf(stderr, "unexpected nlmsg_type %u\n", (unsigned)h->nlmsg_type);
386+
return -1;
387+
}
388+
389+
// Now extract queue len from results
390+
const struct unix_diag_msg *diag = NLMSG_DATA(h);
391+
unsigned int rta_len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*diag));
392+
393+
for (struct rtattr *attr = (struct rtattr *)(diag + 1); RTA_OK(attr, rta_len); attr = RTA_NEXT(attr, rta_len)) {
394+
switch (attr->rta_type) {
395+
case UNIX_DIAG_RQLEN:
396+
*current_queue = ((struct unix_diag_rqlen *) RTA_DATA(attr))->udiag_rqueue;
397+
*max_queue = ((struct unix_diag_rqlen *) RTA_DATA(attr))->udiag_wqueue;
398+
break;
399+
}
400+
}
401+
}
402+
return 0;
403+
}
404+
}
405+
406+
static void get_linux_unix_socket_queue(struct uwsgi_socket *uwsgi_sock) {
407+
int fd = uwsgi_sock->fd;
408+
int inode = get_socket_inode_from_fd(fd);
409+
int diag_socket = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_SOCK_DIAG);
410+
send_netlink_query_for_inode(diag_socket, inode);
411+
receive_netlink_answers(diag_socket, &uwsgi_sock->queue, &uwsgi_sock->max_queue);
412+
close(diag_socket);
413+
}
414+
275415
#endif
416+
276417
#endif
277418

278419
static void master_check_listen_queue() {
@@ -282,15 +423,16 @@ static void master_check_listen_queue() {
282423
while(uwsgi_sock) {
283424
if (uwsgi_sock->family == AF_INET) {
284425
get_tcp_info(uwsgi_sock);
285-
}
426+
}
427+
else if (uwsgi_sock->family == AF_UNIX) {
286428
#ifdef __linux__
287429
#ifdef SIOBKLGQ
288-
else if (uwsgi_sock->family == AF_UNIX) {
289-
get_linux_unbit_SIOBKLGQ(uwsgi_sock);
290-
}
430+
get_linux_unbit_SIOBKLGQ(uwsgi_sock);
431+
#else
432+
get_linux_unix_socket_queue(uwsgi_sock);
291433
#endif
292434
#endif
293-
435+
}
294436
if (uwsgi_sock->queue > backlog) {
295437
backlog = uwsgi_sock->queue;
296438
}

0 commit comments

Comments
 (0)