Page MenuHomeFreeBSD

D57519.diff
No OneTemporary

D57519.diff

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

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)

Event Timeline