git: 06cf36516512 - main - netlink: provide genl_unregister_group()

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Wed, 05 Feb 2025 18:09:21 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=06cf36516512015867ceb56bde7913b1dc51ef3c

commit 06cf36516512015867ceb56bde7913b1dc51ef3c
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2025-02-05 18:09:06 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2025-02-05 18:09:06 +0000

    netlink: provide genl_unregister_group()
    
    Cause generic netlink group IDs are dynamic, we go through all sockets
    and unsubscribe from the group that goes away.  Otherwise they could be
    surprisingly find themselves subscribed to a group created later.
---
 sys/netlink/netlink_ctl.h     |  1 +
 sys/netlink/netlink_domain.c  | 12 ++++++++++++
 sys/netlink/netlink_generic.c | 26 ++++++++++++++++++++++++++
 sys/netlink/netlink_var.h     |  1 +
 4 files changed, 40 insertions(+)

diff --git a/sys/netlink/netlink_ctl.h b/sys/netlink/netlink_ctl.h
index e7566552ea32..7f43e0f2c25e 100644
--- a/sys/netlink/netlink_ctl.h
+++ b/sys/netlink/netlink_ctl.h
@@ -97,6 +97,7 @@ void genl_unregister_family(uint16_t family);
 bool genl_register_cmds(uint16_t family, const struct genl_cmd *cmds,
     u_int count);
 uint32_t genl_register_group(uint16_t family, const char *group_name);
+void genl_unregister_group(uint16_t family, uint32_t group);
 
 typedef void (*genl_family_event_handler_t)(void *arg, const char *family_name,
     uint16_t family_id, u_int action);
diff --git a/sys/netlink/netlink_domain.c b/sys/netlink/netlink_domain.c
index e06c0bf1919e..7fabb222665e 100644
--- a/sys/netlink/netlink_domain.c
+++ b/sys/netlink/netlink_domain.c
@@ -250,6 +250,18 @@ nl_send_group(struct nl_writer *nw)
 	return (true);
 }
 
+void
+nl_clear_group(u_int group)
+{
+	struct nlpcb *nlp;
+
+	NLCTL_WLOCK();
+	CK_LIST_FOREACH(nlp, &V_nl_ctl.ctl_pcb_head, nl_next)
+		if (nlp_memberof_group(nlp, group))
+			nlp_leave_group(nlp, group);
+	NLCTL_WUNLOCK();
+}
+
 static uint32_t
 nl_find_port(void)
 {
diff --git a/sys/netlink/netlink_generic.c b/sys/netlink/netlink_generic.c
index 0714f22382cb..00f47e60f013 100644
--- a/sys/netlink/netlink_generic.c
+++ b/sys/netlink/netlink_generic.c
@@ -497,3 +497,29 @@ genl_register_group(uint16_t family_id, const char *group_name)
 
 	return (group_id);
 }
+
+void
+genl_unregister_group(uint16_t family_id, uint32_t group_id)
+{
+	struct genl_family *gf;
+	struct genl_group *gg;
+
+	MPASS(group_id > MIN_GROUP_NUM &&
+	    group_id < MIN_GROUP_NUM + MAX_GROUPS);
+
+	nl_clear_group(group_id);
+
+	group_id -= MIN_GROUP_NUM;
+
+	GENL_LOCK();
+	gf = genl_family(family_id);
+	gg = &groups[group_id];
+
+	MPASS(gg->group_family == gf);
+	MPASS(gf->family_num_groups > 0);
+
+	gf->family_num_groups--;
+	gg->group_family = NULL;
+	gg->group_name = NULL;
+	GENL_UNLOCK();
+}
diff --git a/sys/netlink/netlink_var.h b/sys/netlink/netlink_var.h
index a59cb2efecd0..23e7395d44c2 100644
--- a/sys/netlink/netlink_var.h
+++ b/sys/netlink/netlink_var.h
@@ -121,6 +121,7 @@ extern struct nl_proto_handler *nl_handlers;
 
 /* netlink_domain.c */
 bool nl_send_group(struct nl_writer *);
+void nl_clear_group(u_int);
 void nl_osd_register(void);
 void nl_osd_unregister(void);
 void nl_set_thread_nlp(struct thread *td, struct nlpcb *nlp);