Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160151427
D57519.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.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
@@ -494,9 +494,11 @@
bool was_bound = nlp->nl_bound;
NLP_UNLOCK(nlp);
- /* Wait till all scheduled work has been completed */
- taskqueue_drain_all(nlp->nl_taskqueue);
- taskqueue_free(nlp->nl_taskqueue);
+ if (nlp->nl_taskqueue != NULL) {
+ /* Wait till all scheduled work has been completed */
+ taskqueue_drain_all(nlp->nl_taskqueue);
+ taskqueue_free(nlp->nl_taskqueue);
+ }
NLCTL_WLOCK();
NLP_LOCK(nlp);
@@ -592,7 +594,19 @@
NL_LOG(LOG_DEBUG2, "sending message to kernel %u bytes", nb->datalen);
+ /* Sync path */
+ if (nlp->nl_flags & NLF_SND_SYNC) {
+ error = nl_process_nbuf_sync(nb, nlp);
+ if (error == 0) {
+ NL_LOG(LOG_DEBUG3, "success");
+ nl_buf_free(nb);
+ nb = NULL;
+ }
+ goto out;
+ }
+
SOCK_SENDBUF_LOCK(so);
+
restart:
if (sb->sb_hiwat - sb->sb_ccc >= nb->datalen) {
TAILQ_INSERT_TAIL(&sb->nl_queue, nb, tailq);
@@ -691,6 +705,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,11 +873,63 @@
return (NLF_STRICT);
case NETLINK_MSG_INFO:
return (NLF_MSG_INFO);
+ case NETLINK_SND_SYNC:
+ return (NLF_SND_SYNC);
}
return (0);
}
+static int
+nl_sosend_switch_sync(struct socket *so, struct nlpcb *nlp, bool turn_on)
+{
+ int error = 0;
+ bool already_sync;
+ struct sockbuf *sb = &so->so_snd;
+
+ error = SOCK_IO_SEND_LOCK(so, SBLOCKWAIT(0));
+ if (error)
+ return (error);
+
+ already_sync = (nlp->nl_flags & NLF_SND_SYNC) != 0;
+ if (already_sync == turn_on) {
+ SOCK_IO_SEND_UNLOCK(so);
+ return (0);
+ }
+
+ if (turn_on) {
+ SOCK_SENDBUF_LOCK(so);
+ if (!TAILQ_EMPTY(&sb->nl_queue)){
+ SOCK_SENDBUF_UNLOCK(so);
+ SOCK_IO_SEND_UNLOCK(so);
+ return (EBUSY);
+ }
+ SOCK_SENDBUF_UNLOCK(so);
+
+ NLCTL_WLOCK();
+ nlp->nl_flags |= NLF_SND_SYNC;
+ NLCTL_WUNLOCK();
+
+ MPASS(nlp->nl_taskqueue != NULL);
+ taskqueue_drain_all(nlp->nl_taskqueue);
+ taskqueue_free(nlp->nl_taskqueue);
+ nlp->nl_taskqueue = NULL;
+ } else {
+ NLCTL_WLOCK();
+ nlp->nl_flags &= ~NLF_SND_SYNC;
+ NLCTL_WUNLOCK();
+
+ MPASS(nlp->nl_taskqueue == NULL);
+ nlp->nl_taskqueue = taskqueue_create("netlink_socket", M_WAITOK,
+ taskqueue_thread_enqueue, &nlp->nl_taskqueue);
+ taskqueue_start_threads(&nlp->nl_taskqueue, 1, PWAIT,
+ "netlink_socket (PID %u)", nlp->nl_process_id);
+ }
+
+ SOCK_IO_SEND_UNLOCK(so);
+ return (error);
+}
+
static int
nl_ctloutput(struct socket *so, struct sockopt *sopt)
{
@@ -881,17 +966,24 @@
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;
}
+ if (flag == NLF_SND_SYNC) {
+ error = nl_sosend_switch_sync(so, nlp, optval != 0);
+ break;
+ }
+
NLCTL_WLOCK();
if (optval != 0)
nlp->nl_flags |= flag;
@@ -915,6 +1007,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
@@ -160,25 +160,11 @@
void
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 */
+ if ((nlp->nl_flags & NLF_SND_SYNC) == 0) {
+ NLP_LOCK(nlp);
+ nl_schedule_taskqueue(nlp);
+ NLP_UNLOCK(nlp);
}
-
- nl_schedule_taskqueue(nlp);
- NLP_UNLOCK(nlp);
}
void
@@ -368,3 +354,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,80 @@
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 to sync mid-session */
+ fd = fullsocket();
+ ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_SND_SYNC, &val, optlen) == -1);
+ ATF_REQUIRE(errno == EBUSY);
+
+ /* sync to async */
+ 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));
+ val = 0;
+ ATF_REQUIRE(setsockopt(fd, SOL_NETLINK, NETLINK_SND_SYNC, &val, optlen) != -1);
+ ATF_REQUIRE(send(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr));
+ ATF_REQUIRE(recv(fd, buf, BUFLEN, 0) > sizeof(hdr));
+}
+
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
Mon, Jun 22, 7:21 PM (3 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34164032
Default Alt Text
D57519.diff (9 KB)
Attached To
Mode
D57519: netlink: Add sync path in user-kernel interface
Attached
Detach File
Event Timeline
Log In to Comment