Page MenuHomeFreeBSD

D60521.diff
No OneTemporary

D60521.diff

diff --git a/sys/netlink/netlink_ctl.h b/sys/netlink/netlink_ctl.h
--- a/sys/netlink/netlink_ctl.h
+++ b/sys/netlink/netlink_ctl.h
@@ -102,6 +102,17 @@
uint32_t genl_register_group(uint16_t family, const char *group_name);
void genl_unregister_group(uint16_t family, uint32_t group);
+/*
+ * Per-socket storage of a family, allocated zeroed on first use by a handler
+ * of the family, which may use it until it returns. It is destroyed when
+ * the socket closes or the family is unregistered, with the generic netlink
+ * lock held: destroy() must not register or unregister anything.
+ */
+typedef void (*genl_sock_priv_destroy_t)(void *priv);
+void genl_register_sock_priv(uint16_t family, size_t size,
+ genl_sock_priv_destroy_t destroy);
+void *genl_sock_priv(uint16_t family, struct nlpcb *nlp);
+
typedef void (*genl_family_event_handler_t)(void *arg, const char *family_name,
uint16_t family_id, u_int action);
EVENTHANDLER_DECLARE(genl_family_event, genl_family_event_handler_t);
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
@@ -497,6 +497,7 @@
/* Wait till all scheduled work has been completed */
taskqueue_drain_all(nlp->nl_taskqueue);
taskqueue_free(nlp->nl_taskqueue);
+ genl_sock_close(nlp);
NLCTL_WLOCK();
NLP_LOCK(nlp);
diff --git a/sys/netlink/netlink_generic.c b/sys/netlink/netlink_generic.c
--- a/sys/netlink/netlink_generic.c
+++ b/sys/netlink/netlink_generic.c
@@ -36,6 +36,7 @@
#include <sys/malloc.h>
#include <sys/priv.h>
#include <sys/proc.h>
+#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/sx.h>
@@ -61,6 +62,12 @@
},
};
+struct genl_sock_priv {
+ LIST_ENTRY(genl_sock_priv) entry;
+ const struct nlpcb *nlp;
+ void *data;
+};
+
static struct genl_family {
const char *family_name;
uint16_t family_hdrsize;
@@ -70,6 +77,9 @@
uint16_t family_num_groups;
struct genl_cmd *family_cmds;
blockcount_t family_busy; /* running handlers */
+ size_t family_priv_size;
+ genl_sock_priv_destroy_t family_priv_destroy;
+ LIST_HEAD(, genl_sock_priv) family_privs;
} families[MAX_FAMILIES] = {
[CTRL_FAMILY_ID] = {
.family_name = CTRL_FAMILY_NAME,
@@ -93,9 +103,9 @@
};
/*
- * Families, commands and groups change under the exclusive lock. Handlers
- * run without it; family_busy counts them so that a family is not freed
- * under a running handler.
+ * Families, commands, groups and the per-socket storage change under the
+ * exclusive lock. Handlers run without it; family_busy counts them so that
+ * a family is not freed under a running handler.
*/
static struct sx sx_lock;
SX_SYSINIT(genl_lock, &sx_lock, "genetlink lock");
@@ -106,6 +116,8 @@
#define GENL_ASSERT_LOCKED() sx_assert(&sx_lock, SA_LOCKED)
#define GENL_ASSERT_XLOCKED() sx_assert(&sx_lock, SA_XLOCKED)
+static void genl_sock_priv_free(struct genl_family *, struct genl_sock_priv *);
+
static inline struct genl_family *
genl_family(uint16_t family_id)
{
@@ -428,6 +440,7 @@
genl_unregister_family(uint16_t family_id)
{
struct genl_family *gf;
+ struct genl_sock_priv *sp;
struct genl_cmd *cmds;
GENL_LOCK();
@@ -452,10 +465,107 @@
free(cmds, M_NETLINK);
GENL_LOCK();
+ while ((sp = LIST_FIRST(&gf->family_privs)) != NULL) {
+ LIST_REMOVE(sp, entry);
+ genl_sock_priv_free(gf, sp);
+ }
bzero(gf, sizeof(*gf));
GENL_UNLOCK();
}
+void
+genl_register_sock_priv(uint16_t family_id, size_t size,
+ genl_sock_priv_destroy_t destroy)
+{
+ struct genl_family *gf;
+
+ MPASS(size > 0);
+
+ GENL_LOCK();
+ gf = genl_family(family_id);
+ MPASS(LIST_EMPTY(&gf->family_privs));
+ gf->family_priv_size = size;
+ gf->family_priv_destroy = destroy;
+ GENL_UNLOCK();
+}
+
+static struct genl_sock_priv *
+genl_sock_priv_find(struct genl_family *gf, const struct nlpcb *nlp)
+{
+ struct genl_sock_priv *sp;
+
+ GENL_ASSERT_LOCKED();
+
+ LIST_FOREACH(sp, &gf->family_privs, entry)
+ if (sp->nlp == nlp)
+ return (sp);
+ return (NULL);
+}
+
+static void
+genl_sock_priv_free(struct genl_family *gf, struct genl_sock_priv *sp)
+{
+ GENL_ASSERT_XLOCKED();
+
+ if (gf->family_priv_destroy != NULL)
+ gf->family_priv_destroy(sp->data);
+ free(sp->data, M_NETLINK);
+ free(sp, M_NETLINK);
+}
+
+/*
+ * Called by a handler of the family, on the socket's own thread: nothing
+ * else creates the socket's storage, the socket does not close while the
+ * handler runs and the family waits for it before it is unregistered.
+ */
+void *
+genl_sock_priv(uint16_t family_id, struct nlpcb *nlp)
+{
+ struct genl_family *gf;
+ struct genl_sock_priv *sp;
+
+ GENL_SLOCK();
+ gf = genl_family(family_id);
+ MPASS(gf->family_priv_size > 0);
+ sp = genl_sock_priv_find(gf, nlp);
+ GENL_SUNLOCK();
+ if (sp != NULL)
+ return (sp->data);
+
+ sp = malloc(sizeof(*sp), M_NETLINK, M_WAITOK);
+ sp->nlp = nlp;
+ sp->data = malloc(gf->family_priv_size, M_NETLINK, M_WAITOK | M_ZERO);
+ GENL_LOCK();
+ LIST_INSERT_HEAD(&gf->family_privs, sp, entry);
+ GENL_UNLOCK();
+
+ return (sp->data);
+}
+
+/* Called when a socket closes, once no handler runs for it. */
+void
+genl_sock_close(struct nlpcb *nlp)
+{
+ struct genl_sock_priv *sp;
+
+ if (nlp->nl_proto != NETLINK_GENERIC)
+ return;
+
+ GENL_LOCK();
+ for (u_int i = 0; i < MAX_FAMILIES; i++) {
+ struct genl_family *gf = &families[i];
+
+ if (LIST_EMPTY(&gf->family_privs))
+ continue;
+ sp = genl_sock_priv_find(gf, nlp);
+ if (sp != NULL) {
+ LIST_REMOVE(sp, entry);
+ genl_sock_priv_free(gf, sp);
+ }
+ }
+ GENL_UNLOCK();
+}
+
bool
genl_register_cmds(uint16_t family_id, const struct genl_cmd *cmds,
u_int count)
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
@@ -139,6 +139,9 @@
struct nl_buf *nl_buf_alloc(size_t len, int mflag);
void nl_buf_free(struct nl_buf *nb);
+/* netlink_generic.c */
+void genl_sock_close(struct nlpcb *nlp);
+
#define MAX_FAMILIES 20
#define MAX_GROUPS 64

File Metadata

Mime Type
text/plain
Expires
Sat, Oct 10, 4:20 PM (16 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40554724
Default Alt Text
D60521.diff (5 KB)

Event Timeline