Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160403322
D57519.id180132.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D57519.id180132.diff
View Options
diff --git a/sys/netlink/netlink.h b/sys/netlink/netlink.h
--- a/sys/netlink/netlink.h
+++ b/sys/netlink/netlink.h
@@ -90,6 +90,7 @@
#define NETLINK_GET_STRICT_CHK 12 /* Strict header checking */
#define NETLINK_MSG_INFO 257 /* (FreeBSD-specific) Receive message originator data in cmsg */
+#define NETLINK_SND_SYNC 258 /* (FreeBSD-specific) Process send operations synchronously */
/*
* RFC 3549, 2.3.2 Netlink Message Header
diff --git a/sys/netlink/netlink_domain.c b/sys/netlink/netlink_domain.c
--- a/sys/netlink/netlink_domain.c
+++ b/sys/netlink/netlink_domain.c
@@ -551,6 +551,35 @@
return (0);
}
+static int
+nl_sosend_sync_drain(struct socket *so, struct nlpcb *nlp, int flags)
+{
+ struct sockbuf *sb = &so->so_snd;
+ int error = 0;
+
+ SOCK_SENDBUF_LOCK_ASSERT(so);
+ while (!TAILQ_EMPTY(&sb->nl_queue)) {
+ /* To avoid potential deadlock, we kick the async path */
+ NLP_LOCK(nlp);
+ nl_schedule_taskqueue(nlp);
+ NLP_UNLOCK(nlp);
+
+ if ((so->so_state & SS_NBIO) ||
+ (flags & (MSG_NBIO | MSG_DONTWAIT)) != 0) {
+ return (EWOULDBLOCK);
+ }
+
+ sb->sb_flags |= SB_WAIT;
+ error = mtx_sleep(&sb->nl_queue, SOCK_SENDBUF_MTX(so),
+ PCATCH, "nlsync", 0);
+
+ if (error != 0)
+ break;
+ }
+
+ return (error);
+}
+
static int
nl_sosend(struct socket *so, struct sockaddr *addr, struct uio *uio,
struct mbuf *m, struct mbuf *control, int flags, struct thread *td)
@@ -593,6 +622,27 @@
NL_LOG(LOG_DEBUG2, "sending message to kernel %u bytes", nb->datalen);
SOCK_SENDBUF_LOCK(so);
+
+ /* Sync path */
+ if (nlp->nl_flags & NLF_SND_SYNC) {
+ /*
+ * Wait for the queue to drain completely to prevent
+ * out-of-order execution of requests.
+ */
+ error = nl_sosend_sync_drain(so, nlp, flags);
+ SOCK_SENDBUF_UNLOCK(so);
+ if (error != 0)
+ goto out;
+
+ error = nl_process_nbuf_sync(nb, nlp);
+ if (error == 0) {
+ NL_LOG(LOG_DEBUG3, "success");
+ nl_buf_free(nb);
+ nb = NULL;
+ }
+ goto out;
+ }
+
restart:
if (sb->sb_hiwat - sb->sb_ccc >= nb->datalen) {
TAILQ_INSERT_TAIL(&sb->nl_queue, nb, tailq);
@@ -691,6 +741,25 @@
if (__predict_false(error))
return (error);
+ if (__predict_false(nlp->nl_dropped_bytes > 0)) {
+ NLP_LOCK(nlp);
+ unsigned long dropped_bytes = nlp->nl_dropped_bytes;
+ unsigned long dropped_messages = nlp->nl_dropped_messages;
+ nlp->nl_dropped_bytes = 0;
+ nlp->nl_dropped_messages = 0;
+ NLP_UNLOCK(nlp);
+
+ if (dropped_bytes > 0) {
+ NLP_LOG(LOG_DEBUG, nlp,
+ "socket RX overflowed, %lu messages (%lu bytes) dropped. "
+ "bytes: [%u/%u]", dropped_messages, dropped_bytes,
+ sb->sb_ccc, sb->sb_hiwat);
+ /* XXX: Original code intended to send a netlink message here. */
+ SOCK_IO_RECV_UNLOCK(so);
+ return (ENOBUFS);
+ }
+ }
+
len = 0;
overflow = 0;
msgrcv = 0;
@@ -840,6 +909,8 @@
return (NLF_STRICT);
case NETLINK_MSG_INFO:
return (NLF_MSG_INFO);
+ case NETLINK_SND_SYNC:
+ return (NLF_SND_SYNC);
}
return (0);
@@ -881,13 +952,15 @@
case NETLINK_EXT_ACK:
case NETLINK_GET_STRICT_CHK:
case NETLINK_MSG_INFO:
+ case NETLINK_SND_SYNC:
error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
if (error != 0)
break;
flag = nl_getoptflag(sopt->sopt_name);
- if ((flag == NLF_MSG_INFO) && nlp->nl_linux) {
+ if ((flag == NLF_MSG_INFO || flag == NLF_SND_SYNC) &&
+ nlp->nl_linux) {
error = EINVAL;
break;
}
@@ -915,6 +988,7 @@
case NETLINK_EXT_ACK:
case NETLINK_GET_STRICT_CHK:
case NETLINK_MSG_INFO:
+ case NETLINK_SND_SYNC:
NLCTL_RLOCK();
optval = (nlp->nl_flags & nl_getoptflag(sopt->sopt_name)) != 0;
NLCTL_RUNLOCK();
diff --git a/sys/netlink/netlink_io.c b/sys/netlink/netlink_io.c
--- a/sys/netlink/netlink_io.c
+++ b/sys/netlink/netlink_io.c
@@ -133,6 +133,10 @@
break;
}
}
+ if (TAILQ_EMPTY(&sb->nl_queue) && (sb->sb_flags & SB_WAIT)) {
+ sb->sb_flags &= ~SB_WAIT;
+ wakeup(&sb->nl_queue);
+ }
SOCK_SENDBUF_UNLOCK(so);
return (reschedule);
@@ -161,22 +165,6 @@
nl_on_transmit(struct nlpcb *nlp)
{
NLP_LOCK(nlp);
-
- struct socket *so = nlp->nl_socket;
- if (__predict_false(nlp->nl_dropped_bytes > 0 && so != NULL)) {
- unsigned long dropped_bytes = nlp->nl_dropped_bytes;
- unsigned long dropped_messages = nlp->nl_dropped_messages;
- nlp->nl_dropped_bytes = 0;
- nlp->nl_dropped_messages = 0;
-
- struct sockbuf *sb = &so->so_rcv;
- NLP_LOG(LOG_DEBUG, nlp,
- "socket RX overflowed, %lu messages (%lu bytes) dropped. "
- "bytes: [%u/%u]", dropped_messages, dropped_bytes,
- sb->sb_ccc, sb->sb_hiwat);
- /* TODO: send netlink message */
- }
-
nl_schedule_taskqueue(nlp);
NLP_UNLOCK(nlp);
}
@@ -368,3 +356,44 @@
} else
return (true);
}
+
+int
+nl_process_nbuf_sync(struct nl_buf *nb, struct nlpcb *nlp)
+{
+ struct nl_writer nw;
+ struct nlmsghdr *hdr;
+ int error;
+
+ NL_LOG(LOG_DEBUG3, "RX netlink buf %p on %p", nb, nlp->nl_socket);
+
+ if (!nl_writer_unicast(&nw, NLMSG_SMALL, nlp, false)) {
+ NL_LOG(LOG_DEBUG, "error allocating socket writer");
+ return (ENOBUFS);
+ }
+
+ struct nl_pstate npt = {
+ .nlp = nlp,
+ .lb.base = &nb->data[roundup2(nb->datalen, 8)],
+ .lb.size = nb->buflen - roundup2(nb->datalen, 8),
+ .nw = &nw,
+ .strict = nlp->nl_flags & NLF_STRICT,
+ };
+
+ for (; nb->offset + sizeof(struct nlmsghdr) <= nb->datalen;) {
+ hdr = (struct nlmsghdr *)&nb->data[nb->offset];
+ /* Save length prior to calling handler */
+ int msglen = NLMSG_ALIGN(hdr->nlmsg_len);
+ NL_LOG(LOG_DEBUG3, "parsing offset %d/%d",
+ nb->offset, nb->datalen);
+ npt_clear(&npt);
+ error = nl_receive_message(hdr, nb->datalen - nb->offset, nlp,
+ &npt);
+ nb->offset += msglen;
+ if (__predict_false(error != 0))
+ return (error);
+ }
+ NL_LOG(LOG_DEBUG3, "packet parsing done");
+ nlmsg_flush(&nw);
+
+ return (0);
+}
diff --git a/sys/netlink/netlink_var.h b/sys/netlink/netlink_var.h
--- a/sys/netlink/netlink_var.h
+++ b/sys/netlink/netlink_var.h
@@ -91,6 +91,7 @@
#define NLF_EXT_ACK 0x02 /* Allow including extended TLVs in ack */
#define NLF_STRICT 0x04 /* Perform strict header checks */
#define NLF_MSG_INFO 0x08 /* Send caller info along with the notifications */
+#define NLF_SND_SYNC 0x10 /* Process send operations synchronously */
SYSCTL_DECL(_net_netlink);
SYSCTL_DECL(_net_netlink_debug);
@@ -138,6 +139,7 @@
void nl_set_source_metadata(struct mbuf *m, int num_messages);
struct nl_buf *nl_buf_alloc(size_t len, int mflag);
void nl_buf_free(struct nl_buf *nb);
+int nl_process_nbuf_sync(struct nl_buf *nb, struct nlpcb *nlp);
#define MAX_FAMILIES 20
#define MAX_GROUPS 64
diff --git a/tests/sys/netlink/netlink_socket.c b/tests/sys/netlink/netlink_socket.c
--- a/tests/sys/netlink/netlink_socket.c
+++ b/tests/sys/netlink/netlink_socket.c
@@ -357,12 +357,77 @@
sizeof(struct in_addr)) == 0);
}
+static int
+fullsocket_sync(void)
+{
+ socklen_t slen = sizeof(int);
+ int fd, sendspace, recvspace, recvavail;
+ int val = 1;
+
+ ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC)) != -1);
+ ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_SND_SYNC, &val, slen) != -1);
+ ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendspace,
+ &slen) == 0);
+ ATF_REQUIRE(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvspace,
+ &slen) == 0);
+
+ /*
+ * Flood the socket with requests, without reading out the replies.
+ * In sync mode, send() will never fail even if recv buffer is full.
+ */
+ do {
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), MSG_DONTWAIT) == sizeof(hdr));
+ ATF_REQUIRE(ioctl(fd, FIONREAD, &recvavail) != -1);
+ } while (recvavail < recvspace);
+ return (fd);
+}
+
+ATF_TC(sync);
+ATF_TC_HEAD(sync, tc)
+{
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
+}
+ATF_TC_BODY(sync, tc)
+{
+ char buf[BUFLEN];
+ int fd, sendsize;
+ int val = 1;
+ socklen_t optlen = sizeof(val);
+
+ /* Normal case */
+ ATF_REQUIRE((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC)) != -1);
+ ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_SND_SYNC, &val, optlen) != -1);
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ATF_REQUIRE(ioctl(fd, FIONWRITE, &sendsize) != -1);
+ ATF_REQUIRE_EQ(sendsize, 0);
+ ATF_REQUIRE(recv(fd, buf, sizeof(hdr), 0) == sizeof(hdr));
+
+ /* Recv buffer full */
+ fd = fullsocket_sync();
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ATF_REQUIRE(recv(fd, buf, BUFLEN, 0) == -1);
+ ATF_REQUIRE(errno == ENOBUFS);
+ ATF_REQUIRE(recv(fd, buf, BUFLEN, 0) > sizeof(hdr));
+
+ /* Async path makes recv buffer full (send buffer full) */
+ fd = fullsocket();
+ ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_SND_SYNC, &val, optlen) != -1);
+ /* As same as async path, when both buffers full: block. */
+ timer_done = 0;
+ ATF_REQUIRE(sigaction(SIGALRM, &sigact, NULL) == 0);
+ ATF_REQUIRE(setitimer(ITIMER_REAL, &itv, NULL) == 0);
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == -1);
+ ATF_REQUIRE(errno == EINTR);
+ ATF_REQUIRE(timer_done == 1);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, overflow);
ATF_TP_ADD_TC(tp, peek);
ATF_TP_ADD_TC(tp, sizes);
ATF_TP_ADD_TC(tp, membership);
+ ATF_TP_ADD_TC(tp, sync);
return (atf_no_error());
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jun 25, 4:05 AM (5 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34303652
Default Alt Text
D57519.id180132.diff (9 KB)
Attached To
Mode
D57519: netlink: Add sync path in user-kernel interface
Attached
Detach File
Event Timeline
Log In to Comment