Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160801981
D57519.id180606.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D57519.id180606.diff
View Options
diff --git a/share/man/man4/netlink.4 b/share/man/man4/netlink.4
--- a/share/man/man4/netlink.4
+++ b/share/man/man4/netlink.4
@@ -248,6 +248,12 @@
without the message body.
.It Dv NETLINK_EXT_ACK
Acknowledges ability to receive additional TLVs in the ACK message.
+.It Dv NETLINK_GET_STRICT_CHK
+Enables strict header checking.
+.It Dv NETLINK_MSG_INFO
+(FreeBSD-specific) Receives message originator data in cmsg.
+.It Dv NETLINK_SND_SYNC
+(FreeBSD-specific) Processes send operations synchronously, bypassing the standard asynchronous queuing.
.El
.Pp
Additionally, netlink overrides the following socket options from the
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(nb, nlp, true);
+ 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);
@@ -842,9 +856,11 @@
SOCK_IO_RECV_UNLOCK(so);
- NLP_LOCK(nlp);
- nl_schedule_taskqueue(nlp);
- NLP_UNLOCK(nlp);
+ if ((nlp->nl_flags & NLF_SND_SYNC) == 0) {
+ NLP_LOCK(nlp);
+ nl_schedule_taskqueue(nlp);
+ NLP_UNLOCK(nlp);
+ }
return (error);
}
@@ -861,11 +877,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)
{
@@ -902,17 +970,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;
@@ -936,6 +1011,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
@@ -51,8 +51,6 @@
* sending netlink data between the kernel and userland.
*/
-static bool nl_process_nbuf(struct nl_buf *nb, struct nlpcb *nlp);
-
struct nl_buf *
nl_buf_alloc(size_t len, int mflag)
{
@@ -119,9 +117,9 @@
while ((nb = TAILQ_FIRST(&sb->nl_queue)) != NULL) {
TAILQ_REMOVE(&sb->nl_queue, nb, tailq);
SOCK_SENDBUF_UNLOCK(so);
- reschedule = nl_process_nbuf(nb, nlp);
+ reschedule = nl_process_nbuf(nb, nlp, false);
SOCK_SENDBUF_LOCK(so);
- if (reschedule) {
+ if (!reschedule) {
sb->sb_acc -= nb->datalen;
sb->sb_ccc -= nb->datalen;
/* XXXGL: potentially can reduce lock&unlock count. */
@@ -293,8 +291,8 @@
/*
* Processes an incoming packet, which can contain multiple netlink messages
*/
-static bool
-nl_process_nbuf(struct nl_buf *nb, struct nlpcb *nlp)
+int
+nl_process_nbuf(struct nl_buf *nb, struct nlpcb *nlp, bool sync)
{
struct nl_writer nw;
struct nlmsghdr *hdr;
@@ -304,10 +302,12 @@
if (!nl_writer_unicast(&nw, NLMSG_SMALL, nlp, false)) {
NL_LOG(LOG_DEBUG, "error allocating socket writer");
- return (true);
+ return (EAGAIN);
}
- nlmsg_ignore_limit(&nw);
+ if (!sync) {
+ nlmsg_ignore_limit(&nw);
+ }
struct nl_pstate npt = {
.nlp = nlp,
@@ -327,17 +327,19 @@
error = nl_receive_message(hdr, nb->datalen - nb->offset, nlp,
&npt);
nb->offset += msglen;
- if (__predict_false(error != 0 || nlp->nl_tx_blocked))
+ if (__predict_false(sync && error != 0))
+ return (error);
+ if (__predict_false(!sync && (error != 0 || nlp->nl_tx_blocked)))
break;
}
NL_LOG(LOG_DEBUG3, "packet parsing done");
nlmsg_flush(&nw);
- if (nlp->nl_tx_blocked) {
+ if (!sync && nlp->nl_tx_blocked) {
NLP_LOCK(nlp);
nlp->nl_tx_blocked = false;
NLP_UNLOCK(nlp);
- return (false);
+ return (EAGAIN);
} else
- return (true);
+ 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);
@@ -137,6 +138,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(struct nl_buf *nb, struct nlpcb *nlp, bool sync);
#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,83 @@
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 */
+ memset(buf, 'A', sizeof(hdr));
+ 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);
+ for (int i = 0; i < sizeof(hdr); i++)
+ ATF_REQUIRE(buf[i] == 'A');
+ 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 29, 12:57 AM (18 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34447647
Default Alt Text
D57519.id180606.diff (10 KB)
Attached To
Mode
D57519: netlink: Add sync path in user-kernel interface
Attached
Detach File
Event Timeline
Log In to Comment