Page MenuHomeFreeBSD

D57519.id180601.diff
No OneTemporary

D57519.id180601.diff

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,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

Mime Type
text/plain
Expires
Mon, Jun 29, 4:23 AM (10 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34452936
Default Alt Text
D57519.id180601.diff (9 KB)

Event Timeline