Netdev List
 help / color / mirror / Atom feed
* [RFC PATCH 0/1] netlink: Netlink process event for cgroup migration
From: Prakash Sangappa @ 2026-04-07 17:23 UTC (permalink / raw)
  To: linux-kernel, netdev, cgroups
  Cc: davem, kuba, edumazet, tj, hannes, mkoutny, tom.hromatka,
	kamalesh.babulal, prakash.sangappa

With cgroup based resource management, it becomes useful for
userspace to be notified when a task changes cgroup membership.
Unexpected migrations can lead to incorrect resource accounting
and enforcement resulting in undesirable behavior or failures.
Applications/userspace have to poll /proc to detect changes to 
cgroup membership, which is inefficient when dealing with a large
number of tasks.

Add a new netlink proc connector event that gets generated when
a task migrates between cgroups. This allows applications/tools
to monitor cgroup membership changes without periodic polling. 

The netlink proc event will include task's pid/tgid, initiator
process pid/tgid and the cgroup id.  

Prakash Sangappa (1):
  netlink: Add Netlink process event for cgroup migration

 drivers/connector/cn_proc.c  | 28 ++++++++++++++++++++++++++++
 include/linux/cn_proc.h      |  3 +++
 include/uapi/linux/cn_proc.h | 14 ++++++++++++--
 kernel/cgroup/cgroup-v1.c    |  7 ++++++-
 kernel/cgroup/cgroup.c       |  5 ++++-
 5 files changed, 53 insertions(+), 4 deletions(-)

-- 
2.43.7


^ permalink raw reply

* [RFC PATCH 1/1] netlink: Add Netlink process event for cgroup migration
From: Prakash Sangappa @ 2026-04-07 17:23 UTC (permalink / raw)
  To: linux-kernel, netdev, cgroups
  Cc: davem, kuba, edumazet, tj, hannes, mkoutny, tom.hromatka,
	kamalesh.babulal, prakash.sangappa
In-Reply-To: <20260407172339.2017158-1-prakash.sangappa@oracle.com>

Introduce a netlink process event that gets generated
when a task migrates between cgroup. The process event
includes the task's pid,tgid and the initiator process
pid,tgid along with the destination cgroup id.

Signed-off-by: Prakash Sangappa <prakash.sangappa@oracle.com>
---
 drivers/connector/cn_proc.c  | 28 ++++++++++++++++++++++++++++
 include/linux/cn_proc.h      |  3 +++
 include/uapi/linux/cn_proc.h | 14 ++++++++++++--
 kernel/cgroup/cgroup-v1.c    |  7 ++++++-
 kernel/cgroup/cgroup.c       |  5 ++++-
 5 files changed, 53 insertions(+), 4 deletions(-)

diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index 0056ab81fbc3..4a17572ae171 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -19,6 +19,7 @@
 
 #include <linux/cn_proc.h>
 #include <linux/local_lock.h>
+#include <linux/cgroup.h>
 
 /*
  * Size of a cn_msg followed by a proc_event structure.  Since the
@@ -355,6 +356,33 @@ void proc_exit_connector(struct task_struct *task)
 	send_msg(msg);
 }
 
+void proc_cgroup_migrate_connector(struct task_struct *task, struct cgroup *cgrp)
+{
+	struct cn_msg *msg;
+	struct proc_event *ev;
+	__u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
+
+	if (atomic_read(&proc_event_num_listeners) < 1)
+		return;
+
+	msg = buffer_to_cn_msg(buffer);
+	ev = (struct proc_event *)msg->data;
+	memset(&ev->event_data, 0, sizeof(ev->event_data));
+	ev->timestamp_ns = ktime_get_ns();
+	ev->what = PROC_EVENT_CGRP_MIGRATE;
+	ev->event_data.cgrp.process_pid = task->pid;
+	ev->event_data.cgrp.process_tgid = task->tgid;
+	ev->event_data.cgrp.initiator_pid = current->pid;
+	ev->event_data.cgrp.initiator_tgid = current->tgid;
+	ev->event_data.cgrp.cgroup_id = cgroup_id(cgrp);
+
+	memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
+	msg->ack = 0; /* not used */
+	msg->len = sizeof(*ev);
+	msg->flags = 0; /* not used */
+	send_msg(msg);
+}
+
 /*
  * Send an acknowledgement message to userspace
  *
diff --git a/include/linux/cn_proc.h b/include/linux/cn_proc.h
index 1d5b02a96c46..9b16a0456af6 100644
--- a/include/linux/cn_proc.h
+++ b/include/linux/cn_proc.h
@@ -28,6 +28,7 @@ void proc_ptrace_connector(struct task_struct *task, int which_id);
 void proc_comm_connector(struct task_struct *task);
 void proc_coredump_connector(struct task_struct *task);
 void proc_exit_connector(struct task_struct *task);
+void proc_cgroup_migrate_connector(struct task_struct *task, struct cgroup *cgrp);
 #else
 static inline void proc_fork_connector(struct task_struct *task)
 {}
@@ -54,5 +55,7 @@ static inline void proc_coredump_connector(struct task_struct *task)
 
 static inline void proc_exit_connector(struct task_struct *task)
 {}
+static inline void proc_cgrp_migrate_connector(struct task_struct *task)
+{}
 #endif	/* CONFIG_PROC_EVENTS */
 #endif	/* CN_PROC_H */
diff --git a/include/uapi/linux/cn_proc.h b/include/uapi/linux/cn_proc.h
index 18e3745b86cd..c202d7fdab28 100644
--- a/include/uapi/linux/cn_proc.h
+++ b/include/uapi/linux/cn_proc.h
@@ -33,7 +33,8 @@ enum proc_cn_mcast_op {
 #define PROC_EVENT_ALL (PROC_EVENT_FORK | PROC_EVENT_EXEC | PROC_EVENT_UID |  \
 			PROC_EVENT_GID | PROC_EVENT_SID | PROC_EVENT_PTRACE | \
 			PROC_EVENT_COMM | PROC_EVENT_NONZERO_EXIT |           \
-			PROC_EVENT_COREDUMP | PROC_EVENT_EXIT)
+			PROC_EVENT_COREDUMP | PROC_EVENT_EXIT | \
+			PROC_EVENT_CGRP_MIGRATE)
 
 /*
  * If you add an entry in proc_cn_event, make sure you add it in
@@ -51,7 +52,8 @@ enum proc_cn_event {
 	PROC_EVENT_SID  = 0x00000080,
 	PROC_EVENT_PTRACE = 0x00000100,
 	PROC_EVENT_COMM = 0x00000200,
-	/* "next" should be 0x00000400 */
+	PROC_EVENT_CGRP_MIGRATE = 0x00000400,
+	/* "next" should be 0x00000800 */
 	/* "last" is the last process event: exit,
 	 * while "next to last" is coredumping event
 	 * before that is report only if process dies
@@ -153,6 +155,14 @@ struct proc_event {
 			__kernel_pid_t parent_tgid;
 		} exit;
 
+		struct cgrp_proc_event {
+			__kernel_pid_t process_pid;
+			__kernel_pid_t process_tgid;
+			__kernel_pid_t initiator_pid;
+			__kernel_pid_t initiator_tgid;
+			__u64          cgroup_id;
+		} cgrp;
+
 	} event_data;
 };
 
diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c
index a4337c9b5287..9b07c9ad9b43 100644
--- a/kernel/cgroup/cgroup-v1.c
+++ b/kernel/cgroup/cgroup-v1.c
@@ -16,6 +16,8 @@
 #include <linux/pid_namespace.h>
 #include <linux/cgroupstats.h>
 #include <linux/fs_parser.h>
+#include <linux/cn_proc.h>
+
 
 #include <trace/events/cgroup.h>
 
@@ -147,8 +149,11 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
 
 		if (task) {
 			ret = cgroup_migrate(task, false, &mgctx);
-			if (!ret)
+			if (!ret) {
+				proc_cgroup_migrate_connector(task, to);
 				TRACE_CGROUP_PATH(transfer_tasks, to, task, false);
+			}
+
 			put_task_struct(task);
 		}
 	} while (task && !ret);
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 01fc2a93f3ef..4cac29d5c1b5 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -59,6 +59,7 @@
 #include <linux/nstree.h>
 #include <linux/irq_work.h>
 #include <net/sock.h>
+#include <linux/cn_proc.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/cgroup.h>
@@ -3040,8 +3041,10 @@ int cgroup_attach_task(struct cgroup *dst_cgrp, struct task_struct *leader,
 
 	cgroup_migrate_finish(&mgctx);
 
-	if (!ret)
+	if (!ret) {
+		proc_cgroup_migrate_connector(leader, dst_cgrp);
 		TRACE_CGROUP_PATH(attach_task, dst_cgrp, leader, threadgroup);
+	}
 
 	return ret;
 }
-- 
2.43.7


^ permalink raw reply related

* [PATCH net-next 0/3] net: dsa: mxl862xx: VLAN support and minor improvements
From: Daniel Golle @ 2026-04-07 17:30 UTC (permalink / raw)
  To: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King, netdev,
	linux-kernel
  Cc: Frank Wunderlich, Chad Monroe, Cezary Wilmanski, Liang Xu,
	Benny (Ying-Tsan) Weng, Jose Maria Verdu Munoz, Avinash Jayaraman,
	John Crispin

This series adds VLAN offloading to the mxl862xx DSA driver along
with two minor improvements to port setup and bridge configuration.
VLAN support uses a hybrid architecture combining the Extended VLAN
engine for PVID insertion and tag stripping with the VLAN Filter
engine for per-port VID membership, both drawing from shared
1024-entry hardware pools partitioned across user ports at probe time.

Daniel Golle (3):
  net: dsa: mxl862xx: reject DSA_PORT_TYPE_DSA
  net: dsa: mxl862xx: don't skip early bridge port configuration
  net: dsa: mxl862xx: implement VLAN functionality

 drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 ++++++++++
 drivers/net/dsa/mxl862xx/mxl862xx-cmd.h |  12 +
 drivers/net/dsa/mxl862xx/mxl862xx.c     | 793 +++++++++++++++++++++++-
 drivers/net/dsa/mxl862xx/mxl862xx.h     | 103 ++-
 4 files changed, 1219 insertions(+), 18 deletions(-)

-- 
2.53.0

^ permalink raw reply

* [PATCH net-next 1/3] net: dsa: mxl862xx: reject DSA_PORT_TYPE_DSA
From: Daniel Golle @ 2026-04-07 17:30 UTC (permalink / raw)
  To: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King, netdev,
	linux-kernel
  Cc: Frank Wunderlich, Chad Monroe, Cezary Wilmanski, Liang Xu,
	Benny (Ying-Tsan) Weng, Jose Maria Verdu Munoz, Avinash Jayaraman,
	John Crispin
In-Reply-To: <cover.1775581804.git.daniel@makrotopia.org>

DSA links aren't supported by the mxl862xx driver.

Instead of returning early from .port_setup when called for
DSA_PORT_TYPE_DSA ports rather return -EOPNOTSUPP and show an error
message.

The desired side-effect is that the framework will switch the port to
DSA_PORT_TYPE_UNUSED, so we can stop caring about DSA_PORT_TYPE_DSA in
all other places.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
 drivers/net/dsa/mxl862xx/mxl862xx.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx/mxl862xx.c
index e4e16c7207630..9a9714c4859b1 100644
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -544,10 +544,14 @@ static int mxl862xx_port_setup(struct dsa_switch *ds, int port)
 
 	mxl862xx_port_fast_age(ds, port);
 
-	if (dsa_port_is_unused(dp) ||
-	    dsa_port_is_dsa(dp))
+	if (dsa_port_is_unused(dp))
 		return 0;
 
+	if (dsa_port_is_dsa(dp)) {
+		dev_err(ds->dev, "port %d: DSA links not supported\n", port);
+		return -EOPNOTSUPP;
+	}
+
 	ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port);
 	if (ret)
 		return ret;
@@ -591,7 +595,7 @@ static void mxl862xx_port_teardown(struct dsa_switch *ds, int port)
 	struct mxl862xx_priv *priv = ds->priv;
 	struct dsa_port *dp = dsa_to_port(ds, port);
 
-	if (dsa_port_is_unused(dp) || dsa_port_is_dsa(dp))
+	if (dsa_port_is_unused(dp))
 		return;
 
 	/* Prevent deferred host_flood_work from acting on stale state.
-- 
2.53.0

^ permalink raw reply related

* [PATCH net-next 2/3] net: dsa: mxl862xx: don't skip early bridge port configuration
From: Daniel Golle @ 2026-04-07 17:30 UTC (permalink / raw)
  To: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King, netdev,
	linux-kernel
  Cc: Frank Wunderlich, Chad Monroe, Cezary Wilmanski, Liang Xu,
	Benny (Ying-Tsan) Weng, Jose Maria Verdu Munoz, Avinash Jayaraman,
	John Crispin
In-Reply-To: <cover.1775581804.git.daniel@makrotopia.org>

mxl862xx_bridge_port_set() is currently guarded by the
mxl8622_port->setup_done flag, as the early call to
mxl862xx_bridge_port_set() from mxl862xx_port_stp_state_set() would
otherwise cause a NULL-pointer dereference on unused ports which don't
have dp->cpu_dp despite not being a CPU port.

Using the setup_done flag (which is never set for unused ports),
however, also prevents mxl862xx_bridge_port_set() from configuring
user ports' single-port bridges early, which was unintended.

Fix this by returning early from mxl862xx_bridge_port_set() in case
dsa_port_is_unused().

Fixes: 340bdf984613c ("net: dsa: mxl862xx: implement bridge offloading")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
 drivers/net/dsa/mxl862xx/mxl862xx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx/mxl862xx.c
index 9a9714c4859b1..f65525aff5e52 100644
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -278,7 +278,7 @@ static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
 	bool enable;
 	int i, idx;
 
-	if (!p->setup_done)
+	if (dsa_port_is_unused(dp))
 		return 0;
 
 	if (dsa_port_is_cpu(dp)) {
-- 
2.53.0

^ permalink raw reply related

* Re: [PATCH] net: hamradio: 6pack: fix uninit-value in sixpack_receive_buf
From: Mashiro Chen @ 2026-04-07 17:30 UTC (permalink / raw)
  To: horms; +Cc: netdev, linux-hams
In-Reply-To: <20260407165007.GB469338@kernel.org>

On Wed, Apr 08, 2026, Simon Horman wrote:
> Sorry for not noticing this earlier, but AI generated review flags
> that while this change looks correct, it's not clear how it relates
> to the sysbot report: IOW, how is it that bytes with TTY error flags
> may be uninitialized?

Thanks for the question, and for the earlier review.

The TTY receive_buf() contract is that for positions where fp[i] != 0
(error flag set), the caller must discard cp[i] -- the TTY driver does
not guarantee a meaningful value there.  In syzbot's fuzzing harness
(pty/ldisc injection), the fuzzer allocates a tty_buffer, sets error
flags on certain byte positions, but leaves the corresponding entries in
the char buffer uninitialized.  KMSAN then detects that sixpack_decode()
reads those uninitialized bytes.

slip_receive_buf() and mkiss_receive_buf() already handle this correctly
by advancing their char pointer past error-flagged bytes before calling
their decode functions.  sixpack_receive_buf() had the same structure
but cp was never advanced, so the full original buffer -- including the
uninit error-flagged positions -- was forwarded to sixpack_decode().

I have also dropped the Suggested-by tag per your note, and submitted
a v2 with the commit message expanded to explain the uninit-value
connection more clearly.

Thanks,
Mashiro Chen

^ permalink raw reply

* [PATCH net-next 3/3] net: dsa: mxl862xx: implement VLAN functionality
From: Daniel Golle @ 2026-04-07 17:31 UTC (permalink / raw)
  To: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Russell King, netdev,
	linux-kernel
  Cc: Frank Wunderlich, Chad Monroe, Cezary Wilmanski, Liang Xu,
	Benny (Ying-Tsan) Weng, Jose Maria Verdu Munoz, Avinash Jayaraman,
	John Crispin
In-Reply-To: <cover.1775581804.git.daniel@makrotopia.org>

Add VLAN support using both the Extended VLAN (EVLAN) engine and the
VLAN Filter (VF) engine in a hybrid architecture that allows a higher
number of VIDs than either engine could achieve alone.

The VLAN Filter engine handles per-port VID membership checks with
discard-unmatched semantics. The Extended VLAN engine handles PVID
insertion on ingress (via fixed catchall rules) and tag stripping on
egress (2 rules per untagged VID). Tagged-only VIDs need no EVLAN
egress rules at all, so they consume only a VF entry.

Both engines draw from shared 1024-entry hardware pools. The VF pool
is divided equally among user ports for VID membership, while the
EVLAN pool is partitioned into small fixed-size ingress blocks (7
entries of catchall rules per port) and fixed-size egress blocks for
tag stripping.

With 5 user ports this yields up to 204 VIDs per port (limited by VF),
of which up to 98 can be untagged (limited by EVLAN egress budget).
With 9 user ports the numbers are 113 total and 53 untagged.

Wire up .port_vlan_add, .port_vlan_del, and .port_vlan_filtering.
Reprogram all EVLAN rules when the PVID or filtering mode changes.
Detach blocks from the bridge port before freeing them on bridge leave
to satisfy the firmware's internal refcount.

Future optimizations could increase VID capacity by dynamically sizing
the egress EVLAN blocks based on actual per-port untagged VID counts
rather than worst-case pre-allocation, or by sharing EVLAN egress and
VLAN Filter blocks across ports with identical VID sets.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
 drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 ++++++++++
 drivers/net/dsa/mxl862xx/mxl862xx-cmd.h |  12 +
 drivers/net/dsa/mxl862xx/mxl862xx.c     | 781 +++++++++++++++++++++++-
 drivers/net/dsa/mxl862xx/mxl862xx.h     | 103 +++-
 4 files changed, 1211 insertions(+), 14 deletions(-)

diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-api.h b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
index 8677763544d78..c902e90397e5f 100644
--- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
@@ -731,6 +731,335 @@ struct mxl862xx_cfg {
 	u8 pause_mac_src[ETH_ALEN];
 } __packed;
 
+/**
+ * enum mxl862xx_extended_vlan_filter_type - Extended VLAN filter tag type
+ * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL: Normal tagged
+ * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER: No filter (wildcard)
+ * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT: Default entry
+ * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG: Untagged
+ */
+enum mxl862xx_extended_vlan_filter_type {
+	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL = 0,
+	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER = 1,
+	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT = 2,
+	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG = 3,
+};
+
+/**
+ * enum mxl862xx_extended_vlan_filter_tpid - Extended VLAN filter TPID
+ * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER: No TPID filter
+ * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q: 802.1Q TPID
+ * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE: VLAN type extension
+ */
+enum mxl862xx_extended_vlan_filter_tpid {
+	MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER = 0,
+	MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q = 1,
+	MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE = 2,
+};
+
+/**
+ * enum mxl862xx_extended_vlan_filter_dei - Extended VLAN filter DEI
+ * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER: No DEI filter
+ * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_0: DEI = 0
+ * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_1: DEI = 1
+ */
+enum mxl862xx_extended_vlan_filter_dei {
+	MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER = 0,
+	MXL862XX_EXTENDEDVLAN_FILTER_DEI_0 = 1,
+	MXL862XX_EXTENDEDVLAN_FILTER_DEI_1 = 2,
+};
+
+/**
+ * enum mxl862xx_extended_vlan_treatment_remove_tag - Tag removal action
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG: Do not remove tag
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG: Remove one tag
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG: Remove two tags
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM: Discard frame
+ */
+enum mxl862xx_extended_vlan_treatment_remove_tag {
+	MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG = 0,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG = 1,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG = 2,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM = 3,
+};
+
+/**
+ * enum mxl862xx_extended_vlan_treatment_priority - Treatment priority mode
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL: Use explicit value
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRIORITY: Copy from inner tag
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRIORITY: Copy from outer tag
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP: Derive from DSCP
+ */
+enum mxl862xx_extended_vlan_treatment_priority {
+	MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRIORITY = 1,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRIORITY = 2,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3,
+};
+
+/**
+ * enum mxl862xx_extended_vlan_treatment_vid - Treatment VID mode
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL: Use explicit VID value
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID: Copy from inner tag
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID: Copy from outer tag
+ */
+enum mxl862xx_extended_vlan_treatment_vid {
+	MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL = 0,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID = 1,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID = 2,
+};
+
+/**
+ * enum mxl862xx_extended_vlan_treatment_tpid - Treatment TPID mode
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID: Copy from inner tag
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID: Copy from outer tag
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE: Use VLAN type extension
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q: Use 802.1Q TPID
+ */
+enum mxl862xx_extended_vlan_treatment_tpid {
+	MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID = 0,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID = 1,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE = 2,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q = 3,
+};
+
+/**
+ * enum mxl862xx_extended_vlan_treatment_dei - Treatment DEI mode
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI: Copy from inner tag
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI: Copy from outer tag
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0: Set DEI to 0
+ * @MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1: Set DEI to 1
+ */
+enum mxl862xx_extended_vlan_treatment_dei {
+	MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI = 0,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI = 1,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0 = 2,
+	MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1 = 3,
+};
+
+/**
+ * enum mxl862xx_extended_vlan_4_tpid_mode - 4-TPID mode selector
+ * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1: VLAN TPID type 1
+ * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2: VLAN TPID type 2
+ * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3: VLAN TPID type 3
+ * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4: VLAN TPID type 4
+ */
+enum mxl862xx_extended_vlan_4_tpid_mode {
+	MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1 = 0,
+	MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2 = 1,
+	MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3 = 2,
+	MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4 = 3,
+};
+
+/**
+ * enum mxl862xx_extended_vlan_filter_ethertype - Filter EtherType match
+ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER: No filter
+ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE: IPoE
+ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE: PPPoE
+ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP: ARP
+ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE: IPv6 IPoE
+ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL: EAPOL
+ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4: DHCPv4
+ * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6: DHCPv6
+ */
+enum mxl862xx_extended_vlan_filter_ethertype {
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER = 0,
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE = 1,
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE = 2,
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP = 3,
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE = 4,
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL = 5,
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4 = 6,
+	MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6 = 7,
+};
+
+/**
+ * struct mxl862xx_extendedvlan_filter_vlan - Per-tag filter in Extended VLAN
+ * @type: Tag presence/type match (see &enum mxl862xx_extended_vlan_filter_type)
+ * @priority_enable: Enable PCP value matching
+ * @priority_val: PCP value to match
+ * @vid_enable: Enable VID matching
+ * @vid_val: VID value to match
+ * @tpid: TPID match mode (see &enum mxl862xx_extended_vlan_filter_tpid)
+ * @dei: DEI match mode (see &enum mxl862xx_extended_vlan_filter_dei)
+ */
+struct mxl862xx_extendedvlan_filter_vlan {
+	__le32 type;
+	u8 priority_enable;
+	__le32 priority_val;
+	u8 vid_enable;
+	__le32 vid_val;
+	__le32 tpid;
+	__le32 dei;
+} __packed;
+
+/**
+ * struct mxl862xx_extendedvlan_filter - Extended VLAN filter configuration
+ * @original_packet_filter_mode: If true, filter on original (pre-treatment)
+ *                               packet
+ * @filter_4_tpid_mode: 4-TPID mode (see &enum mxl862xx_extended_vlan_4_tpid_mode)
+ * @outer_vlan: Outer VLAN tag filter
+ * @inner_vlan: Inner VLAN tag filter
+ * @ether_type: EtherType filter (see
+ *              &enum mxl862xx_extended_vlan_filter_ethertype)
+ */
+struct mxl862xx_extendedvlan_filter {
+	u8 original_packet_filter_mode;
+	__le32 filter_4_tpid_mode;
+	struct mxl862xx_extendedvlan_filter_vlan outer_vlan;
+	struct mxl862xx_extendedvlan_filter_vlan inner_vlan;
+	__le32 ether_type;
+} __packed;
+
+/**
+ * struct mxl862xx_extendedvlan_treatment_vlan - Per-tag treatment in
+ *                                               Extended VLAN
+ * @priority_mode: Priority assignment mode
+ *                 (see &enum mxl862xx_extended_vlan_treatment_priority)
+ * @priority_val: Priority value (when mode is VAL)
+ * @vid_mode: VID assignment mode
+ *            (see &enum mxl862xx_extended_vlan_treatment_vid)
+ * @vid_val: VID value (when mode is VAL)
+ * @tpid: TPID assignment mode
+ *        (see &enum mxl862xx_extended_vlan_treatment_tpid)
+ * @dei: DEI assignment mode
+ *       (see &enum mxl862xx_extended_vlan_treatment_dei)
+ */
+struct mxl862xx_extendedvlan_treatment_vlan {
+	__le32 priority_mode;
+	__le32 priority_val;
+	__le32 vid_mode;
+	__le32 vid_val;
+	__le32 tpid;
+	__le32 dei;
+} __packed;
+
+/**
+ * struct mxl862xx_extendedvlan_treatment - Extended VLAN treatment
+ * @remove_tag: Tag removal action
+ *              (see &enum mxl862xx_extended_vlan_treatment_remove_tag)
+ * @treatment_4_tpid_mode: 4-TPID treatment mode
+ * @add_outer_vlan: Add outer VLAN tag
+ * @outer_vlan: Outer VLAN tag treatment parameters
+ * @add_inner_vlan: Add inner VLAN tag
+ * @inner_vlan: Inner VLAN tag treatment parameters
+ * @reassign_bridge_port: Reassign to different bridge port
+ * @new_bridge_port_id: New bridge port ID
+ * @new_dscp_enable: Enable new DSCP assignment
+ * @new_dscp: New DSCP value
+ * @new_traffic_class_enable: Enable new traffic class assignment
+ * @new_traffic_class: New traffic class value
+ * @new_meter_enable: Enable new metering
+ * @s_new_traffic_meter_id: New traffic meter ID
+ * @dscp2pcp_map: DSCP to PCP mapping table (64 entries)
+ * @loopback_enable: Enable loopback
+ * @da_sa_swap_enable: Enable DA/SA swap
+ * @mirror_enable: Enable mirroring
+ */
+struct mxl862xx_extendedvlan_treatment {
+	__le32 remove_tag;
+	__le32 treatment_4_tpid_mode;
+	u8 add_outer_vlan;
+	struct mxl862xx_extendedvlan_treatment_vlan outer_vlan;
+	u8 add_inner_vlan;
+	struct mxl862xx_extendedvlan_treatment_vlan inner_vlan;
+	u8 reassign_bridge_port;
+	__le16 new_bridge_port_id;
+	u8 new_dscp_enable;
+	__le16 new_dscp;
+	u8 new_traffic_class_enable;
+	u8 new_traffic_class;
+	u8 new_meter_enable;
+	__le16 s_new_traffic_meter_id;
+	u8 dscp2pcp_map[64];
+	u8 loopback_enable;
+	u8 da_sa_swap_enable;
+	u8 mirror_enable;
+} __packed;
+
+/**
+ * struct mxl862xx_extendedvlan_alloc - Extended VLAN block allocation
+ * @number_of_entries: Number of entries to allocate (input) / allocated
+ *                     (output)
+ * @extended_vlan_block_id: Block ID assigned by firmware (output on alloc,
+ *                          input on free)
+ *
+ * Used with %MXL862XX_EXTENDEDVLAN_ALLOC and %MXL862XX_EXTENDEDVLAN_FREE.
+ */
+struct mxl862xx_extendedvlan_alloc {
+	__le16 number_of_entries;
+	__le16 extended_vlan_block_id;
+} __packed;
+
+/**
+ * struct mxl862xx_extendedvlan_config - Extended VLAN entry configuration
+ * @extended_vlan_block_id: Block ID from allocation
+ * @entry_index: Entry index within the block
+ * @filter: Filter (match) configuration
+ * @treatment: Treatment (action) configuration
+ *
+ * Used with %MXL862XX_EXTENDEDVLAN_SET and %MXL862XX_EXTENDEDVLAN_GET.
+ */
+struct mxl862xx_extendedvlan_config {
+	__le16 extended_vlan_block_id;
+	__le16 entry_index;
+	struct mxl862xx_extendedvlan_filter filter;
+	struct mxl862xx_extendedvlan_treatment treatment;
+} __packed;
+
+/**
+ * enum mxl862xx_vlan_filter_tci_mask - VLAN Filter TCI mask
+ * @MXL862XX_VLAN_FILTER_TCI_MASK_VID: TCI mask for VLAN ID
+ * @MXL862XX_VLAN_FILTER_TCI_MASK_PCP: TCI mask for VLAN PCP
+ * @MXL862XX_VLAN_FILTER_TCI_MASK_TCI: TCI mask for VLAN TCI
+ */
+enum mxl862xx_vlan_filter_tci_mask {
+	MXL862XX_VLAN_FILTER_TCI_MASK_VID = 0,
+	MXL862XX_VLAN_FILTER_TCI_MASK_PCP = 1,
+	MXL862XX_VLAN_FILTER_TCI_MASK_TCI = 2,
+};
+
+/**
+ * struct mxl862xx_vlanfilter_alloc - VLAN Filter block allocation
+ * @number_of_entries: Number of entries to allocate (input) / allocated
+ *                     (output)
+ * @vlan_filter_block_id: Block ID assigned by firmware (output on alloc,
+ *                        input on free)
+ * @discard_untagged: Discard untagged packets
+ * @discard_unmatched_tagged: Discard tagged packets that do not match any
+ *                            entry in the block
+ * @use_default_port_vid: Use default port VLAN ID for filtering
+ *
+ * Used with %MXL862XX_VLANFILTER_ALLOC and %MXL862XX_VLANFILTER_FREE.
+ */
+struct mxl862xx_vlanfilter_alloc {
+	__le16 number_of_entries;
+	__le16 vlan_filter_block_id;
+	u8 discard_untagged;
+	u8 discard_unmatched_tagged;
+	u8 use_default_port_vid;
+} __packed;
+
+/**
+ * struct mxl862xx_vlanfilter_config - VLAN Filter entry configuration
+ * @vlan_filter_block_id: Block ID from allocation
+ * @entry_index: Entry index within the block
+ * @vlan_filter_mask: TCI field(s) to match (see
+ *                    &enum mxl862xx_vlan_filter_tci_mask)
+ * @val: TCI value(s) to match (VID, PCP, or full TCI depending on mask)
+ * @discard_matched: When true, discard frames matching this entry;
+ *                   when false, allow them
+ *
+ * Used with %MXL862XX_VLANFILTER_SET and %MXL862XX_VLANFILTER_GET.
+ */
+struct mxl862xx_vlanfilter_config {
+	__le16 vlan_filter_block_id;
+	__le16 entry_index;
+	__le32 vlan_filter_mask; /* enum mxl862xx_vlan_filter_tci_mask */
+	__le32 val;
+	u8 discard_matched;
+} __packed;
+
 /**
  * enum mxl862xx_ss_sp_tag_mask - Special tag valid field indicator bits
  * @MXL862XX_SS_SP_TAG_MASK_RX: valid RX special tag mode
diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
index 9f6c5bf9fdf21..45df37cde40d1 100644
--- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
@@ -17,6 +17,8 @@
 #define MXL862XX_CTP_MAGIC		0x500
 #define MXL862XX_QOS_MAGIC		0x600
 #define MXL862XX_SWMAC_MAGIC		0xa00
+#define MXL862XX_EXTVLAN_MAGIC		0xb00
+#define MXL862XX_VLANFILTER_MAGIC	0xc00
 #define MXL862XX_STP_MAGIC		0xf00
 #define MXL862XX_SS_MAGIC		0x1600
 #define GPY_GPY2XX_MAGIC		0x1800
@@ -47,6 +49,16 @@
 #define MXL862XX_MAC_TABLEENTRYREMOVE	(MXL862XX_SWMAC_MAGIC + 0x5)
 #define MXL862XX_MAC_TABLECLEARCOND	(MXL862XX_SWMAC_MAGIC + 0x8)
 
+#define MXL862XX_EXTENDEDVLAN_ALLOC	(MXL862XX_EXTVLAN_MAGIC + 0x1)
+#define MXL862XX_EXTENDEDVLAN_SET	(MXL862XX_EXTVLAN_MAGIC + 0x2)
+#define MXL862XX_EXTENDEDVLAN_GET	(MXL862XX_EXTVLAN_MAGIC + 0x3)
+#define MXL862XX_EXTENDEDVLAN_FREE	(MXL862XX_EXTVLAN_MAGIC + 0x4)
+
+#define MXL862XX_VLANFILTER_ALLOC	(MXL862XX_VLANFILTER_MAGIC + 0x1)
+#define MXL862XX_VLANFILTER_SET		(MXL862XX_VLANFILTER_MAGIC + 0x2)
+#define MXL862XX_VLANFILTER_GET		(MXL862XX_VLANFILTER_MAGIC + 0x3)
+#define MXL862XX_VLANFILTER_FREE	(MXL862XX_VLANFILTER_MAGIC + 0x4)
+
 #define MXL862XX_SS_SPTAG_SET		(MXL862XX_SS_MAGIC + 0x2)
 
 #define MXL862XX_STP_PORTCFGSET		(MXL862XX_STP_MAGIC + 0x2)
diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx/mxl862xx.c
index f65525aff5e52..fca9a3e36bb69 100644
--- a/drivers/net/dsa/mxl862xx/mxl862xx.c
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
@@ -50,6 +50,85 @@ static const int mxl862xx_flood_meters[] = {
 	MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST,
 };
 
+enum mxl862xx_evlan_action {
+	EVLAN_ACCEPT,			/* pass-through, no tag removal */
+	EVLAN_STRIP_IF_UNTAGGED,	/* remove 1 tag if entry's untagged flag set */
+	EVLAN_PVID_OR_DISCARD,		/* insert PVID tag or discard if no PVID */
+	EVLAN_STRIP1_AND_PVID_OR_DISCARD,/* strip 1 tag + insert PVID, or discard */
+};
+
+struct mxl862xx_evlan_rule_desc {
+	u8 outer_type;		/* enum mxl862xx_extended_vlan_filter_type */
+	u8 inner_type;		/* enum mxl862xx_extended_vlan_filter_type */
+	u8 outer_tpid;		/* enum mxl862xx_extended_vlan_filter_tpid */
+	u8 inner_tpid;		/* enum mxl862xx_extended_vlan_filter_tpid */
+	bool match_vid;		/* true: match on VID from the vid parameter */
+	u8 action;		/* enum mxl862xx_evlan_action */
+};
+
+/* Shorthand constants for readability */
+#define FT_NORMAL	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL
+#define FT_NO_FILTER	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER
+#define FT_DEFAULT	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT
+#define FT_NO_TAG	MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG
+#define TP_NONE		MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER
+#define TP_8021Q	MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q
+
+/*
+ * VLAN-aware ingress: 7 final catchall rules.
+ *
+ * VLAN Filter handles VID membership for tagged frames, so the
+ * Extended VLAN ingress block only needs to handle:
+ * - Priority-tagged (VID=0): strip + insert PVID
+ * - Untagged: insert PVID or discard
+ * - Standard 802.1Q VID>0: pass through (VF handles membership)
+ * - Non-8021Q TPID (0x88A8 etc.): treat as untagged
+ *
+ * Rule ordering is critical: the EVLAN engine scans entries in
+ * ascending index order and stops at the first match.
+ *
+ * The 802.1Q ACCEPT rules (indices 3--4) must appear BEFORE the
+ * NO_FILTER catchalls (indices 5--6). NO_FILTER matches any tag
+ * regardless of TPID, so without the ACCEPT guard, it would also
+ * catch standard 802.1Q VID>0 frames and corrupt them. With the
+ * guard, 802.1Q VID>0 frames match the ACCEPT rules first and
+ * pass through untouched; only non-8021Q TPID frames pass through
+ * to the NO_FILTER catchalls.
+ */
+static const struct mxl862xx_evlan_rule_desc ingress_aware_final[] = {
+	/* 802.1p / priority-tagged (VID 0): strip + PVID */
+	{ FT_NORMAL,    FT_NORMAL, TP_8021Q, TP_8021Q, true,  EVLAN_STRIP1_AND_PVID_OR_DISCARD },
+	{ FT_NORMAL,    FT_NO_TAG, TP_8021Q, TP_NONE,  true,  EVLAN_STRIP1_AND_PVID_OR_DISCARD },
+	/* Untagged: PVID insertion or discard */
+	{ FT_NO_TAG,    FT_NO_TAG, TP_NONE,  TP_NONE,  false, EVLAN_PVID_OR_DISCARD },
+	/* 802.1Q VID>0: accept - VF handles membership.
+	 * match_vid=false means any VID; VID=0 is already caught above.
+	 */
+	{ FT_NORMAL,    FT_NORMAL, TP_8021Q, TP_8021Q, false, EVLAN_ACCEPT },
+	{ FT_NORMAL,    FT_NO_TAG, TP_8021Q, TP_NONE,  false, EVLAN_ACCEPT },
+	/* Non-8021Q TPID (0x88A8 etc.): treat as untagged - strip + PVID */
+	{ FT_NO_FILTER, FT_NO_FILTER, TP_NONE, TP_NONE, false, EVLAN_STRIP1_AND_PVID_OR_DISCARD },
+	{ FT_NO_FILTER, FT_NO_TAG,    TP_NONE, TP_NONE, false, EVLAN_STRIP1_AND_PVID_OR_DISCARD },
+};
+
+/*
+ * VID-specific accept rules (VLAN-aware, standard tag, 2 per VID).
+ * Outer tag carries the VLAN; inner may or may not be present.
+ */
+static const struct mxl862xx_evlan_rule_desc vid_accept_standard[] = {
+	{ FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED },
+	{ FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE,  true, EVLAN_STRIP_IF_UNTAGGED },
+};
+
+/*
+ * Egress tag-stripping rules for VLAN-unaware mode (2 per untagged VID).
+ * The HW sees the MxL tag as outer; the real VLAN tag, if any, is inner.
+ */
+static const struct mxl862xx_evlan_rule_desc vid_accept_egress_unaware[] = {
+	{ FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true,  EVLAN_STRIP_IF_UNTAGGED },
+	{ FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE,  false, EVLAN_STRIP_IF_UNTAGGED },
+};
+
 static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
 						       int port,
 						       enum dsa_tag_protocol m)
@@ -275,6 +354,7 @@ static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
 	struct mxl862xx_port *p = &priv->ports[port];
 	struct dsa_port *member_dp;
 	u16 bridge_id;
+	u16 vf_scan;
 	bool enable;
 	int i, idx;
 
@@ -312,9 +392,69 @@ static int mxl862xx_set_bridge_port(struct dsa_switch *ds, int port)
 	br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
 				       MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
 				       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING |
-				       MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER);
+				       MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER |
+				       MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN |
+				       MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN |
+				       MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER |
+				       MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 |
+				       MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING);
 	br_port_cfg.src_mac_learning_disable = !p->learning;
 
+	/* Extended VLAN block assignments.
+	 * Ingress: block_size is sent as-is (all entries are finals).
+	 * Egress: n_active narrows the scan window to only the
+	 * entries actually written by evlan_program_egress.
+	 */
+	br_port_cfg.ingress_extended_vlan_enable = p->ingress_evlan.in_use;
+	br_port_cfg.ingress_extended_vlan_block_id =
+		cpu_to_le16(p->ingress_evlan.block_id);
+	br_port_cfg.ingress_extended_vlan_block_size =
+		cpu_to_le16(p->ingress_evlan.block_size);
+	br_port_cfg.egress_extended_vlan_enable = p->egress_evlan.in_use;
+	br_port_cfg.egress_extended_vlan_block_id =
+		cpu_to_le16(p->egress_evlan.block_id);
+	br_port_cfg.egress_extended_vlan_block_size =
+		cpu_to_le16(p->egress_evlan.n_active);
+
+	/* VLAN Filter block assignments (per-port).
+	 * The block_size sent to the firmware narrows the HW scan
+	 * window to [block_id, block_id + active_count), relying on
+	 * discard_unmatched_tagged for frames outside that range.
+	 * When active_count=0, send 1 to scan only the DISCARD
+	 * sentinel at index 0 (block_size=0 would disable narrowing
+	 * and scan the entire allocated block).
+	 *
+	 * The bridge check ensures VF is disabled when the port
+	 * leaves the bridge, without needing to prematurely clear
+	 * vlan_filtering (which the DSA framework handles later via
+	 * port_vlan_filtering).
+	 */
+	if (p->vf.allocated && p->vlan_filtering &&
+	    dsa_port_bridge_dev_get(dp)) {
+		vf_scan = max_t(u16, p->vf.active_count, 1);
+		br_port_cfg.ingress_vlan_filter_enable = 1;
+		br_port_cfg.ingress_vlan_filter_block_id =
+			cpu_to_le16(p->vf.block_id);
+		br_port_cfg.ingress_vlan_filter_block_size =
+			cpu_to_le16(vf_scan);
+
+		br_port_cfg.egress_vlan_filter1enable = 1;
+		br_port_cfg.egress_vlan_filter1block_id =
+			cpu_to_le16(p->vf.block_id);
+		br_port_cfg.egress_vlan_filter1block_size =
+			cpu_to_le16(vf_scan);
+	} else {
+		br_port_cfg.ingress_vlan_filter_enable = 0;
+		br_port_cfg.egress_vlan_filter1enable = 0;
+	}
+
+	/* IVL when VLAN-aware: include VID in FDB lookup keys so that
+	 * learned entries are per-VID. In VLAN-unaware mode, SVL is
+	 * used (VID excluded from key).
+	 */
+	br_port_cfg.vlan_src_mac_vid_enable = p->vlan_filtering;
+	br_port_cfg.vlan_dst_mac_vid_enable = p->vlan_filtering;
+
 	for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
 		idx = mxl862xx_flood_meters[i];
 		enable = !!(p->flood_block & BIT(idx));
@@ -343,6 +483,72 @@ static int mxl862xx_sync_bridge_members(struct dsa_switch *ds,
 	return ret;
 }
 
+static int mxl862xx_evlan_block_alloc(struct mxl862xx_priv *priv,
+				      struct mxl862xx_evlan_block *blk)
+{
+	struct mxl862xx_extendedvlan_alloc param = {};
+	int ret;
+
+	param.number_of_entries = cpu_to_le16(blk->block_size);
+
+	ret = MXL862XX_API_READ(priv, MXL862XX_EXTENDEDVLAN_ALLOC, param);
+	if (ret)
+		return ret;
+
+	blk->block_id = le16_to_cpu(param.extended_vlan_block_id);
+	blk->allocated = true;
+
+	return 0;
+}
+
+static int mxl862xx_vf_block_alloc(struct mxl862xx_priv *priv,
+				   u16 size, u16 *block_id)
+{
+	struct mxl862xx_vlanfilter_alloc param = {};
+	int ret;
+
+	param.number_of_entries = cpu_to_le16(size);
+	param.discard_untagged = 0;
+	param.discard_unmatched_tagged = 1;
+
+	ret = MXL862XX_API_READ(priv, MXL862XX_VLANFILTER_ALLOC, param);
+	if (ret)
+		return ret;
+
+	*block_id = le16_to_cpu(param.vlan_filter_block_id);
+	return 0;
+}
+
+static int mxl862xx_vf_entry_discard(struct mxl862xx_priv *priv,
+				     u16 block_id, u16 index)
+{
+	struct mxl862xx_vlanfilter_config cfg = {};
+
+	cfg.vlan_filter_block_id = cpu_to_le16(block_id);
+	cfg.entry_index = cpu_to_le16(index);
+	cfg.vlan_filter_mask = cpu_to_le32(MXL862XX_VLAN_FILTER_TCI_MASK_VID);
+	cfg.val = cpu_to_le32(0);
+	cfg.discard_matched = 1;
+
+	return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
+}
+
+static int mxl862xx_vf_alloc(struct mxl862xx_priv *priv,
+			     struct mxl862xx_vf_block *vf)
+{
+	int ret;
+
+	ret = mxl862xx_vf_block_alloc(priv, vf->block_size, &vf->block_id);
+	if (ret)
+		return ret;
+
+	vf->allocated = true;
+	vf->active_count = 0;
+
+	/* Sentinel: block VID-0 when scan window covers only index 0 */
+	return mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
+}
+
 static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv)
 {
 	struct mxl862xx_bridge_alloc br_alloc = {};
@@ -378,6 +584,9 @@ static void mxl862xx_free_bridge(struct dsa_switch *ds,
 static int mxl862xx_setup(struct dsa_switch *ds)
 {
 	struct mxl862xx_priv *priv = ds->priv;
+	int n_user_ports = 0, max_vlans;
+	int ingress_finals, vid_rules;
+	struct dsa_port *dp;
 	int ret;
 
 	ret = mxl862xx_reset(priv);
@@ -388,6 +597,50 @@ static int mxl862xx_setup(struct dsa_switch *ds)
 	if (ret)
 		return ret;
 
+	/* Calculate Extended VLAN block sizes.
+	 * With VLAN Filter handling VID membership checks:
+	 *   Ingress: only final catchall rules (PVID insertion, 802.1Q
+	 *            accept, non-8021Q TPID handling, discard).
+	 *            Block sized to exactly fit the finals -- no per-VID
+	 *            ingress EVLAN rules are needed. (7 entries.)
+	 *   Egress:  2 rules per VID that needs tag stripping (untagged VIDs).
+	 *            No egress final catchalls -- VLAN Filter does the discard.
+	 *   CPU:     EVLAN is left disabled on CPU ports -- frames pass
+	 *            through without EVLAN processing.
+	 *
+	 * Total EVLAN budget:
+	 *   n_user_ports * (ingress + egress) <= 1024.
+	 * Ingress blocks are small (7 entries), so almost all capacity
+	 * goes to egress VID rules.
+	 */
+	dsa_switch_for_each_user_port(dp, ds)
+		n_user_ports++;
+
+	if (n_user_ports) {
+		ingress_finals = ARRAY_SIZE(ingress_aware_final);
+		vid_rules = ARRAY_SIZE(vid_accept_standard);
+
+		/* Ingress block: fixed at finals count (7 entries) */
+		priv->evlan_ingress_size = ingress_finals;
+
+		/* Egress block: remaining budget divided equally among
+		 * user ports. Each untagged VID needs vid_rules (2)
+		 * EVLAN entries for tag stripping. Tagged-only VIDs
+		 * need no EVLAN rules at all.
+		 */
+		max_vlans = (MXL862XX_TOTAL_EVLAN_ENTRIES -
+			     n_user_ports * ingress_finals) /
+			    (n_user_ports * vid_rules);
+		priv->evlan_egress_size = vid_rules * max_vlans;
+
+		/* VLAN Filter block: one per user port. The 1024-entry
+		 * table is divided equally among user ports. Each port
+		 * gets its own VF block for per-port VID membership --
+		 * discard_unmatched_tagged handles the rest.
+		 */
+		priv->vf_block_size = MXL862XX_TOTAL_VF_ENTRIES / n_user_ports;
+	}
+
 	ret = mxl862xx_setup_drop_meter(ds);
 	if (ret)
 		return ret;
@@ -469,12 +722,509 @@ static int mxl862xx_configure_sp_tag_proto(struct dsa_switch *ds, int port,
 	return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag);
 }
 
+static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv,
+				     u16 block_id, u16 entry_index,
+				     const struct mxl862xx_evlan_rule_desc *desc,
+				     u16 vid, bool untagged, u16 pvid)
+{
+	struct mxl862xx_extendedvlan_config cfg = {};
+	struct mxl862xx_extendedvlan_filter_vlan *fv;
+
+	cfg.extended_vlan_block_id = cpu_to_le16(block_id);
+	cfg.entry_index = cpu_to_le16(entry_index);
+
+	/* Populate filter */
+	cfg.filter.outer_vlan.type = cpu_to_le32(desc->outer_type);
+	cfg.filter.inner_vlan.type = cpu_to_le32(desc->inner_type);
+	cfg.filter.outer_vlan.tpid = cpu_to_le32(desc->outer_tpid);
+	cfg.filter.inner_vlan.tpid = cpu_to_le32(desc->inner_tpid);
+
+	if (desc->match_vid) {
+		/* For egress unaware: outer=NO_FILTER, match on inner tag */
+		if (desc->outer_type == FT_NO_FILTER)
+			fv = &cfg.filter.inner_vlan;
+		else
+			fv = &cfg.filter.outer_vlan;
+
+		fv->vid_enable = 1;
+		fv->vid_val = cpu_to_le32(vid);
+	}
+
+	/* Populate treatment based on action */
+	switch (desc->action) {
+	case EVLAN_ACCEPT:
+		cfg.treatment.remove_tag =
+			cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
+		break;
+
+	case EVLAN_STRIP_IF_UNTAGGED:
+		cfg.treatment.remove_tag = cpu_to_le32(untagged ?
+			MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG :
+			MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
+		break;
+
+	case EVLAN_PVID_OR_DISCARD:
+		if (pvid) {
+			cfg.treatment.remove_tag =
+				cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
+			cfg.treatment.add_outer_vlan = 1;
+			cfg.treatment.outer_vlan.vid_mode =
+				cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
+			cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
+			cfg.treatment.outer_vlan.tpid =
+				cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
+		} else {
+			cfg.treatment.remove_tag =
+				cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
+		}
+		break;
+
+	case EVLAN_STRIP1_AND_PVID_OR_DISCARD:
+		if (pvid) {
+			cfg.treatment.remove_tag =
+				cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG);
+			cfg.treatment.add_outer_vlan = 1;
+			cfg.treatment.outer_vlan.vid_mode =
+				cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
+			cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
+			cfg.treatment.outer_vlan.tpid =
+				cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
+		} else {
+			cfg.treatment.remove_tag =
+				cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
+		}
+		break;
+	}
+
+	return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
+}
+
+static int mxl862xx_evlan_deactivate_entry(struct mxl862xx_priv *priv,
+					   u16 block_id, u16 entry_index)
+{
+	struct mxl862xx_extendedvlan_config cfg = {};
+
+	cfg.extended_vlan_block_id = cpu_to_le16(block_id);
+	cfg.entry_index = cpu_to_le16(entry_index);
+
+	/* Use an unreachable filter (DEFAULT+DEFAULT) with DISCARD treatment.
+	 * A zeroed entry would have NORMAL+NORMAL filter which matches
+	 * real double-tagged traffic and passes it through.
+	 */
+	cfg.filter.outer_vlan.type =
+		cpu_to_le32(MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT);
+	cfg.filter.inner_vlan.type =
+		cpu_to_le32(MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT);
+	cfg.treatment.remove_tag =
+		cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
+
+	return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
+}
+
+static int mxl862xx_evlan_write_final_rules(struct mxl862xx_priv *priv,
+					    struct mxl862xx_evlan_block *blk,
+					    const struct mxl862xx_evlan_rule_desc *rules,
+					    int n_rules, u16 pvid)
+{
+	u16 start_idx = blk->block_size - n_rules;
+	int i, ret;
+
+	for (i = 0; i < n_rules; i++) {
+		ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
+						start_idx + i, &rules[i],
+						0, false, pvid);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int mxl862xx_vf_entry_set(struct mxl862xx_priv *priv,
+				 u16 block_id, u16 index, u16 vid)
+{
+	struct mxl862xx_vlanfilter_config cfg = {};
+
+	cfg.vlan_filter_block_id = cpu_to_le16(block_id);
+	cfg.entry_index = cpu_to_le16(index);
+	cfg.vlan_filter_mask = cpu_to_le32(MXL862XX_VLAN_FILTER_TCI_MASK_VID);
+	cfg.val = cpu_to_le32(vid);
+	cfg.discard_matched = 0;
+
+	return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
+}
+
+static struct mxl862xx_vf_vid *mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf,
+						    u16 vid)
+{
+	struct mxl862xx_vf_vid *ve;
+
+	list_for_each_entry(ve, &vf->vids, list)
+		if (ve->vid == vid)
+			return ve;
+
+	return NULL;
+}
+
+static int mxl862xx_vf_add_vid(struct mxl862xx_priv *priv,
+			       struct mxl862xx_vf_block *vf,
+			       u16 vid, bool untagged)
+{
+	struct mxl862xx_vf_vid *ve;
+	int ret;
+
+	ve = mxl862xx_vf_find_vid(vf, vid);
+	if (ve) {
+		ve->untagged = untagged;
+		return 0;
+	}
+
+	if (vf->active_count >= vf->block_size)
+		return -ENOSPC;
+
+	ve = kzalloc_obj(*ve);
+	if (!ve)
+		return -ENOMEM;
+
+	ve->vid = vid;
+	ve->index = vf->active_count;
+	ve->untagged = untagged;
+
+	ret = mxl862xx_vf_entry_set(priv, vf->block_id, ve->index, vid);
+	if (ret) {
+		kfree(ve);
+		return ret;
+	}
+
+	list_add_tail(&ve->list, &vf->vids);
+	vf->active_count++;
+
+	return 0;
+}
+
+static int mxl862xx_vf_del_vid(struct mxl862xx_priv *priv,
+			       struct mxl862xx_vf_block *vf, u16 vid)
+{
+	struct mxl862xx_vf_vid *ve, *last_ve;
+	u16 gap, last;
+	int ret;
+
+	ve = mxl862xx_vf_find_vid(vf, vid);
+	if (!ve)
+		return 0;
+
+	if (!vf->allocated) {
+		/* Software-only state -- just remove the tracking entry */
+		list_del(&ve->list);
+		kfree(ve);
+		vf->active_count--;
+		return 0;
+	}
+
+	gap = ve->index;
+	last = vf->active_count - 1;
+
+	if (vf->active_count == 1) {
+		/* Last VID -- restore DISCARD sentinel at index 0 */
+		ret = mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
+		if (ret)
+			return ret;
+	} else if (gap < last) {
+		/* Swap: move the last ALLOW entry into the gap */
+		list_for_each_entry(last_ve, &vf->vids, list)
+			if (last_ve->index == last)
+				break;
+
+		if (WARN_ON(list_entry_is_head(last_ve, &vf->vids, list)))
+			return -EINVAL;
+
+		ret = mxl862xx_vf_entry_set(priv, vf->block_id,
+					    gap, last_ve->vid);
+		if (ret)
+			return ret;
+
+		last_ve->index = gap;
+	}
+
+	list_del(&ve->list);
+	kfree(ve);
+	vf->active_count--;
+
+	return 0;
+}
+
+static int mxl862xx_evlan_program_ingress(struct mxl862xx_priv *priv, int port)
+{
+	struct mxl862xx_port *p = &priv->ports[port];
+	struct mxl862xx_evlan_block *blk = &p->ingress_evlan;
+
+	if (!p->vlan_filtering)
+		return 0;
+
+	blk->in_use = true;
+	blk->n_active = blk->block_size;
+
+	return mxl862xx_evlan_write_final_rules(priv, blk,
+						ingress_aware_final,
+						ARRAY_SIZE(ingress_aware_final),
+						p->pvid);
+}
+
+static int mxl862xx_evlan_program_egress(struct mxl862xx_priv *priv, int port)
+{
+	struct mxl862xx_port *p = &priv->ports[port];
+	struct mxl862xx_evlan_block *blk = &p->egress_evlan;
+	const struct mxl862xx_evlan_rule_desc *vid_rules;
+	struct mxl862xx_vf_vid *vfv;
+	u16 old_active = blk->n_active;
+	u16 idx = 0, i;
+	int n_vid, ret;
+
+	if (p->vlan_filtering) {
+		vid_rules = vid_accept_standard;
+		n_vid = ARRAY_SIZE(vid_accept_standard);
+	} else {
+		vid_rules = vid_accept_egress_unaware;
+		n_vid = ARRAY_SIZE(vid_accept_egress_unaware);
+	}
+
+	list_for_each_entry(vfv, &p->vf.vids, list) {
+		if (!vfv->untagged)
+			continue;
+
+		if (idx + n_vid > blk->block_size)
+			return -ENOSPC;
+
+		ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
+						idx++, &vid_rules[0],
+						vfv->vid, vfv->untagged,
+						p->pvid);
+		if (ret)
+			return ret;
+
+		if (n_vid > 1) {
+			ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
+							idx++, &vid_rules[1],
+							vfv->vid,
+							vfv->untagged,
+							p->pvid);
+			if (ret)
+				return ret;
+		}
+	}
+
+	/* Deactivate stale entries that are no longer needed.
+	 * This closes the brief window between writing the new rules
+	 * and set_bridge_port narrowing the scan window.
+	 */
+	for (i = idx; i < old_active; i++) {
+		ret = mxl862xx_evlan_deactivate_entry(priv,
+						      blk->block_id,
+						      i);
+		if (ret)
+			return ret;
+	}
+
+	blk->n_active = idx;
+	blk->in_use = idx > 0;
+
+	return 0;
+}
+
+static int mxl862xx_port_vlan_filtering(struct dsa_switch *ds, int port,
+					bool vlan_filtering,
+					struct netlink_ext_ack *extack)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	struct mxl862xx_port *p = &priv->ports[port];
+	bool old_vlan_filtering = p->vlan_filtering;
+	bool old_in_use = p->ingress_evlan.in_use;
+	bool changed = (p->vlan_filtering != vlan_filtering);
+	int ret;
+
+	p->vlan_filtering = vlan_filtering;
+
+	if (changed) {
+		/* When leaving VLAN-aware mode, release the ingress HW
+		 * block. The firmware passes frames through unchanged
+		 * when no ingress EVLAN block is assigned, so the block
+		 * is unnecessary in unaware mode.
+		 */
+		if (!vlan_filtering)
+			p->ingress_evlan.in_use = false;
+
+		ret = mxl862xx_evlan_program_ingress(priv, port);
+		if (ret)
+			goto err_restore;
+
+		ret = mxl862xx_evlan_program_egress(priv, port);
+		if (ret)
+			goto err_restore;
+	}
+
+	return mxl862xx_set_bridge_port(ds, port);
+
+	/* No HW rollback -- restoring SW state is sufficient for a correct retry. */
+err_restore:
+	p->vlan_filtering = old_vlan_filtering;
+	p->ingress_evlan.in_use = old_in_use;
+	return ret;
+}
+
+static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan,
+				  struct netlink_ext_ack *extack)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	struct mxl862xx_port *p = &priv->ports[port];
+	bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
+	u16 vid = vlan->vid;
+	u16 old_pvid = p->pvid;
+	bool pvid_changed = false;
+	int ret;
+
+	/* CPU port is VLAN-transparent: the SP tag handles port
+	 * identification and the host-side DSA tagger manages VLAN
+	 * delivery. Egress EVLAN catchalls are set up once in
+	 * setup_cpu_bridge; no per-VID VF/EVLAN programming needed.
+	 */
+	if (dsa_is_cpu_port(ds, port))
+		return 0;
+
+	/* Update PVID tracking */
+	if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
+		if (p->pvid != vid) {
+			p->pvid = vid;
+			pvid_changed = true;
+		}
+	} else if (p->pvid == vid) {
+		p->pvid = 0;
+		pvid_changed = true;
+	}
+
+	/* Add/update VID in this port's VLAN Filter block.
+	 * VF must be updated before programming egress EVLAN because
+	 * evlan_program_egress walks the VF VID list.
+	 */
+	ret = mxl862xx_vf_add_vid(priv, &p->vf, vid, untagged);
+	if (ret)
+		goto err_pvid;
+
+	/* Reprogram ingress finals if PVID changed */
+	if (pvid_changed) {
+		ret = mxl862xx_evlan_program_ingress(priv, port);
+		if (ret)
+			goto err_rollback;
+	}
+
+	/* Reprogram egress tag-stripping rules (walks VF VID list) */
+	ret = mxl862xx_evlan_program_egress(priv, port);
+	if (ret)
+		goto err_rollback;
+
+	/* Apply VLAN block IDs and MAC learning flags to bridge port */
+	ret = mxl862xx_set_bridge_port(ds, port);
+	if (ret)
+		goto err_rollback;
+
+	return 0;
+
+err_rollback:
+	/* Best-effort: undo VF add and restore consistent hardware state.
+	 * A retry of port_vlan_add will converge since vf_add_vid is
+	 * idempotent.
+	 */
+	p->pvid = old_pvid;
+	mxl862xx_vf_del_vid(priv, &p->vf, vid);
+	mxl862xx_evlan_program_ingress(priv, port);
+	mxl862xx_evlan_program_egress(priv, port);
+	mxl862xx_set_bridge_port(ds, port);
+	return ret;
+err_pvid:
+	p->pvid = old_pvid;
+	return ret;
+}
+
+static int mxl862xx_port_vlan_del(struct dsa_switch *ds, int port,
+				  const struct switchdev_obj_port_vlan *vlan)
+{
+	struct mxl862xx_priv *priv = ds->priv;
+	struct mxl862xx_port *p = &priv->ports[port];
+	struct mxl862xx_vf_vid *ve;
+	bool pvid_changed = false;
+	u16 vid = vlan->vid;
+	bool old_untagged;
+	u16 old_pvid;
+	int ret;
+
+	if (dsa_is_cpu_port(ds, port))
+		return 0;
+
+	ve = mxl862xx_vf_find_vid(&p->vf, vid);
+	if (!ve)
+		return 0;
+	old_untagged = ve->untagged;
+	old_pvid = p->pvid;
+
+	/* Clear PVID if we're deleting it */
+	if (p->pvid == vid) {
+		p->pvid = 0;
+		pvid_changed = true;
+	}
+
+	/* Remove VID from this port's VLAN Filter block.
+	 * Must happen before egress reprogram so the VID is no
+	 * longer in the list that evlan_program_egress walks.
+	 */
+	ret = mxl862xx_vf_del_vid(priv, &p->vf, vid);
+	if (ret)
+		goto err_pvid;
+
+	/* Reprogram egress tag-stripping rules (VID is now gone) */
+	ret = mxl862xx_evlan_program_egress(priv, port);
+	if (ret)
+		goto err_rollback;
+
+	/* If PVID changed, reprogram ingress finals */
+	if (pvid_changed) {
+		ret = mxl862xx_evlan_program_ingress(priv, port);
+		if (ret)
+			goto err_rollback;
+	}
+
+	ret = mxl862xx_set_bridge_port(ds, port);
+	if (ret)
+		goto err_rollback;
+
+	return 0;
+
+err_rollback:
+	/* Best-effort: re-add the VID and restore consistent hardware
+	 * state. A retry of port_vlan_del will converge.
+	 */
+	p->pvid = old_pvid;
+	mxl862xx_vf_add_vid(priv, &p->vf, vid, old_untagged);
+	mxl862xx_evlan_program_egress(priv, port);
+	mxl862xx_evlan_program_ingress(priv, port);
+	mxl862xx_set_bridge_port(ds, port);
+	return ret;
+err_pvid:
+	p->pvid = old_pvid;
+	return ret;
+}
+
 static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
 {
 	struct mxl862xx_priv *priv = ds->priv;
+	struct mxl862xx_port *p = &priv->ports[port];
 
-	priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
-	priv->ports[port].learning = true;
+	p->fid = MXL862XX_DEFAULT_BRIDGE;
+	p->learning = true;
+
+	/* EVLAN is left disabled on CPU ports -- frames pass through
+	 * without EVLAN processing. Only the portmap and bridge
+	 * assignment need to be configured.
+	 */
 
 	return mxl862xx_set_bridge_port(ds, port);
 }
@@ -510,6 +1260,8 @@ static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
 static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
 				       const struct dsa_bridge bridge)
 {
+	struct mxl862xx_priv *priv = ds->priv;
+	struct mxl862xx_port *p = &priv->ports[port];
 	int err;
 
 	err = mxl862xx_sync_bridge_members(ds, &bridge);
@@ -521,6 +1273,10 @@ static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
 	/* Revert leaving port, omitted by the sync above, to its
 	 * single-port bridge
 	 */
+	p->pvid = 0;
+	p->ingress_evlan.in_use = false;
+	p->egress_evlan.in_use = false;
+
 	err = mxl862xx_set_bridge_port(ds, port);
 	if (err)
 		dev_err(ds->dev,
@@ -585,6 +1341,22 @@ static int mxl862xx_port_setup(struct dsa_switch *ds, int port)
 	if (ret)
 		return ret;
 
+	priv->ports[port].ingress_evlan.block_size = priv->evlan_ingress_size;
+	ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan);
+	if (ret)
+		return ret;
+
+	priv->ports[port].egress_evlan.block_size = priv->evlan_egress_size;
+	ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].egress_evlan);
+	if (ret)
+		return ret;
+
+	priv->ports[port].vf.block_size = priv->vf_block_size;
+	INIT_LIST_HEAD(&priv->ports[port].vf.vids);
+	ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf);
+	if (ret)
+		return ret;
+
 	priv->ports[port].setup_done = true;
 
 	return 0;
@@ -983,6 +1755,9 @@ static const struct dsa_switch_ops mxl862xx_switch_ops = {
 	.port_fdb_dump = mxl862xx_port_fdb_dump,
 	.port_mdb_add = mxl862xx_port_mdb_add,
 	.port_mdb_del = mxl862xx_port_mdb_del,
+	.port_vlan_filtering = mxl862xx_port_vlan_filtering,
+	.port_vlan_add = mxl862xx_port_vlan_add,
+	.port_vlan_del = mxl862xx_port_vlan_del,
 };
 
 static void mxl862xx_phylink_mac_config(struct phylink_config *config,
diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.h b/drivers/net/dsa/mxl862xx/mxl862xx.h
index 8d03dd24faeeb..a010cf6b961a9 100644
--- a/drivers/net/dsa/mxl862xx/mxl862xx.h
+++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
@@ -13,6 +13,8 @@ struct mxl862xx_priv;
 #define MXL862XX_DEFAULT_BRIDGE		0
 #define MXL862XX_MAX_BRIDGES		48
 #define MXL862XX_MAX_BRIDGE_PORTS	128
+#define MXL862XX_TOTAL_EVLAN_ENTRIES	1024
+#define MXL862XX_TOTAL_VF_ENTRIES	1024
 
 /* Number of __le16 words in a firmware portmap (128-bit bitmap). */
 #define MXL862XX_FW_PORTMAP_WORDS	(MXL862XX_MAX_BRIDGE_PORTS / 16)
@@ -54,6 +56,66 @@ static inline bool mxl862xx_fw_portmap_is_empty(const __le16 *map)
 	return true;
 }
 
+/**
+ * struct mxl862xx_vf_vid - Per-VID entry within a VLAN Filter block
+ * @list:     Linked into &mxl862xx_vf_block.vids
+ * @vid:      VLAN ID
+ * @index:    Entry index within the VLAN Filter HW block
+ * @untagged: Strip tag on egress for this VID (drives EVLAN tag-stripping)
+ */
+struct mxl862xx_vf_vid {
+	struct list_head list;
+	u16 vid;
+	u16 index;
+	bool untagged;
+};
+
+/**
+ * struct mxl862xx_vf_block - Per-port VLAN Filter block
+ * @allocated:    Whether the HW block has been allocated via VLANFILTER_ALLOC
+ * @block_id:     HW VLAN Filter block ID from VLANFILTER_ALLOC
+ * @block_size:   Total entries allocated in this block
+ * @active_count: Number of ALLOW entries at indices [0, active_count).
+ *                The bridge port config sends max(active_count, 1) as
+ *                block_size to narrow the HW scan window.
+ *                discard_unmatched_tagged handles frames outside this range.
+ * @vids:         List of &mxl862xx_vf_vid entries programmed in this block
+ */
+struct mxl862xx_vf_block {
+	bool allocated;
+	u16 block_id;
+	u16 block_size;
+	u16 active_count;
+	struct list_head vids;
+};
+
+/**
+ * struct mxl862xx_evlan_block - Per-port per-direction extended VLAN block
+ * @allocated:  Whether the HW block has been allocated via EXTENDEDVLAN_ALLOC.
+ *              Guards alloc/free idempotency--the block_id is only valid
+ *              while allocated is true.
+ * @in_use:     Whether the EVLAN engine should be enabled for this block
+ *              on the bridge port (sent as the enable flag in
+ *              set_bridge_port). Can be false while allocated is still
+ *              true -- e.g. when all egress VIDs are removed (idx == 0 in
+ *              evlan_program_egress) the block stays allocated for
+ *              potential reuse, but the engine is disabled so an empty
+ *              rule set does not discard all traffic.
+ * @block_id:   HW block ID from EXTENDEDVLAN_ALLOC
+ * @block_size: Total entries allocated
+ * @n_active:   Number of HW entries currently written. The bridge port
+ *              config sends this as the egress scan window, so entries
+ *              beyond n_active are never scanned. Always equals
+ *              block_size for ingress blocks (fixed catchall rules).
+ */
+struct mxl862xx_evlan_block {
+	bool allocated;
+	bool in_use;
+	u16 block_id;
+	u16 block_size;
+	u16 n_active;
+};
+
 /**
  * struct mxl862xx_port - per-port state tracked by the driver
  * @priv:                back-pointer to switch private data; needed by
@@ -68,6 +130,11 @@ static inline bool mxl862xx_fw_portmap_is_empty(const __le16 *map)
  * @setup_done:          set at end of port_setup, cleared at start of
  *                       port_teardown; guards deferred work against
  *                       acting on torn-down state
+ * @pvid:                port VLAN ID (native VLAN) assigned to untagged traffic
+ * @vlan_filtering:      true when VLAN filtering is enabled on this port
+ * @vf:                  per-port VLAN Filter block state
+ * @ingress_evlan:       ingress extended VLAN block state
+ * @egress_evlan:        egress extended VLAN block state
  * @host_flood_uc:       desired host unicast flood state (true = flood);
  *                       updated atomically by port_set_host_flood, consumed
  *                       by the deferred host_flood_work
@@ -85,6 +152,11 @@ struct mxl862xx_port {
 	unsigned long flood_block;
 	bool learning;
 	bool setup_done;
+	u16 pvid;
+	bool vlan_filtering;
+	struct mxl862xx_vf_block vf;
+	struct mxl862xx_evlan_block ingress_evlan;
+	struct mxl862xx_evlan_block egress_evlan;
 	bool host_flood_uc;
 	bool host_flood_mc;
 	struct work_struct host_flood_work;
@@ -92,17 +164,23 @@ struct mxl862xx_port {
 
 /**
  * struct mxl862xx_priv - driver private data for an MxL862xx switch
- * @ds:            pointer to the DSA switch instance
- * @mdiodev:       MDIO device used to communicate with the switch firmware
- * @crc_err_work:  deferred work for shutting down all ports on MDIO CRC errors
- * @crc_err:       set atomically before CRC-triggered shutdown, cleared after
- * @drop_meter:    index of the single shared zero-rate firmware meter used
- *                 to unconditionally drop traffic (used to block flooding)
- * @ports:         per-port state, indexed by switch port number
- * @bridges:       maps DSA bridge number to firmware bridge ID;
- *                 zero means no firmware bridge allocated for that
- *                 DSA bridge number.  Indexed by dsa_bridge.num
- *                 (0 .. ds->max_num_bridges).
+ * @ds:                 pointer to the DSA switch instance
+ * @mdiodev:            MDIO device used to communicate with the switch firmware
+ * @crc_err_work:       deferred work for shutting down all ports on MDIO CRC
+ *                      errors
+ * @crc_err:            set atomically before CRC-triggered shutdown, cleared
+ *                      after
+ * @drop_meter:         index of the single shared zero-rate firmware meter
+ *                      used to unconditionally drop traffic (used to block
+ *                      flooding)
+ * @ports:              per-port state, indexed by switch port number
+ * @bridges:            maps DSA bridge number to firmware bridge ID;
+ *                      zero means no firmware bridge allocated for that
+ *                      DSA bridge number. Indexed by dsa_bridge.num
+ *                      (0 .. ds->max_num_bridges).
+ * @evlan_ingress_size: per-port ingress Extended VLAN block size
+ * @evlan_egress_size:  per-port egress Extended VLAN block size
+ * @vf_block_size:      per-port VLAN Filter block size
  */
 struct mxl862xx_priv {
 	struct dsa_switch *ds;
@@ -112,6 +190,9 @@ struct mxl862xx_priv {
 	u16 drop_meter;
 	struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
 	u16 bridges[MXL862XX_MAX_BRIDGES + 1];
+	u16 evlan_ingress_size;
+	u16 evlan_egress_size;
+	u16 vf_block_size;
 };
 
 #endif /* __MXL862XX_H */
-- 
2.53.0

^ permalink raw reply related

* [PATCH v2] net: hamradio: 6pack: fix uninit-value in sixpack_receive_buf
From: Mashiro Chen @ 2026-04-07 17:31 UTC (permalink / raw)
  To: netdev
  Cc: horms, davem, edumazet, kuba, pabeni, linux-hams, stable,
	Mashiro Chen, syzbot+ecdb8c9878a81eb21e54
In-Reply-To: <20260407165007.GB469338@kernel.org>

sixpack_receive_buf() does not properly skip bytes with TTY error flags.
The while loop iterates through the flags buffer but never advances the
data pointer (cp), and passes the original count (including error bytes)
to sixpack_decode(). This causes sixpack_decode() to process bytes that
should have been skipped due to TTY errors.  The TTY layer does not
guarantee that cp[i] holds a meaningful value when fp[i] is set, so
passing those positions to sixpack_decode() results in KMSAN reporting
an uninit-value read.

Fix this by processing bytes one at a time, advancing cp on each
iteration, and only passing valid (non-error) bytes to sixpack_decode().
This matches the pattern used by slip_receive_buf() and
mkiss_receive_buf() for the same purpose.

Reported-by: syzbot+ecdb8c9878a81eb21e54@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=ecdb8c9878a81eb21e54
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Mashiro Chen <mashiro.chen@mailbox.org>
---
 drivers/net/hamradio/6pack.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 885992951e8a6..c8b2dc5c1becc 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -391,7 +391,6 @@ static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp,
 				const u8 *fp, size_t count)
 {
 	struct sixpack *sp;
-	size_t count1;
 
 	if (!count)
 		return;
@@ -401,16 +400,16 @@ static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp,
 		return;
 
 	/* Read the characters out of the buffer */
-	count1 = count;
-	while (count) {
-		count--;
+	while (count--) {
 		if (fp && *fp++) {
 			if (!test_and_set_bit(SIXPF_ERROR, &sp->flags))
 				sp->dev->stats.rx_errors++;
+			cp++;
 			continue;
 		}
+		sixpack_decode(sp, cp, 1);
+		cp++;
 	}
-	sixpack_decode(sp, cp, count1);
 
 	tty_unthrottle(tty);
 }
-- 
2.53.0


^ permalink raw reply related

* [PATCH 0/4] drbd: switch from genl_magic to YNL
From: Christoph Böhmwalder @ 2026-04-07 17:33 UTC (permalink / raw)
  To: Jens Axboe
  Cc: drbd-dev, linux-kernel, Lars Ellenberg, Philipp Reisner,
	linux-block, Donald Hunter, Eric Dumazet, Jakub Kicinski, netdev,
	Christoph Böhmwalder

DRBD's genetlink interface was defined using a custom multi-include
macro system, genl_magic_{func,struct}.h. This system generated struct
definitions, netlink policies, serialization functions and more
from a single "magic" header.
It never really caught on; DRBD is its only user, its internal macro
jungle is next to impossible to understand, and even harder to debug.

This series replaces it with the standard solution, YNL.

First, shuffle some headers around because the YNL generator expects
to generate UAPI headers.
Then, add some optional extensions to the YNL generator to support a
minimal set of old genl_magic features required for backward
compatibility.
Then create a "genetlink-legacy" based spec for DRBD and switch to it.

Note: this family primarily aims for compatiblity with existing
userspace. The next planned step is a new (also YNL-based) family,
"drbd2", which will implement all the actual modern recommendations for
new netlink families.

Christoph Böhmwalder (4):
  drbd: move UAPI headers to include/uapi/linux/
  tools: ynl-gen-c: optionally emit structs and helpers
  drbd: add YNL genetlink specification
  drbd: switch from genl_magic macros to YNL-generated code

 Documentation/netlink/genetlink-legacy.yaml   |   23 +
 Documentation/netlink/specs/drbd.yaml         | 1109 +++++++
 drivers/block/drbd/Makefile                   |    1 +
 drivers/block/drbd/drbd_buildtag.c            |    2 +-
 .../block/drbd}/drbd_config.h                 |    0
 drivers/block/drbd/drbd_debugfs.c             |    2 +-
 drivers/block/drbd/drbd_int.h                 |    6 +-
 drivers/block/drbd/drbd_main.c                |    6 +-
 drivers/block/drbd/drbd_nl.c                  |  416 +--
 drivers/block/drbd/drbd_nl_gen.c              | 2610 +++++++++++++++++
 drivers/block/drbd/drbd_nl_gen.h              |  399 +++
 drivers/block/drbd/drbd_proc.c                |    2 +-
 include/linux/drbd_genl.h                     |  536 ----
 include/linux/drbd_genl_api.h                 |   56 -
 include/linux/genl_magic_func.h               |  413 ---
 include/linux/genl_magic_struct.h             |  272 --
 include/{ => uapi}/linux/drbd.h               |   45 +-
 include/uapi/linux/drbd_genl.h                |  363 +++
 include/{ => uapi}/linux/drbd_limits.h        |    2 +-
 tools/net/ynl/pyynl/ynl_gen_c.py              |  293 +-
 20 files changed, 5087 insertions(+), 1469 deletions(-)
 create mode 100644 Documentation/netlink/specs/drbd.yaml
 rename {include/linux => drivers/block/drbd}/drbd_config.h (100%)
 create mode 100644 drivers/block/drbd/drbd_nl_gen.c
 create mode 100644 drivers/block/drbd/drbd_nl_gen.h
 delete mode 100644 include/linux/drbd_genl.h
 delete mode 100644 include/linux/drbd_genl_api.h
 delete mode 100644 include/linux/genl_magic_func.h
 delete mode 100644 include/linux/genl_magic_struct.h
 rename include/{ => uapi}/linux/drbd.h (86%)
 create mode 100644 include/uapi/linux/drbd_genl.h
 rename include/{ => uapi}/linux/drbd_limits.h (99%)


base-commit: a9c4b1d37622ed01b75f94a4f68cf55f33153a31
-- 
2.53.0


^ permalink raw reply

* [PATCH 1/4] drbd: move UAPI headers to include/uapi/linux/
From: Christoph Böhmwalder @ 2026-04-07 17:33 UTC (permalink / raw)
  To: Jens Axboe
  Cc: drbd-dev, linux-kernel, Lars Ellenberg, Philipp Reisner,
	linux-block, Donald Hunter, Eric Dumazet, Jakub Kicinski, netdev,
	Christoph Böhmwalder
In-Reply-To: <20260407173356.873887-1-christoph.boehmwalder@linbit.com>

drbd.h and drbd_limits.h contain only type definitions, enums, and
constants shared between kernel and userspace. These should be part of
UAPI.

Split the genl_api header into two: the genlmsghdr and the enums are
UAPI, the rest stays there for now (it will be removed by one of the
next commits in this series).

drbd_config.h is clearly DRBD-internal, so move it there.

Signed-off-by: Christoph Böhmwalder <christoph.boehmwalder@linbit.com>
---
 drivers/block/drbd/drbd_buildtag.c            |  2 +-
 .../block/drbd}/drbd_config.h                 |  0
 drivers/block/drbd/drbd_int.h                 |  2 +-
 include/linux/drbd_genl_api.h                 | 40 ------------------
 include/{ => uapi}/linux/drbd.h               | 42 ++++++++++++++++++-
 include/{ => uapi}/linux/drbd_limits.h        |  2 +-
 6 files changed, 44 insertions(+), 44 deletions(-)
 rename {include/linux => drivers/block/drbd}/drbd_config.h (100%)
 rename include/{ => uapi}/linux/drbd.h (87%)
 rename include/{ => uapi}/linux/drbd_limits.h (99%)

diff --git a/drivers/block/drbd/drbd_buildtag.c b/drivers/block/drbd/drbd_buildtag.c
index cb1aa66d7d5d..cd0389488f63 100644
--- a/drivers/block/drbd/drbd_buildtag.c
+++ b/drivers/block/drbd/drbd_buildtag.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
-#include <linux/drbd_config.h>
+#include "drbd_config.h"
 #include <linux/module.h>
 
 const char *drbd_buildtag(void)
diff --git a/include/linux/drbd_config.h b/drivers/block/drbd/drbd_config.h
similarity index 100%
rename from include/linux/drbd_config.h
rename to drivers/block/drbd/drbd_config.h
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index f6d6276974ee..f3d746a6d6fd 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -34,7 +34,7 @@
 #include <linux/prefetch.h>
 #include <linux/drbd_genl_api.h>
 #include <linux/drbd.h>
-#include <linux/drbd_config.h>
+#include "drbd_config.h"
 #include "drbd_strings.h"
 #include "drbd_state.h"
 #include "drbd_protocol.h"
diff --git a/include/linux/drbd_genl_api.h b/include/linux/drbd_genl_api.h
index 70682c058027..19d263924852 100644
--- a/include/linux/drbd_genl_api.h
+++ b/include/linux/drbd_genl_api.h
@@ -2,46 +2,6 @@
 #ifndef DRBD_GENL_STRUCT_H
 #define DRBD_GENL_STRUCT_H
 
-/**
- * struct drbd_genlmsghdr - DRBD specific header used in NETLINK_GENERIC requests
- * @minor:
- *     For admin requests (user -> kernel): which minor device to operate on.
- *     For (unicast) replies or informational (broadcast) messages
- *     (kernel -> user): which minor device the information is about.
- *     If we do not operate on minors, but on connections or resources,
- *     the minor value shall be (~0), and the attribute DRBD_NLA_CFG_CONTEXT
- *     is used instead.
- * @flags: possible operation modifiers (relevant only for user->kernel):
- *     DRBD_GENL_F_SET_DEFAULTS
- * @volume:
- *     When creating a new minor (adding it to a resource), the resource needs
- *     to know which volume number within the resource this is supposed to be.
- *     The volume number corresponds to the same volume number on the remote side,
- *     whereas the minor number on the remote side may be different
- *     (union with flags).
- * @ret_code: kernel->userland unicast cfg reply return code (union with flags);
- */
-struct drbd_genlmsghdr {
-	__u32 minor;
-	union {
-	__u32 flags;
-	__s32 ret_code;
-	};
-};
-
-/* To be used in drbd_genlmsghdr.flags */
-enum {
-	DRBD_GENL_F_SET_DEFAULTS = 1,
-};
-
-enum drbd_state_info_bcast_reason {
-	SIB_GET_STATUS_REPLY = 1,
-	SIB_STATE_CHANGE = 2,
-	SIB_HELPER_PRE = 3,
-	SIB_HELPER_POST = 4,
-	SIB_SYNC_PROGRESS = 5,
-};
-
 /* hack around predefined gcc/cpp "linux=1",
  * we cannot possibly include <1/drbd_genl.h> */
 #undef linux
diff --git a/include/linux/drbd.h b/include/uapi/linux/drbd.h
similarity index 87%
rename from include/linux/drbd.h
rename to include/uapi/linux/drbd.h
index 5468a2399d48..7930a972d8a4 100644
--- a/include/linux/drbd.h
+++ b/include/uapi/linux/drbd.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
 /*
   drbd.h
   Kernel module for 2.6.x Kernels
@@ -389,4 +389,44 @@ enum write_ordering_e {
 
 #define DRBD_CPU_MASK_SIZE 32
 
+/**
+ * struct drbd_genlmsghdr - DRBD specific header used in NETLINK_GENERIC requests
+ * @minor:
+ *     For admin requests (user -> kernel): which minor device to operate on.
+ *     For (unicast) replies or informational (broadcast) messages
+ *     (kernel -> user): which minor device the information is about.
+ *     If we do not operate on minors, but on connections or resources,
+ *     the minor value shall be (~0), and the attribute DRBD_NLA_CFG_CONTEXT
+ *     is used instead.
+ * @flags: possible operation modifiers (relevant only for user->kernel):
+ *     DRBD_GENL_F_SET_DEFAULTS
+ * @volume:
+ *     When creating a new minor (adding it to a resource), the resource needs
+ *     to know which volume number within the resource this is supposed to be.
+ *     The volume number corresponds to the same volume number on the remote side,
+ *     whereas the minor number on the remote side may be different
+ *     (union with flags).
+ * @ret_code: kernel->userland unicast cfg reply return code (union with flags);
+ */
+struct drbd_genlmsghdr {
+	__u32 minor;
+	union {
+		__u32 flags;
+		__s32 ret_code;
+	};
+};
+
+/* To be used in drbd_genlmsghdr.flags */
+enum {
+	DRBD_GENL_F_SET_DEFAULTS = 1,
+};
+
+enum drbd_state_info_bcast_reason {
+	SIB_GET_STATUS_REPLY = 1,
+	SIB_STATE_CHANGE = 2,
+	SIB_HELPER_PRE = 3,
+	SIB_HELPER_POST = 4,
+	SIB_SYNC_PROGRESS = 5,
+};
+
 #endif
diff --git a/include/linux/drbd_limits.h b/include/uapi/linux/drbd_limits.h
similarity index 99%
rename from include/linux/drbd_limits.h
rename to include/uapi/linux/drbd_limits.h
index 5b042fb427e9..a72a102d1ca7 100644
--- a/include/linux/drbd_limits.h
+++ b/include/uapi/linux/drbd_limits.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
 /*
   drbd_limits.h
   This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
-- 
2.53.0


^ permalink raw reply related

* [PATCH 2/4] tools: ynl-gen-c: optionally emit structs and helpers
From: Christoph Böhmwalder @ 2026-04-07 17:33 UTC (permalink / raw)
  To: Jens Axboe
  Cc: drbd-dev, linux-kernel, Lars Ellenberg, Philipp Reisner,
	linux-block, Donald Hunter, Eric Dumazet, Jakub Kicinski, netdev,
	Christoph Böhmwalder
In-Reply-To: <20260407173356.873887-1-christoph.boehmwalder@linbit.com>

Add a "emit-structs" option to the genetlink-legacy spec.

Enabling "emit-structs" adds struct declarations for nested attribute
sets to the generated kernel headers.

It also adds some useful serialization helpers:
  - from_attrs() with 'required' attribute enforcement
  - to_skb() for struct-to-netlink serialization
  - set_defaults() driven by the 'default' YAML key

The motivation is to replace the existing deprecated genl_magic system.
Some genl_magic features are dropped entirely because they had no
significant users, some are carried over to YNL (the genetlink-legacy
spec).

The new flags in the genetlink-legacy spec that are required for
existing consumers to keep working are:

  "default": a literal value or C define that sets the default value
  for an attribute, consumed by set_defaults().

  "required": if true, from_attrs() returns an error when this
  attribute is missing from the request message.

  "nla-policy-type": can be used to override the NLA type used in
  policy arrays. This is needed when the semantic type differs from
  the wire type for backward compatibility: genl_magic maps s32 fields
  to NLA_U32/nla_get_u32, and existing userspace might depend on this
  encoding. The immediate motivation is DRBD, whose genl spec
  definition predates the addition of signed types in genl. However,
  this is a generic issue that potentially affects multiple families:
  for example, nftables has NFTA_HOOK_PRIORITY as s32 in the spec but
  NLA_U32 in the actual kernel policy.

All new properties are backward-compatible; existing specs that do not
use them are unaffected.

Signed-off-by: Christoph Böhmwalder <christoph.boehmwalder@linbit.com>
---
 Documentation/netlink/genetlink-legacy.yaml |  23 ++
 tools/net/ynl/pyynl/ynl_gen_c.py            | 293 +++++++++++++++++++-
 2 files changed, 313 insertions(+), 3 deletions(-)

diff --git a/Documentation/netlink/genetlink-legacy.yaml b/Documentation/netlink/genetlink-legacy.yaml
index 66fb8653a344..8e87e1c7915e 100644
--- a/Documentation/netlink/genetlink-legacy.yaml
+++ b/Documentation/netlink/genetlink-legacy.yaml
@@ -270,6 +270,23 @@ properties:
                       For string attributes, do not check whether attribute
                       contains the terminating null character.
                     type: boolean
+              default:
+                description: |
+                  Default value expression (C macro or literal) for this attribute.
+                  Used to generate set_defaults() initialization functions.
+                type: [ string, integer ]
+              required:
+                description: |
+                  If true, from_attrs() returns an error when this attribute is
+                  missing from the request message.
+                type: boolean
+              nla-policy-type:
+                description: |
+                  Override the NLA type used in kernel policy arrays. Use this when
+                  the semantic type differs from the wire type for backward compat
+                  (e.g., s32 fields that must use NLA_U32 on the wire because
+                  userspace predates NLA_S32 support).
+                enum: [ u8, u16, u32, u64, s8, s16, s32, s64 ]
               sub-type: *attr-type
               display-hint: *display-hint
               # Start genetlink-c
@@ -471,3 +488,9 @@ properties:
           to store the socket state. The type / structure is internal
           to the kernel, and is not defined in the spec.
         type: string
+      emit-structs:
+        description: |
+          Generate C struct declarations and serialization helpers
+          (from_attrs, to_skb, set_defaults) in the kernel header
+          and source.
+        type: boolean
diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py
index 0e1e486c1185..fc20b05f1c95 100755
--- a/tools/net/ynl/pyynl/ynl_gen_c.py
+++ b/tools/net/ynl/pyynl/ynl_gen_c.py
@@ -225,10 +225,11 @@ class Type(SpecAttr):
         return '{ .type = ' + policy + ', }'
 
     def attr_policy(self, cw):
-        policy = f'NLA_{c_upper(self.type)}'
+        policy_type = self.attr.get('nla-policy-type', self.type)
+        policy = f'NLA_{c_upper(policy_type)}'
         if self.attr.get('byte-order') == 'big-endian':
-            if self.type in {'u16', 'u32'}:
-                policy = f'NLA_BE{self.type[1:]}'
+            if policy_type in {'u16', 'u32'}:
+                policy = f'NLA_BE{policy_type[1:]}'
 
         spec = self._attr_policy(policy)
         cw.p(f"\t[{self.enum_name}] = {spec},")
@@ -3415,6 +3416,255 @@ def find_kernel_root(full_path):
             return full_path, sub_path[:-1]
 
 
+def _struct_c_type(attr_type):
+    """Map YNL attribute type to C type for struct field declarations."""
+    type_map = {
+        'u8': 'unsigned char', 'u16': '__u16', 'u32': '__u32', 'u64': '__u64',
+        's8': '__s8', 's16': '__s16', 's32': '__s32', 's64': '__s64',
+    }
+    return type_map.get(attr_type)
+
+
+def _nested_attr_sets(family):
+    """Yield (name, attr_set) for non-root attr-sets in spec order.
+
+    The root attr-set (same name as family) contains nest-type attributes
+    that point to the nested sets.  Only the nested sets have scalar/array
+    fields that translate to struct members.
+    """
+    root_name = family['name']
+    for name, attr_set in family.attr_sets.items():
+        if name == root_name or attr_set.subset_of:
+            continue
+        yield name, attr_set
+
+
+def render_struct_decl(family, cw):
+    """Generate C struct declarations from nested attribute sets."""
+    for set_name, attr_set in _nested_attr_sets(family):
+        s_name = c_lower(set_name)
+        cw.p(f"struct {s_name} {{")
+        for _, attr in attr_set.items():
+            c_name = c_lower(attr.name)
+            c_type = _struct_c_type(attr['type'])
+            if c_type:
+                cw.p(f"\t{c_type} {c_name};")
+            elif attr['type'] in ('string', 'binary'):
+                maxlen = attr.get('checks', {}).get('max-len', 0)
+                cw.p(f"\tchar {c_name}[{maxlen}];")
+                cw.p(f"\t__u32 {c_name}_len;")
+        cw.p('};')
+        cw.nl()
+
+
+def _nla_get_fn(attr_type):
+    """Return the nla_get function name for a scalar type."""
+    fn_map = {
+        'u8': 'nla_get_u8', 'u16': 'nla_get_u16',
+        'u32': 'nla_get_u32', 'u64': 'nla_get_u64',
+        's8': 'nla_get_s8', 's16': 'nla_get_s16',
+        's32': 'nla_get_s32', 's64': 'nla_get_s64',
+    }
+    return fn_map.get(attr_type)
+
+
+def _nla_put_fn(attr_type):
+    """Return (function_name, extra_args) for a scalar nla_put."""
+    fn_map = {
+        'u8':  ('nla_put_u8', ''),
+        'u16': ('nla_put_u16', ''),
+        'u32': ('nla_put_u32', ''),
+        'u64': ('nla_put_u64_64bit', ', 0'),
+        's8':  ('nla_put_s8', ''),
+        's16': ('nla_put_s16', ''),
+        's32': ('nla_put_s32', ''),
+        's64': ('nla_put_s64', ''),
+    }
+    return fn_map.get(attr_type)
+
+
+def render_from_attrs(family, cw):
+    """Generate from_attrs() deserialization functions."""
+    root_set = family.attr_sets.get(family['name'])
+
+    for set_name, attr_set in _nested_attr_sets(family):
+        s_name = c_lower(set_name)
+        struct = family.pure_nested_structs.get(set_name)
+        if not struct or not struct.request:
+            continue
+        tla_name = None
+        if root_set:
+            for _, tla_attr in root_set.items():
+                if tla_attr.attr.get('nested-attributes') == set_name:
+                    tla_name = tla_attr.enum_name
+                    break
+        if tla_name is None:
+            continue
+
+        policy_name = f"{struct.render_name}_nl_policy"
+        max_attr = struct.attr_max_val.enum_name
+        cw.p(f"static int __{s_name}_from_attrs(struct {s_name} *s,")
+        cw.p(f"\t\tstruct nlattr ***ret_nested_attribute_table,")
+        cw.p(f"\t\tstruct genl_info *info)")
+        cw.block_start()
+        cw.p(f"const int maxtype = {max_attr};")
+        cw.p(f"struct nlattr *tla = info->attrs[{tla_name}];")
+        cw.p('struct nlattr **ntb;')
+        cw.p('struct nlattr *nla;')
+        cw.p('int err = 0;')
+        cw.nl()
+        cw.p('if (ret_nested_attribute_table)')
+        cw.p('*ret_nested_attribute_table = NULL;')
+        cw.p('if (!tla)')
+        cw.p('return -ENOMSG;')
+        cw.p(f"ntb = kcalloc({max_attr} + 1, sizeof(*ntb), GFP_KERNEL);")
+        cw.p('if (!ntb)')
+        cw.p('return -ENOMEM;')
+        cw.p(f"err = nla_parse_nested_deprecated(ntb, maxtype, tla, {policy_name}, NULL);")
+        cw.p('if (err)')
+        cw.p('goto out;')
+        cw.nl()
+
+        for _, attr in attr_set.items():
+            c_name = c_lower(attr.name)
+            is_required = attr.attr.get('required', False)
+            get_fn = _nla_get_fn(attr['type'])
+
+            cw.p(f"nla = ntb[{attr.enum_name}];")
+            if is_required:
+                cw.block_start(line='if (nla)')
+                if get_fn:
+                    cw.p('if (s)')
+                    cw.p(f"s->{c_name} = {get_fn}(nla);")
+                elif attr['type'] == 'string':
+                    maxlen = attr.get('checks', {}).get('max-len', 0)
+                    cw.p('if (s)')
+                    cw.p(f"s->{c_name}_len = nla_strscpy(s->{c_name}, nla, {maxlen});")
+                elif attr['type'] == 'binary':
+                    maxlen = attr.get('checks', {}).get('max-len', 0)
+                    cw.p('if (s)')
+                    cw.p(f"s->{c_name}_len = nla_memcpy(s->{c_name}, nla, {maxlen});")
+                cw.block_end()
+                cw.block_start(line='else')
+                cw.p(f'pr_info("<< missing required attr: {c_name}\\n");')
+                cw.p('err = -ENOMSG;')
+                cw.block_end()
+            else:
+                if get_fn:
+                    cw.p('if (nla && s)')
+                    cw.p(f"s->{c_name} = {get_fn}(nla);")
+                elif attr['type'] == 'string':
+                    maxlen = attr.get('checks', {}).get('max-len', 0)
+                    cw.p('if (nla && s)')
+                    cw.p(f"s->{c_name}_len = nla_strscpy(s->{c_name}, nla, {maxlen});")
+                elif attr['type'] == 'binary':
+                    maxlen = attr.get('checks', {}).get('max-len', 0)
+                    cw.p('if (nla && s)')
+                    cw.p(f"s->{c_name}_len = nla_memcpy(s->{c_name}, nla, {maxlen});")
+            cw.nl()
+
+        cw.p('out:')
+        cw.p('if (ret_nested_attribute_table && (!err || err == -ENOMSG))')
+        cw.p('*ret_nested_attribute_table = ntb;')
+        cw.p('else')
+        cw.p('kfree(ntb);')
+        cw.p('return err;')
+        cw.block_end()
+        cw.nl()
+
+        cw.p(f"int {s_name}_from_attrs(struct {s_name} *s,")
+        cw.p(f"\t\t\t\tstruct genl_info *info)")
+        cw.block_start()
+        cw.p(f"return __{s_name}_from_attrs(s, NULL, info);")
+        cw.block_end()
+        cw.nl()
+
+        cw.p(f"int {s_name}_ntb_from_attrs(")
+        cw.p(f"\t\t\tstruct nlattr ***ret_nested_attribute_table,")
+        cw.p(f"\t\t\tstruct genl_info *info)")
+        cw.block_start()
+        cw.p(f"return __{s_name}_from_attrs(NULL, ret_nested_attribute_table, info);")
+        cw.block_end()
+        cw.nl()
+
+
+def render_to_skb(family, cw):
+    """Generate to_skb() serialization functions."""
+    root_set = family.attr_sets.get(family['name'])
+
+    for set_name, attr_set in _nested_attr_sets(family):
+        s_name = c_lower(set_name)
+        tla_name = None
+        if root_set:
+            for _, tla_attr in root_set.items():
+                if tla_attr.attr.get('nested-attributes') == set_name:
+                    tla_name = tla_attr.enum_name
+                    break
+        if tla_name is None:
+            continue
+
+        cw.p(f"int {s_name}_to_skb(struct sk_buff *skb, struct {s_name} *s)")
+        cw.block_start()
+        cw.p(f"struct nlattr *tla = nla_nest_start(skb, {tla_name});")
+        cw.nl()
+        cw.p('if (!tla)')
+        cw.p('goto nla_put_failure;')
+        cw.nl()
+
+        for _, attr in attr_set.items():
+            c_name = c_lower(attr.name)
+            put = _nla_put_fn(attr['type'])
+
+            if put:
+                fn, extra = put
+                cw.p(f"if ({fn}(skb, {attr.enum_name}, s->{c_name}{extra}))")
+                cw.p('goto nla_put_failure;')
+            elif attr['type'] in ('string', 'binary'):
+                maxlen = attr.get('checks', {}).get('max-len', 0)
+                nul_adj = f" + (s->{c_name}_len < {maxlen})" if attr['type'] == 'string' else ''
+                cw.p(f"if (nla_put(skb, {attr.enum_name}, min_t(int, {maxlen},")
+                cw.p(f"\t\ts->{c_name}_len{nul_adj}), s->{c_name}))")
+                cw.p('goto nla_put_failure;', add_ind=1)
+
+        cw.nl()
+        cw.p('nla_nest_end(skb, tla);')
+        cw.p('return 0;')
+        cw.nl()
+        cw.p('nla_put_failure:')
+        cw.p('if (tla)')
+        cw.p('nla_nest_cancel(skb, tla);')
+        cw.p('return -EMSGSIZE;')
+        cw.block_end()
+        cw.nl()
+
+
+def render_set_defaults(family, cw):
+    """Generate set_defaults() initialization functions."""
+    for set_name, attr_set in _nested_attr_sets(family):
+        s_name = c_lower(set_name)
+        has_defaults = any(
+            'default' in attr.attr for _, attr in attr_set.items()
+        )
+        if not has_defaults:
+            continue
+
+        cw.p(f"void set_{s_name}_defaults(struct {s_name} *x)")
+        cw.block_start()
+        for _, attr in attr_set.items():
+            c_name = c_lower(attr.name)
+            default = attr.attr.get('default')
+            if default is None:
+                continue
+
+            if attr['type'] in ('string', 'binary'):
+                cw.p(f"memset(x->{c_name}, 0, sizeof(x->{c_name}));")
+                cw.p(f"x->{c_name}_len = 0;")
+            else:
+                cw.p(f"x->{c_name} = {default};")
+        cw.block_end()
+        cw.nl()
+
+
 def main():
     parser = argparse.ArgumentParser(description='Netlink simple parsing generator')
     parser.add_argument('--mode', dest='mode', type=str, required=True,
@@ -3487,6 +3737,9 @@ def main():
         cw.p('#include <net/genetlink.h>')
         cw.nl()
         if not args.header:
+            if parsed.kernel_family.get('emit-structs'):
+                cw.p('#include <linux/kernel.h>')
+                cw.p('#include <linux/slab.h>')
             if args.out_file:
                 cw.p(f'#include "{hdr_file}"')
             cw.nl()
@@ -3555,6 +3808,31 @@ def main():
             print_kernel_op_table_hdr(parsed, cw)
             print_kernel_mcgrp_hdr(parsed, cw)
             print_kernel_family_struct_hdr(parsed, cw)
+
+            if parsed.kernel_family.get('emit-structs'):
+                cw.nl()
+                render_struct_decl(parsed, cw)
+                # Function prototypes
+                root_set = parsed.attr_sets.get(parsed['name'])
+                for set_name, attr_set in _nested_attr_sets(parsed):
+                    s_name = c_lower(set_name)
+                    struct = parsed.pure_nested_structs.get(set_name)
+                    has_tla = False
+                    if root_set:
+                        for _, tla_attr in root_set.items():
+                            if tla_attr.attr.get('nested-attributes') == set_name:
+                                has_tla = True
+                                break
+                    if not has_tla:
+                        continue
+                    if struct and struct.request:
+                        cw.p(f"int {s_name}_from_attrs(struct {s_name} *s, struct genl_info *info);")
+                        cw.p(f"int {s_name}_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);")
+                    cw.p(f"int {s_name}_to_skb(struct sk_buff *skb, struct {s_name} *s);")
+                    has_defaults = any('default' in a.attr for _, a in attr_set.items())
+                    if has_defaults:
+                        cw.p(f"void set_{s_name}_defaults(struct {s_name} *x);")
+                    cw.nl()
         else:
             print_kernel_policy_ranges(parsed, cw)
             print_kernel_policy_sparse_enum_validates(parsed, cw)
@@ -3588,6 +3866,15 @@ def main():
             print_kernel_mcgrp_src(parsed, cw)
             print_kernel_family_struct_src(parsed, cw)
 
+            if parsed.kernel_family.get('emit-structs'):
+                cw.nl()
+                render_from_attrs(parsed, cw)
+                render_to_skb(parsed, cw)
+                render_set_defaults(parsed, cw)
+                if cw._block_end:
+                    cw._block_end = False
+                    cw._out.write('}\n')
+
     if args.mode == "user":
         if args.header:
             cw.p('/* Enums */')
-- 
2.53.0


^ permalink raw reply related

* [PATCH 3/4] drbd: add YNL genetlink specification
From: Christoph Böhmwalder @ 2026-04-07 17:33 UTC (permalink / raw)
  To: Jens Axboe
  Cc: drbd-dev, linux-kernel, Lars Ellenberg, Philipp Reisner,
	linux-block, Donald Hunter, Eric Dumazet, Jakub Kicinski, netdev,
	Christoph Böhmwalder
In-Reply-To: <20260407173356.873887-1-christoph.boehmwalder@linbit.com>

Add a YNL-conformant YAML spec for the DRBD genetlink interface.

This uses the genetlink-legacy schema since DRBD has a fixed protocol
header (drbd_genlmsghdr) and pre-existing conventions that userspace
depends on.

s32 fields (meta-dev-idx, resync-after) use nla-policy-type: u32 to
preserve wire compatibility with existing userspace; the original spec
predates kernel NLA_S32 support.

Signed-off-by: Christoph Böhmwalder <christoph.boehmwalder@linbit.com>
---
 Documentation/netlink/specs/drbd.yaml | 1109 +++++++++++++++++++++++++
 include/uapi/linux/drbd.h             |    3 +
 2 files changed, 1112 insertions(+)
 create mode 100644 Documentation/netlink/specs/drbd.yaml

diff --git a/Documentation/netlink/specs/drbd.yaml b/Documentation/netlink/specs/drbd.yaml
new file mode 100644
index 000000000000..d5de2a4506e0
--- /dev/null
+++ b/Documentation/netlink/specs/drbd.yaml
@@ -0,0 +1,1109 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+name: drbd
+protocol: genetlink-legacy
+uapi-header: linux/drbd_genl.h
+kernel-family:
+  emit-structs: true
+doc: |
+  DRBD genetlink protocol for configuring and monitoring replication resources,
+  devices, and connections.
+
+definitions:
+  - name: shared-secret-max
+    type: const
+    header: linux/drbd.h
+  - name: drbd-cpu-mask-size
+    type: const
+    header: linux/drbd.h
+  - name: drbd-nl-uuids-size
+    type: const
+    header: linux/drbd.h
+  - name: drbd-nl-history-uuids-size
+    type: const
+    header: linux/drbd.h
+  - name: drbd-minor-count-max
+    type: const
+    header: linux/drbd_limits.h
+
+attribute-sets:
+  -
+    name: drbd
+    name-prefix: drbd-nla-
+    doc: Top-level DRBD attribute space (all TLAs are nested).
+    attributes:
+      - name: cfg-reply
+        type: nest
+        value: 1
+        nested-attributes: drbd-cfg-reply
+      - name: cfg-context
+        type: nest
+        value: 2
+        nested-attributes: drbd-cfg-context
+      - name: disk-conf
+        type: nest
+        value: 3
+        nested-attributes: disk-conf
+      - name: resource-opts
+        type: nest
+        value: 4
+        nested-attributes: res-opts
+      - name: net-conf
+        type: nest
+        value: 5
+        nested-attributes: net-conf
+      - name: set-role-parms
+        type: nest
+        value: 6
+        nested-attributes: set-role-parms
+      - name: resize-parms
+        type: nest
+        value: 7
+        nested-attributes: resize-parms
+      - name: state-info
+        type: nest
+        value: 8
+        nested-attributes: state-info
+      - name: start-ov-parms
+        type: nest
+        value: 9
+        nested-attributes: start-ov-parms
+      - name: new-c-uuid-parms
+        type: nest
+        value: 10
+        nested-attributes: new-c-uuid-parms
+      - name: timeout-parms
+        type: nest
+        value: 11
+        nested-attributes: timeout-parms
+      - name: disconnect-parms
+        type: nest
+        value: 12
+        nested-attributes: disconnect-parms
+      - name: detach-parms
+        type: nest
+        value: 13
+        nested-attributes: detach-parms
+      - name: resource-info
+        type: nest
+        value: 15
+        nested-attributes: resource-info
+      - name: device-info
+        type: nest
+        value: 16
+        nested-attributes: device-info
+      - name: connection-info
+        type: nest
+        value: 17
+        nested-attributes: connection-info
+      - name: peer-device-info
+        type: nest
+        value: 18
+        nested-attributes: peer-device-info
+      - name: resource-statistics
+        type: nest
+        value: 19
+        nested-attributes: resource-statistics
+      - name: device-statistics
+        type: nest
+        value: 20
+        nested-attributes: device-statistics
+      - name: connection-statistics
+        type: nest
+        value: 21
+        nested-attributes: connection-statistics
+      - name: peer-device-statistics
+        type: nest
+        value: 22
+        nested-attributes: peer-device-statistics
+      - name: notification-header
+        type: nest
+        value: 23
+        nested-attributes: drbd-notification-header
+      - name: helper
+        type: nest
+        value: 24
+        nested-attributes: drbd-helper-info
+  -
+    name: drbd-cfg-reply
+    attributes:
+      - name: info-text
+        type: string
+        value: 1
+  -
+    name: drbd-cfg-context
+    attributes:
+      - name: ctx-volume
+        type: u32
+        value: 1
+      - name: ctx-resource-name
+        type: string
+        value: 2
+        checks:
+          max-len: 128
+      - name: ctx-my-addr
+        type: binary
+        value: 3
+        checks:
+          max-len: 128
+      - name: ctx-peer-addr
+        type: binary
+        value: 4
+        checks:
+          max-len: 128
+  -
+    name: disk-conf
+    attributes:
+      - name: backing-dev
+        type: string
+        value: 1
+        required: true
+        checks:
+          max-len: 128
+      - name: meta-dev
+        type: string
+        value: 2
+        required: true
+        checks:
+          max-len: 128
+      - name: meta-dev-idx
+        type: s32
+        value: 3
+        required: true
+        nla-policy-type: u32
+      - name: disk-size
+        type: u64
+        value: 4
+      - name: max-bio-bvecs
+        type: u32
+        value: 5
+      - name: on-io-error
+        type: u32
+        value: 6
+        default: DRBD_ON_IO_ERROR_DEF
+      - name: fencing
+        type: u32
+        value: 7
+        default: DRBD_FENCING_DEF
+      - name: resync-rate
+        type: u32
+        value: 8
+        default: DRBD_RESYNC_RATE_DEF
+      - name: resync-after
+        type: s32
+        value: 9
+        default: DRBD_MINOR_NUMBER_DEF
+        nla-policy-type: u32
+      - name: al-extents
+        type: u32
+        value: 10
+        default: DRBD_AL_EXTENTS_DEF
+      - name: c-plan-ahead
+        type: u32
+        value: 11
+        default: DRBD_C_PLAN_AHEAD_DEF
+      - name: c-delay-target
+        type: u32
+        value: 12
+        default: DRBD_C_DELAY_TARGET_DEF
+      - name: c-fill-target
+        type: u32
+        value: 13
+        default: DRBD_C_FILL_TARGET_DEF
+      - name: c-max-rate
+        type: u32
+        value: 14
+        default: DRBD_C_MAX_RATE_DEF
+      - name: c-min-rate
+        type: u32
+        value: 15
+        default: DRBD_C_MIN_RATE_DEF
+      - name: disk-barrier
+        type: u8
+        value: 16
+        default: DRBD_DISK_BARRIER_DEF
+      - name: disk-flushes
+        type: u8
+        value: 17
+        default: DRBD_DISK_FLUSHES_DEF
+      - name: disk-drain
+        type: u8
+        value: 18
+        default: DRBD_DISK_DRAIN_DEF
+      - name: md-flushes
+        type: u8
+        value: 19
+        default: DRBD_MD_FLUSHES_DEF
+      - name: disk-timeout
+        type: u32
+        value: 20
+        default: DRBD_DISK_TIMEOUT_DEF
+      - name: read-balancing
+        type: u32
+        value: 21
+        default: DRBD_READ_BALANCING_DEF
+      - name: al-updates
+        type: u8
+        value: 23
+        default: DRBD_AL_UPDATES_DEF
+      - name: discard-zeroes-if-aligned
+        type: u8
+        value: 24
+        default: DRBD_DISCARD_ZEROES_IF_ALIGNED_DEF
+      - name: rs-discard-granularity
+        type: u32
+        value: 25
+        default: DRBD_RS_DISCARD_GRANULARITY_DEF
+      - name: disable-write-same
+        type: u8
+        value: 26
+        default: DRBD_DISABLE_WRITE_SAME_DEF
+  -
+    name: res-opts
+    attributes:
+      - name: cpu-mask
+        type: string
+        value: 1
+        default: ""
+        checks:
+          max-len: DRBD_CPU_MASK_SIZE
+      - name: on-no-data
+        type: u32
+        value: 2
+        default: DRBD_ON_NO_DATA_DEF
+  -
+    name: net-conf
+    attributes:
+      - name: shared-secret
+        type: string
+        value: 1
+        default: ""
+        checks:
+          max-len: SHARED_SECRET_MAX
+      - name: cram-hmac-alg
+        type: string
+        value: 2
+        default: ""
+        checks:
+          max-len: SHARED_SECRET_MAX
+      - name: integrity-alg
+        type: string
+        value: 3
+        default: ""
+        checks:
+          max-len: SHARED_SECRET_MAX
+      - name: verify-alg
+        type: string
+        value: 4
+        default: ""
+        checks:
+          max-len: SHARED_SECRET_MAX
+      - name: csums-alg
+        type: string
+        value: 5
+        default: ""
+        checks:
+          max-len: SHARED_SECRET_MAX
+      - name: wire-protocol
+        type: u32
+        value: 6
+        default: DRBD_PROTOCOL_DEF
+      - name: connect-int
+        type: u32
+        value: 7
+        default: DRBD_CONNECT_INT_DEF
+      - name: timeout
+        type: u32
+        value: 8
+        default: DRBD_TIMEOUT_DEF
+      - name: ping-int
+        type: u32
+        value: 9
+        default: DRBD_PING_INT_DEF
+      - name: ping-timeo
+        type: u32
+        value: 10
+        default: DRBD_PING_TIMEO_DEF
+      - name: sndbuf-size
+        type: u32
+        value: 11
+        default: DRBD_SNDBUF_SIZE_DEF
+      - name: rcvbuf-size
+        type: u32
+        value: 12
+        default: DRBD_RCVBUF_SIZE_DEF
+      - name: ko-count
+        type: u32
+        value: 13
+        default: DRBD_KO_COUNT_DEF
+      - name: max-buffers
+        type: u32
+        value: 14
+        default: DRBD_MAX_BUFFERS_DEF
+      - name: max-epoch-size
+        type: u32
+        value: 15
+        default: DRBD_MAX_EPOCH_SIZE_DEF
+      - name: unplug-watermark
+        type: u32
+        value: 16
+        default: DRBD_UNPLUG_WATERMARK_DEF
+      - name: after-sb-0p
+        type: u32
+        value: 17
+        default: DRBD_AFTER_SB_0P_DEF
+      - name: after-sb-1p
+        type: u32
+        value: 18
+        default: DRBD_AFTER_SB_1P_DEF
+      - name: after-sb-2p
+        type: u32
+        value: 19
+        default: DRBD_AFTER_SB_2P_DEF
+      - name: rr-conflict
+        type: u32
+        value: 20
+        default: DRBD_RR_CONFLICT_DEF
+      - name: on-congestion
+        type: u32
+        value: 21
+        default: DRBD_ON_CONGESTION_DEF
+      - name: cong-fill
+        type: u32
+        value: 22
+        default: DRBD_CONG_FILL_DEF
+      - name: cong-extents
+        type: u32
+        value: 23
+        default: DRBD_CONG_EXTENTS_DEF
+      - name: two-primaries
+        type: u8
+        value: 24
+        default: DRBD_ALLOW_TWO_PRIMARIES_DEF
+      - name: discard-my-data
+        type: u8
+        value: 25
+      - name: tcp-cork
+        type: u8
+        value: 26
+        default: DRBD_TCP_CORK_DEF
+      - name: always-asbp
+        type: u8
+        value: 27
+        default: DRBD_ALWAYS_ASBP_DEF
+      - name: tentative
+        type: u8
+        value: 28
+      - name: use-rle
+        type: u8
+        value: 29
+        default: DRBD_USE_RLE_DEF
+      - name: csums-after-crash-only
+        type: u8
+        value: 33
+        default: DRBD_CSUMS_AFTER_CRASH_ONLY_DEF
+      - name: sock-check-timeo
+        type: u32
+        value: 34
+        default: DRBD_SOCKET_CHECK_TIMEO_DEF
+  -
+    name: set-role-parms
+    attributes:
+      - name: assume-uptodate
+        type: u8
+        value: 1
+  -
+    name: resize-parms
+    attributes:
+      - name: resize-size
+        type: u64
+        value: 1
+      - name: resize-force
+        type: u8
+        value: 2
+      - name: no-resync
+        type: u8
+        value: 3
+      - name: al-stripes
+        type: u32
+        value: 4
+        default: DRBD_AL_STRIPES_DEF
+      - name: al-stripe-size
+        type: u32
+        value: 5
+        default: DRBD_AL_STRIPE_SIZE_DEF
+  -
+    name: state-info
+    attributes:
+      - name: sib-reason
+        type: u32
+        value: 1
+      - name: current-state
+        type: u32
+        value: 2
+        required: true
+      - name: capacity
+        type: u64
+        value: 3
+      - name: ed-uuid
+        type: u64
+        value: 4
+      - name: prev-state
+        type: u32
+        value: 5
+      - name: new-state
+        type: u32
+        value: 6
+      - name: uuids
+        type: binary
+        value: 7
+        checks:
+          max-len: DRBD_NL_UUIDS_SIZE
+      - name: disk-flags
+        type: u32
+        value: 8
+      - name: bits-total
+        type: u64
+        value: 9
+      - name: bits-oos
+        type: u64
+        value: 10
+      - name: bits-rs-total
+        type: u64
+        value: 11
+      - name: bits-rs-failed
+        type: u64
+        value: 12
+      - name: helper
+        type: string
+        value: 13
+        checks:
+          max-len: 32
+      - name: helper-exit-code
+        type: u32
+        value: 14
+      - name: send-cnt
+        type: u64
+        value: 15
+      - name: recv-cnt
+        type: u64
+        value: 16
+      - name: read-cnt
+        type: u64
+        value: 17
+      - name: writ-cnt
+        type: u64
+        value: 18
+      - name: al-writ-cnt
+        type: u64
+        value: 19
+      - name: bm-writ-cnt
+        type: u64
+        value: 20
+      - name: ap-bio-cnt
+        type: u32
+        value: 21
+      - name: ap-pending-cnt
+        type: u32
+        value: 22
+      - name: rs-pending-cnt
+        type: u32
+        value: 23
+  -
+    name: start-ov-parms
+    attributes:
+      - name: ov-start-sector
+        type: u64
+        value: 1
+      - name: ov-stop-sector
+        type: u64
+        value: 2
+  -
+    name: new-c-uuid-parms
+    attributes:
+      - name: clear-bm
+        type: u8
+        value: 1
+  -
+    name: timeout-parms
+    attributes:
+      - name: timeout-type
+        type: u32
+        value: 1
+        required: true
+  -
+    name: disconnect-parms
+    attributes:
+      - name: force-disconnect
+        type: u8
+        value: 1
+  -
+    name: detach-parms
+    attributes:
+      - name: force-detach
+        type: u8
+        value: 1
+  -
+    name: resource-info
+    attributes:
+      - name: res-role
+        type: u32
+        value: 1
+      - name: res-susp
+        type: u8
+        value: 2
+      - name: res-susp-nod
+        type: u8
+        value: 3
+      - name: res-susp-fen
+        type: u8
+        value: 4
+  -
+    name: device-info
+    attributes:
+      - name: dev-disk-state
+        type: u32
+        value: 1
+  -
+    name: connection-info
+    attributes:
+      - name: conn-connection-state
+        type: u32
+        value: 1
+      - name: conn-role
+        type: u32
+        value: 2
+  -
+    name: peer-device-info
+    attributes:
+      - name: peer-repl-state
+        type: u32
+        value: 1
+      - name: peer-disk-state
+        type: u32
+        value: 2
+      - name: peer-resync-susp-user
+        type: u32
+        value: 3
+      - name: peer-resync-susp-peer
+        type: u32
+        value: 4
+      - name: peer-resync-susp-dependency
+        type: u32
+        value: 5
+  -
+    name: resource-statistics
+    attributes:
+      - name: res-stat-write-ordering
+        type: u32
+        value: 1
+  -
+    name: device-statistics
+    attributes:
+      - name: dev-size
+        type: u64
+        value: 1
+      - name: dev-read
+        type: u64
+        value: 2
+      - name: dev-write
+        type: u64
+        value: 3
+      - name: dev-al-writes
+        type: u64
+        value: 4
+      - name: dev-bm-writes
+        type: u64
+        value: 5
+      - name: dev-upper-pending
+        type: u32
+        value: 6
+      - name: dev-lower-pending
+        type: u32
+        value: 7
+      - name: dev-upper-blocked
+        type: u8
+        value: 8
+      - name: dev-lower-blocked
+        type: u8
+        value: 9
+      - name: dev-al-suspended
+        type: u8
+        value: 10
+      - name: dev-exposed-data-uuid
+        type: u64
+        value: 11
+      - name: dev-current-uuid
+        type: u64
+        value: 12
+      - name: dev-disk-flags
+        type: u32
+        value: 13
+      - name: history-uuids
+        type: binary
+        value: 14
+        checks:
+          max-len: DRBD_NL_HISTORY_UUIDS_SIZE
+  -
+    name: connection-statistics
+    attributes:
+      - name: conn-congested
+        type: u8
+        value: 1
+  -
+    name: peer-device-statistics
+    attributes:
+      - name: peer-dev-received
+        type: u64
+        value: 1
+      - name: peer-dev-sent
+        type: u64
+        value: 2
+      - name: peer-dev-pending
+        type: u32
+        value: 3
+      - name: peer-dev-unacked
+        type: u32
+        value: 4
+      - name: peer-dev-out-of-sync
+        type: u64
+        value: 5
+      - name: peer-dev-resync-failed
+        type: u64
+        value: 6
+      - name: peer-dev-bitmap-uuid
+        type: u64
+        value: 7
+      - name: peer-dev-flags
+        type: u32
+        value: 9
+  -
+    name: drbd-notification-header
+    attributes:
+      - name: nh-type
+        type: u32
+        value: 1
+  -
+    name: drbd-helper-info
+    attributes:
+      - name: helper-name
+        type: string
+        value: 1
+        checks:
+          max-len: 32
+      - name: helper-status
+        type: u32
+        value: 2
+
+operations:
+  name-prefix: drbd-adm-
+  fixed-header: drbd-genlmsghdr
+  list:
+    - name: event
+      doc: Legacy state broadcast (DRBD 8.4).
+      value: 1
+
+    - name: get-status
+      doc: Query status of a single device (by minor) or dump all.
+      value: 2
+      attribute-set: drbd
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+      dump:
+        request:
+          attributes:
+            - cfg-context
+    - name: new-minor
+      doc: Add a DRBD minor device as a volume to a resource.
+      value: 5
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: del-minor
+      doc: Delete a DRBD minor device.
+      value: 6
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: new-resource
+      doc: Create a new DRBD resource.
+      value: 7
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - resource-opts
+    - name: del-resource
+      doc: Delete a DRBD resource.
+      value: 8
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: resource-opts
+      doc: Change resource options.
+      value: 9
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - resource-opts
+    - name: connect
+      doc: Connect to a peer.
+      value: 10
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - net-conf
+    - name: disconnect
+      doc: Disconnect from a peer.
+      value: 11
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - disconnect-parms
+    - name: attach
+      doc: Attach a local backing device to a DRBD minor.
+      value: 12
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - disk-conf
+    - name: resize
+      doc: Resize the DRBD device.
+      value: 13
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - resize-parms
+    - name: primary
+      doc: Promote a device to primary role.
+      value: 14
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - set-role-parms
+    - name: secondary
+      doc: Demote a device to secondary role.
+      value: 15
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - set-role-parms
+    - name: new-c-uuid
+      doc: Generate a new current UUID.
+      value: 16
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - new-c-uuid-parms
+    - name: start-ov
+      doc: Start online verification.
+      value: 17
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - start-ov-parms
+    - name: detach
+      doc: Detach the local backing device.
+      value: 18
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - detach-parms
+    - name: invalidate
+      doc: Invalidate local data, start full resync as sync target.
+      value: 19
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: inval-peer
+      doc: Invalidate peer data, start full resync as sync source.
+      value: 20
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: pause-sync
+      doc: Pause resynchronization.
+      value: 21
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: resume-sync
+      doc: Resume resynchronization.
+      value: 22
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: suspend-io
+      doc: Suspend I/O on a device.
+      value: 23
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: resume-io
+      doc: Resume I/O on a device.
+      value: 24
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: outdate
+      doc: Mark a device as outdated.
+      value: 25
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: get-timeout-type
+      doc: Query the current timeout type.
+      value: 26
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: down
+      doc: Shut down a DRBD resource (demote, disconnect, detach, delete).
+      value: 27
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+    - name: chg-disk-opts
+      doc: Change disk options on an attached device.
+      value: 28
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - disk-conf
+    - name: chg-net-opts
+      doc: Change network options on a connection.
+      value: 29
+      attribute-set: drbd
+      flags:
+        - admin-perm
+      do:
+        pre: drbd-pre-doit
+        post: drbd-post-doit
+        request:
+          attributes:
+            - cfg-context
+            - net-conf
+    - name: get-resources
+      doc: Dump all resources with info and statistics.
+      value: 30
+      attribute-set: drbd
+      dump:
+        request:
+          attributes:
+            - cfg-context
+            - resource-info
+            - resource-statistics
+    - name: get-devices
+      doc: Dump all devices with info and statistics.
+      value: 31
+      attribute-set: drbd
+      dump:
+        request:
+          attributes:
+            - cfg-context
+            - device-info
+            - device-statistics
+        post: drbd-adm-dump-devices-done
+    - name: get-connections
+      doc: Dump all connections with info and statistics.
+      value: 32
+      attribute-set: drbd
+      dump:
+        request:
+          attributes:
+            - cfg-context
+            - connection-info
+            - connection-statistics
+        post: drbd-adm-dump-connections-done
+    - name: get-peer-devices
+      doc: Dump all peer devices with info and statistics.
+      value: 33
+      attribute-set: drbd
+      dump:
+        request:
+          attributes:
+            - cfg-context
+            - peer-device-info
+            - peer-device-statistics
+        post: drbd-adm-dump-peer-devices-done
+    - name: resource-state
+      doc: Resource state change notification.
+      value: 34
+
+    - name: device-state
+      doc: Device state change notification.
+      value: 35
+
+    - name: connection-state
+      doc: Connection state change notification.
+      value: 36
+
+    - name: peer-device-state
+      doc: Peer device state change notification.
+      value: 37
+
+    - name: get-initial-state
+      doc: Dump current state of all objects as a notification stream.
+      value: 38
+      attribute-set: drbd
+      dump:
+        request:
+          attributes:
+            - cfg-context
+    - name: helper
+      doc: Helper invocation notification.
+      value: 40
+
+    - name: initial-state-done
+      doc: Signals end of initial state dump.
+      value: 41
+
+
+mcast-groups:
+  list:
+    - name: events
diff --git a/include/uapi/linux/drbd.h b/include/uapi/linux/drbd.h
index 7930a972d8a4..5d4d677cf1ad 100644
--- a/include/uapi/linux/drbd.h
+++ b/include/uapi/linux/drbd.h
@@ -333,6 +333,9 @@ enum drbd_uuid_index {
 
 #define HISTORY_UUIDS MAX_PEERS
 
+#define DRBD_NL_UUIDS_SIZE		(UI_SIZE * sizeof(__u64))
+#define DRBD_NL_HISTORY_UUIDS_SIZE	(HISTORY_UUIDS * sizeof(__u64))
+
 enum drbd_timeout_flag {
 	UT_DEFAULT      = 0,
 	UT_DEGRADED     = 1,
-- 
2.53.0


^ permalink raw reply related

* [PATCH 4/4] drbd: switch from genl_magic macros to YNL-generated code
From: Christoph Böhmwalder @ 2026-04-07 17:33 UTC (permalink / raw)
  To: Jens Axboe
  Cc: drbd-dev, linux-kernel, Lars Ellenberg, Philipp Reisner,
	linux-block, Donald Hunter, Eric Dumazet, Jakub Kicinski, netdev,
	Christoph Böhmwalder
In-Reply-To: <20260407173356.873887-1-christoph.boehmwalder@linbit.com>

Replace the genl_magic multi-include macro system with proper YNL
generation.

The bulk of the changes are mechanical renames to fit the generator
naming conventions:
  - Handler functions: drbd_adm_* -> drbd_nl_*_doit/dumpit
  - GENL_MAGIC_VERSION -> DRBD_FAMILY_VERSION
  - GENL_MAGIC_FAMILY_HDRSZ -> sizeof(struct drbd_genlmsghdr)
  - drbd_genl_family -> drbd_nl_family
  - Attribute IDs: T_* -> DRBD_A_*

Remove the nested_attr_tb static global buffer and move to a per-call
allocation approach: each deserialization manages its own nested
attribute table. This will be needed anyway when we eventually move to
parallel_ops, and it's actually simpler to generate, so make this move
now.

Replace the functionality of the "sensitive" flag: this was only used
by a single field (shared_secret), open-code redaction logic for that
locally.

Also replace the "invariant" flag: this only had a couple of users, and
those basically never change. Hard code the check directly inline.

Define the genl_family struct manually in drbd_nl.c; this is not
emitted by the generator for genetlink-legacy families.

Also replace a couple of drbd-specific wrappers (nla_put_u64_0pad,
drbd_nla_find_nested) and replace by standard kernel functions while
we're at it.

Finally, completely remove the genl_magic system; DRBD was its only
user.

Signed-off-by: Christoph Böhmwalder <christoph.boehmwalder@linbit.com>
---
 drivers/block/drbd/Makefile       |    1 +
 drivers/block/drbd/drbd_debugfs.c |    2 +-
 drivers/block/drbd/drbd_int.h     |    4 +-
 drivers/block/drbd/drbd_main.c    |    6 +-
 drivers/block/drbd/drbd_nl.c      |  416 +++--
 drivers/block/drbd/drbd_nl_gen.c  | 2610 +++++++++++++++++++++++++++++
 drivers/block/drbd/drbd_nl_gen.h  |  399 +++++
 drivers/block/drbd/drbd_proc.c    |    2 +-
 include/linux/drbd_genl.h         |  536 ------
 include/linux/drbd_genl_api.h     |   16 -
 include/linux/genl_magic_func.h   |  413 -----
 include/linux/genl_magic_struct.h |  272 ---
 include/uapi/linux/drbd_genl.h    |  363 ++++
 13 files changed, 3618 insertions(+), 1422 deletions(-)
 create mode 100644 drivers/block/drbd/drbd_nl_gen.c
 create mode 100644 drivers/block/drbd/drbd_nl_gen.h
 delete mode 100644 include/linux/drbd_genl.h
 delete mode 100644 include/linux/drbd_genl_api.h
 delete mode 100644 include/linux/genl_magic_func.h
 delete mode 100644 include/linux/genl_magic_struct.h
 create mode 100644 include/uapi/linux/drbd_genl.h

diff --git a/drivers/block/drbd/Makefile b/drivers/block/drbd/Makefile
index 187eaf81f0f8..5faaa8a8e7f0 100644
--- a/drivers/block/drbd/Makefile
+++ b/drivers/block/drbd/Makefile
@@ -3,6 +3,7 @@ drbd-y := drbd_buildtag.o drbd_bitmap.o drbd_proc.o
 drbd-y += drbd_worker.o drbd_receiver.o drbd_req.o drbd_actlog.o
 drbd-y += drbd_main.o drbd_strings.o drbd_nl.o
 drbd-y += drbd_interval.o drbd_state.o
+drbd-y += drbd_nl_gen.o
 drbd-$(CONFIG_DEBUG_FS) += drbd_debugfs.o
 
 obj-$(CONFIG_BLK_DEV_DRBD)     += drbd.o
diff --git a/drivers/block/drbd/drbd_debugfs.c b/drivers/block/drbd/drbd_debugfs.c
index 12460b584bcb..371abcd7e880 100644
--- a/drivers/block/drbd/drbd_debugfs.c
+++ b/drivers/block/drbd/drbd_debugfs.c
@@ -844,7 +844,7 @@ static int drbd_version_show(struct seq_file *m, void *ignored)
 {
 	seq_printf(m, "# %s\n", drbd_buildtag());
 	seq_printf(m, "VERSION=%s\n", REL_VERSION);
-	seq_printf(m, "API_VERSION=%u\n", GENL_MAGIC_VERSION);
+	seq_printf(m, "API_VERSION=%u\n", DRBD_FAMILY_VERSION);
 	seq_printf(m, "PRO_VERSION_MIN=%u\n", PRO_VERSION_MIN);
 	seq_printf(m, "PRO_VERSION_MAX=%u\n", PRO_VERSION_MAX);
 	return 0;
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index f3d746a6d6fd..48b45c3142f7 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -32,14 +32,16 @@
 #include <net/tcp.h>
 #include <linux/lru_cache.h>
 #include <linux/prefetch.h>
-#include <linux/drbd_genl_api.h>
 #include <linux/drbd.h>
 #include "drbd_config.h"
+#include "drbd_nl_gen.h"
 #include "drbd_strings.h"
 #include "drbd_state.h"
 #include "drbd_protocol.h"
 #include "drbd_polymorph_printk.h"
 
+extern struct genl_family drbd_nl_family;
+
 /* shared module parameters, defined in drbd_main.c */
 #ifdef CONFIG_DRBD_FAULT_INJECTION
 extern int drbd_enable_faults;
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 200d464e984b..a18554ff6314 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -2324,7 +2324,7 @@ static void drbd_cleanup(void)
 	if (retry.wq)
 		destroy_workqueue(retry.wq);
 
-	drbd_genl_unregister();
+	genl_unregister_family(&drbd_nl_family);
 
 	idr_for_each_entry(&drbd_devices, device, i)
 		drbd_delete_device(device);
@@ -2846,7 +2846,7 @@ static int __init drbd_init(void)
 	mutex_init(&resources_mutex);
 	INIT_LIST_HEAD(&drbd_resources);
 
-	err = drbd_genl_register();
+	err = genl_register_family(&drbd_nl_family);
 	if (err) {
 		pr_err("unable to register generic netlink family\n");
 		goto fail;
@@ -2876,7 +2876,7 @@ static int __init drbd_init(void)
 
 	pr_info("initialized. "
 	       "Version: " REL_VERSION " (api:%d/proto:%d-%d)\n",
-	       GENL_MAGIC_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX);
+	       DRBD_FAMILY_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX);
 	pr_info("%s\n", drbd_buildtag());
 	pr_info("registered as block device major %d\n", DRBD_MAJOR);
 	return 0; /* Success! */
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index d997d274092c..00e402808cdb 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -31,59 +31,13 @@
 
 #include <net/genetlink.h>
 
-/* .doit */
-// int drbd_adm_create_resource(struct sk_buff *skb, struct genl_info *info);
-// int drbd_adm_delete_resource(struct sk_buff *skb, struct genl_info *info);
-
-int drbd_adm_new_minor(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_del_minor(struct sk_buff *skb, struct genl_info *info);
-
-int drbd_adm_new_resource(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_del_resource(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_down(struct sk_buff *skb, struct genl_info *info);
-
-int drbd_adm_set_role(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_detach(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_net_opts(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_resize(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_start_ov(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_new_c_uuid(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_disconnect(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_invalidate(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_invalidate_peer(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_pause_sync(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_resume_sync(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_suspend_io(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_resume_io(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_outdate(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_resource_opts(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_get_status(struct sk_buff *skb, struct genl_info *info);
-int drbd_adm_get_timeout_type(struct sk_buff *skb, struct genl_info *info);
-/* .dumpit */
-int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb);
-int drbd_adm_dump_resources(struct sk_buff *skb, struct netlink_callback *cb);
-int drbd_adm_dump_devices(struct sk_buff *skb, struct netlink_callback *cb);
-int drbd_adm_dump_devices_done(struct netlink_callback *cb);
-int drbd_adm_dump_connections(struct sk_buff *skb, struct netlink_callback *cb);
-int drbd_adm_dump_connections_done(struct netlink_callback *cb);
-int drbd_adm_dump_peer_devices(struct sk_buff *skb, struct netlink_callback *cb);
-int drbd_adm_dump_peer_devices_done(struct netlink_callback *cb);
-int drbd_adm_get_initial_state(struct sk_buff *skb, struct netlink_callback *cb);
-
-#include <linux/drbd_genl_api.h>
-
-static int drbd_pre_doit(const struct genl_split_ops *ops,
-			 struct sk_buff *skb, struct genl_info *info);
-static void drbd_post_doit(const struct genl_split_ops *ops,
-			   struct sk_buff *skb, struct genl_info *info);
-
-#define GENL_MAGIC_FAMILY_PRE_DOIT	drbd_pre_doit
-#define GENL_MAGIC_FAMILY_POST_DOIT	drbd_post_doit
-
-#include <linux/genl_magic_func.h>
+#include "drbd_nl_gen.h"
+
+static int drbd_genl_multicast_events(struct sk_buff *skb, gfp_t flags)
+{
+	return genlmsg_multicast(&drbd_nl_family, skb, 0,
+				 DRBD_NLGRP_EVENTS, flags);
+}
 
 static atomic_t drbd_genl_seq = ATOMIC_INIT(2); /* two. */
 static atomic_t notify_genl_seq = ATOMIC_INIT(2); /* two. */
@@ -114,7 +68,7 @@ static int drbd_msg_put_info(struct sk_buff *skb, const char *info)
 	if (!nla)
 		return err;
 
-	err = nla_put_string(skb, T_info_text, info);
+	err = nla_put_string(skb, DRBD_A_DRBD_CFG_REPLY_INFO_TEXT, info);
 	if (err) {
 		nla_nest_cancel(skb, nla);
 		return err;
@@ -135,7 +89,7 @@ static int drbd_msg_sprintf_info(struct sk_buff *skb, const char *fmt, ...)
 	if (!nla)
 		return err;
 
-	txt = nla_reserve(skb, T_info_text, 256);
+	txt = nla_reserve(skb, DRBD_A_DRBD_CFG_REPLY_INFO_TEXT, 256);
 	if (!txt) {
 		nla_nest_cancel(skb, nla);
 		return err;
@@ -187,6 +141,15 @@ static const unsigned int drbd_genl_cmd_flags[] = {
 	[DRBD_ADM_DOWN]           = DRBD_ADM_NEED_RESOURCE,
 };
 
+/* Detect attempts to change invariant attributes in a _change_ handler. */
+#define has_invariant(ntb, attr)						\
+({									\
+	bool __found = !!(ntb)[attr];					\
+	if (__found)							\
+		pr_info("must not change invariant attr: %s\n", #attr);	\
+	__found;							\
+})
+
 /*
  * At this point, we still rely on the global genl_lock().
  * If we want to avoid that, and allow "genl_family.parallel_ops", we may need
@@ -210,7 +173,7 @@ static int drbd_adm_prepare(struct drbd_config_context *adm_ctx,
 	}
 
 	adm_ctx->reply_dh = genlmsg_put_reply(adm_ctx->reply_skb,
-					info, &drbd_genl_family, 0, cmd);
+					info, &drbd_nl_family, 0, cmd);
 	/* put of a few bytes into a fresh skb of >= 4k will always succeed.
 	 * but anyways */
 	if (!adm_ctx->reply_dh) {
@@ -223,9 +186,11 @@ static int drbd_adm_prepare(struct drbd_config_context *adm_ctx,
 
 	adm_ctx->volume = VOLUME_UNSPECIFIED;
 	if (info->attrs[DRBD_NLA_CFG_CONTEXT]) {
+		struct nlattr **ntb;
 		struct nlattr *nla;
-		/* parse and validate only */
-		err = drbd_cfg_context_from_attrs(NULL, info);
+
+		/* parse and validate, get nested attribute table */
+		err = drbd_cfg_context_ntb_from_attrs(&ntb, info);
 		if (err)
 			goto fail;
 
@@ -234,18 +199,21 @@ static int drbd_adm_prepare(struct drbd_config_context *adm_ctx,
 		err = nla_put_nohdr(adm_ctx->reply_skb,
 				info->attrs[DRBD_NLA_CFG_CONTEXT]->nla_len,
 				info->attrs[DRBD_NLA_CFG_CONTEXT]);
-		if (err)
+		if (err) {
+			kfree(ntb);
 			goto fail;
+		}
 
 		/* and assign stuff to the adm_ctx */
-		nla = nested_attr_tb[T_ctx_volume];
+		nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME];
 		if (nla)
 			adm_ctx->volume = nla_get_u32(nla);
-		nla = nested_attr_tb[T_ctx_resource_name];
+		nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME];
 		if (nla)
 			adm_ctx->resource_name = nla_data(nla);
-		adm_ctx->my_addr = nested_attr_tb[T_ctx_my_addr];
-		adm_ctx->peer_addr = nested_attr_tb[T_ctx_peer_addr];
+		adm_ctx->my_addr = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR];
+		adm_ctx->peer_addr = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR];
+		kfree(ntb);
 		if ((adm_ctx->my_addr &&
 		     nla_len(adm_ctx->my_addr) > sizeof(adm_ctx->connection->my_addr)) ||
 		    (adm_ctx->peer_addr &&
@@ -259,7 +227,7 @@ static int drbd_adm_prepare(struct drbd_config_context *adm_ctx,
 	adm_ctx->device = minor_to_device(d_in->minor);
 
 	/* We are protected by the global genl_lock().
-	 * But we may explicitly drop it/retake it in drbd_adm_set_role(),
+	 * But we may explicitly drop it/retake it in drbd_nl_set_role(),
 	 * so make sure this object stays around. */
 	if (adm_ctx->device)
 		kref_get(&adm_ctx->device->kref);
@@ -334,8 +302,8 @@ static int drbd_adm_prepare(struct drbd_config_context *adm_ctx,
 	return err;
 }
 
-static int drbd_pre_doit(const struct genl_split_ops *ops,
-			 struct sk_buff *skb, struct genl_info *info)
+int drbd_pre_doit(const struct genl_split_ops *ops,
+		  struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx;
 	u8 cmd = info->genlhdr->cmd;
@@ -362,8 +330,8 @@ static int drbd_pre_doit(const struct genl_split_ops *ops,
 	return 0;
 }
 
-static void drbd_post_doit(const struct genl_split_ops *ops,
-			   struct sk_buff *skb, struct genl_info *info)
+void drbd_post_doit(const struct genl_split_ops *ops,
+		    struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 
@@ -828,7 +796,7 @@ static const char *from_attrs_err_to_txt(int err)
 		"invalid attribute value";
 }
 
-int drbd_adm_set_role(struct sk_buff *skb, struct genl_info *info)
+static int drbd_nl_set_role(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct set_role_parms parms;
@@ -868,6 +836,16 @@ int drbd_adm_set_role(struct sk_buff *skb, struct genl_info *info)
 	return 0;
 }
 
+int drbd_nl_primary_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return drbd_nl_set_role(skb, info);
+}
+
+int drbd_nl_secondary_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	return drbd_nl_set_role(skb, info);
+}
+
 /* Initializes the md.*_offset members, so we are able to find
  * the on disk meta data.
  *
@@ -962,7 +940,7 @@ char *ppsize(char *buf, unsigned long long size)
  *  peer may not initiate a resize.
  */
 /* Note these are not to be confused with
- * drbd_adm_suspend_io/drbd_adm_resume_io,
+ * drbd_nl_suspend_io_doit/drbd_nl_resume_io_doit,
  * which are (sub) state changes triggered by admin (drbdsetup),
  * and can be long lived.
  * This changes an device->flag, is triggered by drbd internals,
@@ -1574,13 +1552,14 @@ static int disk_opts_check_al_size(struct drbd_device *device, struct disk_conf
 	return err;
 }
 
-int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_chg_disk_opts_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	enum drbd_ret_code retcode;
 	struct drbd_device *device;
 	struct disk_conf *new_disk_conf, *old_disk_conf;
 	struct fifo_buffer *old_plan = NULL, *new_plan = NULL;
+	struct nlattr **ntb;
 	int err;
 	unsigned int fifo_size;
 
@@ -1612,13 +1591,29 @@ int drbd_adm_disk_opts(struct sk_buff *skb, struct genl_info *info)
 	if (should_set_defaults(info))
 		set_disk_conf_defaults(new_disk_conf);
 
-	err = disk_conf_from_attrs_for_change(new_disk_conf, info);
+	err = disk_conf_from_attrs(new_disk_conf, info);
 	if (err && err != -ENOMSG) {
 		retcode = ERR_MANDATORY_TAG;
 		drbd_msg_put_info(adm_ctx->reply_skb, from_attrs_err_to_txt(err));
 		goto fail_unlock;
 	}
 
+	err = disk_conf_ntb_from_attrs(&ntb, info);
+	if (!err) {
+		if (has_invariant(ntb, DRBD_A_DISK_CONF_BACKING_DEV) ||
+		    has_invariant(ntb, DRBD_A_DISK_CONF_META_DEV) ||
+		    has_invariant(ntb, DRBD_A_DISK_CONF_META_DEV_IDX) ||
+		    has_invariant(ntb, DRBD_A_DISK_CONF_DISK_SIZE) ||
+		    has_invariant(ntb, DRBD_A_DISK_CONF_MAX_BIO_BVECS)) {
+			retcode = ERR_MANDATORY_TAG;
+			drbd_msg_put_info(adm_ctx->reply_skb,
+				"cannot change invariant setting");
+			kfree(ntb);
+			goto fail_unlock;
+		}
+		kfree(ntb);
+	}
+
 	if (!expect(device, new_disk_conf->resync_rate >= 1))
 		new_disk_conf->resync_rate = 1;
 
@@ -1796,7 +1791,7 @@ void drbd_backing_dev_free(struct drbd_device *device, struct drbd_backing_dev *
 	kfree(ldev);
 }
 
-int drbd_adm_attach(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_attach_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct drbd_device *device;
@@ -2236,7 +2231,7 @@ static int adm_detach(struct drbd_device *device, int force)
  * Then we transition to D_DISKLESS, and wait for put_ldev() to return all
  * internal references as well.
  * Only then we have finally detached. */
-int drbd_adm_detach(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_detach_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	enum drbd_ret_code retcode;
@@ -2434,12 +2429,13 @@ static void free_crypto(struct crypto *crypto)
 	crypto_free_shash(crypto->verify_tfm);
 }
 
-int drbd_adm_net_opts(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_chg_net_opts_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	enum drbd_ret_code retcode;
 	struct drbd_connection *connection;
 	struct net_conf *old_net_conf, *new_net_conf = NULL;
+	struct nlattr **ntb;
 	int err;
 	int ovr; /* online verify running */
 	int rsr; /* re-sync running */
@@ -2476,13 +2472,26 @@ int drbd_adm_net_opts(struct sk_buff *skb, struct genl_info *info)
 	if (should_set_defaults(info))
 		set_net_conf_defaults(new_net_conf);
 
-	err = net_conf_from_attrs_for_change(new_net_conf, info);
+	err = net_conf_from_attrs(new_net_conf, info);
 	if (err && err != -ENOMSG) {
 		retcode = ERR_MANDATORY_TAG;
 		drbd_msg_put_info(adm_ctx->reply_skb, from_attrs_err_to_txt(err));
 		goto fail;
 	}
 
+	err = net_conf_ntb_from_attrs(&ntb, info);
+	if (!err) {
+		if (has_invariant(ntb, DRBD_A_NET_CONF_DISCARD_MY_DATA) ||
+		    has_invariant(ntb, DRBD_A_NET_CONF_TENTATIVE)) {
+			retcode = ERR_MANDATORY_TAG;
+			drbd_msg_put_info(adm_ctx->reply_skb,
+				"cannot change invariant setting");
+			kfree(ntb);
+			goto fail;
+		}
+		kfree(ntb);
+	}
+
 	retcode = check_net_options(connection, new_net_conf);
 	if (retcode != NO_ERROR)
 		goto fail;
@@ -2575,7 +2584,7 @@ static void peer_device_to_info(struct peer_device_info *info,
 	info->peer_resync_susp_dependency = device->state.aftr_isp;
 }
 
-int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_connect_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct connection_info connection_info;
 	enum drbd_notification_type flags;
@@ -2790,7 +2799,7 @@ static enum drbd_state_rv conn_try_disconnect(struct drbd_connection *connection
 	return rv;
 }
 
-int drbd_adm_disconnect(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_disconnect_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct disconnect_parms parms;
@@ -2845,7 +2854,7 @@ void resync_after_online_grow(struct drbd_device *device)
 		_drbd_request_state(device, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE + CS_SERIALIZE);
 }
 
-int drbd_adm_resize(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_resize_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct disk_conf *old_disk_conf, *new_disk_conf = NULL;
@@ -2981,7 +2990,7 @@ int drbd_adm_resize(struct sk_buff *skb, struct genl_info *info)
 	goto fail;
 }
 
-int drbd_adm_resource_opts(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_resource_opts_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	enum drbd_ret_code retcode;
@@ -3019,7 +3028,7 @@ int drbd_adm_resource_opts(struct sk_buff *skb, struct genl_info *info)
 	return 0;
 }
 
-int drbd_adm_invalidate(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_invalidate_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct drbd_device *device;
@@ -3097,7 +3106,7 @@ static int drbd_bmio_set_susp_al(struct drbd_device *device,
 	return rv;
 }
 
-int drbd_adm_invalidate_peer(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_inval_peer_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	int retcode; /* drbd_ret_code, drbd_state_rv */
@@ -3148,7 +3157,7 @@ int drbd_adm_invalidate_peer(struct sk_buff *skb, struct genl_info *info)
 	return 0;
 }
 
-int drbd_adm_pause_sync(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_pause_sync_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	enum drbd_ret_code retcode;
@@ -3168,7 +3177,7 @@ int drbd_adm_pause_sync(struct sk_buff *skb, struct genl_info *info)
 	return 0;
 }
 
-int drbd_adm_resume_sync(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_resume_sync_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	union drbd_dev_state s;
@@ -3196,12 +3205,12 @@ int drbd_adm_resume_sync(struct sk_buff *skb, struct genl_info *info)
 	return 0;
 }
 
-int drbd_adm_suspend_io(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_suspend_io_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	return drbd_adm_simple_request_state(skb, info, NS(susp, 1));
 }
 
-int drbd_adm_resume_io(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_resume_io_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct drbd_device *device;
@@ -3257,7 +3266,7 @@ int drbd_adm_resume_io(struct sk_buff *skb, struct genl_info *info)
 	return 0;
 }
 
-int drbd_adm_outdate(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_outdate_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED));
 }
@@ -3272,16 +3281,20 @@ static int nla_put_drbd_cfg_context(struct sk_buff *skb,
 	if (!nla)
 		goto nla_put_failure;
 	if (device &&
-	    nla_put_u32(skb, T_ctx_volume, device->vnr))
+	    nla_put_u32(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME, device->vnr))
 		goto nla_put_failure;
-	if (nla_put_string(skb, T_ctx_resource_name, resource->name))
+	if (nla_put_string(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME, resource->name))
 		goto nla_put_failure;
 	if (connection) {
 		if (connection->my_addr_len &&
-		    nla_put(skb, T_ctx_my_addr, connection->my_addr_len, &connection->my_addr))
+		    nla_put(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR,
+			    connection->my_addr_len,
+			    &connection->my_addr))
 			goto nla_put_failure;
 		if (connection->peer_addr_len &&
-		    nla_put(skb, T_ctx_peer_addr, connection->peer_addr_len, &connection->peer_addr))
+		    nla_put(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR,
+			    connection->peer_addr_len,
+			    &connection->peer_addr))
 			goto nla_put_failure;
 	}
 	nla_nest_end(skb, nla);
@@ -3300,7 +3313,7 @@ static int nla_put_drbd_cfg_context(struct sk_buff *skb,
  */
 static struct nlattr *find_cfg_context_attr(const struct nlmsghdr *nlh, int attr)
 {
-	const unsigned hdrlen = GENL_HDRLEN + GENL_MAGIC_FAMILY_HDRSZ;
+	const unsigned int hdrlen = GENL_HDRLEN + sizeof(struct drbd_genlmsghdr);
 	struct nlattr *nla;
 
 	nla = nla_find(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen),
@@ -3312,7 +3325,7 @@ static struct nlattr *find_cfg_context_attr(const struct nlmsghdr *nlh, int attr
 
 static void resource_to_info(struct resource_info *, struct drbd_resource *);
 
-int drbd_adm_dump_resources(struct sk_buff *skb, struct netlink_callback *cb)
+int drbd_nl_get_resources_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct drbd_genlmsghdr *dh;
 	struct drbd_resource *resource;
@@ -3340,7 +3353,7 @@ int drbd_adm_dump_resources(struct sk_buff *skb, struct netlink_callback *cb)
 
 put_result:
 	dh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
-			cb->nlh->nlmsg_seq, &drbd_genl_family,
+			cb->nlh->nlmsg_seq, &drbd_nl_family,
 			NLM_F_MULTI, DRBD_ADM_GET_RESOURCES);
 	err = -ENOMEM;
 	if (!dh)
@@ -3350,15 +3363,15 @@ int drbd_adm_dump_resources(struct sk_buff *skb, struct netlink_callback *cb)
 	err = nla_put_drbd_cfg_context(skb, resource, NULL, NULL);
 	if (err)
 		goto out;
-	err = res_opts_to_skb(skb, &resource->res_opts, !capable(CAP_SYS_ADMIN));
+	err = res_opts_to_skb(skb, &resource->res_opts);
 	if (err)
 		goto out;
 	resource_to_info(&resource_info, resource);
-	err = resource_info_to_skb(skb, &resource_info, !capable(CAP_SYS_ADMIN));
+	err = resource_info_to_skb(skb, &resource_info);
 	if (err)
 		goto out;
 	resource_statistics.res_stat_write_ordering = resource->write_ordering;
-	err = resource_statistics_to_skb(skb, &resource_statistics, !capable(CAP_SYS_ADMIN));
+	err = resource_statistics_to_skb(skb, &resource_statistics);
 	if (err)
 		goto out;
 	cb->args[0] = (long)resource;
@@ -3423,7 +3436,7 @@ int drbd_adm_dump_devices_done(struct netlink_callback *cb) {
 
 static void device_to_info(struct device_info *, struct drbd_device *);
 
-int drbd_adm_dump_devices(struct sk_buff *skb, struct netlink_callback *cb)
+int drbd_nl_get_devices_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct nlattr *resource_filter;
 	struct drbd_resource *resource;
@@ -3436,7 +3449,8 @@ int drbd_adm_dump_devices(struct sk_buff *skb, struct netlink_callback *cb)
 
 	resource = (struct drbd_resource *)cb->args[0];
 	if (!cb->args[0] && !cb->args[1]) {
-		resource_filter = find_cfg_context_attr(cb->nlh, T_ctx_resource_name);
+		resource_filter = find_cfg_context_attr(cb->nlh,
+				DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME);
 		if (resource_filter) {
 			retcode = ERR_RES_NOT_KNOWN;
 			resource = drbd_find_resource(nla_data(resource_filter));
@@ -3465,7 +3479,7 @@ int drbd_adm_dump_devices(struct sk_buff *skb, struct netlink_callback *cb)
 
 put_result:
 	dh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
-			cb->nlh->nlmsg_seq, &drbd_genl_family,
+			cb->nlh->nlmsg_seq, &drbd_nl_family,
 			NLM_F_MULTI, DRBD_ADM_GET_DEVICES);
 	err = -ENOMEM;
 	if (!dh)
@@ -3481,18 +3495,18 @@ int drbd_adm_dump_devices(struct sk_buff *skb, struct netlink_callback *cb)
 			struct disk_conf *disk_conf =
 				rcu_dereference(device->ldev->disk_conf);
 
-			err = disk_conf_to_skb(skb, disk_conf, !capable(CAP_SYS_ADMIN));
+			err = disk_conf_to_skb(skb, disk_conf);
 			put_ldev(device);
 			if (err)
 				goto out;
 		}
 		device_to_info(&device_info, device);
-		err = device_info_to_skb(skb, &device_info, !capable(CAP_SYS_ADMIN));
+		err = device_info_to_skb(skb, &device_info);
 		if (err)
 			goto out;
 
 		device_to_statistics(&device_statistics, device);
-		err = device_statistics_to_skb(skb, &device_statistics, !capable(CAP_SYS_ADMIN));
+		err = device_statistics_to_skb(skb, &device_statistics);
 		if (err)
 			goto out;
 		cb->args[1] = minor + 1;
@@ -3514,7 +3528,7 @@ int drbd_adm_dump_connections_done(struct netlink_callback *cb)
 
 enum { SINGLE_RESOURCE, ITERATE_RESOURCES };
 
-int drbd_adm_dump_connections(struct sk_buff *skb, struct netlink_callback *cb)
+int drbd_nl_get_connections_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct nlattr *resource_filter;
 	struct drbd_resource *resource = NULL, *next_resource;
@@ -3527,7 +3541,8 @@ int drbd_adm_dump_connections(struct sk_buff *skb, struct netlink_callback *cb)
 	rcu_read_lock();
 	resource = (struct drbd_resource *)cb->args[0];
 	if (!cb->args[0]) {
-		resource_filter = find_cfg_context_attr(cb->nlh, T_ctx_resource_name);
+		resource_filter = find_cfg_context_attr(cb->nlh,
+				DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME);
 		if (resource_filter) {
 			retcode = ERR_RES_NOT_KNOWN;
 			resource = drbd_find_resource(nla_data(resource_filter));
@@ -3591,7 +3606,7 @@ int drbd_adm_dump_connections(struct sk_buff *skb, struct netlink_callback *cb)
 
 put_result:
 	dh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
-			cb->nlh->nlmsg_seq, &drbd_genl_family,
+			cb->nlh->nlmsg_seq, &drbd_nl_family,
 			NLM_F_MULTI, DRBD_ADM_GET_CONNECTIONS);
 	err = -ENOMEM;
 	if (!dh)
@@ -3606,16 +3621,16 @@ int drbd_adm_dump_connections(struct sk_buff *skb, struct netlink_callback *cb)
 			goto out;
 		net_conf = rcu_dereference(connection->net_conf);
 		if (net_conf) {
-			err = net_conf_to_skb(skb, net_conf, !capable(CAP_SYS_ADMIN));
+			err = net_conf_to_skb(skb, net_conf);
 			if (err)
 				goto out;
 		}
 		connection_to_info(&connection_info, connection);
-		err = connection_info_to_skb(skb, &connection_info, !capable(CAP_SYS_ADMIN));
+		err = connection_info_to_skb(skb, &connection_info);
 		if (err)
 			goto out;
 		connection_statistics.conn_congested = test_bit(NET_CONGESTED, &connection->flags);
-		err = connection_statistics_to_skb(skb, &connection_statistics, !capable(CAP_SYS_ADMIN));
+		err = connection_statistics_to_skb(skb, &connection_statistics);
 		if (err)
 			goto out;
 		cb->args[2] = (long)connection;
@@ -3676,7 +3691,7 @@ int drbd_adm_dump_peer_devices_done(struct netlink_callback *cb)
 	return put_resource_in_arg0(cb, 9);
 }
 
-int drbd_adm_dump_peer_devices(struct sk_buff *skb, struct netlink_callback *cb)
+int drbd_nl_get_peer_devices_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct nlattr *resource_filter;
 	struct drbd_resource *resource;
@@ -3688,7 +3703,8 @@ int drbd_adm_dump_peer_devices(struct sk_buff *skb, struct netlink_callback *cb)
 
 	resource = (struct drbd_resource *)cb->args[0];
 	if (!cb->args[0] && !cb->args[1]) {
-		resource_filter = find_cfg_context_attr(cb->nlh, T_ctx_resource_name);
+		resource_filter = find_cfg_context_attr(cb->nlh,
+				DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME);
 		if (resource_filter) {
 			retcode = ERR_RES_NOT_KNOWN;
 			resource = drbd_find_resource(nla_data(resource_filter));
@@ -3735,7 +3751,7 @@ int drbd_adm_dump_peer_devices(struct sk_buff *skb, struct netlink_callback *cb)
 
 put_result:
 	dh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
-			cb->nlh->nlmsg_seq, &drbd_genl_family,
+			cb->nlh->nlmsg_seq, &drbd_nl_family,
 			NLM_F_MULTI, DRBD_ADM_GET_PEER_DEVICES);
 	err = -ENOMEM;
 	if (!dh)
@@ -3751,11 +3767,11 @@ int drbd_adm_dump_peer_devices(struct sk_buff *skb, struct netlink_callback *cb)
 		if (err)
 			goto out;
 		peer_device_to_info(&peer_device_info, peer_device);
-		err = peer_device_info_to_skb(skb, &peer_device_info, !capable(CAP_SYS_ADMIN));
+		err = peer_device_info_to_skb(skb, &peer_device_info);
 		if (err)
 			goto out;
 		peer_device_to_statistics(&peer_device_statistics, peer_device);
-		err = peer_device_statistics_to_skb(skb, &peer_device_statistics, !capable(CAP_SYS_ADMIN));
+		err = peer_device_statistics_to_skb(skb, &peer_device_statistics);
 		if (err)
 			goto out;
 		cb->args[1] = minor;
@@ -3795,11 +3811,11 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device,
 	/* If sib != NULL, this is drbd_bcast_event, which anyone can listen
 	 * to.  So we better exclude_sensitive information.
 	 *
-	 * If sib == NULL, this is drbd_adm_get_status, executed synchronously
+	 * If sib == NULL, this is drbd_nl_get_status_doit, executed synchronously
 	 * in the context of the requesting user process. Exclude sensitive
 	 * information, unless current has superuser.
 	 *
-	 * NOTE: for drbd_adm_get_status_all(), this is a netlink dump, and
+	 * NOTE: for drbd_nl_get_status_dumpit(), this is a netlink dump, and
 	 * relies on the current implementation of netlink_dump(), which
 	 * executes the dump callback successively from netlink_recvmsg(),
 	 * always in the context of the receiving process */
@@ -3812,7 +3828,7 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device,
 	if (nla_put_drbd_cfg_context(skb, resource, the_only_connection(resource), device))
 		goto nla_put_failure;
 
-	if (res_opts_to_skb(skb, &device->resource->res_opts, exclude_sensitive))
+	if (res_opts_to_skb(skb, &device->resource->res_opts))
 		goto nla_put_failure;
 
 	rcu_read_lock();
@@ -3820,14 +3836,24 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device,
 		struct disk_conf *disk_conf;
 
 		disk_conf = rcu_dereference(device->ldev->disk_conf);
-		err = disk_conf_to_skb(skb, disk_conf, exclude_sensitive);
+		err = disk_conf_to_skb(skb, disk_conf);
 	}
 	if (!err) {
 		struct net_conf *nc;
 
 		nc = rcu_dereference(first_peer_device(device)->connection->net_conf);
-		if (nc)
-			err = net_conf_to_skb(skb, nc, exclude_sensitive);
+		if (nc) {
+			if (exclude_sensitive) {
+				struct net_conf nc_clean = *nc;
+
+				memset(nc_clean.shared_secret, 0,
+				       sizeof(nc_clean.shared_secret));
+				nc_clean.shared_secret_len = 0;
+				err = net_conf_to_skb(skb, &nc_clean);
+			} else {
+				err = net_conf_to_skb(skb, nc);
+			}
+		}
 	}
 	rcu_read_unlock();
 	if (err)
@@ -3836,42 +3862,57 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device,
 	nla = nla_nest_start_noflag(skb, DRBD_NLA_STATE_INFO);
 	if (!nla)
 		goto nla_put_failure;
-	if (nla_put_u32(skb, T_sib_reason, sib ? sib->sib_reason : SIB_GET_STATUS_REPLY) ||
-	    nla_put_u32(skb, T_current_state, device->state.i) ||
-	    nla_put_u64_0pad(skb, T_ed_uuid, device->ed_uuid) ||
-	    nla_put_u64_0pad(skb, T_capacity, get_capacity(device->vdisk)) ||
-	    nla_put_u64_0pad(skb, T_send_cnt, device->send_cnt) ||
-	    nla_put_u64_0pad(skb, T_recv_cnt, device->recv_cnt) ||
-	    nla_put_u64_0pad(skb, T_read_cnt, device->read_cnt) ||
-	    nla_put_u64_0pad(skb, T_writ_cnt, device->writ_cnt) ||
-	    nla_put_u64_0pad(skb, T_al_writ_cnt, device->al_writ_cnt) ||
-	    nla_put_u64_0pad(skb, T_bm_writ_cnt, device->bm_writ_cnt) ||
-	    nla_put_u32(skb, T_ap_bio_cnt, atomic_read(&device->ap_bio_cnt)) ||
-	    nla_put_u32(skb, T_ap_pending_cnt, atomic_read(&device->ap_pending_cnt)) ||
-	    nla_put_u32(skb, T_rs_pending_cnt, atomic_read(&device->rs_pending_cnt)))
+	if (nla_put_u32(skb, DRBD_A_STATE_INFO_SIB_REASON,
+		       sib ? sib->sib_reason : SIB_GET_STATUS_REPLY) ||
+	    nla_put_u32(skb, DRBD_A_STATE_INFO_CURRENT_STATE,
+		       device->state.i) ||
+	    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_ED_UUID,
+			     device->ed_uuid, 0) ||
+	    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_CAPACITY,
+			     get_capacity(device->vdisk), 0) ||
+	    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_SEND_CNT,
+			     device->send_cnt, 0) ||
+	    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_RECV_CNT,
+			     device->recv_cnt, 0) ||
+	    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_READ_CNT,
+			     device->read_cnt, 0) ||
+	    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_WRIT_CNT,
+			     device->writ_cnt, 0) ||
+	    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_AL_WRIT_CNT,
+			     device->al_writ_cnt, 0) ||
+	    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BM_WRIT_CNT,
+			     device->bm_writ_cnt, 0) ||
+	    nla_put_u32(skb, DRBD_A_STATE_INFO_AP_BIO_CNT,
+		       atomic_read(&device->ap_bio_cnt)) ||
+	    nla_put_u32(skb, DRBD_A_STATE_INFO_AP_PENDING_CNT,
+		       atomic_read(&device->ap_pending_cnt)) ||
+	    nla_put_u32(skb, DRBD_A_STATE_INFO_RS_PENDING_CNT,
+		       atomic_read(&device->rs_pending_cnt)))
 		goto nla_put_failure;
 
 	if (got_ldev) {
 		int err;
 
 		spin_lock_irq(&device->ldev->md.uuid_lock);
-		err = nla_put(skb, T_uuids, sizeof(si->uuids), device->ldev->md.uuid);
+		err = nla_put(skb, DRBD_A_STATE_INFO_UUIDS,
+			     sizeof(si->uuids),
+			     device->ldev->md.uuid);
 		spin_unlock_irq(&device->ldev->md.uuid_lock);
 
 		if (err)
 			goto nla_put_failure;
 
-		if (nla_put_u32(skb, T_disk_flags, device->ldev->md.flags) ||
-		    nla_put_u64_0pad(skb, T_bits_total, drbd_bm_bits(device)) ||
-		    nla_put_u64_0pad(skb, T_bits_oos,
-				     drbd_bm_total_weight(device)))
+		if (nla_put_u32(skb, DRBD_A_STATE_INFO_DISK_FLAGS, device->ldev->md.flags) ||
+		    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_TOTAL, drbd_bm_bits(device), 0) ||
+		    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_OOS,
+				      drbd_bm_total_weight(device), 0))
 			goto nla_put_failure;
 		if (C_SYNC_SOURCE <= device->state.conn &&
 		    C_PAUSED_SYNC_T >= device->state.conn) {
-			if (nla_put_u64_0pad(skb, T_bits_rs_total,
-					     device->rs_total) ||
-			    nla_put_u64_0pad(skb, T_bits_rs_failed,
-					     device->rs_failed))
+			if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_RS_TOTAL,
+					      device->rs_total, 0) ||
+			    nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_RS_FAILED,
+					      device->rs_failed, 0))
 				goto nla_put_failure;
 		}
 	}
@@ -3882,17 +3923,17 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device,
 		case SIB_GET_STATUS_REPLY:
 			break;
 		case SIB_STATE_CHANGE:
-			if (nla_put_u32(skb, T_prev_state, sib->os.i) ||
-			    nla_put_u32(skb, T_new_state, sib->ns.i))
+			if (nla_put_u32(skb, DRBD_A_STATE_INFO_PREV_STATE, sib->os.i) ||
+			    nla_put_u32(skb, DRBD_A_STATE_INFO_NEW_STATE, sib->ns.i))
 				goto nla_put_failure;
 			break;
 		case SIB_HELPER_POST:
-			if (nla_put_u32(skb, T_helper_exit_code,
+			if (nla_put_u32(skb, DRBD_A_STATE_INFO_HELPER_EXIT_CODE,
 					sib->helper_exit_code))
 				goto nla_put_failure;
 			fallthrough;
 		case SIB_HELPER_PRE:
-			if (nla_put_string(skb, T_helper, sib->helper_name))
+			if (nla_put_string(skb, DRBD_A_STATE_INFO_HELPER, sib->helper_name))
 				goto nla_put_failure;
 			break;
 		}
@@ -3907,7 +3948,7 @@ static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device,
 	return err;
 }
 
-int drbd_adm_get_status(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_get_status_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	enum drbd_ret_code retcode;
@@ -3997,7 +4038,7 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb)
 		}
 
 		dh = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
-				cb->nlh->nlmsg_seq, &drbd_genl_family,
+				cb->nlh->nlmsg_seq, &drbd_nl_family,
 				NLM_F_MULTI, DRBD_ADM_GET_STATUS);
 		if (!dh)
 			goto out;
@@ -4017,7 +4058,7 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb)
 				struct net_conf *nc;
 
 				nc = rcu_dereference(connection->net_conf);
-				if (nc && net_conf_to_skb(skb, nc, 1) != 0)
+				if (nc && net_conf_to_skb(skb, nc) != 0)
 					goto cancel;
 			}
 			goto done;
@@ -4059,9 +4100,9 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb)
  *
  * Once things are setup properly, we call into get_one_status().
  */
-int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
+int drbd_nl_get_status_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 {
-	const unsigned hdrlen = GENL_HDRLEN + GENL_MAGIC_FAMILY_HDRSZ;
+	const unsigned int hdrlen = GENL_HDRLEN + sizeof(struct drbd_genlmsghdr);
 	struct nlattr *nla;
 	const char *resource_name;
 	struct drbd_resource *resource;
@@ -4084,7 +4125,7 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
 	/* No explicit context given.  Dump all. */
 	if (!nla)
 		goto dump;
-	nla = nla_find_nested(nla, T_ctx_resource_name);
+	nla = nla_find_nested(nla, DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME);
 	/* context given, but no name present? */
 	if (!nla)
 		return -EINVAL;
@@ -4107,7 +4148,7 @@ int drbd_adm_get_status_all(struct sk_buff *skb, struct netlink_callback *cb)
 	return get_one_status(skb, cb);
 }
 
-int drbd_adm_get_timeout_type(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_get_timeout_type_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	enum drbd_ret_code retcode;
@@ -4125,7 +4166,7 @@ int drbd_adm_get_timeout_type(struct sk_buff *skb, struct genl_info *info)
 		test_bit(USE_DEGR_WFC_T, &adm_ctx->device->flags) ? UT_DEGRADED :
 		UT_DEFAULT;
 
-	err = timeout_parms_to_priv_skb(adm_ctx->reply_skb, &tp);
+	err = timeout_parms_to_skb(adm_ctx->reply_skb, &tp);
 	if (err) {
 		nlmsg_free(adm_ctx->reply_skb);
 		adm_ctx->reply_skb = NULL;
@@ -4136,7 +4177,7 @@ int drbd_adm_get_timeout_type(struct sk_buff *skb, struct genl_info *info)
 	return 0;
 }
 
-int drbd_adm_start_ov(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_start_ov_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct drbd_device *device;
@@ -4182,7 +4223,7 @@ int drbd_adm_start_ov(struct sk_buff *skb, struct genl_info *info)
 }
 
 
-int drbd_adm_new_c_uuid(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_new_c_uuid_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct drbd_device *device;
@@ -4285,7 +4326,7 @@ static void resource_to_info(struct resource_info *info,
 	info->res_susp_fen = resource->susp_fen;
 }
 
-int drbd_adm_new_resource(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_new_resource_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_connection *connection;
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
@@ -4348,7 +4389,7 @@ static void device_to_info(struct device_info *info,
 }
 
 
-int drbd_adm_new_minor(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_new_minor_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct drbd_genlmsghdr *dh = genl_info_userhdr(info);
@@ -4455,7 +4496,7 @@ static enum drbd_ret_code adm_del_minor(struct drbd_device *device)
 		return ERR_MINOR_CONFIGURED;
 }
 
-int drbd_adm_del_minor(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_del_minor_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	enum drbd_ret_code retcode;
@@ -4504,7 +4545,7 @@ static int adm_del_resource(struct drbd_resource *resource)
 	return NO_ERROR;
 }
 
-int drbd_adm_down(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_down_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct drbd_resource *resource;
@@ -4567,7 +4608,7 @@ int drbd_adm_down(struct sk_buff *skb, struct genl_info *info)
 	return 0;
 }
 
-int drbd_adm_del_resource(struct sk_buff *skb, struct genl_info *info)
+int drbd_nl_del_resource_doit(struct sk_buff *skb, struct genl_info *info)
 {
 	struct drbd_config_context *adm_ctx = info->user_ptr[0];
 	struct drbd_resource *resource;
@@ -4601,7 +4642,7 @@ void drbd_bcast_event(struct drbd_device *device, const struct sib_info *sib)
 		goto failed;
 
 	err = -EMSGSIZE;
-	d_out = genlmsg_put(msg, 0, seq, &drbd_genl_family, 0, DRBD_EVENT);
+	d_out = genlmsg_put(msg, 0, seq, &drbd_nl_family, 0, DRBD_ADM_EVENT);
 	if (!d_out) /* cannot happen, but anyways. */
 		goto nla_put_failure;
 	d_out->minor = device_to_minor(device);
@@ -4632,7 +4673,7 @@ static int nla_put_notification_header(struct sk_buff *msg,
 		.nh_type = type,
 	};
 
-	return drbd_notification_header_to_skb(msg, &nh, true);
+	return drbd_notification_header_to_skb(msg, &nh);
 }
 
 int notify_resource_state(struct sk_buff *skb,
@@ -4656,7 +4697,7 @@ int notify_resource_state(struct sk_buff *skb,
 	}
 
 	err = -EMSGSIZE;
-	dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_RESOURCE_STATE);
+	dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_RESOURCE_STATE);
 	if (!dh)
 		goto nla_put_failure;
 	dh->minor = -1U;
@@ -4664,10 +4705,10 @@ int notify_resource_state(struct sk_buff *skb,
 	if (nla_put_drbd_cfg_context(skb, resource, NULL, NULL) ||
 	    nla_put_notification_header(skb, type) ||
 	    ((type & ~NOTIFY_FLAGS) != NOTIFY_DESTROY &&
-	     resource_info_to_skb(skb, resource_info, true)))
+	     resource_info_to_skb(skb, resource_info)))
 		goto nla_put_failure;
 	resource_statistics.res_stat_write_ordering = resource->write_ordering;
-	err = resource_statistics_to_skb(skb, &resource_statistics, !capable(CAP_SYS_ADMIN));
+	err = resource_statistics_to_skb(skb, &resource_statistics);
 	if (err)
 		goto nla_put_failure;
 	genlmsg_end(skb, dh);
@@ -4708,7 +4749,7 @@ int notify_device_state(struct sk_buff *skb,
 	}
 
 	err = -EMSGSIZE;
-	dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_DEVICE_STATE);
+	dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_DEVICE_STATE);
 	if (!dh)
 		goto nla_put_failure;
 	dh->minor = device->minor;
@@ -4716,10 +4757,10 @@ int notify_device_state(struct sk_buff *skb,
 	if (nla_put_drbd_cfg_context(skb, device->resource, NULL, device) ||
 	    nla_put_notification_header(skb, type) ||
 	    ((type & ~NOTIFY_FLAGS) != NOTIFY_DESTROY &&
-	     device_info_to_skb(skb, device_info, true)))
+	     device_info_to_skb(skb, device_info)))
 		goto nla_put_failure;
 	device_to_statistics(&device_statistics, device);
-	device_statistics_to_skb(skb, &device_statistics, !capable(CAP_SYS_ADMIN));
+	device_statistics_to_skb(skb, &device_statistics);
 	genlmsg_end(skb, dh);
 	if (multicast) {
 		err = drbd_genl_multicast_events(skb, GFP_NOWAIT);
@@ -4758,7 +4799,7 @@ int notify_connection_state(struct sk_buff *skb,
 	}
 
 	err = -EMSGSIZE;
-	dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_CONNECTION_STATE);
+	dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_CONNECTION_STATE);
 	if (!dh)
 		goto nla_put_failure;
 	dh->minor = -1U;
@@ -4766,10 +4807,10 @@ int notify_connection_state(struct sk_buff *skb,
 	if (nla_put_drbd_cfg_context(skb, connection->resource, connection, NULL) ||
 	    nla_put_notification_header(skb, type) ||
 	    ((type & ~NOTIFY_FLAGS) != NOTIFY_DESTROY &&
-	     connection_info_to_skb(skb, connection_info, true)))
+	     connection_info_to_skb(skb, connection_info)))
 		goto nla_put_failure;
 	connection_statistics.conn_congested = test_bit(NET_CONGESTED, &connection->flags);
-	connection_statistics_to_skb(skb, &connection_statistics, !capable(CAP_SYS_ADMIN));
+	connection_statistics_to_skb(skb, &connection_statistics);
 	genlmsg_end(skb, dh);
 	if (multicast) {
 		err = drbd_genl_multicast_events(skb, GFP_NOWAIT);
@@ -4809,7 +4850,7 @@ int notify_peer_device_state(struct sk_buff *skb,
 	}
 
 	err = -EMSGSIZE;
-	dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_PEER_DEVICE_STATE);
+	dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_PEER_DEVICE_STATE);
 	if (!dh)
 		goto nla_put_failure;
 	dh->minor = -1U;
@@ -4817,10 +4858,10 @@ int notify_peer_device_state(struct sk_buff *skb,
 	if (nla_put_drbd_cfg_context(skb, resource, peer_device->connection, peer_device->device) ||
 	    nla_put_notification_header(skb, type) ||
 	    ((type & ~NOTIFY_FLAGS) != NOTIFY_DESTROY &&
-	     peer_device_info_to_skb(skb, peer_device_info, true)))
+	     peer_device_info_to_skb(skb, peer_device_info)))
 		goto nla_put_failure;
 	peer_device_to_statistics(&peer_device_statistics, peer_device);
-	peer_device_statistics_to_skb(skb, &peer_device_statistics, !capable(CAP_SYS_ADMIN));
+	peer_device_statistics_to_skb(skb, &peer_device_statistics);
 	genlmsg_end(skb, dh);
 	if (multicast) {
 		err = drbd_genl_multicast_events(skb, GFP_NOWAIT);
@@ -4859,7 +4900,7 @@ void notify_helper(enum drbd_notification_type type,
 		goto fail;
 
 	err = -EMSGSIZE;
-	dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_HELPER);
+	dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_HELPER);
 	if (!dh)
 		goto fail;
 	dh->minor = device ? device->minor : -1;
@@ -4867,7 +4908,7 @@ void notify_helper(enum drbd_notification_type type,
 	mutex_lock(&notification_mutex);
 	if (nla_put_drbd_cfg_context(skb, resource, connection, device) ||
 	    nla_put_notification_header(skb, type) ||
-	    drbd_helper_info_to_skb(skb, &helper_info, true))
+	    drbd_helper_info_to_skb(skb, &helper_info))
 		goto unlock_fail;
 	genlmsg_end(skb, dh);
 	err = drbd_genl_multicast_events(skb, GFP_NOWAIT);
@@ -4892,7 +4933,7 @@ static int notify_initial_state_done(struct sk_buff *skb, unsigned int seq)
 	int err;
 
 	err = -EMSGSIZE;
-	dh = genlmsg_put(skb, 0, seq, &drbd_genl_family, 0, DRBD_INITIAL_STATE_DONE);
+	dh = genlmsg_put(skb, 0, seq, &drbd_nl_family, 0, DRBD_ADM_INITIAL_STATE_DONE);
 	if (!dh)
 		goto nla_put_failure;
 	dh->minor = -1U;
@@ -4987,7 +5028,7 @@ static int get_initial_state(struct sk_buff *skb, struct netlink_callback *cb)
 		return skb->len;
 }
 
-int drbd_adm_get_initial_state(struct sk_buff *skb, struct netlink_callback *cb)
+int drbd_nl_get_initial_state_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct drbd_resource *resource;
 	LIST_HEAD(head);
@@ -5035,3 +5076,20 @@ int drbd_adm_get_initial_state(struct sk_buff *skb, struct netlink_callback *cb)
 	cb->args[2] = cb->nlh->nlmsg_seq;
 	return get_initial_state(skb, cb);
 }
+
+static const struct genl_multicast_group drbd_nl_mcgrps[] = {
+	[DRBD_NLGRP_EVENTS] = { .name = "events", },
+};
+
+struct genl_family drbd_nl_family __ro_after_init = {
+	.name		= "drbd",
+	.version	= DRBD_FAMILY_VERSION,
+	.hdrsize	= NLA_ALIGN(sizeof(struct drbd_genlmsghdr)),
+	.split_ops	= drbd_nl_ops,
+	.n_split_ops	= ARRAY_SIZE(drbd_nl_ops),
+	.mcgrps		= drbd_nl_mcgrps,
+	.n_mcgrps	= ARRAY_SIZE(drbd_nl_mcgrps),
+	.resv_start_op	= 42,
+	.module		= THIS_MODULE,
+	.netnsok	= true,
+};
diff --git a/drivers/block/drbd/drbd_nl_gen.c b/drivers/block/drbd/drbd_nl_gen.c
new file mode 100644
index 000000000000..19462e90f677
--- /dev/null
+++ b/drivers/block/drbd/drbd_nl_gen.c
@@ -0,0 +1,2610 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/*	Documentation/netlink/specs/drbd.yaml */
+/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include "drbd_nl_gen.h"
+
+#include <uapi/linux/drbd_genl.h>
+#include <linux/drbd.h>
+#include <linux/drbd_limits.h>
+
+/* Common nested types */
+const struct nla_policy drbd_connection_info_nl_policy[DRBD_A_CONNECTION_INFO_CONN_ROLE + 1] = {
+	[DRBD_A_CONNECTION_INFO_CONN_CONNECTION_STATE] = { .type = NLA_U32, },
+	[DRBD_A_CONNECTION_INFO_CONN_ROLE] = { .type = NLA_U32, },
+};
+
+const struct nla_policy drbd_connection_statistics_nl_policy[DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED + 1] = {
+	[DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED] = { .type = NLA_U8, },
+};
+
+const struct nla_policy drbd_detach_parms_nl_policy[DRBD_A_DETACH_PARMS_FORCE_DETACH + 1] = {
+	[DRBD_A_DETACH_PARMS_FORCE_DETACH] = { .type = NLA_U8, },
+};
+
+const struct nla_policy drbd_device_info_nl_policy[DRBD_A_DEVICE_INFO_DEV_DISK_STATE + 1] = {
+	[DRBD_A_DEVICE_INFO_DEV_DISK_STATE] = { .type = NLA_U32, },
+};
+
+const struct nla_policy drbd_device_statistics_nl_policy[DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS + 1] = {
+	[DRBD_A_DEVICE_STATISTICS_DEV_SIZE] = { .type = NLA_U64, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_READ] = { .type = NLA_U64, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_WRITE] = { .type = NLA_U64, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_AL_WRITES] = { .type = NLA_U64, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_BM_WRITES] = { .type = NLA_U64, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_UPPER_PENDING] = { .type = NLA_U32, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_LOWER_PENDING] = { .type = NLA_U32, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_UPPER_BLOCKED] = { .type = NLA_U8, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_LOWER_BLOCKED] = { .type = NLA_U8, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_AL_SUSPENDED] = { .type = NLA_U8, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_EXPOSED_DATA_UUID] = { .type = NLA_U64, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_CURRENT_UUID] = { .type = NLA_U64, },
+	[DRBD_A_DEVICE_STATISTICS_DEV_DISK_FLAGS] = { .type = NLA_U32, },
+	[DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS] = NLA_POLICY_MAX_LEN(DRBD_NL_HISTORY_UUIDS_SIZE),
+};
+
+const struct nla_policy drbd_disconnect_parms_nl_policy[DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT + 1] = {
+	[DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT] = { .type = NLA_U8, },
+};
+
+const struct nla_policy drbd_disk_conf_nl_policy[DRBD_A_DISK_CONF_DISABLE_WRITE_SAME + 1] = {
+	[DRBD_A_DISK_CONF_BACKING_DEV] = { .type = NLA_NUL_STRING, .len = 128, },
+	[DRBD_A_DISK_CONF_META_DEV] = { .type = NLA_NUL_STRING, .len = 128, },
+	[DRBD_A_DISK_CONF_META_DEV_IDX] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_DISK_SIZE] = { .type = NLA_U64, },
+	[DRBD_A_DISK_CONF_MAX_BIO_BVECS] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_ON_IO_ERROR] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_FENCING] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_RESYNC_RATE] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_RESYNC_AFTER] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_AL_EXTENTS] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_C_PLAN_AHEAD] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_C_DELAY_TARGET] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_C_FILL_TARGET] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_C_MAX_RATE] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_C_MIN_RATE] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_DISK_BARRIER] = { .type = NLA_U8, },
+	[DRBD_A_DISK_CONF_DISK_FLUSHES] = { .type = NLA_U8, },
+	[DRBD_A_DISK_CONF_DISK_DRAIN] = { .type = NLA_U8, },
+	[DRBD_A_DISK_CONF_MD_FLUSHES] = { .type = NLA_U8, },
+	[DRBD_A_DISK_CONF_DISK_TIMEOUT] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_READ_BALANCING] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_AL_UPDATES] = { .type = NLA_U8, },
+	[DRBD_A_DISK_CONF_DISCARD_ZEROES_IF_ALIGNED] = { .type = NLA_U8, },
+	[DRBD_A_DISK_CONF_RS_DISCARD_GRANULARITY] = { .type = NLA_U32, },
+	[DRBD_A_DISK_CONF_DISABLE_WRITE_SAME] = { .type = NLA_U8, },
+};
+
+const struct nla_policy drbd_drbd_cfg_context_nl_policy[DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR + 1] = {
+	[DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME] = { .type = NLA_U32, },
+	[DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME] = { .type = NLA_NUL_STRING, .len = 128, },
+	[DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR] = NLA_POLICY_MAX_LEN(128),
+	[DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR] = NLA_POLICY_MAX_LEN(128),
+};
+
+const struct nla_policy drbd_net_conf_nl_policy[DRBD_A_NET_CONF_SOCK_CHECK_TIMEO + 1] = {
+	[DRBD_A_NET_CONF_SHARED_SECRET] = { .type = NLA_NUL_STRING, .len = SHARED_SECRET_MAX, },
+	[DRBD_A_NET_CONF_CRAM_HMAC_ALG] = { .type = NLA_NUL_STRING, .len = SHARED_SECRET_MAX, },
+	[DRBD_A_NET_CONF_INTEGRITY_ALG] = { .type = NLA_NUL_STRING, .len = SHARED_SECRET_MAX, },
+	[DRBD_A_NET_CONF_VERIFY_ALG] = { .type = NLA_NUL_STRING, .len = SHARED_SECRET_MAX, },
+	[DRBD_A_NET_CONF_CSUMS_ALG] = { .type = NLA_NUL_STRING, .len = SHARED_SECRET_MAX, },
+	[DRBD_A_NET_CONF_WIRE_PROTOCOL] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_CONNECT_INT] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_TIMEOUT] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_PING_INT] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_PING_TIMEO] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_SNDBUF_SIZE] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_RCVBUF_SIZE] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_KO_COUNT] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_MAX_BUFFERS] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_MAX_EPOCH_SIZE] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_UNPLUG_WATERMARK] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_AFTER_SB_0P] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_AFTER_SB_1P] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_AFTER_SB_2P] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_RR_CONFLICT] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_ON_CONGESTION] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_CONG_FILL] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_CONG_EXTENTS] = { .type = NLA_U32, },
+	[DRBD_A_NET_CONF_TWO_PRIMARIES] = { .type = NLA_U8, },
+	[DRBD_A_NET_CONF_DISCARD_MY_DATA] = { .type = NLA_U8, },
+	[DRBD_A_NET_CONF_TCP_CORK] = { .type = NLA_U8, },
+	[DRBD_A_NET_CONF_ALWAYS_ASBP] = { .type = NLA_U8, },
+	[DRBD_A_NET_CONF_TENTATIVE] = { .type = NLA_U8, },
+	[DRBD_A_NET_CONF_USE_RLE] = { .type = NLA_U8, },
+	[DRBD_A_NET_CONF_CSUMS_AFTER_CRASH_ONLY] = { .type = NLA_U8, },
+	[DRBD_A_NET_CONF_SOCK_CHECK_TIMEO] = { .type = NLA_U32, },
+};
+
+const struct nla_policy drbd_new_c_uuid_parms_nl_policy[DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM + 1] = {
+	[DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM] = { .type = NLA_U8, },
+};
+
+const struct nla_policy drbd_peer_device_info_nl_policy[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY + 1] = {
+	[DRBD_A_PEER_DEVICE_INFO_PEER_REPL_STATE] = { .type = NLA_U32, },
+	[DRBD_A_PEER_DEVICE_INFO_PEER_DISK_STATE] = { .type = NLA_U32, },
+	[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_USER] = { .type = NLA_U32, },
+	[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_PEER] = { .type = NLA_U32, },
+	[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY] = { .type = NLA_U32, },
+};
+
+const struct nla_policy drbd_peer_device_statistics_nl_policy[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS + 1] = {
+	[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RECEIVED] = { .type = NLA_U64, },
+	[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_SENT] = { .type = NLA_U64, },
+	[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_PENDING] = { .type = NLA_U32, },
+	[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_UNACKED] = { .type = NLA_U32, },
+	[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_OUT_OF_SYNC] = { .type = NLA_U64, },
+	[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RESYNC_FAILED] = { .type = NLA_U64, },
+	[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_BITMAP_UUID] = { .type = NLA_U64, },
+	[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS] = { .type = NLA_U32, },
+};
+
+const struct nla_policy drbd_res_opts_nl_policy[DRBD_A_RES_OPTS_ON_NO_DATA + 1] = {
+	[DRBD_A_RES_OPTS_CPU_MASK] = { .type = NLA_NUL_STRING, .len = DRBD_CPU_MASK_SIZE, },
+	[DRBD_A_RES_OPTS_ON_NO_DATA] = { .type = NLA_U32, },
+};
+
+const struct nla_policy drbd_resize_parms_nl_policy[DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE + 1] = {
+	[DRBD_A_RESIZE_PARMS_RESIZE_SIZE] = { .type = NLA_U64, },
+	[DRBD_A_RESIZE_PARMS_RESIZE_FORCE] = { .type = NLA_U8, },
+	[DRBD_A_RESIZE_PARMS_NO_RESYNC] = { .type = NLA_U8, },
+	[DRBD_A_RESIZE_PARMS_AL_STRIPES] = { .type = NLA_U32, },
+	[DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE] = { .type = NLA_U32, },
+};
+
+const struct nla_policy drbd_resource_info_nl_policy[DRBD_A_RESOURCE_INFO_RES_SUSP_FEN + 1] = {
+	[DRBD_A_RESOURCE_INFO_RES_ROLE] = { .type = NLA_U32, },
+	[DRBD_A_RESOURCE_INFO_RES_SUSP] = { .type = NLA_U8, },
+	[DRBD_A_RESOURCE_INFO_RES_SUSP_NOD] = { .type = NLA_U8, },
+	[DRBD_A_RESOURCE_INFO_RES_SUSP_FEN] = { .type = NLA_U8, },
+};
+
+const struct nla_policy drbd_resource_statistics_nl_policy[DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING + 1] = {
+	[DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING] = { .type = NLA_U32, },
+};
+
+const struct nla_policy drbd_set_role_parms_nl_policy[DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE + 1] = {
+	[DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE] = { .type = NLA_U8, },
+};
+
+const struct nla_policy drbd_start_ov_parms_nl_policy[DRBD_A_START_OV_PARMS_OV_STOP_SECTOR + 1] = {
+	[DRBD_A_START_OV_PARMS_OV_START_SECTOR] = { .type = NLA_U64, },
+	[DRBD_A_START_OV_PARMS_OV_STOP_SECTOR] = { .type = NLA_U64, },
+};
+
+/* DRBD_ADM_GET_STATUS - do */
+static const struct nla_policy drbd_get_status_do_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_GET_STATUS - dump */
+static const struct nla_policy drbd_get_status_dump_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_NEW_MINOR - do */
+static const struct nla_policy drbd_new_minor_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_DEL_MINOR - do */
+static const struct nla_policy drbd_del_minor_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_NEW_RESOURCE - do */
+static const struct nla_policy drbd_new_resource_nl_policy[DRBD_NLA_RESOURCE_OPTS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_RESOURCE_OPTS] = NLA_POLICY_NESTED(drbd_res_opts_nl_policy),
+};
+
+/* DRBD_ADM_DEL_RESOURCE - do */
+static const struct nla_policy drbd_del_resource_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_RESOURCE_OPTS - do */
+static const struct nla_policy drbd_resource_opts_nl_policy[DRBD_NLA_RESOURCE_OPTS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_RESOURCE_OPTS] = NLA_POLICY_NESTED(drbd_res_opts_nl_policy),
+};
+
+/* DRBD_ADM_CONNECT - do */
+static const struct nla_policy drbd_connect_nl_policy[DRBD_NLA_NET_CONF + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_NET_CONF] = NLA_POLICY_NESTED(drbd_net_conf_nl_policy),
+};
+
+/* DRBD_ADM_DISCONNECT - do */
+static const struct nla_policy drbd_disconnect_nl_policy[DRBD_NLA_DISCONNECT_PARMS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_DISCONNECT_PARMS] = NLA_POLICY_NESTED(drbd_disconnect_parms_nl_policy),
+};
+
+/* DRBD_ADM_ATTACH - do */
+static const struct nla_policy drbd_attach_nl_policy[DRBD_NLA_DISK_CONF + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_DISK_CONF] = NLA_POLICY_NESTED(drbd_disk_conf_nl_policy),
+};
+
+/* DRBD_ADM_RESIZE - do */
+static const struct nla_policy drbd_resize_nl_policy[DRBD_NLA_RESIZE_PARMS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_RESIZE_PARMS] = NLA_POLICY_NESTED(drbd_resize_parms_nl_policy),
+};
+
+/* DRBD_ADM_PRIMARY - do */
+static const struct nla_policy drbd_primary_nl_policy[DRBD_NLA_SET_ROLE_PARMS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_SET_ROLE_PARMS] = NLA_POLICY_NESTED(drbd_set_role_parms_nl_policy),
+};
+
+/* DRBD_ADM_SECONDARY - do */
+static const struct nla_policy drbd_secondary_nl_policy[DRBD_NLA_SET_ROLE_PARMS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_SET_ROLE_PARMS] = NLA_POLICY_NESTED(drbd_set_role_parms_nl_policy),
+};
+
+/* DRBD_ADM_NEW_C_UUID - do */
+static const struct nla_policy drbd_new_c_uuid_nl_policy[DRBD_NLA_NEW_C_UUID_PARMS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_NEW_C_UUID_PARMS] = NLA_POLICY_NESTED(drbd_new_c_uuid_parms_nl_policy),
+};
+
+/* DRBD_ADM_START_OV - do */
+static const struct nla_policy drbd_start_ov_nl_policy[DRBD_NLA_START_OV_PARMS + 1] = {
+	[DRBD_NLA_START_OV_PARMS] = NLA_POLICY_NESTED(drbd_start_ov_parms_nl_policy),
+};
+
+/* DRBD_ADM_DETACH - do */
+static const struct nla_policy drbd_detach_nl_policy[DRBD_NLA_DETACH_PARMS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_DETACH_PARMS] = NLA_POLICY_NESTED(drbd_detach_parms_nl_policy),
+};
+
+/* DRBD_ADM_INVALIDATE - do */
+static const struct nla_policy drbd_invalidate_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_INVAL_PEER - do */
+static const struct nla_policy drbd_inval_peer_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_PAUSE_SYNC - do */
+static const struct nla_policy drbd_pause_sync_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_RESUME_SYNC - do */
+static const struct nla_policy drbd_resume_sync_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_SUSPEND_IO - do */
+static const struct nla_policy drbd_suspend_io_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_RESUME_IO - do */
+static const struct nla_policy drbd_resume_io_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_OUTDATE - do */
+static const struct nla_policy drbd_outdate_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_GET_TIMEOUT_TYPE - do */
+static const struct nla_policy drbd_get_timeout_type_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_DOWN - do */
+static const struct nla_policy drbd_down_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* DRBD_ADM_CHG_DISK_OPTS - do */
+static const struct nla_policy drbd_chg_disk_opts_nl_policy[DRBD_NLA_DISK_CONF + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_DISK_CONF] = NLA_POLICY_NESTED(drbd_disk_conf_nl_policy),
+};
+
+/* DRBD_ADM_CHG_NET_OPTS - do */
+static const struct nla_policy drbd_chg_net_opts_nl_policy[DRBD_NLA_NET_CONF + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_NET_CONF] = NLA_POLICY_NESTED(drbd_net_conf_nl_policy),
+};
+
+/* DRBD_ADM_GET_RESOURCES - dump */
+static const struct nla_policy drbd_get_resources_nl_policy[DRBD_NLA_RESOURCE_STATISTICS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_RESOURCE_INFO] = NLA_POLICY_NESTED(drbd_resource_info_nl_policy),
+	[DRBD_NLA_RESOURCE_STATISTICS] = NLA_POLICY_NESTED(drbd_resource_statistics_nl_policy),
+};
+
+/* DRBD_ADM_GET_DEVICES - dump */
+static const struct nla_policy drbd_get_devices_nl_policy[DRBD_NLA_DEVICE_STATISTICS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_DEVICE_INFO] = NLA_POLICY_NESTED(drbd_device_info_nl_policy),
+	[DRBD_NLA_DEVICE_STATISTICS] = NLA_POLICY_NESTED(drbd_device_statistics_nl_policy),
+};
+
+/* DRBD_ADM_GET_CONNECTIONS - dump */
+static const struct nla_policy drbd_get_connections_nl_policy[DRBD_NLA_CONNECTION_STATISTICS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_CONNECTION_INFO] = NLA_POLICY_NESTED(drbd_connection_info_nl_policy),
+	[DRBD_NLA_CONNECTION_STATISTICS] = NLA_POLICY_NESTED(drbd_connection_statistics_nl_policy),
+};
+
+/* DRBD_ADM_GET_PEER_DEVICES - dump */
+static const struct nla_policy drbd_get_peer_devices_nl_policy[DRBD_NLA_PEER_DEVICE_STATISTICS + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+	[DRBD_NLA_PEER_DEVICE_INFO] = NLA_POLICY_NESTED(drbd_peer_device_info_nl_policy),
+	[DRBD_NLA_PEER_DEVICE_STATISTICS] = NLA_POLICY_NESTED(drbd_peer_device_statistics_nl_policy),
+};
+
+/* DRBD_ADM_GET_INITIAL_STATE - dump */
+static const struct nla_policy drbd_get_initial_state_nl_policy[DRBD_NLA_CFG_CONTEXT + 1] = {
+	[DRBD_NLA_CFG_CONTEXT] = NLA_POLICY_NESTED(drbd_drbd_cfg_context_nl_policy),
+};
+
+/* Ops table for drbd */
+const struct genl_split_ops drbd_nl_ops[32] = {
+	{
+		.cmd		= DRBD_ADM_GET_STATUS,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_get_status_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_get_status_do_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_GET_STATUS,
+		.dumpit		= drbd_nl_get_status_dumpit,
+		.policy		= drbd_get_status_dump_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_CMD_CAP_DUMP,
+	},
+	{
+		.cmd		= DRBD_ADM_NEW_MINOR,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_new_minor_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_new_minor_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_DEL_MINOR,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_del_minor_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_del_minor_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_NEW_RESOURCE,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_new_resource_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_new_resource_nl_policy,
+		.maxattr	= DRBD_NLA_RESOURCE_OPTS,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_DEL_RESOURCE,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_del_resource_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_del_resource_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_RESOURCE_OPTS,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_resource_opts_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_resource_opts_nl_policy,
+		.maxattr	= DRBD_NLA_RESOURCE_OPTS,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_CONNECT,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_connect_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_connect_nl_policy,
+		.maxattr	= DRBD_NLA_NET_CONF,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_DISCONNECT,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_disconnect_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_disconnect_nl_policy,
+		.maxattr	= DRBD_NLA_DISCONNECT_PARMS,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_ATTACH,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_attach_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_attach_nl_policy,
+		.maxattr	= DRBD_NLA_DISK_CONF,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_RESIZE,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_resize_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_resize_nl_policy,
+		.maxattr	= DRBD_NLA_RESIZE_PARMS,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_PRIMARY,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_primary_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_primary_nl_policy,
+		.maxattr	= DRBD_NLA_SET_ROLE_PARMS,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_SECONDARY,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_secondary_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_secondary_nl_policy,
+		.maxattr	= DRBD_NLA_SET_ROLE_PARMS,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_NEW_C_UUID,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_new_c_uuid_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_new_c_uuid_nl_policy,
+		.maxattr	= DRBD_NLA_NEW_C_UUID_PARMS,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_START_OV,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_start_ov_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_start_ov_nl_policy,
+		.maxattr	= DRBD_NLA_START_OV_PARMS,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_DETACH,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_detach_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_detach_nl_policy,
+		.maxattr	= DRBD_NLA_DETACH_PARMS,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_INVALIDATE,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_invalidate_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_invalidate_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_INVAL_PEER,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_inval_peer_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_inval_peer_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_PAUSE_SYNC,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_pause_sync_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_pause_sync_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_RESUME_SYNC,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_resume_sync_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_resume_sync_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_SUSPEND_IO,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_suspend_io_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_suspend_io_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_RESUME_IO,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_resume_io_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_resume_io_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_OUTDATE,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_outdate_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_outdate_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_GET_TIMEOUT_TYPE,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_get_timeout_type_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_get_timeout_type_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_DOWN,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_down_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_down_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_CHG_DISK_OPTS,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_chg_disk_opts_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_chg_disk_opts_nl_policy,
+		.maxattr	= DRBD_NLA_DISK_CONF,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_CHG_NET_OPTS,
+		.pre_doit	= drbd_pre_doit,
+		.doit		= drbd_nl_chg_net_opts_doit,
+		.post_doit	= drbd_post_doit,
+		.policy		= drbd_chg_net_opts_nl_policy,
+		.maxattr	= DRBD_NLA_NET_CONF,
+		.flags		= GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+	},
+	{
+		.cmd		= DRBD_ADM_GET_RESOURCES,
+		.dumpit		= drbd_nl_get_resources_dumpit,
+		.policy		= drbd_get_resources_nl_policy,
+		.maxattr	= DRBD_NLA_RESOURCE_STATISTICS,
+		.flags		= GENL_CMD_CAP_DUMP,
+	},
+	{
+		.cmd		= DRBD_ADM_GET_DEVICES,
+		.dumpit		= drbd_nl_get_devices_dumpit,
+		.done		= drbd_adm_dump_devices_done,
+		.policy		= drbd_get_devices_nl_policy,
+		.maxattr	= DRBD_NLA_DEVICE_STATISTICS,
+		.flags		= GENL_CMD_CAP_DUMP,
+	},
+	{
+		.cmd		= DRBD_ADM_GET_CONNECTIONS,
+		.dumpit		= drbd_nl_get_connections_dumpit,
+		.done		= drbd_adm_dump_connections_done,
+		.policy		= drbd_get_connections_nl_policy,
+		.maxattr	= DRBD_NLA_CONNECTION_STATISTICS,
+		.flags		= GENL_CMD_CAP_DUMP,
+	},
+	{
+		.cmd		= DRBD_ADM_GET_PEER_DEVICES,
+		.dumpit		= drbd_nl_get_peer_devices_dumpit,
+		.done		= drbd_adm_dump_peer_devices_done,
+		.policy		= drbd_get_peer_devices_nl_policy,
+		.maxattr	= DRBD_NLA_PEER_DEVICE_STATISTICS,
+		.flags		= GENL_CMD_CAP_DUMP,
+	},
+	{
+		.cmd		= DRBD_ADM_GET_INITIAL_STATE,
+		.dumpit		= drbd_nl_get_initial_state_dumpit,
+		.policy		= drbd_get_initial_state_nl_policy,
+		.maxattr	= DRBD_NLA_CFG_CONTEXT,
+		.flags		= GENL_CMD_CAP_DUMP,
+	},
+};
+
+static const struct genl_multicast_group drbd_nl_mcgrps[] = {
+	[DRBD_NLGRP_EVENTS] = { "events", },
+};
+
+static int __drbd_cfg_context_from_attrs(struct drbd_cfg_context *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR;
+	struct nlattr *tla = info->attrs[DRBD_NLA_CFG_CONTEXT];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_drbd_cfg_context_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME];
+	if (nla && s)
+		s->ctx_volume = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME];
+	if (nla && s)
+		s->ctx_resource_name_len = nla_strscpy(s->ctx_resource_name, nla, 128);
+
+	nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR];
+	if (nla && s)
+		s->ctx_my_addr_len = nla_memcpy(s->ctx_my_addr, nla, 128);
+
+	nla = ntb[DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR];
+	if (nla && s)
+		s->ctx_peer_addr_len = nla_memcpy(s->ctx_peer_addr, nla, 128);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int drbd_cfg_context_from_attrs(struct drbd_cfg_context *s,
+				struct genl_info *info)
+{
+	return __drbd_cfg_context_from_attrs(s, NULL, info);
+}
+
+int drbd_cfg_context_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __drbd_cfg_context_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __disk_conf_from_attrs(struct disk_conf *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_DISK_CONF_DISABLE_WRITE_SAME;
+	struct nlattr *tla = info->attrs[DRBD_NLA_DISK_CONF];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_DISK_CONF_DISABLE_WRITE_SAME + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_disk_conf_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_DISK_CONF_BACKING_DEV];
+	if (nla) {
+		if (s)
+			s->backing_dev_len = nla_strscpy(s->backing_dev, nla, 128);
+	} else {
+		pr_info("<< missing required attr: backing_dev\n");
+		err = -ENOMSG;
+	}
+
+	nla = ntb[DRBD_A_DISK_CONF_META_DEV];
+	if (nla) {
+		if (s)
+			s->meta_dev_len = nla_strscpy(s->meta_dev, nla, 128);
+	} else {
+		pr_info("<< missing required attr: meta_dev\n");
+		err = -ENOMSG;
+	}
+
+	nla = ntb[DRBD_A_DISK_CONF_META_DEV_IDX];
+	if (nla) {
+		if (s)
+			s->meta_dev_idx = nla_get_s32(nla);
+	} else {
+		pr_info("<< missing required attr: meta_dev_idx\n");
+		err = -ENOMSG;
+	}
+
+	nla = ntb[DRBD_A_DISK_CONF_DISK_SIZE];
+	if (nla && s)
+		s->disk_size = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_MAX_BIO_BVECS];
+	if (nla && s)
+		s->max_bio_bvecs = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_ON_IO_ERROR];
+	if (nla && s)
+		s->on_io_error = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_FENCING];
+	if (nla && s)
+		s->fencing = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_RESYNC_RATE];
+	if (nla && s)
+		s->resync_rate = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_RESYNC_AFTER];
+	if (nla && s)
+		s->resync_after = nla_get_s32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_AL_EXTENTS];
+	if (nla && s)
+		s->al_extents = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_C_PLAN_AHEAD];
+	if (nla && s)
+		s->c_plan_ahead = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_C_DELAY_TARGET];
+	if (nla && s)
+		s->c_delay_target = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_C_FILL_TARGET];
+	if (nla && s)
+		s->c_fill_target = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_C_MAX_RATE];
+	if (nla && s)
+		s->c_max_rate = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_C_MIN_RATE];
+	if (nla && s)
+		s->c_min_rate = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_DISK_BARRIER];
+	if (nla && s)
+		s->disk_barrier = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_DISK_FLUSHES];
+	if (nla && s)
+		s->disk_flushes = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_DISK_DRAIN];
+	if (nla && s)
+		s->disk_drain = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_MD_FLUSHES];
+	if (nla && s)
+		s->md_flushes = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_DISK_TIMEOUT];
+	if (nla && s)
+		s->disk_timeout = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_READ_BALANCING];
+	if (nla && s)
+		s->read_balancing = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_AL_UPDATES];
+	if (nla && s)
+		s->al_updates = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_DISCARD_ZEROES_IF_ALIGNED];
+	if (nla && s)
+		s->discard_zeroes_if_aligned = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_RS_DISCARD_GRANULARITY];
+	if (nla && s)
+		s->rs_discard_granularity = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DISK_CONF_DISABLE_WRITE_SAME];
+	if (nla && s)
+		s->disable_write_same = nla_get_u8(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int disk_conf_from_attrs(struct disk_conf *s,
+				struct genl_info *info)
+{
+	return __disk_conf_from_attrs(s, NULL, info);
+}
+
+int disk_conf_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __disk_conf_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __res_opts_from_attrs(struct res_opts *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_RES_OPTS_ON_NO_DATA;
+	struct nlattr *tla = info->attrs[DRBD_NLA_RESOURCE_OPTS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_RES_OPTS_ON_NO_DATA + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_res_opts_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_RES_OPTS_CPU_MASK];
+	if (nla && s)
+		s->cpu_mask_len = nla_strscpy(s->cpu_mask, nla, DRBD_CPU_MASK_SIZE);
+
+	nla = ntb[DRBD_A_RES_OPTS_ON_NO_DATA];
+	if (nla && s)
+		s->on_no_data = nla_get_u32(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int res_opts_from_attrs(struct res_opts *s,
+				struct genl_info *info)
+{
+	return __res_opts_from_attrs(s, NULL, info);
+}
+
+int res_opts_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __res_opts_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __net_conf_from_attrs(struct net_conf *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_NET_CONF_SOCK_CHECK_TIMEO;
+	struct nlattr *tla = info->attrs[DRBD_NLA_NET_CONF];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_NET_CONF_SOCK_CHECK_TIMEO + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_net_conf_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_NET_CONF_SHARED_SECRET];
+	if (nla && s)
+		s->shared_secret_len = nla_strscpy(s->shared_secret, nla, SHARED_SECRET_MAX);
+
+	nla = ntb[DRBD_A_NET_CONF_CRAM_HMAC_ALG];
+	if (nla && s)
+		s->cram_hmac_alg_len = nla_strscpy(s->cram_hmac_alg, nla, SHARED_SECRET_MAX);
+
+	nla = ntb[DRBD_A_NET_CONF_INTEGRITY_ALG];
+	if (nla && s)
+		s->integrity_alg_len = nla_strscpy(s->integrity_alg, nla, SHARED_SECRET_MAX);
+
+	nla = ntb[DRBD_A_NET_CONF_VERIFY_ALG];
+	if (nla && s)
+		s->verify_alg_len = nla_strscpy(s->verify_alg, nla, SHARED_SECRET_MAX);
+
+	nla = ntb[DRBD_A_NET_CONF_CSUMS_ALG];
+	if (nla && s)
+		s->csums_alg_len = nla_strscpy(s->csums_alg, nla, SHARED_SECRET_MAX);
+
+	nla = ntb[DRBD_A_NET_CONF_WIRE_PROTOCOL];
+	if (nla && s)
+		s->wire_protocol = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_CONNECT_INT];
+	if (nla && s)
+		s->connect_int = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_TIMEOUT];
+	if (nla && s)
+		s->timeout = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_PING_INT];
+	if (nla && s)
+		s->ping_int = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_PING_TIMEO];
+	if (nla && s)
+		s->ping_timeo = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_SNDBUF_SIZE];
+	if (nla && s)
+		s->sndbuf_size = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_RCVBUF_SIZE];
+	if (nla && s)
+		s->rcvbuf_size = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_KO_COUNT];
+	if (nla && s)
+		s->ko_count = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_MAX_BUFFERS];
+	if (nla && s)
+		s->max_buffers = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_MAX_EPOCH_SIZE];
+	if (nla && s)
+		s->max_epoch_size = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_UNPLUG_WATERMARK];
+	if (nla && s)
+		s->unplug_watermark = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_AFTER_SB_0P];
+	if (nla && s)
+		s->after_sb_0p = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_AFTER_SB_1P];
+	if (nla && s)
+		s->after_sb_1p = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_AFTER_SB_2P];
+	if (nla && s)
+		s->after_sb_2p = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_RR_CONFLICT];
+	if (nla && s)
+		s->rr_conflict = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_ON_CONGESTION];
+	if (nla && s)
+		s->on_congestion = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_CONG_FILL];
+	if (nla && s)
+		s->cong_fill = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_CONG_EXTENTS];
+	if (nla && s)
+		s->cong_extents = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_TWO_PRIMARIES];
+	if (nla && s)
+		s->two_primaries = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_DISCARD_MY_DATA];
+	if (nla && s)
+		s->discard_my_data = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_TCP_CORK];
+	if (nla && s)
+		s->tcp_cork = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_ALWAYS_ASBP];
+	if (nla && s)
+		s->always_asbp = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_TENTATIVE];
+	if (nla && s)
+		s->tentative = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_USE_RLE];
+	if (nla && s)
+		s->use_rle = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_CSUMS_AFTER_CRASH_ONLY];
+	if (nla && s)
+		s->csums_after_crash_only = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_NET_CONF_SOCK_CHECK_TIMEO];
+	if (nla && s)
+		s->sock_check_timeo = nla_get_u32(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int net_conf_from_attrs(struct net_conf *s,
+				struct genl_info *info)
+{
+	return __net_conf_from_attrs(s, NULL, info);
+}
+
+int net_conf_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __net_conf_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __set_role_parms_from_attrs(struct set_role_parms *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE;
+	struct nlattr *tla = info->attrs[DRBD_NLA_SET_ROLE_PARMS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_set_role_parms_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE];
+	if (nla && s)
+		s->assume_uptodate = nla_get_u8(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int set_role_parms_from_attrs(struct set_role_parms *s,
+				struct genl_info *info)
+{
+	return __set_role_parms_from_attrs(s, NULL, info);
+}
+
+int set_role_parms_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __set_role_parms_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __resize_parms_from_attrs(struct resize_parms *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE;
+	struct nlattr *tla = info->attrs[DRBD_NLA_RESIZE_PARMS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_resize_parms_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_RESIZE_PARMS_RESIZE_SIZE];
+	if (nla && s)
+		s->resize_size = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_RESIZE_PARMS_RESIZE_FORCE];
+	if (nla && s)
+		s->resize_force = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_RESIZE_PARMS_NO_RESYNC];
+	if (nla && s)
+		s->no_resync = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_RESIZE_PARMS_AL_STRIPES];
+	if (nla && s)
+		s->al_stripes = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE];
+	if (nla && s)
+		s->al_stripe_size = nla_get_u32(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int resize_parms_from_attrs(struct resize_parms *s,
+				struct genl_info *info)
+{
+	return __resize_parms_from_attrs(s, NULL, info);
+}
+
+int resize_parms_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __resize_parms_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __start_ov_parms_from_attrs(struct start_ov_parms *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_START_OV_PARMS_OV_STOP_SECTOR;
+	struct nlattr *tla = info->attrs[DRBD_NLA_START_OV_PARMS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_START_OV_PARMS_OV_STOP_SECTOR + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_start_ov_parms_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_START_OV_PARMS_OV_START_SECTOR];
+	if (nla && s)
+		s->ov_start_sector = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_START_OV_PARMS_OV_STOP_SECTOR];
+	if (nla && s)
+		s->ov_stop_sector = nla_get_u64(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int start_ov_parms_from_attrs(struct start_ov_parms *s,
+				struct genl_info *info)
+{
+	return __start_ov_parms_from_attrs(s, NULL, info);
+}
+
+int start_ov_parms_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __start_ov_parms_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __new_c_uuid_parms_from_attrs(struct new_c_uuid_parms *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM;
+	struct nlattr *tla = info->attrs[DRBD_NLA_NEW_C_UUID_PARMS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_new_c_uuid_parms_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM];
+	if (nla && s)
+		s->clear_bm = nla_get_u8(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int new_c_uuid_parms_from_attrs(struct new_c_uuid_parms *s,
+				struct genl_info *info)
+{
+	return __new_c_uuid_parms_from_attrs(s, NULL, info);
+}
+
+int new_c_uuid_parms_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __new_c_uuid_parms_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __disconnect_parms_from_attrs(struct disconnect_parms *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT;
+	struct nlattr *tla = info->attrs[DRBD_NLA_DISCONNECT_PARMS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_disconnect_parms_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT];
+	if (nla && s)
+		s->force_disconnect = nla_get_u8(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int disconnect_parms_from_attrs(struct disconnect_parms *s,
+				struct genl_info *info)
+{
+	return __disconnect_parms_from_attrs(s, NULL, info);
+}
+
+int disconnect_parms_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __disconnect_parms_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __detach_parms_from_attrs(struct detach_parms *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_DETACH_PARMS_FORCE_DETACH;
+	struct nlattr *tla = info->attrs[DRBD_NLA_DETACH_PARMS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_DETACH_PARMS_FORCE_DETACH + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_detach_parms_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_DETACH_PARMS_FORCE_DETACH];
+	if (nla && s)
+		s->force_detach = nla_get_u8(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int detach_parms_from_attrs(struct detach_parms *s,
+				struct genl_info *info)
+{
+	return __detach_parms_from_attrs(s, NULL, info);
+}
+
+int detach_parms_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __detach_parms_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __resource_info_from_attrs(struct resource_info *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_RESOURCE_INFO_RES_SUSP_FEN;
+	struct nlattr *tla = info->attrs[DRBD_NLA_RESOURCE_INFO];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_RESOURCE_INFO_RES_SUSP_FEN + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_resource_info_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_RESOURCE_INFO_RES_ROLE];
+	if (nla && s)
+		s->res_role = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_RESOURCE_INFO_RES_SUSP];
+	if (nla && s)
+		s->res_susp = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_RESOURCE_INFO_RES_SUSP_NOD];
+	if (nla && s)
+		s->res_susp_nod = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_RESOURCE_INFO_RES_SUSP_FEN];
+	if (nla && s)
+		s->res_susp_fen = nla_get_u8(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int resource_info_from_attrs(struct resource_info *s,
+				struct genl_info *info)
+{
+	return __resource_info_from_attrs(s, NULL, info);
+}
+
+int resource_info_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __resource_info_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __device_info_from_attrs(struct device_info *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_DEVICE_INFO_DEV_DISK_STATE;
+	struct nlattr *tla = info->attrs[DRBD_NLA_DEVICE_INFO];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_DEVICE_INFO_DEV_DISK_STATE + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_device_info_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_DEVICE_INFO_DEV_DISK_STATE];
+	if (nla && s)
+		s->dev_disk_state = nla_get_u32(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int device_info_from_attrs(struct device_info *s,
+				struct genl_info *info)
+{
+	return __device_info_from_attrs(s, NULL, info);
+}
+
+int device_info_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __device_info_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __connection_info_from_attrs(struct connection_info *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_CONNECTION_INFO_CONN_ROLE;
+	struct nlattr *tla = info->attrs[DRBD_NLA_CONNECTION_INFO];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_CONNECTION_INFO_CONN_ROLE + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_connection_info_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_CONNECTION_INFO_CONN_CONNECTION_STATE];
+	if (nla && s)
+		s->conn_connection_state = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_CONNECTION_INFO_CONN_ROLE];
+	if (nla && s)
+		s->conn_role = nla_get_u32(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int connection_info_from_attrs(struct connection_info *s,
+				struct genl_info *info)
+{
+	return __connection_info_from_attrs(s, NULL, info);
+}
+
+int connection_info_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __connection_info_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __peer_device_info_from_attrs(struct peer_device_info *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY;
+	struct nlattr *tla = info->attrs[DRBD_NLA_PEER_DEVICE_INFO];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_peer_device_info_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_PEER_DEVICE_INFO_PEER_REPL_STATE];
+	if (nla && s)
+		s->peer_repl_state = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_INFO_PEER_DISK_STATE];
+	if (nla && s)
+		s->peer_disk_state = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_USER];
+	if (nla && s)
+		s->peer_resync_susp_user = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_PEER];
+	if (nla && s)
+		s->peer_resync_susp_peer = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY];
+	if (nla && s)
+		s->peer_resync_susp_dependency = nla_get_u32(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int peer_device_info_from_attrs(struct peer_device_info *s,
+				struct genl_info *info)
+{
+	return __peer_device_info_from_attrs(s, NULL, info);
+}
+
+int peer_device_info_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __peer_device_info_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __resource_statistics_from_attrs(struct resource_statistics *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING;
+	struct nlattr *tla = info->attrs[DRBD_NLA_RESOURCE_STATISTICS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_resource_statistics_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING];
+	if (nla && s)
+		s->res_stat_write_ordering = nla_get_u32(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int resource_statistics_from_attrs(struct resource_statistics *s,
+				struct genl_info *info)
+{
+	return __resource_statistics_from_attrs(s, NULL, info);
+}
+
+int resource_statistics_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __resource_statistics_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __device_statistics_from_attrs(struct device_statistics *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS;
+	struct nlattr *tla = info->attrs[DRBD_NLA_DEVICE_STATISTICS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_device_statistics_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_SIZE];
+	if (nla && s)
+		s->dev_size = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_READ];
+	if (nla && s)
+		s->dev_read = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_WRITE];
+	if (nla && s)
+		s->dev_write = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_AL_WRITES];
+	if (nla && s)
+		s->dev_al_writes = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_BM_WRITES];
+	if (nla && s)
+		s->dev_bm_writes = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_UPPER_PENDING];
+	if (nla && s)
+		s->dev_upper_pending = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_LOWER_PENDING];
+	if (nla && s)
+		s->dev_lower_pending = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_UPPER_BLOCKED];
+	if (nla && s)
+		s->dev_upper_blocked = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_LOWER_BLOCKED];
+	if (nla && s)
+		s->dev_lower_blocked = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_AL_SUSPENDED];
+	if (nla && s)
+		s->dev_al_suspended = nla_get_u8(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_EXPOSED_DATA_UUID];
+	if (nla && s)
+		s->dev_exposed_data_uuid = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_CURRENT_UUID];
+	if (nla && s)
+		s->dev_current_uuid = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_DEV_DISK_FLAGS];
+	if (nla && s)
+		s->dev_disk_flags = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS];
+	if (nla && s)
+		s->history_uuids_len = nla_memcpy(s->history_uuids, nla, DRBD_NL_HISTORY_UUIDS_SIZE);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int device_statistics_from_attrs(struct device_statistics *s,
+				struct genl_info *info)
+{
+	return __device_statistics_from_attrs(s, NULL, info);
+}
+
+int device_statistics_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __device_statistics_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __connection_statistics_from_attrs(struct connection_statistics *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED;
+	struct nlattr *tla = info->attrs[DRBD_NLA_CONNECTION_STATISTICS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_connection_statistics_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED];
+	if (nla && s)
+		s->conn_congested = nla_get_u8(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int connection_statistics_from_attrs(struct connection_statistics *s,
+				struct genl_info *info)
+{
+	return __connection_statistics_from_attrs(s, NULL, info);
+}
+
+int connection_statistics_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __connection_statistics_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+static int __peer_device_statistics_from_attrs(struct peer_device_statistics *s,
+		struct nlattr ***ret_nested_attribute_table,
+		struct genl_info *info)
+{
+	const int maxtype = DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS;
+	struct nlattr *tla = info->attrs[DRBD_NLA_PEER_DEVICE_STATISTICS];
+	struct nlattr **ntb;
+	struct nlattr *nla;
+	int err = 0;
+
+	if (ret_nested_attribute_table)
+		*ret_nested_attribute_table = NULL;
+	if (!tla)
+		return -ENOMSG;
+	ntb = kcalloc(DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS + 1, sizeof(*ntb), GFP_KERNEL);
+	if (!ntb)
+		return -ENOMEM;
+	err = nla_parse_nested_deprecated(ntb, maxtype, tla, drbd_peer_device_statistics_nl_policy, NULL);
+	if (err)
+		goto out;
+
+	nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RECEIVED];
+	if (nla && s)
+		s->peer_dev_received = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_SENT];
+	if (nla && s)
+		s->peer_dev_sent = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_PENDING];
+	if (nla && s)
+		s->peer_dev_pending = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_UNACKED];
+	if (nla && s)
+		s->peer_dev_unacked = nla_get_u32(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_OUT_OF_SYNC];
+	if (nla && s)
+		s->peer_dev_out_of_sync = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RESYNC_FAILED];
+	if (nla && s)
+		s->peer_dev_resync_failed = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_BITMAP_UUID];
+	if (nla && s)
+		s->peer_dev_bitmap_uuid = nla_get_u64(nla);
+
+	nla = ntb[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS];
+	if (nla && s)
+		s->peer_dev_flags = nla_get_u32(nla);
+
+out:
+	if (ret_nested_attribute_table && (!err || err == -ENOMSG))
+		*ret_nested_attribute_table = ntb;
+	else
+		kfree(ntb);
+	return err;
+}
+
+int peer_device_statistics_from_attrs(struct peer_device_statistics *s,
+				struct genl_info *info)
+{
+	return __peer_device_statistics_from_attrs(s, NULL, info);
+}
+
+int peer_device_statistics_ntb_from_attrs(
+			struct nlattr ***ret_nested_attribute_table,
+			struct genl_info *info)
+{
+	return __peer_device_statistics_from_attrs(NULL, ret_nested_attribute_table, info);
+}
+
+int drbd_cfg_reply_to_skb(struct sk_buff *skb, struct drbd_cfg_reply *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_CFG_REPLY);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put(skb, DRBD_A_DRBD_CFG_REPLY_INFO_TEXT, min_t(int, 0,
+			s->info_text_len + (s->info_text_len < 0)), s->info_text))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int drbd_cfg_context_to_skb(struct sk_buff *skb, struct drbd_cfg_context *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u32(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME, s->ctx_volume))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME, min_t(int, 128,
+			s->ctx_resource_name_len + (s->ctx_resource_name_len < 128)), s->ctx_resource_name))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR, min_t(int, 128,
+			s->ctx_my_addr_len), s->ctx_my_addr))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR, min_t(int, 128,
+			s->ctx_peer_addr_len), s->ctx_peer_addr))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int disk_conf_to_skb(struct sk_buff *skb, struct disk_conf *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_DISK_CONF);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put(skb, DRBD_A_DISK_CONF_BACKING_DEV, min_t(int, 128,
+			s->backing_dev_len + (s->backing_dev_len < 128)), s->backing_dev))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_DISK_CONF_META_DEV, min_t(int, 128,
+			s->meta_dev_len + (s->meta_dev_len < 128)), s->meta_dev))
+		goto nla_put_failure;
+	if (nla_put_s32(skb, DRBD_A_DISK_CONF_META_DEV_IDX, s->meta_dev_idx))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_DISK_CONF_DISK_SIZE, s->disk_size, 0))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_MAX_BIO_BVECS, s->max_bio_bvecs))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_ON_IO_ERROR, s->on_io_error))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_FENCING, s->fencing))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_RESYNC_RATE, s->resync_rate))
+		goto nla_put_failure;
+	if (nla_put_s32(skb, DRBD_A_DISK_CONF_RESYNC_AFTER, s->resync_after))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_AL_EXTENTS, s->al_extents))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_C_PLAN_AHEAD, s->c_plan_ahead))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_C_DELAY_TARGET, s->c_delay_target))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_C_FILL_TARGET, s->c_fill_target))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_C_MAX_RATE, s->c_max_rate))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_C_MIN_RATE, s->c_min_rate))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_DISK_CONF_DISK_BARRIER, s->disk_barrier))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_DISK_CONF_DISK_FLUSHES, s->disk_flushes))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_DISK_CONF_DISK_DRAIN, s->disk_drain))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_DISK_CONF_MD_FLUSHES, s->md_flushes))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_DISK_TIMEOUT, s->disk_timeout))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_READ_BALANCING, s->read_balancing))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_DISK_CONF_AL_UPDATES, s->al_updates))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_DISK_CONF_DISCARD_ZEROES_IF_ALIGNED, s->discard_zeroes_if_aligned))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DISK_CONF_RS_DISCARD_GRANULARITY, s->rs_discard_granularity))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_DISK_CONF_DISABLE_WRITE_SAME, s->disable_write_same))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int res_opts_to_skb(struct sk_buff *skb, struct res_opts *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_RESOURCE_OPTS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put(skb, DRBD_A_RES_OPTS_CPU_MASK, min_t(int, DRBD_CPU_MASK_SIZE,
+			s->cpu_mask_len + (s->cpu_mask_len < DRBD_CPU_MASK_SIZE)), s->cpu_mask))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_RES_OPTS_ON_NO_DATA, s->on_no_data))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int net_conf_to_skb(struct sk_buff *skb, struct net_conf *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_NET_CONF);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put(skb, DRBD_A_NET_CONF_SHARED_SECRET, min_t(int, SHARED_SECRET_MAX,
+			s->shared_secret_len + (s->shared_secret_len < SHARED_SECRET_MAX)), s->shared_secret))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_NET_CONF_CRAM_HMAC_ALG, min_t(int, SHARED_SECRET_MAX,
+			s->cram_hmac_alg_len + (s->cram_hmac_alg_len < SHARED_SECRET_MAX)), s->cram_hmac_alg))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_NET_CONF_INTEGRITY_ALG, min_t(int, SHARED_SECRET_MAX,
+			s->integrity_alg_len + (s->integrity_alg_len < SHARED_SECRET_MAX)), s->integrity_alg))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_NET_CONF_VERIFY_ALG, min_t(int, SHARED_SECRET_MAX,
+			s->verify_alg_len + (s->verify_alg_len < SHARED_SECRET_MAX)), s->verify_alg))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_NET_CONF_CSUMS_ALG, min_t(int, SHARED_SECRET_MAX,
+			s->csums_alg_len + (s->csums_alg_len < SHARED_SECRET_MAX)), s->csums_alg))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_WIRE_PROTOCOL, s->wire_protocol))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_CONNECT_INT, s->connect_int))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_TIMEOUT, s->timeout))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_PING_INT, s->ping_int))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_PING_TIMEO, s->ping_timeo))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_SNDBUF_SIZE, s->sndbuf_size))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_RCVBUF_SIZE, s->rcvbuf_size))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_KO_COUNT, s->ko_count))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_MAX_BUFFERS, s->max_buffers))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_MAX_EPOCH_SIZE, s->max_epoch_size))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_UNPLUG_WATERMARK, s->unplug_watermark))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_AFTER_SB_0P, s->after_sb_0p))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_AFTER_SB_1P, s->after_sb_1p))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_AFTER_SB_2P, s->after_sb_2p))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_RR_CONFLICT, s->rr_conflict))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_ON_CONGESTION, s->on_congestion))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_CONG_FILL, s->cong_fill))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_CONG_EXTENTS, s->cong_extents))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_NET_CONF_TWO_PRIMARIES, s->two_primaries))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_NET_CONF_DISCARD_MY_DATA, s->discard_my_data))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_NET_CONF_TCP_CORK, s->tcp_cork))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_NET_CONF_ALWAYS_ASBP, s->always_asbp))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_NET_CONF_TENTATIVE, s->tentative))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_NET_CONF_USE_RLE, s->use_rle))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_NET_CONF_CSUMS_AFTER_CRASH_ONLY, s->csums_after_crash_only))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_NET_CONF_SOCK_CHECK_TIMEO, s->sock_check_timeo))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int set_role_parms_to_skb(struct sk_buff *skb, struct set_role_parms *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_SET_ROLE_PARMS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u8(skb, DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE, s->assume_uptodate))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int resize_parms_to_skb(struct sk_buff *skb, struct resize_parms *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_RESIZE_PARMS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u64_64bit(skb, DRBD_A_RESIZE_PARMS_RESIZE_SIZE, s->resize_size, 0))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_RESIZE_PARMS_RESIZE_FORCE, s->resize_force))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_RESIZE_PARMS_NO_RESYNC, s->no_resync))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_RESIZE_PARMS_AL_STRIPES, s->al_stripes))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE, s->al_stripe_size))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int state_info_to_skb(struct sk_buff *skb, struct state_info *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_STATE_INFO);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u32(skb, DRBD_A_STATE_INFO_SIB_REASON, s->sib_reason))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_STATE_INFO_CURRENT_STATE, s->current_state))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_CAPACITY, s->capacity, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_ED_UUID, s->ed_uuid, 0))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_STATE_INFO_PREV_STATE, s->prev_state))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_STATE_INFO_NEW_STATE, s->new_state))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_STATE_INFO_UUIDS, min_t(int, DRBD_NL_UUIDS_SIZE,
+			s->uuids_len), s->uuids))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_STATE_INFO_DISK_FLAGS, s->disk_flags))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_TOTAL, s->bits_total, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_OOS, s->bits_oos, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_RS_TOTAL, s->bits_rs_total, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BITS_RS_FAILED, s->bits_rs_failed, 0))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_STATE_INFO_HELPER, min_t(int, 32,
+			s->helper_len + (s->helper_len < 32)), s->helper))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_STATE_INFO_HELPER_EXIT_CODE, s->helper_exit_code))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_SEND_CNT, s->send_cnt, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_RECV_CNT, s->recv_cnt, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_READ_CNT, s->read_cnt, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_WRIT_CNT, s->writ_cnt, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_AL_WRIT_CNT, s->al_writ_cnt, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_STATE_INFO_BM_WRIT_CNT, s->bm_writ_cnt, 0))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_STATE_INFO_AP_BIO_CNT, s->ap_bio_cnt))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_STATE_INFO_AP_PENDING_CNT, s->ap_pending_cnt))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_STATE_INFO_RS_PENDING_CNT, s->rs_pending_cnt))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int start_ov_parms_to_skb(struct sk_buff *skb, struct start_ov_parms *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_START_OV_PARMS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u64_64bit(skb, DRBD_A_START_OV_PARMS_OV_START_SECTOR, s->ov_start_sector, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_START_OV_PARMS_OV_STOP_SECTOR, s->ov_stop_sector, 0))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int new_c_uuid_parms_to_skb(struct sk_buff *skb, struct new_c_uuid_parms *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_NEW_C_UUID_PARMS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u8(skb, DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM, s->clear_bm))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int timeout_parms_to_skb(struct sk_buff *skb, struct timeout_parms *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_TIMEOUT_PARMS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u32(skb, DRBD_A_TIMEOUT_PARMS_TIMEOUT_TYPE, s->timeout_type))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int disconnect_parms_to_skb(struct sk_buff *skb, struct disconnect_parms *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_DISCONNECT_PARMS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u8(skb, DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT, s->force_disconnect))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int detach_parms_to_skb(struct sk_buff *skb, struct detach_parms *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_DETACH_PARMS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u8(skb, DRBD_A_DETACH_PARMS_FORCE_DETACH, s->force_detach))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int resource_info_to_skb(struct sk_buff *skb, struct resource_info *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_RESOURCE_INFO);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u32(skb, DRBD_A_RESOURCE_INFO_RES_ROLE, s->res_role))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_RESOURCE_INFO_RES_SUSP, s->res_susp))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_RESOURCE_INFO_RES_SUSP_NOD, s->res_susp_nod))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_RESOURCE_INFO_RES_SUSP_FEN, s->res_susp_fen))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int device_info_to_skb(struct sk_buff *skb, struct device_info *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_DEVICE_INFO);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u32(skb, DRBD_A_DEVICE_INFO_DEV_DISK_STATE, s->dev_disk_state))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int connection_info_to_skb(struct sk_buff *skb, struct connection_info *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_CONNECTION_INFO);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u32(skb, DRBD_A_CONNECTION_INFO_CONN_CONNECTION_STATE, s->conn_connection_state))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_CONNECTION_INFO_CONN_ROLE, s->conn_role))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int peer_device_info_to_skb(struct sk_buff *skb, struct peer_device_info *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_PEER_DEVICE_INFO);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_INFO_PEER_REPL_STATE, s->peer_repl_state))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_INFO_PEER_DISK_STATE, s->peer_disk_state))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_USER, s->peer_resync_susp_user))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_PEER, s->peer_resync_susp_peer))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY, s->peer_resync_susp_dependency))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int resource_statistics_to_skb(struct sk_buff *skb, struct resource_statistics *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_RESOURCE_STATISTICS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u32(skb, DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING, s->res_stat_write_ordering))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int device_statistics_to_skb(struct sk_buff *skb, struct device_statistics *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_DEVICE_STATISTICS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_SIZE, s->dev_size, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_READ, s->dev_read, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_WRITE, s->dev_write, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_AL_WRITES, s->dev_al_writes, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_BM_WRITES, s->dev_bm_writes, 0))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DEVICE_STATISTICS_DEV_UPPER_PENDING, s->dev_upper_pending))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DEVICE_STATISTICS_DEV_LOWER_PENDING, s->dev_lower_pending))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_DEVICE_STATISTICS_DEV_UPPER_BLOCKED, s->dev_upper_blocked))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_DEVICE_STATISTICS_DEV_LOWER_BLOCKED, s->dev_lower_blocked))
+		goto nla_put_failure;
+	if (nla_put_u8(skb, DRBD_A_DEVICE_STATISTICS_DEV_AL_SUSPENDED, s->dev_al_suspended))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_EXPOSED_DATA_UUID, s->dev_exposed_data_uuid, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_DEVICE_STATISTICS_DEV_CURRENT_UUID, s->dev_current_uuid, 0))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DEVICE_STATISTICS_DEV_DISK_FLAGS, s->dev_disk_flags))
+		goto nla_put_failure;
+	if (nla_put(skb, DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS, min_t(int, DRBD_NL_HISTORY_UUIDS_SIZE,
+			s->history_uuids_len), s->history_uuids))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int connection_statistics_to_skb(struct sk_buff *skb, struct connection_statistics *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_CONNECTION_STATISTICS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u8(skb, DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED, s->conn_congested))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int peer_device_statistics_to_skb(struct sk_buff *skb, struct peer_device_statistics *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_PEER_DEVICE_STATISTICS);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u64_64bit(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RECEIVED, s->peer_dev_received, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_SENT, s->peer_dev_sent, 0))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_PENDING, s->peer_dev_pending))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_UNACKED, s->peer_dev_unacked))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_OUT_OF_SYNC, s->peer_dev_out_of_sync, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RESYNC_FAILED, s->peer_dev_resync_failed, 0))
+		goto nla_put_failure;
+	if (nla_put_u64_64bit(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_BITMAP_UUID, s->peer_dev_bitmap_uuid, 0))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS, s->peer_dev_flags))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int drbd_notification_header_to_skb(struct sk_buff *skb, struct drbd_notification_header *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_NOTIFICATION_HEADER);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put_u32(skb, DRBD_A_DRBD_NOTIFICATION_HEADER_NH_TYPE, s->nh_type))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+int drbd_helper_info_to_skb(struct sk_buff *skb, struct drbd_helper_info *s)
+{
+	struct nlattr *tla = nla_nest_start(skb, DRBD_NLA_HELPER);
+
+	if (!tla)
+		goto nla_put_failure;
+
+	if (nla_put(skb, DRBD_A_DRBD_HELPER_INFO_HELPER_NAME, min_t(int, 32,
+			s->helper_name_len + (s->helper_name_len < 32)), s->helper_name))
+		goto nla_put_failure;
+	if (nla_put_u32(skb, DRBD_A_DRBD_HELPER_INFO_HELPER_STATUS, s->helper_status))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, tla);
+	return 0;
+
+nla_put_failure:
+	if (tla)
+		nla_nest_cancel(skb, tla);
+	return -EMSGSIZE;
+}
+
+void set_disk_conf_defaults(struct disk_conf *x)
+{
+	x->on_io_error = DRBD_ON_IO_ERROR_DEF;
+	x->fencing = DRBD_FENCING_DEF;
+	x->resync_rate = DRBD_RESYNC_RATE_DEF;
+	x->resync_after = DRBD_MINOR_NUMBER_DEF;
+	x->al_extents = DRBD_AL_EXTENTS_DEF;
+	x->c_plan_ahead = DRBD_C_PLAN_AHEAD_DEF;
+	x->c_delay_target = DRBD_C_DELAY_TARGET_DEF;
+	x->c_fill_target = DRBD_C_FILL_TARGET_DEF;
+	x->c_max_rate = DRBD_C_MAX_RATE_DEF;
+	x->c_min_rate = DRBD_C_MIN_RATE_DEF;
+	x->disk_barrier = DRBD_DISK_BARRIER_DEF;
+	x->disk_flushes = DRBD_DISK_FLUSHES_DEF;
+	x->disk_drain = DRBD_DISK_DRAIN_DEF;
+	x->md_flushes = DRBD_MD_FLUSHES_DEF;
+	x->disk_timeout = DRBD_DISK_TIMEOUT_DEF;
+	x->read_balancing = DRBD_READ_BALANCING_DEF;
+	x->al_updates = DRBD_AL_UPDATES_DEF;
+	x->discard_zeroes_if_aligned = DRBD_DISCARD_ZEROES_IF_ALIGNED_DEF;
+	x->rs_discard_granularity = DRBD_RS_DISCARD_GRANULARITY_DEF;
+	x->disable_write_same = DRBD_DISABLE_WRITE_SAME_DEF;
+}
+
+void set_res_opts_defaults(struct res_opts *x)
+{
+	memset(x->cpu_mask, 0, sizeof(x->cpu_mask));
+	x->cpu_mask_len = 0;
+	x->on_no_data = DRBD_ON_NO_DATA_DEF;
+}
+
+void set_net_conf_defaults(struct net_conf *x)
+{
+	memset(x->shared_secret, 0, sizeof(x->shared_secret));
+	x->shared_secret_len = 0;
+	memset(x->cram_hmac_alg, 0, sizeof(x->cram_hmac_alg));
+	x->cram_hmac_alg_len = 0;
+	memset(x->integrity_alg, 0, sizeof(x->integrity_alg));
+	x->integrity_alg_len = 0;
+	memset(x->verify_alg, 0, sizeof(x->verify_alg));
+	x->verify_alg_len = 0;
+	memset(x->csums_alg, 0, sizeof(x->csums_alg));
+	x->csums_alg_len = 0;
+	x->wire_protocol = DRBD_PROTOCOL_DEF;
+	x->connect_int = DRBD_CONNECT_INT_DEF;
+	x->timeout = DRBD_TIMEOUT_DEF;
+	x->ping_int = DRBD_PING_INT_DEF;
+	x->ping_timeo = DRBD_PING_TIMEO_DEF;
+	x->sndbuf_size = DRBD_SNDBUF_SIZE_DEF;
+	x->rcvbuf_size = DRBD_RCVBUF_SIZE_DEF;
+	x->ko_count = DRBD_KO_COUNT_DEF;
+	x->max_buffers = DRBD_MAX_BUFFERS_DEF;
+	x->max_epoch_size = DRBD_MAX_EPOCH_SIZE_DEF;
+	x->unplug_watermark = DRBD_UNPLUG_WATERMARK_DEF;
+	x->after_sb_0p = DRBD_AFTER_SB_0P_DEF;
+	x->after_sb_1p = DRBD_AFTER_SB_1P_DEF;
+	x->after_sb_2p = DRBD_AFTER_SB_2P_DEF;
+	x->rr_conflict = DRBD_RR_CONFLICT_DEF;
+	x->on_congestion = DRBD_ON_CONGESTION_DEF;
+	x->cong_fill = DRBD_CONG_FILL_DEF;
+	x->cong_extents = DRBD_CONG_EXTENTS_DEF;
+	x->two_primaries = DRBD_ALLOW_TWO_PRIMARIES_DEF;
+	x->tcp_cork = DRBD_TCP_CORK_DEF;
+	x->always_asbp = DRBD_ALWAYS_ASBP_DEF;
+	x->use_rle = DRBD_USE_RLE_DEF;
+	x->csums_after_crash_only = DRBD_CSUMS_AFTER_CRASH_ONLY_DEF;
+	x->sock_check_timeo = DRBD_SOCKET_CHECK_TIMEO_DEF;
+}
+
+void set_resize_parms_defaults(struct resize_parms *x)
+{
+	x->al_stripes = DRBD_AL_STRIPES_DEF;
+	x->al_stripe_size = DRBD_AL_STRIPE_SIZE_DEF;
+}
diff --git a/drivers/block/drbd/drbd_nl_gen.h b/drivers/block/drbd/drbd_nl_gen.h
new file mode 100644
index 000000000000..5f195fedce93
--- /dev/null
+++ b/drivers/block/drbd/drbd_nl_gen.h
@@ -0,0 +1,399 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/*	Documentation/netlink/specs/drbd.yaml */
+/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#ifndef _LINUX_DRBD_GEN_H
+#define _LINUX_DRBD_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/drbd_genl.h>
+#include <linux/drbd.h>
+#include <linux/drbd_limits.h>
+
+/* Common nested types */
+extern const struct nla_policy drbd_connection_info_nl_policy[DRBD_A_CONNECTION_INFO_CONN_ROLE + 1];
+extern const struct nla_policy drbd_connection_statistics_nl_policy[DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED + 1];
+extern const struct nla_policy drbd_detach_parms_nl_policy[DRBD_A_DETACH_PARMS_FORCE_DETACH + 1];
+extern const struct nla_policy drbd_device_info_nl_policy[DRBD_A_DEVICE_INFO_DEV_DISK_STATE + 1];
+extern const struct nla_policy drbd_device_statistics_nl_policy[DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS + 1];
+extern const struct nla_policy drbd_disconnect_parms_nl_policy[DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT + 1];
+extern const struct nla_policy drbd_disk_conf_nl_policy[DRBD_A_DISK_CONF_DISABLE_WRITE_SAME + 1];
+extern const struct nla_policy drbd_drbd_cfg_context_nl_policy[DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR + 1];
+extern const struct nla_policy drbd_net_conf_nl_policy[DRBD_A_NET_CONF_SOCK_CHECK_TIMEO + 1];
+extern const struct nla_policy drbd_new_c_uuid_parms_nl_policy[DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM + 1];
+extern const struct nla_policy drbd_peer_device_info_nl_policy[DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY + 1];
+extern const struct nla_policy drbd_peer_device_statistics_nl_policy[DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS + 1];
+extern const struct nla_policy drbd_res_opts_nl_policy[DRBD_A_RES_OPTS_ON_NO_DATA + 1];
+extern const struct nla_policy drbd_resize_parms_nl_policy[DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE + 1];
+extern const struct nla_policy drbd_resource_info_nl_policy[DRBD_A_RESOURCE_INFO_RES_SUSP_FEN + 1];
+extern const struct nla_policy drbd_resource_statistics_nl_policy[DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING + 1];
+extern const struct nla_policy drbd_set_role_parms_nl_policy[DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE + 1];
+extern const struct nla_policy drbd_start_ov_parms_nl_policy[DRBD_A_START_OV_PARMS_OV_STOP_SECTOR + 1];
+
+/* Ops table for drbd */
+extern const struct genl_split_ops drbd_nl_ops[32];
+
+int drbd_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+		  struct genl_info *info);
+void
+drbd_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+	       struct genl_info *info);
+int drbd_adm_dump_devices_done(struct netlink_callback *cb);
+int drbd_adm_dump_connections_done(struct netlink_callback *cb);
+int drbd_adm_dump_peer_devices_done(struct netlink_callback *cb);
+
+int drbd_nl_get_status_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_get_status_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int drbd_nl_new_minor_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_del_minor_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_new_resource_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_del_resource_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_resource_opts_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_connect_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_disconnect_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_attach_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_resize_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_primary_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_secondary_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_new_c_uuid_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_start_ov_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_detach_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_invalidate_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_inval_peer_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_pause_sync_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_resume_sync_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_suspend_io_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_resume_io_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_outdate_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_get_timeout_type_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_down_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_chg_disk_opts_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_chg_net_opts_doit(struct sk_buff *skb, struct genl_info *info);
+int drbd_nl_get_resources_dumpit(struct sk_buff *skb,
+				 struct netlink_callback *cb);
+int drbd_nl_get_devices_dumpit(struct sk_buff *skb,
+			       struct netlink_callback *cb);
+int drbd_nl_get_connections_dumpit(struct sk_buff *skb,
+				   struct netlink_callback *cb);
+int drbd_nl_get_peer_devices_dumpit(struct sk_buff *skb,
+				    struct netlink_callback *cb);
+int drbd_nl_get_initial_state_dumpit(struct sk_buff *skb,
+				     struct netlink_callback *cb);
+
+enum {
+	DRBD_NLGRP_EVENTS,
+};
+
+struct drbd_cfg_reply {
+	char info_text[0];
+	__u32 info_text_len;
+};
+
+struct drbd_cfg_context {
+	__u32 ctx_volume;
+	char ctx_resource_name[128];
+	__u32 ctx_resource_name_len;
+	char ctx_my_addr[128];
+	__u32 ctx_my_addr_len;
+	char ctx_peer_addr[128];
+	__u32 ctx_peer_addr_len;
+};
+
+struct disk_conf {
+	char backing_dev[128];
+	__u32 backing_dev_len;
+	char meta_dev[128];
+	__u32 meta_dev_len;
+	__s32 meta_dev_idx;
+	__u64 disk_size;
+	__u32 max_bio_bvecs;
+	__u32 on_io_error;
+	__u32 fencing;
+	__u32 resync_rate;
+	__s32 resync_after;
+	__u32 al_extents;
+	__u32 c_plan_ahead;
+	__u32 c_delay_target;
+	__u32 c_fill_target;
+	__u32 c_max_rate;
+	__u32 c_min_rate;
+	unsigned char disk_barrier;
+	unsigned char disk_flushes;
+	unsigned char disk_drain;
+	unsigned char md_flushes;
+	__u32 disk_timeout;
+	__u32 read_balancing;
+	unsigned char al_updates;
+	unsigned char discard_zeroes_if_aligned;
+	__u32 rs_discard_granularity;
+	unsigned char disable_write_same;
+};
+
+struct res_opts {
+	char cpu_mask[DRBD_CPU_MASK_SIZE];
+	__u32 cpu_mask_len;
+	__u32 on_no_data;
+};
+
+struct net_conf {
+	char shared_secret[SHARED_SECRET_MAX];
+	__u32 shared_secret_len;
+	char cram_hmac_alg[SHARED_SECRET_MAX];
+	__u32 cram_hmac_alg_len;
+	char integrity_alg[SHARED_SECRET_MAX];
+	__u32 integrity_alg_len;
+	char verify_alg[SHARED_SECRET_MAX];
+	__u32 verify_alg_len;
+	char csums_alg[SHARED_SECRET_MAX];
+	__u32 csums_alg_len;
+	__u32 wire_protocol;
+	__u32 connect_int;
+	__u32 timeout;
+	__u32 ping_int;
+	__u32 ping_timeo;
+	__u32 sndbuf_size;
+	__u32 rcvbuf_size;
+	__u32 ko_count;
+	__u32 max_buffers;
+	__u32 max_epoch_size;
+	__u32 unplug_watermark;
+	__u32 after_sb_0p;
+	__u32 after_sb_1p;
+	__u32 after_sb_2p;
+	__u32 rr_conflict;
+	__u32 on_congestion;
+	__u32 cong_fill;
+	__u32 cong_extents;
+	unsigned char two_primaries;
+	unsigned char discard_my_data;
+	unsigned char tcp_cork;
+	unsigned char always_asbp;
+	unsigned char tentative;
+	unsigned char use_rle;
+	unsigned char csums_after_crash_only;
+	__u32 sock_check_timeo;
+};
+
+struct set_role_parms {
+	unsigned char assume_uptodate;
+};
+
+struct resize_parms {
+	__u64 resize_size;
+	unsigned char resize_force;
+	unsigned char no_resync;
+	__u32 al_stripes;
+	__u32 al_stripe_size;
+};
+
+struct state_info {
+	__u32 sib_reason;
+	__u32 current_state;
+	__u64 capacity;
+	__u64 ed_uuid;
+	__u32 prev_state;
+	__u32 new_state;
+	char uuids[DRBD_NL_UUIDS_SIZE];
+	__u32 uuids_len;
+	__u32 disk_flags;
+	__u64 bits_total;
+	__u64 bits_oos;
+	__u64 bits_rs_total;
+	__u64 bits_rs_failed;
+	char helper[32];
+	__u32 helper_len;
+	__u32 helper_exit_code;
+	__u64 send_cnt;
+	__u64 recv_cnt;
+	__u64 read_cnt;
+	__u64 writ_cnt;
+	__u64 al_writ_cnt;
+	__u64 bm_writ_cnt;
+	__u32 ap_bio_cnt;
+	__u32 ap_pending_cnt;
+	__u32 rs_pending_cnt;
+};
+
+struct start_ov_parms {
+	__u64 ov_start_sector;
+	__u64 ov_stop_sector;
+};
+
+struct new_c_uuid_parms {
+	unsigned char clear_bm;
+};
+
+struct timeout_parms {
+	__u32 timeout_type;
+};
+
+struct disconnect_parms {
+	unsigned char force_disconnect;
+};
+
+struct detach_parms {
+	unsigned char force_detach;
+};
+
+struct resource_info {
+	__u32 res_role;
+	unsigned char res_susp;
+	unsigned char res_susp_nod;
+	unsigned char res_susp_fen;
+};
+
+struct device_info {
+	__u32 dev_disk_state;
+};
+
+struct connection_info {
+	__u32 conn_connection_state;
+	__u32 conn_role;
+};
+
+struct peer_device_info {
+	__u32 peer_repl_state;
+	__u32 peer_disk_state;
+	__u32 peer_resync_susp_user;
+	__u32 peer_resync_susp_peer;
+	__u32 peer_resync_susp_dependency;
+};
+
+struct resource_statistics {
+	__u32 res_stat_write_ordering;
+};
+
+struct device_statistics {
+	__u64 dev_size;
+	__u64 dev_read;
+	__u64 dev_write;
+	__u64 dev_al_writes;
+	__u64 dev_bm_writes;
+	__u32 dev_upper_pending;
+	__u32 dev_lower_pending;
+	unsigned char dev_upper_blocked;
+	unsigned char dev_lower_blocked;
+	unsigned char dev_al_suspended;
+	__u64 dev_exposed_data_uuid;
+	__u64 dev_current_uuid;
+	__u32 dev_disk_flags;
+	char history_uuids[DRBD_NL_HISTORY_UUIDS_SIZE];
+	__u32 history_uuids_len;
+};
+
+struct connection_statistics {
+	unsigned char conn_congested;
+};
+
+struct peer_device_statistics {
+	__u64 peer_dev_received;
+	__u64 peer_dev_sent;
+	__u32 peer_dev_pending;
+	__u32 peer_dev_unacked;
+	__u64 peer_dev_out_of_sync;
+	__u64 peer_dev_resync_failed;
+	__u64 peer_dev_bitmap_uuid;
+	__u32 peer_dev_flags;
+};
+
+struct drbd_notification_header {
+	__u32 nh_type;
+};
+
+struct drbd_helper_info {
+	char helper_name[32];
+	__u32 helper_name_len;
+	__u32 helper_status;
+};
+
+int drbd_cfg_reply_to_skb(struct sk_buff *skb, struct drbd_cfg_reply *s);
+
+int drbd_cfg_context_from_attrs(struct drbd_cfg_context *s, struct genl_info *info);
+int drbd_cfg_context_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int drbd_cfg_context_to_skb(struct sk_buff *skb, struct drbd_cfg_context *s);
+
+int disk_conf_from_attrs(struct disk_conf *s, struct genl_info *info);
+int disk_conf_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int disk_conf_to_skb(struct sk_buff *skb, struct disk_conf *s);
+void set_disk_conf_defaults(struct disk_conf *x);
+
+int res_opts_from_attrs(struct res_opts *s, struct genl_info *info);
+int res_opts_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int res_opts_to_skb(struct sk_buff *skb, struct res_opts *s);
+void set_res_opts_defaults(struct res_opts *x);
+
+int net_conf_from_attrs(struct net_conf *s, struct genl_info *info);
+int net_conf_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int net_conf_to_skb(struct sk_buff *skb, struct net_conf *s);
+void set_net_conf_defaults(struct net_conf *x);
+
+int set_role_parms_from_attrs(struct set_role_parms *s, struct genl_info *info);
+int set_role_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int set_role_parms_to_skb(struct sk_buff *skb, struct set_role_parms *s);
+
+int resize_parms_from_attrs(struct resize_parms *s, struct genl_info *info);
+int resize_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int resize_parms_to_skb(struct sk_buff *skb, struct resize_parms *s);
+void set_resize_parms_defaults(struct resize_parms *x);
+
+int state_info_to_skb(struct sk_buff *skb, struct state_info *s);
+
+int start_ov_parms_from_attrs(struct start_ov_parms *s, struct genl_info *info);
+int start_ov_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int start_ov_parms_to_skb(struct sk_buff *skb, struct start_ov_parms *s);
+
+int new_c_uuid_parms_from_attrs(struct new_c_uuid_parms *s, struct genl_info *info);
+int new_c_uuid_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int new_c_uuid_parms_to_skb(struct sk_buff *skb, struct new_c_uuid_parms *s);
+
+int timeout_parms_to_skb(struct sk_buff *skb, struct timeout_parms *s);
+
+int disconnect_parms_from_attrs(struct disconnect_parms *s, struct genl_info *info);
+int disconnect_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int disconnect_parms_to_skb(struct sk_buff *skb, struct disconnect_parms *s);
+
+int detach_parms_from_attrs(struct detach_parms *s, struct genl_info *info);
+int detach_parms_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int detach_parms_to_skb(struct sk_buff *skb, struct detach_parms *s);
+
+int resource_info_from_attrs(struct resource_info *s, struct genl_info *info);
+int resource_info_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int resource_info_to_skb(struct sk_buff *skb, struct resource_info *s);
+
+int device_info_from_attrs(struct device_info *s, struct genl_info *info);
+int device_info_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int device_info_to_skb(struct sk_buff *skb, struct device_info *s);
+
+int connection_info_from_attrs(struct connection_info *s, struct genl_info *info);
+int connection_info_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int connection_info_to_skb(struct sk_buff *skb, struct connection_info *s);
+
+int peer_device_info_from_attrs(struct peer_device_info *s, struct genl_info *info);
+int peer_device_info_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int peer_device_info_to_skb(struct sk_buff *skb, struct peer_device_info *s);
+
+int resource_statistics_from_attrs(struct resource_statistics *s, struct genl_info *info);
+int resource_statistics_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int resource_statistics_to_skb(struct sk_buff *skb, struct resource_statistics *s);
+
+int device_statistics_from_attrs(struct device_statistics *s, struct genl_info *info);
+int device_statistics_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int device_statistics_to_skb(struct sk_buff *skb, struct device_statistics *s);
+
+int connection_statistics_from_attrs(struct connection_statistics *s, struct genl_info *info);
+int connection_statistics_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int connection_statistics_to_skb(struct sk_buff *skb, struct connection_statistics *s);
+
+int peer_device_statistics_from_attrs(struct peer_device_statistics *s, struct genl_info *info);
+int peer_device_statistics_ntb_from_attrs(struct nlattr ***ret_nested_attribute_table, struct genl_info *info);
+int peer_device_statistics_to_skb(struct sk_buff *skb, struct peer_device_statistics *s);
+
+int drbd_notification_header_to_skb(struct sk_buff *skb, struct drbd_notification_header *s);
+
+int drbd_helper_info_to_skb(struct sk_buff *skb, struct drbd_helper_info *s);
+
+#endif /* _LINUX_DRBD_GEN_H */
diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c
index 1d0feafceadc..6d0c12c10260 100644
--- a/drivers/block/drbd/drbd_proc.c
+++ b/drivers/block/drbd/drbd_proc.c
@@ -228,7 +228,7 @@ int drbd_seq_show(struct seq_file *seq, void *v)
 	};
 
 	seq_printf(seq, "version: " REL_VERSION " (api:%d/proto:%d-%d)\n%s\n",
-		   GENL_MAGIC_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag());
+		   DRBD_FAMILY_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag());
 
 	/*
 	  cs .. connection state
diff --git a/include/linux/drbd_genl.h b/include/linux/drbd_genl.h
deleted file mode 100644
index f53c534aba0c..000000000000
--- a/include/linux/drbd_genl.h
+++ /dev/null
@@ -1,536 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * General overview:
- * full generic netlink message:
- * |nlmsghdr|genlmsghdr|<payload>
- *
- * payload:
- * |optional fixed size family header|<sequence of netlink attributes>
- *
- * sequence of netlink attributes:
- * I chose to have all "top level" attributes NLA_NESTED,
- * corresponding to some real struct.
- * So we have a sequence of |tla, len|<nested nla sequence>
- *
- * nested nla sequence:
- * may be empty, or contain a sequence of netlink attributes
- * representing the struct fields.
- *
- * The tag number of any field (regardless of containing struct)
- * will be available as T_ ## field_name,
- * so you cannot have the same field name in two differnt structs.
- *
- * The tag numbers themselves are per struct, though,
- * so should always begin at 1 (not 0, that is the special "NLA_UNSPEC" type,
- * which we won't use here).
- * The tag numbers are used as index in the respective nla_policy array.
- *
- * GENL_struct(tag_name, tag_number, struct name, struct fields) - struct and policy
- *	genl_magic_struct.h
- *		generates the struct declaration,
- *		generates an entry in the tla enum,
- *	genl_magic_func.h
- *		generates an entry in the static tla policy
- *		with .type = NLA_NESTED
- *		generates the static <struct_name>_nl_policy definition,
- *		and static conversion functions
- *
- *	genl_magic_func.h
- *
- * GENL_mc_group(group)
- *	genl_magic_struct.h
- *		does nothing
- *	genl_magic_func.h
- *		defines and registers the mcast group,
- *		and provides a send helper
- *
- * GENL_notification(op_name, op_num, mcast_group, tla list)
- *	These are notifications to userspace.
- *
- *	genl_magic_struct.h
- *		generates an entry in the genl_ops enum,
- *	genl_magic_func.h
- *		does nothing
- *
- *	mcast group: the name of the mcast group this notification should be
- *	expected on
- *	tla list: the list of expected top level attributes,
- *	for documentation and sanity checking.
- *
- * GENL_op(op_name, op_num, flags and handler, tla list) - "genl operations"
- *	These are requests from userspace.
- *
- *	_op and _notification share the same "number space",
- *	op_nr will be assigned to "genlmsghdr->cmd"
- *
- *	genl_magic_struct.h
- *		generates an entry in the genl_ops enum,
- *	genl_magic_func.h
- *		generates an entry in the static genl_ops array,
- *		and static register/unregister functions to
- *		genl_register_family().
- *
- *	flags and handler:
- *		GENL_op_init( .doit = x, .dumpit = y, .flags = something)
- *		GENL_doit(x) => .dumpit = NULL, .flags = GENL_ADMIN_PERM
- *	tla list: the list of expected top level attributes,
- *	for documentation and sanity checking.
- */
-
-/*
- * STRUCTS
- */
-
-/* this is sent kernel -> userland on various error conditions, and contains
- * informational textual info, which is supposedly human readable.
- * The computer relevant return code is in the drbd_genlmsghdr.
- */
-GENL_struct(DRBD_NLA_CFG_REPLY, 1, drbd_cfg_reply,
-		/* "arbitrary" size strings, nla_policy.len = 0 */
-	__str_field(1, 0,	info_text, 0)
-)
-
-/* Configuration requests typically need a context to operate on.
- * Possible keys are device minor (fits in the drbd_genlmsghdr),
- * the replication link (aka connection) name,
- * and/or the replication group (aka resource) name,
- * and the volume id within the resource. */
-GENL_struct(DRBD_NLA_CFG_CONTEXT, 2, drbd_cfg_context,
-	__u32_field(1, 0,	ctx_volume)
-	__str_field(2, 0,	ctx_resource_name, 128)
-	__bin_field(3, 0,	ctx_my_addr, 128)
-	__bin_field(4, 0,	ctx_peer_addr, 128)
-)
-
-GENL_struct(DRBD_NLA_DISK_CONF, 3, disk_conf,
-	__str_field(1, DRBD_F_REQUIRED | DRBD_F_INVARIANT,	backing_dev,	128)
-	__str_field(2, DRBD_F_REQUIRED | DRBD_F_INVARIANT,	meta_dev,	128)
-	__s32_field(3, DRBD_F_REQUIRED | DRBD_F_INVARIANT,	meta_dev_idx)
-
-	/* use the resize command to try and change the disk_size */
-	__u64_field(4, DRBD_F_INVARIANT,	disk_size)
-	/* we could change the max_bio_bvecs,
-	 * but it won't propagate through the stack */
-	__u32_field(5, DRBD_F_INVARIANT,	max_bio_bvecs)
-
-	__u32_field_def(6, 0,	on_io_error, DRBD_ON_IO_ERROR_DEF)
-	__u32_field_def(7, 0,	fencing, DRBD_FENCING_DEF)
-
-	__u32_field_def(8,	0,	resync_rate, DRBD_RESYNC_RATE_DEF)
-	__s32_field_def(9,	0,	resync_after, DRBD_MINOR_NUMBER_DEF)
-	__u32_field_def(10,	0,	al_extents, DRBD_AL_EXTENTS_DEF)
-	__u32_field_def(11,	0,	c_plan_ahead, DRBD_C_PLAN_AHEAD_DEF)
-	__u32_field_def(12,	0,	c_delay_target, DRBD_C_DELAY_TARGET_DEF)
-	__u32_field_def(13,	0,	c_fill_target, DRBD_C_FILL_TARGET_DEF)
-	__u32_field_def(14,	0,	c_max_rate, DRBD_C_MAX_RATE_DEF)
-	__u32_field_def(15,	0,	c_min_rate, DRBD_C_MIN_RATE_DEF)
-	__u32_field_def(20,     0, disk_timeout, DRBD_DISK_TIMEOUT_DEF)
-	__u32_field_def(21,     0 /* OPTIONAL */,       read_balancing, DRBD_READ_BALANCING_DEF)
-	__u32_field_def(25,     0 /* OPTIONAL */,       rs_discard_granularity, DRBD_RS_DISCARD_GRANULARITY_DEF)
-
-	__flg_field_def(16, 0,	disk_barrier, DRBD_DISK_BARRIER_DEF)
-	__flg_field_def(17, 0,	disk_flushes, DRBD_DISK_FLUSHES_DEF)
-	__flg_field_def(18, 0,	disk_drain, DRBD_DISK_DRAIN_DEF)
-	__flg_field_def(19, 0,	md_flushes, DRBD_MD_FLUSHES_DEF)
-	__flg_field_def(23,     0 /* OPTIONAL */,	al_updates, DRBD_AL_UPDATES_DEF)
-	__flg_field_def(24,     0 /* OPTIONAL */,	discard_zeroes_if_aligned, DRBD_DISCARD_ZEROES_IF_ALIGNED_DEF)
-	__flg_field_def(26,     0 /* OPTIONAL */,	disable_write_same, DRBD_DISABLE_WRITE_SAME_DEF)
-)
-
-GENL_struct(DRBD_NLA_RESOURCE_OPTS, 4, res_opts,
-	__str_field_def(1,	0,	cpu_mask,       DRBD_CPU_MASK_SIZE)
-	__u32_field_def(2,	0,	on_no_data, DRBD_ON_NO_DATA_DEF)
-)
-
-GENL_struct(DRBD_NLA_NET_CONF, 5, net_conf,
-	__str_field_def(1,	DRBD_F_SENSITIVE,
-						shared_secret,	SHARED_SECRET_MAX)
-	__str_field_def(2,	0,	cram_hmac_alg,	SHARED_SECRET_MAX)
-	__str_field_def(3,	0,	integrity_alg,	SHARED_SECRET_MAX)
-	__str_field_def(4,	0,	verify_alg,     SHARED_SECRET_MAX)
-	__str_field_def(5,	0,	csums_alg,	SHARED_SECRET_MAX)
-	__u32_field_def(6,	0,	wire_protocol, DRBD_PROTOCOL_DEF)
-	__u32_field_def(7,	0,	connect_int, DRBD_CONNECT_INT_DEF)
-	__u32_field_def(8,	0,	timeout, DRBD_TIMEOUT_DEF)
-	__u32_field_def(9,	0,	ping_int, DRBD_PING_INT_DEF)
-	__u32_field_def(10,	0,	ping_timeo, DRBD_PING_TIMEO_DEF)
-	__u32_field_def(11,	0,	sndbuf_size, DRBD_SNDBUF_SIZE_DEF)
-	__u32_field_def(12,	0,	rcvbuf_size, DRBD_RCVBUF_SIZE_DEF)
-	__u32_field_def(13,	0,	ko_count, DRBD_KO_COUNT_DEF)
-	__u32_field_def(14,	0,	max_buffers, DRBD_MAX_BUFFERS_DEF)
-	__u32_field_def(15,	0,	max_epoch_size, DRBD_MAX_EPOCH_SIZE_DEF)
-	__u32_field_def(16,	0,	unplug_watermark, DRBD_UNPLUG_WATERMARK_DEF)
-	__u32_field_def(17,	0,	after_sb_0p, DRBD_AFTER_SB_0P_DEF)
-	__u32_field_def(18,	0,	after_sb_1p, DRBD_AFTER_SB_1P_DEF)
-	__u32_field_def(19,	0,	after_sb_2p, DRBD_AFTER_SB_2P_DEF)
-	__u32_field_def(20,	0,	rr_conflict, DRBD_RR_CONFLICT_DEF)
-	__u32_field_def(21,	0,	on_congestion, DRBD_ON_CONGESTION_DEF)
-	__u32_field_def(22,	0,	cong_fill, DRBD_CONG_FILL_DEF)
-	__u32_field_def(23,	0,	cong_extents, DRBD_CONG_EXTENTS_DEF)
-	__flg_field_def(24, 0,	two_primaries, DRBD_ALLOW_TWO_PRIMARIES_DEF)
-	__flg_field(25, DRBD_F_INVARIANT,	discard_my_data)
-	__flg_field_def(26, 0,	tcp_cork, DRBD_TCP_CORK_DEF)
-	__flg_field_def(27, 0,	always_asbp, DRBD_ALWAYS_ASBP_DEF)
-	__flg_field(28, DRBD_F_INVARIANT,	tentative)
-	__flg_field_def(29,	0,	use_rle, DRBD_USE_RLE_DEF)
-	/* 9: __u32_field_def(30,	0,	fencing_policy, DRBD_FENCING_DEF) */
-	/* 9: __str_field_def(31,     0, name, SHARED_SECRET_MAX) */
-	/* 9: __u32_field(32,         DRBD_F_REQUIRED | DRBD_F_INVARIANT,     peer_node_id) */
-	__flg_field_def(33, 0 /* OPTIONAL */,	csums_after_crash_only, DRBD_CSUMS_AFTER_CRASH_ONLY_DEF)
-	__u32_field_def(34, 0 /* OPTIONAL */, sock_check_timeo, DRBD_SOCKET_CHECK_TIMEO_DEF)
-)
-
-GENL_struct(DRBD_NLA_SET_ROLE_PARMS, 6, set_role_parms,
-	__flg_field(1, 0,	assume_uptodate)
-)
-
-GENL_struct(DRBD_NLA_RESIZE_PARMS, 7, resize_parms,
-	__u64_field(1, 0,	resize_size)
-	__flg_field(2, 0,	resize_force)
-	__flg_field(3, 0,	no_resync)
-	__u32_field_def(4, 0 /* OPTIONAL */, al_stripes, DRBD_AL_STRIPES_DEF)
-	__u32_field_def(5, 0 /* OPTIONAL */, al_stripe_size, DRBD_AL_STRIPE_SIZE_DEF)
-)
-
-GENL_struct(DRBD_NLA_STATE_INFO, 8, state_info,
-	/* the reason of the broadcast,
-	 * if this is an event triggered broadcast. */
-	__u32_field(1, 0,	sib_reason)
-	__u32_field(2, DRBD_F_REQUIRED,	current_state)
-	__u64_field(3, 0,	capacity)
-	__u64_field(4, 0,	ed_uuid)
-
-	/* These are for broadcast from after state change work.
-	 * prev_state and new_state are from the moment the state change took
-	 * place, new_state is not neccessarily the same as current_state,
-	 * there may have been more state changes since.  Which will be
-	 * broadcasted soon, in their respective after state change work.  */
-	__u32_field(5, 0,	prev_state)
-	__u32_field(6, 0,	new_state)
-
-	/* if we have a local disk: */
-	__bin_field(7, 0,	uuids, (UI_SIZE*sizeof(__u64)))
-	__u32_field(8, 0,	disk_flags)
-	__u64_field(9, 0,	bits_total)
-	__u64_field(10, 0,	bits_oos)
-	/* and in case resync or online verify is active */
-	__u64_field(11, 0,	bits_rs_total)
-	__u64_field(12, 0,	bits_rs_failed)
-
-	/* for pre and post notifications of helper execution */
-	__str_field(13, 0,	helper, 32)
-	__u32_field(14, 0,	helper_exit_code)
-
-	__u64_field(15,                      0, send_cnt)
-	__u64_field(16,                      0, recv_cnt)
-	__u64_field(17,                      0, read_cnt)
-	__u64_field(18,                      0, writ_cnt)
-	__u64_field(19,                      0, al_writ_cnt)
-	__u64_field(20,                      0, bm_writ_cnt)
-	__u32_field(21,                      0, ap_bio_cnt)
-	__u32_field(22,                      0, ap_pending_cnt)
-	__u32_field(23,                      0, rs_pending_cnt)
-)
-
-GENL_struct(DRBD_NLA_START_OV_PARMS, 9, start_ov_parms,
-	__u64_field(1, 0,	ov_start_sector)
-	__u64_field(2, 0,	ov_stop_sector)
-)
-
-GENL_struct(DRBD_NLA_NEW_C_UUID_PARMS, 10, new_c_uuid_parms,
-	__flg_field(1, 0, clear_bm)
-)
-
-GENL_struct(DRBD_NLA_TIMEOUT_PARMS, 11, timeout_parms,
-	__u32_field(1,	DRBD_F_REQUIRED,	timeout_type)
-)
-
-GENL_struct(DRBD_NLA_DISCONNECT_PARMS, 12, disconnect_parms,
-	__flg_field(1, 0,	force_disconnect)
-)
-
-GENL_struct(DRBD_NLA_DETACH_PARMS, 13, detach_parms,
-	__flg_field(1, 0,	force_detach)
-)
-
-GENL_struct(DRBD_NLA_RESOURCE_INFO, 15, resource_info,
-	__u32_field(1, 0, res_role)
-	__flg_field(2, 0, res_susp)
-	__flg_field(3, 0, res_susp_nod)
-	__flg_field(4, 0, res_susp_fen)
-	/* __flg_field(5, 0, res_weak) */
-)
-
-GENL_struct(DRBD_NLA_DEVICE_INFO, 16, device_info,
-	__u32_field(1, 0, dev_disk_state)
-)
-
-GENL_struct(DRBD_NLA_CONNECTION_INFO, 17, connection_info,
-	__u32_field(1, 0, conn_connection_state)
-	__u32_field(2, 0, conn_role)
-)
-
-GENL_struct(DRBD_NLA_PEER_DEVICE_INFO, 18, peer_device_info,
-	__u32_field(1, 0, peer_repl_state)
-	__u32_field(2, 0, peer_disk_state)
-	__u32_field(3, 0, peer_resync_susp_user)
-	__u32_field(4, 0, peer_resync_susp_peer)
-	__u32_field(5, 0, peer_resync_susp_dependency)
-)
-
-GENL_struct(DRBD_NLA_RESOURCE_STATISTICS, 19, resource_statistics,
-	__u32_field(1, 0, res_stat_write_ordering)
-)
-
-GENL_struct(DRBD_NLA_DEVICE_STATISTICS, 20, device_statistics,
-	__u64_field(1, 0, dev_size)  /* (sectors) */
-	__u64_field(2, 0, dev_read)  /* (sectors) */
-	__u64_field(3, 0, dev_write)  /* (sectors) */
-	__u64_field(4, 0, dev_al_writes)  /* activity log writes (count) */
-	__u64_field(5, 0, dev_bm_writes)  /*  bitmap writes  (count) */
-	__u32_field(6, 0, dev_upper_pending)  /* application requests in progress */
-	__u32_field(7, 0, dev_lower_pending)  /* backing device requests in progress */
-	__flg_field(8, 0, dev_upper_blocked)
-	__flg_field(9, 0, dev_lower_blocked)
-	__flg_field(10, 0, dev_al_suspended)  /* activity log suspended */
-	__u64_field(11, 0, dev_exposed_data_uuid)
-	__u64_field(12, 0, dev_current_uuid)
-	__u32_field(13, 0, dev_disk_flags)
-	__bin_field(14, 0, history_uuids, HISTORY_UUIDS * sizeof(__u64))
-)
-
-GENL_struct(DRBD_NLA_CONNECTION_STATISTICS, 21, connection_statistics,
-	__flg_field(1, 0, conn_congested)
-)
-
-GENL_struct(DRBD_NLA_PEER_DEVICE_STATISTICS, 22, peer_device_statistics,
-	__u64_field(1, 0, peer_dev_received)  /* sectors */
-	__u64_field(2, 0, peer_dev_sent)  /* sectors */
-	__u32_field(3, 0, peer_dev_pending)  /* number of requests */
-	__u32_field(4, 0, peer_dev_unacked)  /* number of requests */
-	__u64_field(5, 0, peer_dev_out_of_sync)  /* sectors */
-	__u64_field(6, 0, peer_dev_resync_failed)  /* sectors */
-	__u64_field(7, 0, peer_dev_bitmap_uuid)
-	__u32_field(9, 0, peer_dev_flags)
-)
-
-GENL_struct(DRBD_NLA_NOTIFICATION_HEADER, 23, drbd_notification_header,
-	__u32_field(1, 0, nh_type)
-)
-
-GENL_struct(DRBD_NLA_HELPER, 24, drbd_helper_info,
-	__str_field(1, 0, helper_name, 32)
-	__u32_field(2, 0, helper_status)
-)
-
-/*
- * Notifications and commands (genlmsghdr->cmd)
- */
-GENL_mc_group(events)
-
-	/* kernel -> userspace announcement of changes */
-GENL_notification(
-	DRBD_EVENT, 1, events,
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_STATE_INFO, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_NET_CONF, 0)
-	GENL_tla_expected(DRBD_NLA_DISK_CONF, 0)
-	GENL_tla_expected(DRBD_NLA_SYNCER_CONF, 0)
-)
-
-	/* query kernel for specific or all info */
-GENL_op(
-	DRBD_ADM_GET_STATUS, 2,
-	GENL_op_init(
-		.doit = drbd_adm_get_status,
-		.dumpit = drbd_adm_get_status_all,
-		/* anyone may ask for the status,
-		 * it is broadcasted anyways */
-	),
-	/* To select the object .doit.
-	 * Or a subset of objects in .dumpit. */
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, 0)
-)
-
-	/* add DRBD minor devices as volumes to resources */
-GENL_op(DRBD_ADM_NEW_MINOR, 5, GENL_doit(drbd_adm_new_minor),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-GENL_op(DRBD_ADM_DEL_MINOR, 6, GENL_doit(drbd_adm_del_minor),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-
-	/* add or delete resources */
-GENL_op(DRBD_ADM_NEW_RESOURCE, 7, GENL_doit(drbd_adm_new_resource),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-GENL_op(DRBD_ADM_DEL_RESOURCE, 8, GENL_doit(drbd_adm_del_resource),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-
-GENL_op(DRBD_ADM_RESOURCE_OPTS, 9,
-	GENL_doit(drbd_adm_resource_opts),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_RESOURCE_OPTS, 0)
-)
-
-GENL_op(
-	DRBD_ADM_CONNECT, 10,
-	GENL_doit(drbd_adm_connect),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_NET_CONF, DRBD_F_REQUIRED)
-)
-
-GENL_op(
-	DRBD_ADM_CHG_NET_OPTS, 29,
-	GENL_doit(drbd_adm_net_opts),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_NET_CONF, DRBD_F_REQUIRED)
-)
-
-GENL_op(DRBD_ADM_DISCONNECT, 11, GENL_doit(drbd_adm_disconnect),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-
-GENL_op(DRBD_ADM_ATTACH, 12,
-	GENL_doit(drbd_adm_attach),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_DISK_CONF, DRBD_F_REQUIRED)
-)
-
-GENL_op(DRBD_ADM_CHG_DISK_OPTS, 28,
-	GENL_doit(drbd_adm_disk_opts),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_DISK_OPTS, DRBD_F_REQUIRED)
-)
-
-GENL_op(
-	DRBD_ADM_RESIZE, 13,
-	GENL_doit(drbd_adm_resize),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_RESIZE_PARMS, 0)
-)
-
-GENL_op(
-	DRBD_ADM_PRIMARY, 14,
-	GENL_doit(drbd_adm_set_role),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_SET_ROLE_PARMS, DRBD_F_REQUIRED)
-)
-
-GENL_op(
-	DRBD_ADM_SECONDARY, 15,
-	GENL_doit(drbd_adm_set_role),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_SET_ROLE_PARMS, DRBD_F_REQUIRED)
-)
-
-GENL_op(
-	DRBD_ADM_NEW_C_UUID, 16,
-	GENL_doit(drbd_adm_new_c_uuid),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_NEW_C_UUID_PARMS, 0)
-)
-
-GENL_op(
-	DRBD_ADM_START_OV, 17,
-	GENL_doit(drbd_adm_start_ov),
-	GENL_tla_expected(DRBD_NLA_START_OV_PARMS, 0)
-)
-
-GENL_op(DRBD_ADM_DETACH,	18, GENL_doit(drbd_adm_detach),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_DETACH_PARMS, 0))
-
-GENL_op(DRBD_ADM_INVALIDATE,	19, GENL_doit(drbd_adm_invalidate),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-GENL_op(DRBD_ADM_INVAL_PEER,	20, GENL_doit(drbd_adm_invalidate_peer),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-GENL_op(DRBD_ADM_PAUSE_SYNC,	21, GENL_doit(drbd_adm_pause_sync),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-GENL_op(DRBD_ADM_RESUME_SYNC,	22, GENL_doit(drbd_adm_resume_sync),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-GENL_op(DRBD_ADM_SUSPEND_IO,	23, GENL_doit(drbd_adm_suspend_io),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-GENL_op(DRBD_ADM_RESUME_IO,	24, GENL_doit(drbd_adm_resume_io),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-GENL_op(DRBD_ADM_OUTDATE,	25, GENL_doit(drbd_adm_outdate),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-GENL_op(DRBD_ADM_GET_TIMEOUT_TYPE, 26, GENL_doit(drbd_adm_get_timeout_type),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-GENL_op(DRBD_ADM_DOWN,		27, GENL_doit(drbd_adm_down),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED))
-
-GENL_op(DRBD_ADM_GET_RESOURCES, 30,
-	 GENL_op_init(
-		 .dumpit = drbd_adm_dump_resources,
-	 ),
-	 GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, 0)
-	 GENL_tla_expected(DRBD_NLA_RESOURCE_INFO, 0)
-	 GENL_tla_expected(DRBD_NLA_RESOURCE_STATISTICS, 0))
-
-GENL_op(DRBD_ADM_GET_DEVICES, 31,
-	 GENL_op_init(
-		 .dumpit = drbd_adm_dump_devices,
-		 .done = drbd_adm_dump_devices_done,
-	 ),
-	 GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, 0)
-	 GENL_tla_expected(DRBD_NLA_DEVICE_INFO, 0)
-	 GENL_tla_expected(DRBD_NLA_DEVICE_STATISTICS, 0))
-
-GENL_op(DRBD_ADM_GET_CONNECTIONS, 32,
-	 GENL_op_init(
-		 .dumpit = drbd_adm_dump_connections,
-		 .done = drbd_adm_dump_connections_done,
-	 ),
-	 GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, 0)
-	 GENL_tla_expected(DRBD_NLA_CONNECTION_INFO, 0)
-	 GENL_tla_expected(DRBD_NLA_CONNECTION_STATISTICS, 0))
-
-GENL_op(DRBD_ADM_GET_PEER_DEVICES, 33,
-	 GENL_op_init(
-		 .dumpit = drbd_adm_dump_peer_devices,
-		 .done = drbd_adm_dump_peer_devices_done,
-	 ),
-	 GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, 0)
-	 GENL_tla_expected(DRBD_NLA_PEER_DEVICE_INFO, 0)
-	 GENL_tla_expected(DRBD_NLA_PEER_DEVICE_STATISTICS, 0))
-
-GENL_notification(
-	DRBD_RESOURCE_STATE, 34, events,
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_NOTIFICATION_HEADER, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_RESOURCE_INFO, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_RESOURCE_STATISTICS, DRBD_F_REQUIRED))
-
-GENL_notification(
-	DRBD_DEVICE_STATE, 35, events,
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_NOTIFICATION_HEADER, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_DEVICE_INFO, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_DEVICE_STATISTICS, DRBD_F_REQUIRED))
-
-GENL_notification(
-	DRBD_CONNECTION_STATE, 36, events,
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_NOTIFICATION_HEADER, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_CONNECTION_INFO, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_CONNECTION_STATISTICS, DRBD_F_REQUIRED))
-
-GENL_notification(
-	DRBD_PEER_DEVICE_STATE, 37, events,
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_NOTIFICATION_HEADER, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_PEER_DEVICE_INFO, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_PEER_DEVICE_STATISTICS, DRBD_F_REQUIRED))
-
-GENL_op(
-	DRBD_ADM_GET_INITIAL_STATE, 38,
-	GENL_op_init(
-	        .dumpit = drbd_adm_get_initial_state,
-	),
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, 0))
-
-GENL_notification(
-	DRBD_HELPER, 40, events,
-	GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)
-	GENL_tla_expected(DRBD_NLA_HELPER, DRBD_F_REQUIRED))
-
-GENL_notification(
-	DRBD_INITIAL_STATE_DONE, 41, events,
-	GENL_tla_expected(DRBD_NLA_NOTIFICATION_HEADER, DRBD_F_REQUIRED))
diff --git a/include/linux/drbd_genl_api.h b/include/linux/drbd_genl_api.h
deleted file mode 100644
index 19d263924852..000000000000
--- a/include/linux/drbd_genl_api.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef DRBD_GENL_STRUCT_H
-#define DRBD_GENL_STRUCT_H
-
-/* hack around predefined gcc/cpp "linux=1",
- * we cannot possibly include <1/drbd_genl.h> */
-#undef linux
-
-#include <linux/drbd.h>
-#define GENL_MAGIC_VERSION	1
-#define GENL_MAGIC_FAMILY	drbd
-#define GENL_MAGIC_FAMILY_HDRSZ	sizeof(struct drbd_genlmsghdr)
-#define GENL_MAGIC_INCLUDE_FILE <linux/drbd_genl.h>
-#include <linux/genl_magic_struct.h>
-
-#endif
diff --git a/include/linux/genl_magic_func.h b/include/linux/genl_magic_func.h
deleted file mode 100644
index a7d36c9ea924..000000000000
--- a/include/linux/genl_magic_func.h
+++ /dev/null
@@ -1,413 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef GENL_MAGIC_FUNC_H
-#define GENL_MAGIC_FUNC_H
-
-#include <linux/args.h>
-#include <linux/build_bug.h>
-#include <linux/genl_magic_struct.h>
-
-/*
- * Magic: declare tla policy						{{{1
- * Magic: declare nested policies
- *									{{{2
- */
-#undef GENL_mc_group
-#define GENL_mc_group(group)
-
-#undef GENL_notification
-#define GENL_notification(op_name, op_num, mcast_group, tla_list)
-
-#undef GENL_op
-#define GENL_op(op_name, op_num, handler, tla_list)
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)		\
-	[tag_name] = { .type = NLA_NESTED },
-
-static struct nla_policy CONCATENATE(GENL_MAGIC_FAMILY, _tla_nl_policy)[] = {
-#include GENL_MAGIC_INCLUDE_FILE
-};
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)		\
-static struct nla_policy s_name ## _nl_policy[] __read_mostly =		\
-{ s_fields };
-
-#undef __field
-#define __field(attr_nr, attr_flag, name, nla_type, _type, __get,	\
-		 __put, __is_signed)					\
-	[attr_nr] = { .type = nla_type },
-
-#undef __array
-#define __array(attr_nr, attr_flag, name, nla_type, _type, maxlen,	\
-		__get, __put, __is_signed)				\
-	[attr_nr] = { .type = nla_type,					\
-		      .len = maxlen - (nla_type == NLA_NUL_STRING) },
-
-#include GENL_MAGIC_INCLUDE_FILE
-
-#ifndef __KERNEL__
-#ifndef pr_info
-#define pr_info(args...)	fprintf(stderr, args);
-#endif
-#endif
-
-#ifdef GENL_MAGIC_DEBUG
-static void dprint_field(const char *dir, int nla_type,
-		const char *name, void *valp)
-{
-	__u64 val = valp ? *(__u32 *)valp : 1;
-	switch (nla_type) {
-	case NLA_U8:  val = (__u8)val;
-	case NLA_U16: val = (__u16)val;
-	case NLA_U32: val = (__u32)val;
-		pr_info("%s attr %s: %d 0x%08x\n", dir,
-			name, (int)val, (unsigned)val);
-		break;
-	case NLA_U64:
-		val = *(__u64*)valp;
-		pr_info("%s attr %s: %lld 0x%08llx\n", dir,
-			name, (long long)val, (unsigned long long)val);
-		break;
-	case NLA_FLAG:
-		if (val)
-			pr_info("%s attr %s: set\n", dir, name);
-		break;
-	}
-}
-
-static void dprint_array(const char *dir, int nla_type,
-		const char *name, const char *val, unsigned len)
-{
-	switch (nla_type) {
-	case NLA_NUL_STRING:
-		if (len && val[len-1] == '\0')
-			len--;
-		pr_info("%s attr %s: [len:%u] '%s'\n", dir, name, len, val);
-		break;
-	default:
-		/* we can always show 4 byte,
-		 * thats what nlattr are aligned to. */
-		pr_info("%s attr %s: [len:%u] %02x%02x%02x%02x ...\n",
-			dir, name, len, val[0], val[1], val[2], val[3]);
-	}
-}
-
-#define DPRINT_TLA(a, op, b) pr_info("%s %s %s\n", a, op, b);
-
-/* Name is a member field name of the struct s.
- * If s is NULL (only parsing, no copy requested in *_from_attrs()),
- * nla is supposed to point to the attribute containing the information
- * corresponding to that struct member. */
-#define DPRINT_FIELD(dir, nla_type, name, s, nla)			\
-	do {								\
-		if (s)							\
-			dprint_field(dir, nla_type, #name, &s->name);	\
-		else if (nla)						\
-			dprint_field(dir, nla_type, #name,		\
-				(nla_type == NLA_FLAG) ? NULL		\
-						: nla_data(nla));	\
-	} while (0)
-
-#define	DPRINT_ARRAY(dir, nla_type, name, s, nla)			\
-	do {								\
-		if (s)							\
-			dprint_array(dir, nla_type, #name,		\
-					s->name, s->name ## _len);	\
-		else if (nla)						\
-			dprint_array(dir, nla_type, #name,		\
-					nla_data(nla), nla_len(nla));	\
-	} while (0)
-#else
-#define DPRINT_TLA(a, op, b) do {} while (0)
-#define DPRINT_FIELD(dir, nla_type, name, s, nla) do {} while (0)
-#define	DPRINT_ARRAY(dir, nla_type, name, s, nla) do {} while (0)
-#endif
-
-/*
- * Magic: provide conversion functions					{{{1
- * populate struct from attribute table:
- *									{{{2
- */
-
-/* processing of generic netlink messages is serialized.
- * use one static buffer for parsing of nested attributes */
-static struct nlattr *nested_attr_tb[128];
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)		\
-/* *_from_attrs functions are static, but potentially unused */		\
-static int __ ## s_name ## _from_attrs(struct s_name *s,		\
-		struct genl_info *info, bool exclude_invariants)	\
-{									\
-	const int maxtype = ARRAY_SIZE(s_name ## _nl_policy)-1;		\
-	struct nlattr *tla = info->attrs[tag_number];			\
-	struct nlattr **ntb = nested_attr_tb;				\
-	struct nlattr *nla;						\
-	int err;							\
-	BUILD_BUG_ON(ARRAY_SIZE(s_name ## _nl_policy) > ARRAY_SIZE(nested_attr_tb));	\
-	if (!tla)							\
-		return -ENOMSG;						\
-	DPRINT_TLA(#s_name, "<=-", #tag_name);				\
-	err = nla_parse_nested_deprecated(ntb, maxtype, tla,			\
-					  s_name ## _nl_policy, NULL);	\
-	if (err)							\
-		return err;						\
-									\
-	s_fields							\
-	return 0;							\
-}					__attribute__((unused))		\
-static int s_name ## _from_attrs(struct s_name *s,			\
-						struct genl_info *info)	\
-{									\
-	return __ ## s_name ## _from_attrs(s, info, false);		\
-}					__attribute__((unused))		\
-static int s_name ## _from_attrs_for_change(struct s_name *s,		\
-						struct genl_info *info)	\
-{									\
-	return __ ## s_name ## _from_attrs(s, info, true);		\
-}					__attribute__((unused))		\
-
-#define __assign(attr_nr, attr_flag, name, nla_type, type, assignment...)	\
-		nla = ntb[attr_nr];						\
-		if (nla) {						\
-			if (exclude_invariants && !!((attr_flag) & DRBD_F_INVARIANT)) {		\
-				pr_info("<< must not change invariant attr: %s\n", #name);	\
-				return -EEXIST;				\
-			}						\
-			assignment;					\
-		} else if (exclude_invariants && !!((attr_flag) & DRBD_F_INVARIANT)) {		\
-			/* attribute missing from payload, */		\
-			/* which was expected */			\
-		} else if ((attr_flag) & DRBD_F_REQUIRED) {		\
-			pr_info("<< missing attr: %s\n", #name);	\
-			return -ENOMSG;					\
-		}
-
-#undef __field
-#define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put,	\
-		__is_signed)						\
-	__assign(attr_nr, attr_flag, name, nla_type, type,		\
-			if (s)						\
-				s->name = __get(nla);			\
-			DPRINT_FIELD("<<", nla_type, name, s, nla))
-
-/* validate_nla() already checked nla_len <= maxlen appropriately. */
-#undef __array
-#define __array(attr_nr, attr_flag, name, nla_type, type, maxlen,	\
-		__get, __put, __is_signed)				\
-	__assign(attr_nr, attr_flag, name, nla_type, type,		\
-			if (s)						\
-				s->name ## _len =			\
-					__get(s->name, nla, maxlen);	\
-			DPRINT_ARRAY("<<", nla_type, name, s, nla))
-
-#include GENL_MAGIC_INCLUDE_FILE
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)
-
-/*
- * Magic: define op number to op name mapping				{{{1
- *									{{{2
- */
-static const char *CONCATENATE(GENL_MAGIC_FAMILY, _genl_cmd_to_str)(__u8 cmd)
-{
-	switch (cmd) {
-#undef GENL_op
-#define GENL_op(op_name, op_num, handler, tla_list)		\
-	case op_num: return #op_name;
-#include GENL_MAGIC_INCLUDE_FILE
-	default:
-		     return "unknown";
-	}
-}
-
-#ifdef __KERNEL__
-#include <linux/stringify.h>
-/*
- * Magic: define genl_ops						{{{1
- *									{{{2
- */
-
-#undef GENL_op
-#define GENL_op(op_name, op_num, handler, tla_list)		\
-{								\
-	handler							\
-	.cmd = op_name,						\
-},
-
-#define ZZZ_genl_ops		CONCATENATE(GENL_MAGIC_FAMILY, _genl_ops)
-static struct genl_ops ZZZ_genl_ops[] __read_mostly = {
-#include GENL_MAGIC_INCLUDE_FILE
-};
-
-#undef GENL_op
-#define GENL_op(op_name, op_num, handler, tla_list)
-
-/*
- * Define the genl_family, multicast groups,				{{{1
- * and provide register/unregister functions.
- *									{{{2
- */
-#define ZZZ_genl_family		CONCATENATE(GENL_MAGIC_FAMILY, _genl_family)
-static struct genl_family ZZZ_genl_family;
-/*
- * Magic: define multicast groups
- * Magic: define multicast group registration helper
- */
-#define ZZZ_genl_mcgrps		CONCATENATE(GENL_MAGIC_FAMILY, _genl_mcgrps)
-static const struct genl_multicast_group ZZZ_genl_mcgrps[] = {
-#undef GENL_mc_group
-#define GENL_mc_group(group) { .name = #group, },
-#include GENL_MAGIC_INCLUDE_FILE
-};
-
-enum CONCATENATE(GENL_MAGIC_FAMILY, group_ids) {
-#undef GENL_mc_group
-#define GENL_mc_group(group) CONCATENATE(GENL_MAGIC_FAMILY, _group_ ## group),
-#include GENL_MAGIC_INCLUDE_FILE
-};
-
-#undef GENL_mc_group
-#define GENL_mc_group(group)						\
-static int CONCATENATE(GENL_MAGIC_FAMILY, _genl_multicast_ ## group)(	\
-	struct sk_buff *skb, gfp_t flags)				\
-{									\
-	unsigned int group_id =						\
-		CONCATENATE(GENL_MAGIC_FAMILY, _group_ ## group);		\
-	return genlmsg_multicast(&ZZZ_genl_family, skb, 0,		\
-				 group_id, flags);			\
-}
-
-#include GENL_MAGIC_INCLUDE_FILE
-
-#undef GENL_mc_group
-#define GENL_mc_group(group)
-
-static struct genl_family ZZZ_genl_family __ro_after_init = {
-	.name = __stringify(GENL_MAGIC_FAMILY),
-	.version = GENL_MAGIC_VERSION,
-#ifdef GENL_MAGIC_FAMILY_HDRSZ
-	.hdrsize = NLA_ALIGN(GENL_MAGIC_FAMILY_HDRSZ),
-#endif
-	.maxattr = ARRAY_SIZE(CONCATENATE(GENL_MAGIC_FAMILY, _tla_nl_policy))-1,
-	.policy	= CONCATENATE(GENL_MAGIC_FAMILY, _tla_nl_policy),
-#ifdef GENL_MAGIC_FAMILY_PRE_DOIT
-	.pre_doit = GENL_MAGIC_FAMILY_PRE_DOIT,
-	.post_doit = GENL_MAGIC_FAMILY_POST_DOIT,
-#endif
-	.ops = ZZZ_genl_ops,
-	.n_ops = ARRAY_SIZE(ZZZ_genl_ops),
-	.mcgrps = ZZZ_genl_mcgrps,
-	.resv_start_op = 42, /* drbd is currently the only user */
-	.n_mcgrps = ARRAY_SIZE(ZZZ_genl_mcgrps),
-	.module = THIS_MODULE,
-};
-
-int CONCATENATE(GENL_MAGIC_FAMILY, _genl_register)(void)
-{
-	return genl_register_family(&ZZZ_genl_family);
-}
-
-void CONCATENATE(GENL_MAGIC_FAMILY, _genl_unregister)(void)
-{
-	genl_unregister_family(&ZZZ_genl_family);
-}
-
-/*
- * Magic: provide conversion functions					{{{1
- * populate skb from struct.
- *									{{{2
- */
-
-#undef GENL_op
-#define GENL_op(op_name, op_num, handler, tla_list)
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)		\
-static int s_name ## _to_skb(struct sk_buff *skb, struct s_name *s,	\
-		const bool exclude_sensitive)				\
-{									\
-	struct nlattr *tla = nla_nest_start(skb, tag_number);		\
-	if (!tla)							\
-		goto nla_put_failure;					\
-	DPRINT_TLA(#s_name, "-=>", #tag_name);				\
-	s_fields							\
-	nla_nest_end(skb, tla);						\
-	return 0;							\
-									\
-nla_put_failure:							\
-	if (tla)							\
-		nla_nest_cancel(skb, tla);				\
-        return -EMSGSIZE;						\
-}									\
-static inline int s_name ## _to_priv_skb(struct sk_buff *skb,		\
-		struct s_name *s)					\
-{									\
-	return s_name ## _to_skb(skb, s, 0);				\
-}									\
-static inline int s_name ## _to_unpriv_skb(struct sk_buff *skb,		\
-		struct s_name *s)					\
-{									\
-	return s_name ## _to_skb(skb, s, 1);				\
-}
-
-
-#undef __field
-#define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put,	\
-		__is_signed)						\
-	if (!exclude_sensitive || !((attr_flag) & DRBD_F_SENSITIVE)) {	\
-		DPRINT_FIELD(">>", nla_type, name, s, NULL);		\
-		if (__put(skb, attr_nr, s->name))			\
-			goto nla_put_failure;				\
-	}
-
-#undef __array
-#define __array(attr_nr, attr_flag, name, nla_type, type, maxlen,	\
-		__get, __put, __is_signed)				\
-	if (!exclude_sensitive || !((attr_flag) & DRBD_F_SENSITIVE)) {	\
-		DPRINT_ARRAY(">>",nla_type, name, s, NULL);		\
-		if (__put(skb, attr_nr, min_t(int, maxlen,		\
-			s->name ## _len + (nla_type == NLA_NUL_STRING)),\
-						s->name))		\
-			goto nla_put_failure;				\
-	}
-
-#include GENL_MAGIC_INCLUDE_FILE
-
-
-/* Functions for initializing structs to default values.  */
-
-#undef __field
-#define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put,	\
-		__is_signed)
-#undef __array
-#define __array(attr_nr, attr_flag, name, nla_type, type, maxlen,	\
-		__get, __put, __is_signed)
-#undef __u32_field_def
-#define __u32_field_def(attr_nr, attr_flag, name, default)		\
-	x->name = default;
-#undef __s32_field_def
-#define __s32_field_def(attr_nr, attr_flag, name, default)		\
-	x->name = default;
-#undef __flg_field_def
-#define __flg_field_def(attr_nr, attr_flag, name, default)		\
-	x->name = default;
-#undef __str_field_def
-#define __str_field_def(attr_nr, attr_flag, name, maxlen)		\
-	memset(x->name, 0, sizeof(x->name));				\
-	x->name ## _len = 0;
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)		\
-static void set_ ## s_name ## _defaults(struct s_name *x) __attribute__((unused)); \
-static void set_ ## s_name ## _defaults(struct s_name *x) {	\
-s_fields								\
-}
-
-#include GENL_MAGIC_INCLUDE_FILE
-
-#endif /* __KERNEL__ */
-
-/* }}}1 */
-#endif /* GENL_MAGIC_FUNC_H */
diff --git a/include/linux/genl_magic_struct.h b/include/linux/genl_magic_struct.h
deleted file mode 100644
index 2200cedd160a..000000000000
--- a/include/linux/genl_magic_struct.h
+++ /dev/null
@@ -1,272 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef GENL_MAGIC_STRUCT_H
-#define GENL_MAGIC_STRUCT_H
-
-#ifndef GENL_MAGIC_FAMILY
-# error "you need to define GENL_MAGIC_FAMILY before inclusion"
-#endif
-
-#ifndef GENL_MAGIC_VERSION
-# error "you need to define GENL_MAGIC_VERSION before inclusion"
-#endif
-
-#ifndef GENL_MAGIC_INCLUDE_FILE
-# error "you need to define GENL_MAGIC_INCLUDE_FILE before inclusion"
-#endif
-
-#include <linux/args.h>
-#include <linux/types.h>
-#include <net/genetlink.h>
-
-extern int CONCATENATE(GENL_MAGIC_FAMILY, _genl_register)(void);
-extern void CONCATENATE(GENL_MAGIC_FAMILY, _genl_unregister)(void);
-
-/*
- * Extension of genl attribute validation policies			{{{2
- */
-
-/*
- * Flags specific to drbd and not visible at the netlink layer, used in
- * <struct>_from_attrs and <struct>_to_skb:
- *
- * @DRBD_F_REQUIRED: Attribute is required; a request without this attribute is
- * invalid.
- *
- * @DRBD_F_SENSITIVE: Attribute includes sensitive information and must not be
- * included in unpriviledged get requests or broadcasts.
- *
- * @DRBD_F_INVARIANT: Attribute is set when an object is initially created, but
- * cannot subsequently be changed.
- */
-#define DRBD_F_REQUIRED (1 << 0)
-#define DRBD_F_SENSITIVE (1 << 1)
-#define DRBD_F_INVARIANT (1 << 2)
-
-
-/*									}}}1
- * MAGIC
- * multi-include macro expansion magic starts here
- */
-
-/* MAGIC helpers							{{{2 */
-
-static inline int nla_put_u64_0pad(struct sk_buff *skb, int attrtype, u64 value)
-{
-	return nla_put_64bit(skb, attrtype, sizeof(u64), &value, 0);
-}
-
-/* possible field types */
-#define __flg_field(attr_nr, attr_flag, name) \
-	__field(attr_nr, attr_flag, name, NLA_U8, char, \
-			nla_get_u8, nla_put_u8, false)
-#define __u8_field(attr_nr, attr_flag, name)	\
-	__field(attr_nr, attr_flag, name, NLA_U8, unsigned char, \
-			nla_get_u8, nla_put_u8, false)
-#define __u16_field(attr_nr, attr_flag, name)	\
-	__field(attr_nr, attr_flag, name, NLA_U16, __u16, \
-			nla_get_u16, nla_put_u16, false)
-#define __u32_field(attr_nr, attr_flag, name)	\
-	__field(attr_nr, attr_flag, name, NLA_U32, __u32, \
-			nla_get_u32, nla_put_u32, false)
-#define __s32_field(attr_nr, attr_flag, name)	\
-	__field(attr_nr, attr_flag, name, NLA_U32, __s32, \
-			nla_get_u32, nla_put_u32, true)
-#define __u64_field(attr_nr, attr_flag, name)	\
-	__field(attr_nr, attr_flag, name, NLA_U64, __u64, \
-			nla_get_u64, nla_put_u64_0pad, false)
-#define __str_field(attr_nr, attr_flag, name, maxlen) \
-	__array(attr_nr, attr_flag, name, NLA_NUL_STRING, char, maxlen, \
-			nla_strscpy, nla_put, false)
-#define __bin_field(attr_nr, attr_flag, name, maxlen) \
-	__array(attr_nr, attr_flag, name, NLA_BINARY, char, maxlen, \
-			nla_memcpy, nla_put, false)
-
-/* fields with default values */
-#define __flg_field_def(attr_nr, attr_flag, name, default) \
-	__flg_field(attr_nr, attr_flag, name)
-#define __u32_field_def(attr_nr, attr_flag, name, default) \
-	__u32_field(attr_nr, attr_flag, name)
-#define __s32_field_def(attr_nr, attr_flag, name, default) \
-	__s32_field(attr_nr, attr_flag, name)
-#define __str_field_def(attr_nr, attr_flag, name, maxlen) \
-	__str_field(attr_nr, attr_flag, name, maxlen)
-
-#define GENL_op_init(args...)	args
-#define GENL_doit(handler)		\
-	.doit = handler,		\
-	.flags = GENL_ADMIN_PERM,
-#define GENL_dumpit(handler)		\
-	.dumpit = handler,		\
-	.flags = GENL_ADMIN_PERM,
-
-/*									}}}1
- * Magic: define the enum symbols for genl_ops
- * Magic: define the enum symbols for top level attributes
- * Magic: define the enum symbols for nested attributes
- *									{{{2
- */
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)
-
-#undef GENL_mc_group
-#define GENL_mc_group(group)
-
-#undef GENL_notification
-#define GENL_notification(op_name, op_num, mcast_group, tla_list)	\
-	op_name = op_num,
-
-#undef GENL_op
-#define GENL_op(op_name, op_num, handler, tla_list)			\
-	op_name = op_num,
-
-enum {
-#include GENL_MAGIC_INCLUDE_FILE
-};
-
-#undef GENL_notification
-#define GENL_notification(op_name, op_num, mcast_group, tla_list)
-
-#undef GENL_op
-#define GENL_op(op_name, op_num, handler, attr_list)
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields) \
-		tag_name = tag_number,
-
-enum {
-#include GENL_MAGIC_INCLUDE_FILE
-};
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)	\
-enum {								\
-	s_fields						\
-};
-
-#undef __field
-#define __field(attr_nr, attr_flag, name, nla_type, type,	\
-		__get, __put, __is_signed)			\
-	T_ ## name = (__u16)(attr_nr),
-
-#undef __array
-#define __array(attr_nr, attr_flag, name, nla_type, type,	\
-		maxlen, __get, __put, __is_signed)		\
-	T_ ## name = (__u16)(attr_nr),
-
-#include GENL_MAGIC_INCLUDE_FILE
-
-/*									}}}1
- * Magic: compile time assert unique numbers for operations
- * Magic: -"- unique numbers for top level attributes
- * Magic: -"- unique numbers for nested attributes
- *									{{{2
- */
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)
-
-#undef GENL_op
-#define GENL_op(op_name, op_num, handler, attr_list)	\
-	case op_name:
-
-#undef GENL_notification
-#define GENL_notification(op_name, op_num, mcast_group, tla_list)	\
-	case op_name:
-
-static inline void ct_assert_unique_operations(void)
-{
-	switch (0) {
-#include GENL_MAGIC_INCLUDE_FILE
-	case 0:
-		;
-	}
-}
-
-#undef GENL_op
-#define GENL_op(op_name, op_num, handler, attr_list)
-
-#undef GENL_notification
-#define GENL_notification(op_name, op_num, mcast_group, tla_list)
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)		\
-		case tag_number:
-
-static inline void ct_assert_unique_top_level_attributes(void)
-{
-	switch (0) {
-#include GENL_MAGIC_INCLUDE_FILE
-	case 0:
-		;
-	}
-}
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)		\
-static inline void ct_assert_unique_ ## s_name ## _attributes(void)	\
-{									\
-	switch (0) {							\
-	s_fields							\
-	case 0:								\
-			;						\
-	}								\
-}
-
-#undef __field
-#define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put,	\
-		__is_signed)						\
-	case attr_nr:
-
-#undef __array
-#define __array(attr_nr, attr_flag, name, nla_type, type, maxlen,	\
-		__get, __put, __is_signed)				\
-	case attr_nr:
-
-#include GENL_MAGIC_INCLUDE_FILE
-
-/*									}}}1
- * Magic: declare structs
- * struct <name> {
- *	fields
- * };
- *									{{{2
- */
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)		\
-struct s_name { s_fields };
-
-#undef __field
-#define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put,	\
-		__is_signed)						\
-	type name;
-
-#undef __array
-#define __array(attr_nr, attr_flag, name, nla_type, type, maxlen,	\
-		__get, __put, __is_signed)				\
-	type name[maxlen];	\
-	__u32 name ## _len;
-
-#include GENL_MAGIC_INCLUDE_FILE
-
-#undef GENL_struct
-#define GENL_struct(tag_name, tag_number, s_name, s_fields)		\
-enum {									\
-	s_fields							\
-};
-
-#undef __field
-#define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put,	\
-		is_signed)						\
-	F_ ## name ## _IS_SIGNED = is_signed,
-
-#undef __array
-#define __array(attr_nr, attr_flag, name, nla_type, type, maxlen,	\
-		__get, __put, is_signed)				\
-	F_ ## name ## _IS_SIGNED = is_signed,
-
-#include GENL_MAGIC_INCLUDE_FILE
-
-/* }}}1 */
-#endif /* GENL_MAGIC_STRUCT_H */
diff --git a/include/uapi/linux/drbd_genl.h b/include/uapi/linux/drbd_genl.h
new file mode 100644
index 000000000000..961f20248c47
--- /dev/null
+++ b/include/uapi/linux/drbd_genl.h
@@ -0,0 +1,363 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/*	Documentation/netlink/specs/drbd.yaml */
+/* YNL-GEN uapi header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#ifndef _UAPI_LINUX_DRBD_GENL_H
+#define _UAPI_LINUX_DRBD_GENL_H
+
+#define DRBD_FAMILY_NAME	"drbd"
+#define DRBD_FAMILY_VERSION	1
+
+enum {
+	DRBD_NLA_CFG_REPLY = 1,
+	DRBD_NLA_CFG_CONTEXT,
+	DRBD_NLA_DISK_CONF,
+	DRBD_NLA_RESOURCE_OPTS,
+	DRBD_NLA_NET_CONF,
+	DRBD_NLA_SET_ROLE_PARMS,
+	DRBD_NLA_RESIZE_PARMS,
+	DRBD_NLA_STATE_INFO,
+	DRBD_NLA_START_OV_PARMS,
+	DRBD_NLA_NEW_C_UUID_PARMS,
+	DRBD_NLA_TIMEOUT_PARMS,
+	DRBD_NLA_DISCONNECT_PARMS,
+	DRBD_NLA_DETACH_PARMS,
+	DRBD_NLA_RESOURCE_INFO = 15,
+	DRBD_NLA_DEVICE_INFO,
+	DRBD_NLA_CONNECTION_INFO,
+	DRBD_NLA_PEER_DEVICE_INFO,
+	DRBD_NLA_RESOURCE_STATISTICS,
+	DRBD_NLA_DEVICE_STATISTICS,
+	DRBD_NLA_CONNECTION_STATISTICS,
+	DRBD_NLA_PEER_DEVICE_STATISTICS,
+	DRBD_NLA_NOTIFICATION_HEADER,
+	DRBD_NLA_HELPER,
+
+	__DRBD_NLA_MAX,
+	DRBD_NLA_MAX = (__DRBD_NLA_MAX - 1)
+};
+
+enum {
+	DRBD_A_DRBD_CFG_REPLY_INFO_TEXT = 1,
+
+	__DRBD_A_DRBD_CFG_REPLY_MAX,
+	DRBD_A_DRBD_CFG_REPLY_MAX = (__DRBD_A_DRBD_CFG_REPLY_MAX - 1)
+};
+
+enum {
+	DRBD_A_DRBD_CFG_CONTEXT_CTX_VOLUME = 1,
+	DRBD_A_DRBD_CFG_CONTEXT_CTX_RESOURCE_NAME,
+	DRBD_A_DRBD_CFG_CONTEXT_CTX_MY_ADDR,
+	DRBD_A_DRBD_CFG_CONTEXT_CTX_PEER_ADDR,
+
+	__DRBD_A_DRBD_CFG_CONTEXT_MAX,
+	DRBD_A_DRBD_CFG_CONTEXT_MAX = (__DRBD_A_DRBD_CFG_CONTEXT_MAX - 1)
+};
+
+enum {
+	DRBD_A_DISK_CONF_BACKING_DEV = 1,
+	DRBD_A_DISK_CONF_META_DEV,
+	DRBD_A_DISK_CONF_META_DEV_IDX,
+	DRBD_A_DISK_CONF_DISK_SIZE,
+	DRBD_A_DISK_CONF_MAX_BIO_BVECS,
+	DRBD_A_DISK_CONF_ON_IO_ERROR,
+	DRBD_A_DISK_CONF_FENCING,
+	DRBD_A_DISK_CONF_RESYNC_RATE,
+	DRBD_A_DISK_CONF_RESYNC_AFTER,
+	DRBD_A_DISK_CONF_AL_EXTENTS,
+	DRBD_A_DISK_CONF_C_PLAN_AHEAD,
+	DRBD_A_DISK_CONF_C_DELAY_TARGET,
+	DRBD_A_DISK_CONF_C_FILL_TARGET,
+	DRBD_A_DISK_CONF_C_MAX_RATE,
+	DRBD_A_DISK_CONF_C_MIN_RATE,
+	DRBD_A_DISK_CONF_DISK_BARRIER,
+	DRBD_A_DISK_CONF_DISK_FLUSHES,
+	DRBD_A_DISK_CONF_DISK_DRAIN,
+	DRBD_A_DISK_CONF_MD_FLUSHES,
+	DRBD_A_DISK_CONF_DISK_TIMEOUT,
+	DRBD_A_DISK_CONF_READ_BALANCING,
+	DRBD_A_DISK_CONF_AL_UPDATES = 23,
+	DRBD_A_DISK_CONF_DISCARD_ZEROES_IF_ALIGNED,
+	DRBD_A_DISK_CONF_RS_DISCARD_GRANULARITY,
+	DRBD_A_DISK_CONF_DISABLE_WRITE_SAME,
+
+	__DRBD_A_DISK_CONF_MAX,
+	DRBD_A_DISK_CONF_MAX = (__DRBD_A_DISK_CONF_MAX - 1)
+};
+
+enum {
+	DRBD_A_RES_OPTS_CPU_MASK = 1,
+	DRBD_A_RES_OPTS_ON_NO_DATA,
+
+	__DRBD_A_RES_OPTS_MAX,
+	DRBD_A_RES_OPTS_MAX = (__DRBD_A_RES_OPTS_MAX - 1)
+};
+
+enum {
+	DRBD_A_NET_CONF_SHARED_SECRET = 1,
+	DRBD_A_NET_CONF_CRAM_HMAC_ALG,
+	DRBD_A_NET_CONF_INTEGRITY_ALG,
+	DRBD_A_NET_CONF_VERIFY_ALG,
+	DRBD_A_NET_CONF_CSUMS_ALG,
+	DRBD_A_NET_CONF_WIRE_PROTOCOL,
+	DRBD_A_NET_CONF_CONNECT_INT,
+	DRBD_A_NET_CONF_TIMEOUT,
+	DRBD_A_NET_CONF_PING_INT,
+	DRBD_A_NET_CONF_PING_TIMEO,
+	DRBD_A_NET_CONF_SNDBUF_SIZE,
+	DRBD_A_NET_CONF_RCVBUF_SIZE,
+	DRBD_A_NET_CONF_KO_COUNT,
+	DRBD_A_NET_CONF_MAX_BUFFERS,
+	DRBD_A_NET_CONF_MAX_EPOCH_SIZE,
+	DRBD_A_NET_CONF_UNPLUG_WATERMARK,
+	DRBD_A_NET_CONF_AFTER_SB_0P,
+	DRBD_A_NET_CONF_AFTER_SB_1P,
+	DRBD_A_NET_CONF_AFTER_SB_2P,
+	DRBD_A_NET_CONF_RR_CONFLICT,
+	DRBD_A_NET_CONF_ON_CONGESTION,
+	DRBD_A_NET_CONF_CONG_FILL,
+	DRBD_A_NET_CONF_CONG_EXTENTS,
+	DRBD_A_NET_CONF_TWO_PRIMARIES,
+	DRBD_A_NET_CONF_DISCARD_MY_DATA,
+	DRBD_A_NET_CONF_TCP_CORK,
+	DRBD_A_NET_CONF_ALWAYS_ASBP,
+	DRBD_A_NET_CONF_TENTATIVE,
+	DRBD_A_NET_CONF_USE_RLE,
+	DRBD_A_NET_CONF_CSUMS_AFTER_CRASH_ONLY = 33,
+	DRBD_A_NET_CONF_SOCK_CHECK_TIMEO,
+
+	__DRBD_A_NET_CONF_MAX,
+	DRBD_A_NET_CONF_MAX = (__DRBD_A_NET_CONF_MAX - 1)
+};
+
+enum {
+	DRBD_A_SET_ROLE_PARMS_ASSUME_UPTODATE = 1,
+
+	__DRBD_A_SET_ROLE_PARMS_MAX,
+	DRBD_A_SET_ROLE_PARMS_MAX = (__DRBD_A_SET_ROLE_PARMS_MAX - 1)
+};
+
+enum {
+	DRBD_A_RESIZE_PARMS_RESIZE_SIZE = 1,
+	DRBD_A_RESIZE_PARMS_RESIZE_FORCE,
+	DRBD_A_RESIZE_PARMS_NO_RESYNC,
+	DRBD_A_RESIZE_PARMS_AL_STRIPES,
+	DRBD_A_RESIZE_PARMS_AL_STRIPE_SIZE,
+
+	__DRBD_A_RESIZE_PARMS_MAX,
+	DRBD_A_RESIZE_PARMS_MAX = (__DRBD_A_RESIZE_PARMS_MAX - 1)
+};
+
+enum {
+	DRBD_A_STATE_INFO_SIB_REASON = 1,
+	DRBD_A_STATE_INFO_CURRENT_STATE,
+	DRBD_A_STATE_INFO_CAPACITY,
+	DRBD_A_STATE_INFO_ED_UUID,
+	DRBD_A_STATE_INFO_PREV_STATE,
+	DRBD_A_STATE_INFO_NEW_STATE,
+	DRBD_A_STATE_INFO_UUIDS,
+	DRBD_A_STATE_INFO_DISK_FLAGS,
+	DRBD_A_STATE_INFO_BITS_TOTAL,
+	DRBD_A_STATE_INFO_BITS_OOS,
+	DRBD_A_STATE_INFO_BITS_RS_TOTAL,
+	DRBD_A_STATE_INFO_BITS_RS_FAILED,
+	DRBD_A_STATE_INFO_HELPER,
+	DRBD_A_STATE_INFO_HELPER_EXIT_CODE,
+	DRBD_A_STATE_INFO_SEND_CNT,
+	DRBD_A_STATE_INFO_RECV_CNT,
+	DRBD_A_STATE_INFO_READ_CNT,
+	DRBD_A_STATE_INFO_WRIT_CNT,
+	DRBD_A_STATE_INFO_AL_WRIT_CNT,
+	DRBD_A_STATE_INFO_BM_WRIT_CNT,
+	DRBD_A_STATE_INFO_AP_BIO_CNT,
+	DRBD_A_STATE_INFO_AP_PENDING_CNT,
+	DRBD_A_STATE_INFO_RS_PENDING_CNT,
+
+	__DRBD_A_STATE_INFO_MAX,
+	DRBD_A_STATE_INFO_MAX = (__DRBD_A_STATE_INFO_MAX - 1)
+};
+
+enum {
+	DRBD_A_START_OV_PARMS_OV_START_SECTOR = 1,
+	DRBD_A_START_OV_PARMS_OV_STOP_SECTOR,
+
+	__DRBD_A_START_OV_PARMS_MAX,
+	DRBD_A_START_OV_PARMS_MAX = (__DRBD_A_START_OV_PARMS_MAX - 1)
+};
+
+enum {
+	DRBD_A_NEW_C_UUID_PARMS_CLEAR_BM = 1,
+
+	__DRBD_A_NEW_C_UUID_PARMS_MAX,
+	DRBD_A_NEW_C_UUID_PARMS_MAX = (__DRBD_A_NEW_C_UUID_PARMS_MAX - 1)
+};
+
+enum {
+	DRBD_A_TIMEOUT_PARMS_TIMEOUT_TYPE = 1,
+
+	__DRBD_A_TIMEOUT_PARMS_MAX,
+	DRBD_A_TIMEOUT_PARMS_MAX = (__DRBD_A_TIMEOUT_PARMS_MAX - 1)
+};
+
+enum {
+	DRBD_A_DISCONNECT_PARMS_FORCE_DISCONNECT = 1,
+
+	__DRBD_A_DISCONNECT_PARMS_MAX,
+	DRBD_A_DISCONNECT_PARMS_MAX = (__DRBD_A_DISCONNECT_PARMS_MAX - 1)
+};
+
+enum {
+	DRBD_A_DETACH_PARMS_FORCE_DETACH = 1,
+
+	__DRBD_A_DETACH_PARMS_MAX,
+	DRBD_A_DETACH_PARMS_MAX = (__DRBD_A_DETACH_PARMS_MAX - 1)
+};
+
+enum {
+	DRBD_A_RESOURCE_INFO_RES_ROLE = 1,
+	DRBD_A_RESOURCE_INFO_RES_SUSP,
+	DRBD_A_RESOURCE_INFO_RES_SUSP_NOD,
+	DRBD_A_RESOURCE_INFO_RES_SUSP_FEN,
+
+	__DRBD_A_RESOURCE_INFO_MAX,
+	DRBD_A_RESOURCE_INFO_MAX = (__DRBD_A_RESOURCE_INFO_MAX - 1)
+};
+
+enum {
+	DRBD_A_DEVICE_INFO_DEV_DISK_STATE = 1,
+
+	__DRBD_A_DEVICE_INFO_MAX,
+	DRBD_A_DEVICE_INFO_MAX = (__DRBD_A_DEVICE_INFO_MAX - 1)
+};
+
+enum {
+	DRBD_A_CONNECTION_INFO_CONN_CONNECTION_STATE = 1,
+	DRBD_A_CONNECTION_INFO_CONN_ROLE,
+
+	__DRBD_A_CONNECTION_INFO_MAX,
+	DRBD_A_CONNECTION_INFO_MAX = (__DRBD_A_CONNECTION_INFO_MAX - 1)
+};
+
+enum {
+	DRBD_A_PEER_DEVICE_INFO_PEER_REPL_STATE = 1,
+	DRBD_A_PEER_DEVICE_INFO_PEER_DISK_STATE,
+	DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_USER,
+	DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_PEER,
+	DRBD_A_PEER_DEVICE_INFO_PEER_RESYNC_SUSP_DEPENDENCY,
+
+	__DRBD_A_PEER_DEVICE_INFO_MAX,
+	DRBD_A_PEER_DEVICE_INFO_MAX = (__DRBD_A_PEER_DEVICE_INFO_MAX - 1)
+};
+
+enum {
+	DRBD_A_RESOURCE_STATISTICS_RES_STAT_WRITE_ORDERING = 1,
+
+	__DRBD_A_RESOURCE_STATISTICS_MAX,
+	DRBD_A_RESOURCE_STATISTICS_MAX = (__DRBD_A_RESOURCE_STATISTICS_MAX - 1)
+};
+
+enum {
+	DRBD_A_DEVICE_STATISTICS_DEV_SIZE = 1,
+	DRBD_A_DEVICE_STATISTICS_DEV_READ,
+	DRBD_A_DEVICE_STATISTICS_DEV_WRITE,
+	DRBD_A_DEVICE_STATISTICS_DEV_AL_WRITES,
+	DRBD_A_DEVICE_STATISTICS_DEV_BM_WRITES,
+	DRBD_A_DEVICE_STATISTICS_DEV_UPPER_PENDING,
+	DRBD_A_DEVICE_STATISTICS_DEV_LOWER_PENDING,
+	DRBD_A_DEVICE_STATISTICS_DEV_UPPER_BLOCKED,
+	DRBD_A_DEVICE_STATISTICS_DEV_LOWER_BLOCKED,
+	DRBD_A_DEVICE_STATISTICS_DEV_AL_SUSPENDED,
+	DRBD_A_DEVICE_STATISTICS_DEV_EXPOSED_DATA_UUID,
+	DRBD_A_DEVICE_STATISTICS_DEV_CURRENT_UUID,
+	DRBD_A_DEVICE_STATISTICS_DEV_DISK_FLAGS,
+	DRBD_A_DEVICE_STATISTICS_HISTORY_UUIDS,
+
+	__DRBD_A_DEVICE_STATISTICS_MAX,
+	DRBD_A_DEVICE_STATISTICS_MAX = (__DRBD_A_DEVICE_STATISTICS_MAX - 1)
+};
+
+enum {
+	DRBD_A_CONNECTION_STATISTICS_CONN_CONGESTED = 1,
+
+	__DRBD_A_CONNECTION_STATISTICS_MAX,
+	DRBD_A_CONNECTION_STATISTICS_MAX = (__DRBD_A_CONNECTION_STATISTICS_MAX - 1)
+};
+
+enum {
+	DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RECEIVED = 1,
+	DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_SENT,
+	DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_PENDING,
+	DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_UNACKED,
+	DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_OUT_OF_SYNC,
+	DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_RESYNC_FAILED,
+	DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_BITMAP_UUID,
+	DRBD_A_PEER_DEVICE_STATISTICS_PEER_DEV_FLAGS = 9,
+
+	__DRBD_A_PEER_DEVICE_STATISTICS_MAX,
+	DRBD_A_PEER_DEVICE_STATISTICS_MAX = (__DRBD_A_PEER_DEVICE_STATISTICS_MAX - 1)
+};
+
+enum {
+	DRBD_A_DRBD_NOTIFICATION_HEADER_NH_TYPE = 1,
+
+	__DRBD_A_DRBD_NOTIFICATION_HEADER_MAX,
+	DRBD_A_DRBD_NOTIFICATION_HEADER_MAX = (__DRBD_A_DRBD_NOTIFICATION_HEADER_MAX - 1)
+};
+
+enum {
+	DRBD_A_DRBD_HELPER_INFO_HELPER_NAME = 1,
+	DRBD_A_DRBD_HELPER_INFO_HELPER_STATUS,
+
+	__DRBD_A_DRBD_HELPER_INFO_MAX,
+	DRBD_A_DRBD_HELPER_INFO_MAX = (__DRBD_A_DRBD_HELPER_INFO_MAX - 1)
+};
+
+enum {
+	DRBD_ADM_EVENT = 1,
+	DRBD_ADM_GET_STATUS,
+	DRBD_ADM_NEW_MINOR = 5,
+	DRBD_ADM_DEL_MINOR,
+	DRBD_ADM_NEW_RESOURCE,
+	DRBD_ADM_DEL_RESOURCE,
+	DRBD_ADM_RESOURCE_OPTS,
+	DRBD_ADM_CONNECT,
+	DRBD_ADM_DISCONNECT,
+	DRBD_ADM_ATTACH,
+	DRBD_ADM_RESIZE,
+	DRBD_ADM_PRIMARY,
+	DRBD_ADM_SECONDARY,
+	DRBD_ADM_NEW_C_UUID,
+	DRBD_ADM_START_OV,
+	DRBD_ADM_DETACH,
+	DRBD_ADM_INVALIDATE,
+	DRBD_ADM_INVAL_PEER,
+	DRBD_ADM_PAUSE_SYNC,
+	DRBD_ADM_RESUME_SYNC,
+	DRBD_ADM_SUSPEND_IO,
+	DRBD_ADM_RESUME_IO,
+	DRBD_ADM_OUTDATE,
+	DRBD_ADM_GET_TIMEOUT_TYPE,
+	DRBD_ADM_DOWN,
+	DRBD_ADM_CHG_DISK_OPTS,
+	DRBD_ADM_CHG_NET_OPTS,
+	DRBD_ADM_GET_RESOURCES,
+	DRBD_ADM_GET_DEVICES,
+	DRBD_ADM_GET_CONNECTIONS,
+	DRBD_ADM_GET_PEER_DEVICES,
+	DRBD_ADM_RESOURCE_STATE,
+	DRBD_ADM_DEVICE_STATE,
+	DRBD_ADM_CONNECTION_STATE,
+	DRBD_ADM_PEER_DEVICE_STATE,
+	DRBD_ADM_GET_INITIAL_STATE,
+	DRBD_ADM_HELPER = 40,
+	DRBD_ADM_INITIAL_STATE_DONE,
+
+	__DRBD_ADM_MAX,
+	DRBD_ADM_MAX = (__DRBD_ADM_MAX - 1)
+};
+
+#define DRBD_MCGRP_EVENTS	"events"
+
+#endif /* _UAPI_LINUX_DRBD_GENL_H */
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH net-next v4 1/2] net: hsr: require valid EOT supervision TLV
From: Felix Maurer @ 2026-04-07 17:40 UTC (permalink / raw)
  To: luka.gejak; +Cc: davem, edumazet, kuba, pabeni, netdev, horms
In-Reply-To: <20260401092324.52266-2-luka.gejak@linux.dev>

On Wed, Apr 01, 2026 at 11:23:22AM +0200, luka.gejak@linux.dev wrote:
> From: Luka Gejak <luka.gejak@linux.dev>
>
> Supervision frames are only valid if terminated with a zero-length EOT
> TLV. The current check fails to reject non-EOT entries as the terminal
> TLV, potentially allowing malformed supervision traffic.
>
> Fix this by strictly requiring the terminal TLV to be HSR_TLV_EOT
> with a length of zero.
>
> Reviewed-by: Felix Maurer <fmaurer@redhat.com>

As Fernando already pointed out: I did not review this.

> Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
> ---
>  net/hsr/hsr_forward.c | 41 ++++++++++++++++++++++-------------------
>  1 file changed, 22 insertions(+), 19 deletions(-)
>
> diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
> index 0aca859c88cb..17b705235c4a 100644
> --- a/net/hsr/hsr_forward.c
> +++ b/net/hsr/hsr_forward.c
> @@ -82,39 +82,42 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
>  	    hsr_sup_tag->tlv.HSR_TLV_length != sizeof(struct hsr_sup_payload))
>  		return false;
>
> -	/* Get next tlv */
> +	/* Advance past the first TLV payload to reach next TLV header */
>  	total_length += hsr_sup_tag->tlv.HSR_TLV_length;
> -	if (!pskb_may_pull(skb, total_length))
> +	/* Linearize next TLV header before access */
> +	if (!pskb_may_pull(skb, total_length + sizeof(struct hsr_sup_tlv)))
>  		return false;
>  	skb_pull(skb, total_length);
>  	hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
>  	skb_push(skb, total_length);
>
> -	/* if this is a redbox supervision frame we need to verify
> -	 * that more data is available
> +	/* Walk through TLVs to find end-of-TLV marker, skipping any unknown
> +	 * extension TLVs to maintain forward compatibility.
>  	 */
> -	if (hsr_sup_tlv->HSR_TLV_type == PRP_TLV_REDBOX_MAC) {
> -		/* tlv length must be a length of a mac address */
> -		if (hsr_sup_tlv->HSR_TLV_length != sizeof(struct hsr_sup_payload))
> -			return false;
> +	for (;;) {
> +		if (hsr_sup_tlv->HSR_TLV_type == HSR_TLV_EOT &&
> +		    hsr_sup_tlv->HSR_TLV_length == 0)
> +			return true;
>
> -		/* make sure another tlv follows */
> -		total_length += sizeof(struct hsr_sup_tlv) + hsr_sup_tlv->HSR_TLV_length;
> -		if (!pskb_may_pull(skb, total_length))
> +		/* Validate known TLV types */
> +		if (hsr_sup_tlv->HSR_TLV_type == PRP_TLV_REDBOX_MAC) {
> +			if (hsr_sup_tlv->HSR_TLV_length !=
> +			    sizeof(struct hsr_sup_payload))
> +				return false;
> +		}
> +
> +		/* Advance past current TLV: header + payload */
> +		total_length += sizeof(struct hsr_sup_tlv) +
> +				hsr_sup_tlv->HSR_TLV_length;
> +		/* Linearize next TLV header before access */
> +		if (!pskb_may_pull(skb,
> +				   total_length + sizeof(struct hsr_sup_tlv)))
>  			return false;
>
> -		/* get next tlv */
>  		skb_pull(skb, total_length);
>  		hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data;
>  		skb_push(skb, total_length);
>  	}

As the commit message doesn't match the patch, I can only guess why this
loop is there: I assume this is because Jakub pointed you [1] to the AI
review of the patchset [2], explicitly stating that he didn't further
investigate it. The AI review touches two points, 1) a missing
pskb_may_pull() check, and 2) handling of unknown extension TLVs.

As discussed in the other email, I don't think we should do 2) here at
all. The frame format is fully specified and versioned, frames that
don't follow this are invalid.

Point 1) is actually a valid point, but the rambling of the AI why this
previously worked is wrong (it was a bug already previously). Please add
the pskb_may_pull() check in the next version.

Thanks,
   Felix


[1]: https://lore.kernel.org/netdev/20260331193758.5dd027f6@kernel.org/
[2]: https://sashiko.dev/#/patchset/20260329112313.17164-2-luka.gejak@linux.dev


^ permalink raw reply

* [PATCH v2] Bluetooth: Add Broadcom channel priority commands
From: Sasha Finkelstein via B4 Relay @ 2026-04-07 17:46 UTC (permalink / raw)
  To: Sven Peter, Janne Grunau, Neal Gompa, Marcel Holtmann,
	Luiz Augusto von Dentz, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman
  Cc: linux-kernel, asahi, linux-arm-kernel, linux-bluetooth, netdev,
	Sasha Finkelstein

From: Sasha Finkelstein <fnkl.kernel@gmail.com>

Certain Broadcom bluetooth chips (bcm4377/bcm4378/bcm438) need ACL
streams carrying audio to be set as "high priority" using a vendor
specific command to prevent 10-ish second-long dropouts whenever
something does a device scan. This patch sends the command when the
socket priority is set to TC_PRIO_INTERACTIVE, as BlueZ does for audio.

Signed-off-by: Sasha Finkelstein <fnkl.kernel@gmail.com>
---
Changes in v2:
- new ioctl got nack-ed, so let's use sk_priority as the trigger
- Link to v1: https://lore.kernel.org/r/20260407-brcm-prio-v1-1-f38b17376640@gmail.com
---
 MAINTAINERS                       |  2 ++
 drivers/bluetooth/hci_bcm4377.c   |  2 ++
 include/net/bluetooth/bluetooth.h |  4 ++++
 include/net/bluetooth/hci_core.h  | 11 +++++++++++
 net/bluetooth/Kconfig             |  7 +++++++
 net/bluetooth/Makefile            |  1 +
 net/bluetooth/brcm.c              | 29 +++++++++++++++++++++++++++++
 net/bluetooth/brcm.h              | 17 +++++++++++++++++
 net/bluetooth/hci_conn.c          | 28 ++++++++++++++++++++++++++++
 net/bluetooth/l2cap_sock.c        | 13 +++++++++++++
 10 files changed, 114 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index c3fe46d7c4bc..81be021367ec 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2562,6 +2562,8 @@ F:	include/dt-bindings/pinctrl/apple.h
 F:	include/linux/mfd/macsmc.h
 F:	include/linux/soc/apple/*
 F:	include/uapi/drm/asahi_drm.h
+F:	net/bluetooth/brcm.c
+F:	net/bluetooth/brcm.h
 
 ARM/ARTPEC MACHINE SUPPORT
 M:	Jesper Nilsson <jesper.nilsson@axis.com>
diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
index 925d0a635945..5f79920c0306 100644
--- a/drivers/bluetooth/hci_bcm4377.c
+++ b/drivers/bluetooth/hci_bcm4377.c
@@ -2397,6 +2397,8 @@ static int bcm4377_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if (bcm4377->hw->broken_le_ext_adv_report_phy)
 		hci_set_quirk(hdev, HCI_QUIRK_FIXUP_LE_EXT_ADV_REPORT_PHY);
 
+	hci_set_brcm_capable(hdev);
+
 	pci_set_drvdata(pdev, bcm4377);
 	hci_set_drvdata(hdev, bcm4377);
 	SET_HCIDEV_DEV(hdev, &pdev->dev);
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 69eed69f7f26..07a250673950 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -457,6 +457,7 @@ struct l2cap_ctrl {
 };
 
 struct hci_dev;
+struct hci_conn;
 
 typedef void (*hci_req_complete_t)(struct hci_dev *hdev, u8 status, u16 opcode);
 typedef void (*hci_req_complete_skb_t)(struct hci_dev *hdev, u8 status,
@@ -469,6 +470,9 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
 int hci_ethtool_ts_info(unsigned int index, int sk_proto,
 			struct kernel_ethtool_ts_info *ts_info);
 
+int hci_conn_setsockopt(struct hci_conn *conn, struct sock *sk, int level,
+			int optname, sockptr_t optval, unsigned int optlen);
+
 #define HCI_REQ_START	BIT(0)
 #define HCI_REQ_SKB	BIT(1)
 
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index a7bffb908c1e..947e7c2b08dd 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -642,6 +642,10 @@ struct hci_dev {
 	bool			aosp_quality_report;
 #endif
 
+#if IS_ENABLED(CONFIG_BT_BRCMEXT)
+	bool			brcm_capable;
+#endif
+
 	int (*open)(struct hci_dev *hdev);
 	int (*close)(struct hci_dev *hdev);
 	int (*flush)(struct hci_dev *hdev);
@@ -1791,6 +1795,13 @@ static inline void hci_set_aosp_capable(struct hci_dev *hdev)
 #endif
 }
 
+static inline void hci_set_brcm_capable(struct hci_dev *hdev)
+{
+#if IS_ENABLED(CONFIG_BT_BRCMEXT)
+	hdev->brcm_capable = true;
+#endif
+}
+
 static inline void hci_devcd_setup(struct hci_dev *hdev)
 {
 #ifdef CONFIG_DEV_COREDUMP
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 6b2b65a66700..0f2a5fbcafc5 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -110,6 +110,13 @@ config BT_AOSPEXT
 	  This options enables support for the Android Open Source
 	  Project defined HCI vendor extensions.
 
+config BT_BRCMEXT
+	bool "Enable Broadcom extensions"
+	depends on BT
+	help
+	  This option enables support for the Broadcom defined HCI
+	  vendor extensions.
+
 config BT_DEBUGFS
 	bool "Export Bluetooth internals in debugfs"
 	depends on BT && DEBUG_FS
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index a7eede7616d8..b4c9013a46ce 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -24,5 +24,6 @@ bluetooth-$(CONFIG_BT_LE) += iso.o
 bluetooth-$(CONFIG_BT_LEDS) += leds.o
 bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
 bluetooth-$(CONFIG_BT_AOSPEXT) += aosp.o
+bluetooth-$(CONFIG_BT_BRCMEXT) += brcm.o
 bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
 bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
diff --git a/net/bluetooth/brcm.c b/net/bluetooth/brcm.c
new file mode 100644
index 000000000000..9aa0a265ab3d
--- /dev/null
+++ b/net/bluetooth/brcm.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 The Asahi Linux Contributors
+ */
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "brcm.h"
+
+int brcm_set_high_priority(struct hci_dev *hdev, u16 handle, bool enable)
+{
+	struct sk_buff *skb;
+	u8 cmd[3];
+
+	if (!hdev->brcm_capable)
+		return 0;
+
+	cmd[0] = handle;
+	cmd[1] = handle >> 8;
+	cmd[2] = !!enable;
+
+	skb = hci_cmd_sync(hdev, 0xfc57, sizeof(cmd), cmd, HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	kfree_skb(skb);
+	return 0;
+}
diff --git a/net/bluetooth/brcm.h b/net/bluetooth/brcm.h
new file mode 100644
index 000000000000..fdaee63bd1d2
--- /dev/null
+++ b/net/bluetooth/brcm.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 The Asahi Linux Contributors
+ */
+
+#if IS_ENABLED(CONFIG_BT_BRCMEXT)
+
+int brcm_set_high_priority(struct hci_dev *hdev, u16 handle, bool enable);
+
+#else
+
+static inline int brcm_set_high_priority(struct hci_dev *hdev, u16 handle, bool enable)
+{
+	return 0;
+}
+
+#endif
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 11d3ad8d2551..096163840f62 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -35,6 +35,7 @@
 #include <net/bluetooth/iso.h>
 #include <net/bluetooth/mgmt.h>
 
+#include "brcm.h"
 #include "smp.h"
 #include "eir.h"
 
@@ -3070,6 +3071,33 @@ int hci_conn_set_phy(struct hci_conn *conn, u32 phys)
 	}
 }
 
+int hci_conn_setsockopt(struct hci_conn *conn, struct sock *sk, int level,
+			int optname, sockptr_t optval, unsigned int optlen)
+{
+	int val;
+	bool old_high, new_high, changed;
+
+	if (level != SOL_SOCKET)
+		return 0;
+
+	if (optname != SO_PRIORITY)
+		return 0;
+
+	if (optlen < sizeof(int))
+		return -EINVAL;
+
+	if (copy_from_sockptr(&val, optval, sizeof(val)))
+		return -EFAULT;
+
+	old_high = sk->sk_priority >= TC_PRIO_INTERACTIVE;
+	new_high = val >= TC_PRIO_INTERACTIVE;
+	changed = old_high != new_high;
+	if (!changed)
+		return 0;
+
+	return brcm_set_high_priority(conn->hdev, conn->handle, new_high);
+}
+
 static int abort_conn_sync(struct hci_dev *hdev, void *data)
 {
 	struct hci_conn *conn = data;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 71e8c1b45bce..d5eef87accc4 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -891,6 +891,16 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
 
 	BT_DBG("sk %p", sk);
 
+	if (level == SOL_SOCKET) {
+		conn = chan->conn;
+		if (conn)
+			err = hci_conn_setsockopt(conn->hcon, sock->sk, level,
+						  optname, optval, optlen);
+		if (err)
+			return err;
+		return sock_setsockopt(sock, level, optname, optval, optlen);
+	}
+
 	if (level == SOL_L2CAP)
 		return l2cap_sock_setsockopt_old(sock, optname, optval, optlen);
 
@@ -1931,6 +1941,9 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
 
 	INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
 
+	if (sock)
+		set_bit(SOCK_CUSTOM_SOCKOPT, &sock->flags);
+
 	chan = l2cap_chan_create();
 	if (!chan) {
 		sk_free(sk);

---
base-commit: bfe62a454542cfad3379f6ef5680b125f41e20f4
change-id: 20260407-brcm-prio-b630e6cc3834

Best regards,
-- 
Sasha Finkelstein <fnkl.kernel@gmail.com>



^ permalink raw reply related

* Re: [PATCH v9 02/10] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: Pawan Gupta @ 2026-04-07 17:52 UTC (permalink / raw)
  To: Jon Kohler
  Cc: Jim Mattson, x86@kernel.org, Nikolay Borisov, H. Peter Anvin,
	Josh Poimboeuf, David Kaplan, Sean Christopherson,
	Borislav Petkov, Dave Hansen, Peter Zijlstra, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, KP Singh, Jiri Olsa,
	David S. Miller, David Laight, Andy Lutomirski, Thomas Gleixner,
	Ingo Molnar, David Ahern, Martin KaFai Lau, Eduard Zingerman,
	Song Liu, Yonghong Song, John Fastabend, Stanislav Fomichev,
	Hao Luo, Paolo Bonzini, Jonathan Corbet,
	linux-kernel@vger.kernel.org, kvm@vger.kernel.org, Asit Mallick,
	Tao Zhang, bpf@vger.kernel.org, netdev@vger.kernel.org,
	linux-doc@vger.kernel.org, chao.gao@intel.com
In-Reply-To: <FAA31092-E1CA-4D79-8CEC-3DB0F6F1C792@nutanix.com>

On Tue, Apr 07, 2026 at 05:12:06PM +0000, Jon Kohler wrote:
> 
> 
> > On Apr 7, 2026, at 11:46 AM, Jim Mattson <jmattson@google.com> wrote:
> > 
> > On Tue, Apr 7, 2026 at 9:40 AM Pawan Gupta
> > <pawan.kumar.gupta@linux.intel.com> wrote:
> >> 
> >> On Mon, Apr 06, 2026 at 07:23:25AM -0700, Jim Mattson wrote:
> >>> Yes, but the guest needs a way to determine whether the hypervisor
> >>> will do what's necessary to make the short sequence effective. And, in
> >>> particular, no KVM hypervisor today is prepared to do that.
> >>> 
> >>> When running under a hypervisor, without BHI_CTRL and without any
> >>> evidence to the contrary, the guest must assume that the longer
> >>> sequence is necessary. At the very least, we need a CPUID or MSR bit
> >>> that says, "the short BHB clearing sequence is adequate for this
> >>> vCPU."
> >> 
> >> After discussing this internally, the consensus is that the best path
> >> forward is to add virtual SPEC_CTRL support to KVM, which also aligns with
> >> Intel's guidance. In the long term, virtual SPEC_CTRL can benefit future
> >> mitigations as well. As with many other mitigations (e.g. microcode), the
> >> guest would rely on the host to enforce the appropriate protections.
> 
> Would we have to wait for virtual SPEC_CTRL to get this optimization?

The optimization works with or without virtual-SPEC_CTRL.

> Or would that be a future enhancement to make this more prescriptive?

Virtual-SPEC_CTRL enables safer guest migrations between pre and post Alder
Lake CPUs w.r.t. Native BHI mitigation. It is not related to VMSCAPE.

^ permalink raw reply

* Re: [PATCH net-next v4 3/3] net: phy: add a PHY write barrier when disabling interrupts
From: Charles Perry @ 2026-04-07 17:59 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Charles Perry, Russell King (Oracle), netdev, Heiner Kallweit,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	linux-kernel
In-Reply-To: <b63c401b-bea9-4a2d-a43e-cd20c3013f53@lunn.ch>

On Tue, Apr 07, 2026 at 06:59:00PM +0200, Andrew Lunn wrote:
> > static void phy_write_barrier(struct phy_device *phydev)
> > {
> >        int err;
> >  
> >        err = mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, MII_PHYSID1);
> >        if (err == -EOPNOTSUPP)
> >                mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
> >                                 __ffs(phydev->c45_ids.mmds_present),
> >                                 MII_PHYSID1);
> > }
> 
> Using __ffs() is maybe more complex than needed.
> 
> All you are trying to do is ensure the last write happened, by doing a
> read. Any read should work, even if the device does not respond. We
> just need to be careful not to read a register which might clear on
> read, such as an interrupt status register, or the link status, which
> latches. MII_PHYID1 is safe. Since we throw away the value, we don't
> care if the MMD is not present, the read will still flush the previous
> write. So i would replace the _ffs() with a hard coded value. KISS.

Ok, I'll use device 1 (MDIO_MMD_PMAPMD).

Thanks for the explanations.

> 
> > Do you think there's any way I can test this on my VSC8574 or VSC8541? It
> > supports some C45 registers for EEE but not the device discovery part.
> 
> It is not so easy to do. You need to hack the C22 read so that is
> returns EOPNOTSUPP, but you also need the first few reads to return a
> valid value otherwise the probe will fail.
> 
> I think this is one of the cases that if the reviewers thinks its
> looks O.K, we can accept it without extensive testing.
> 

Ok

Thanks,
Charles



^ permalink raw reply

* Re: [PATCH net-next v2 1/2] dt-bindings: net: document Microchip PIC64-HPSC/HX MDIO controller
From: Charles Perry @ 2026-04-07 18:01 UTC (permalink / raw)
  To: Rob Herring
  Cc: Charles Perry, netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Krzysztof Kozlowski, Conor Dooley,
	Heiner Kallweit, Russell King, devicetree, linux-kernel
In-Reply-To: <20260407170033.GA2954294-robh@kernel.org>

On Tue, Apr 07, 2026 at 12:00:33PM -0500, Rob Herring wrote:
> On Mon, Mar 23, 2026 at 03:02:53PM -0700, Charles Perry wrote:
> > This MDIO hardware is based on a Microsemi design supported in Linux by
> > mdio-mscc-miim.c. However, The register interface is completely different
> > with pic64hpsc, hence the need for separate documentation.
> > 
> > The hardware supports C22 and C45.
> > 
> > The documentation recommends an input clock of 156.25MHz and a prescaler
> > of 39, which yields an MDIO clock of 1.95MHz.
> > 
> > The hardware supports an interrupt pin to signal transaction completion
> > which is not strictly needed as the software can also poll a "TRIGGER"
> > bit for this.
> > 
> > Signed-off-by: Charles Perry <charles.perry@microchip.com>
> > ---
> > 
> > Notes:
> >     Changes in v2:
> >       - Make "clocks" and "interrupts" required (Andrew)
> >       - Add a default value to "clock-frequency" (Andrew)
> > 
> >  .../net/microchip,pic64hpsc-mdio.yaml         | 68 +++++++++++++++++++
> >  1 file changed, 68 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/net/microchip,pic64hpsc-mdio.yaml
> > 
> > diff --git a/Documentation/devicetree/bindings/net/microchip,pic64hpsc-mdio.yaml b/Documentation/devicetree/bindings/net/microchip,pic64hpsc-mdio.yaml
> > new file mode 100644
> > index 000000000000..d690afe3d3cf
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/net/microchip,pic64hpsc-mdio.yaml
> > @@ -0,0 +1,68 @@
> > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/net/microchip,pic64hpsc-mdio.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Microchip PIC64-HPSC/HX MDIO controller
> > +
> > +maintainers:
> > +  - Charles Perry <charles.perry@microchip.com>
> > +
> > +description: |
> 
> Don't need '|' if no formatting to preserve.
> 

Ok

> > +  Microchip PIC64-HPSC/HX SoCs have two MDIO bus controller. This MDIO bus
> 
> s/controller/controllers/
> 
> Though how many instances there are is not really applicable to the 
> binding unless there is some difference.

There are no differences between the two instances. I'll remove the mention
about the SoC containing two instances.

>
> > +  controller supports C22 and C45 register access. It is named "MDIO Initiator"
> > +  in the documentation.
> > +
> > +allOf:
> > +  - $ref: mdio.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    oneOf:
> > +      - const: microchip,pic64hpsc-mdio
> > +      - items:
> > +          - const: microchip,pic64hx-mdio
> > +          - const: microchip,pic64hpsc-mdio
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    maxItems: 1
> > +
> > +  clock-frequency:
> > +    default: 2500000
> > +
> > +  interrupts:
> > +    maxItems: 1
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - clocks
> > +  - interrupts
> > +
> > +unevaluatedProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/interrupt-controller/irq.h>
> > +    bus {
> > +        #address-cells = <2>;
> > +        #size-cells = <2>;
> > +
> > +        mdio@4000C21E000 {
> 
> Unit-addresses are lower case hex.

Ok

> 
> > +            compatible = "microchip,pic64hpsc-mdio";
> > +            reg = <0x400 0x0C21E000 0x0 0x1000>;
> 
> And generally we use lower case here too.

Ok

> 
> > +            #address-cells = <1>;
> > +            #size-cells = <0>;
> > +            clocks = <&svc_clk>;
> > +            interrupt-parent = <&saplic0>;
> > +            interrupts = <168 IRQ_TYPE_LEVEL_HIGH>;
> > +
> > +            phy0: ethernet-phy@0 {
> 
> Drop unused labels.

Ok

Thanks,
Charles

^ permalink raw reply

* Re: [PATCH net-next 0/3] psp: add crypt-offset and spi-threshold attributes
From: Daniel Zahka @ 2026-04-07 18:07 UTC (permalink / raw)
  To: Akhilesh Samineni, Jakub Kicinski
  Cc: davem, edumazet, pabeni, andrew+netdev, horms, willemb, netdev,
	linux-kernel, jayakrishnan.udayavarma, ajit.khaparde, kiran.kella,
	sachin.suman
In-Reply-To: <CANQF7iAF6DAMQWv=VSu=nTmb1CyALvVtH8xT2k0CCY0rKk3ggQ@mail.gmail.com>


On 4/7/26 11:39 AM, Akhilesh Samineni wrote:
> On Tue, Apr 7, 2026 at 6:44 AM Jakub Kicinski <kuba@kernel.org> wrote:
>> On Mon,  6 Apr 2026 15:23:02 -0700 Akhilesh Samineni wrote:
>>> This series introduces two new configurable per-device attributes to
>>> the PSP (PSP Security Protocol) subsystem: crypt-offset and spi-threshold.
>> Please read this document:
>> https://www.kernel.org/doc/html/next/process/maintainer-netdev.html
>> --
> Thank you for the link. I have reviewed the netdev process documentation.
> I’ve ensured my patches pass checkpatch and include the correct maintainers.


Hi Akhilesh. There's a sentence in the netdevsim section of that 
document that is problematic for the series as it is currently: 
"netdevsim in itself is not considered a use case/user. You must also 
implement the new APIs in a real driver."


^ permalink raw reply

* Re: [PATCH net-next v4 1/2] net: hsr: require valid EOT supervision TLV
From: Felix Maurer @ 2026-04-07 18:10 UTC (permalink / raw)
  To: Luka Gejak
  Cc: Fernando Fernandez Mancera, davem, edumazet, kuba, pabeni, netdev,
	horms
In-Reply-To: <811E5B8F-85E7-47E2-BEA1-25E62C68929C@linux.dev>

On Thu, Apr 02, 2026 at 08:34:47AM +0200, Luka Gejak wrote:
> On April 2, 2026 1:30:57 AM GMT+02:00, Fernando Fernandez Mancera <fmancera@suse.de> wrote:
> >On 4/1/26 7:05 PM, Luka Gejak wrote:
> >> On Wed Apr 1, 2026 at 11:52 AM CEST, Fernando Fernandez Mancera wrote:
> >>> On 4/1/26 11:23 AM, luka.gejak@linux.dev wrote:
> >>>> From: Luka Gejak <luka.gejak@linux.dev>
> >>>>
> >>>> Supervision frames are only valid if terminated with a zero-length EOT
> >>>> TLV. The current check fails to reject non-EOT entries as the terminal
> >>>> TLV, potentially allowing malformed supervision traffic.
> >>>>
> >>>> Fix this by strictly requiring the terminal TLV to be HSR_TLV_EOT
> >>>> with a length of zero.
> >>>>
> >>>> Reviewed-by: Felix Maurer <fmaurer@redhat.com>
> >>>> Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
> >>>> ---
> >>>
> >>> Hi,
> >>>
> >>> This has not been reviewed by Felix. Felix provided his Reviewed-by tag
> >>> for the v1 which was completely different than this.
> >>>
> >>> Revisions of this patch:
> >>>
> >>> v3: https://lore.kernel.org/all/20260329112313.17164-4-luka.gejak@linux.dev/
> >>>
> >>> v2: https://lore.kernel.org/all/20260326154715.38405-4-luka.gejak@linux.dev/
> >>>
> >>> v1:
> >>> https://lore.kernel.org/all/20260324143503.187642-4-luka.gejak@linux.dev/
> >>>
> >>> Are these contributions LLM/AI generated? I believe so based on the
> >>> email history.
> >>>
> >>> AI generated review on rtl8723bs:
> >>> https://lore.kernel.org/all/B2394A3C-25FD-4CEA-8557-3E68F1F60357@linux.dev/
> >>>
> >>> Another AI generated review on rtl8723bs:
> >>> https://lore.kernel.org/all/3831D599-655E-40B2-9E5D-9DF956013088@linux.dev/
> >>>
> >>> Likely an AI generated review on a 1 year old HSR patch:
> >>> https://lore.kernel.org/all/DHFG26KI6L23.1YCOVQ5SSYMO5@linux.dev/
> >>>
> >>> If these are indeed, AI generated contributions or reviews they should
> >>> be disclosed beforehand. Also there is the Assisted-by: tag. Also note
> >>> that developer must take full responsibility for the contribution which
> >>> means understanding it completely.
> >>>
> >>> https://docs.kernel.org/process/coding-assistants.html#signed-off-by-and-developer-certificate-of-origin
> >>>
> >>> Thanks,
> >>> Fernando.
> >>
> >> HI Fernando,
> >> One more question, should I include Assisted-by tag in v5 if AI was not
> >> used for writing code but only for formating and translation of the
> >> emails to English as I previously mentioned.
> >>
> >
> >I think yes, you should. I also think that AI was actually used for the generated code and also for spotting the valid and invalid issues. If you don't have a real environment for HSR, why would you look into it?
> >
> >Sorry, it is not my intention to be harsh but there are some things that don't add up for me. Maybe I am being too careful here and you just coincidentally found these problems.
> >
> >> Best regards,
> >> Luka Gejak
> >>
> >
>
> Hi Fernando,
> I understand the concern. To clarify, I am a student, and I discovered
> these issues while reading the HSR source to learn about network
> redundancy. Since I lack industrial hardware, I verified the logic
> using network namespaces and veth pairs on my Arch laptop. The code
> and logic are entirely my. As previously mentioned, I only use AI for
> English translation and formatting to ensure my communication is
> clear.

Hi Luka,

I honestly appreciate that you are learning about networking
technologies and the kernel and that you are posting patches to improve
the kernel!

I do, however, share some of Fernando's concerns. I especially doubt
that AI tools were not used for the code here. This isn't forbidden or
even a problem in and of itself, but we'd like to have proper disclosure
of the fact and attribution of the tool, as the policy linked to by
Fernando states.

Let me add a few words for why I (personally) think this is important.
When you submit patches to the mailing lists, there are real humans
taking real time to review the changes. The reviews are much more easy
if the reviewers have all the relevant context available to them. The
information that the code was developed with assistance of an AI tool is
relevant because reviewers pay attention to different types of issues
depending on whether AI wrote the code or a human wrote the code, simply
because they make different mistakes. Additionally, by not disclosing AI
tool usage, you add one more step to a review (determining if the code
is AI generated) which takes away capacity from the review itself.

With that said: I don't want to express blame here for anyone. It's just
a reminder to be helpful and considerate towards the community and the
reviewers, so they can be helpful to you with your submissions.

Thanks,
   Felix


^ permalink raw reply

* Re: [PATCH net-next v2 1/2] net: phy: c45 scanning: Don't consider -ENODEV fatal
From: Charles Perry @ 2026-04-07 18:15 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Florian Fainelli, Vladimir Oltean,
	netdev, Tim Menninger, Florian Fainelli, charles.perry
In-Reply-To: <20240204-unify-c22-c45-scan-error-handling-v2-1-0273623f9c57@lunn.ch>

On Sun, Feb 04, 2024 at 05:14:14PM -0600, Andrew Lunn wrote:
> When scanning the MDIO bus for C22 devices, the driver returning
> -ENODEV is not considered fatal, it just indicates the MDIO bus master
> knows there is no device at that address, maybe because of hardware
> limitation.
> 
> Make the C45 scan code act on -ENODEV the same way, to make C22 and
> C45 more uniform.
> 
> It is expected all reads for a given address will return -ENODEV, so
> within get_phy_c45_ids() only the first place a read occurs has been
> changed.
> 
> Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> ---
>  drivers/net/phy/phy_device.c | 8 ++++++--
>  1 file changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> index 52828d1c64f7..962ab53c23ff 100644
> --- a/drivers/net/phy/phy_device.c
> +++ b/drivers/net/phy/phy_device.c
> @@ -780,7 +780,7 @@ static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
>   * and identifiers in @c45_ids.
>   *
>   * Returns zero on success, %-EIO on bus access error, or %-ENODEV if
> - * the "devices in package" is invalid.
> + * the "devices in package" is invalid or no device responds.
>   */
>  static int get_phy_c45_ids(struct mii_bus *bus, int addr,
>  			   struct phy_c45_device_ids *c45_ids)
> @@ -803,7 +803,11 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
>  			 */
>  			ret = phy_c45_probe_present(bus, addr, i);
>  			if (ret < 0)
> -				return -EIO;
> +				/* returning -ENODEV doesn't stop bus
> +				 * scanning
> +				 */
> +				return (phy_reg == -EIO ||
> +					phy_reg == -ENODEV) ? -ENODEV : -EIO;

Hello Andrew,

Did you meant "ret" instead of "phy_reg" here?

"phy_reg" doesn't make sense here because you would have already returned
in the previous iteration of the loop.

Thanks,
Charles

>  
>  			if (!ret)
>  				continue;
> 
> -- 
> 2.43.0
> 
> 

^ permalink raw reply

* Re: [PATCH 3/5] uaccess: add copy_struct_{from,to}_bounce_buffer() helpers
From: David Laight @ 2026-04-07 18:25 UTC (permalink / raw)
  To: Stefan Metzmacher
  Cc: linux-kernel, Dmitry Safonov, Dmitry Safonov, Francesco Ruggeri,
	Salam Noureddine, David Ahern, David S . Miller, Michal Luczaj,
	David Wei, Luiz Augusto von Dentz, Luiz Augusto von Dentz,
	Marcel Holtmann, Xin Long, Eric Dumazet, Kuniyuki Iwashima,
	Paolo Abeni, Willem de Bruijn, Neal Cardwell, Jakub Kicinski,
	Simon Horman, Aleksa Sarai, Christian Brauner, Kees Cook, netdev,
	linux-bluetooth
In-Reply-To: <f29570914590c50b9b6f451eb3a38d0fe1d954df.1775576651.git.metze@samba.org>

On Tue,  7 Apr 2026 18:03:15 +0200
Stefan Metzmacher <metze@samba.org> wrote:

> These are similar to copy_struct_{from,to}_user() but operate
> on kernel buffers instead of user buffers.
> 
> They can be used when there is a temporary bounce buffer used,
> e.g. in msg_control or similar places.
> 
> It allows us to have the same logic to handle old vs. current
> and current vs. new structures in the same compatible way.
> 
> copy_struct_from_sockptr() will also be able to
> use copy_struct_from_bounce_buffer() for the kernel
> case as follow us patch.
> 
> I'll use this in my IPPROTO_SMBDIRECT work,
> but maybe it will also be useful for others...
> IPPROTO_QUIC will likely also use it.
> 
> Cc: Dmitry Safonov <0x7f454c46@gmail.com>
> Cc: Dmitry Safonov <dima@arista.com>
> Cc: Francesco Ruggeri <fruggeri@arista.com>
> Cc: Salam Noureddine <noureddine@arista.com>
> Cc: David Ahern <dsahern@kernel.org>
> Cc: David S. Miller <davem@davemloft.net>
> Cc: Michal Luczaj <mhal@rbox.co>
> Cc: David Wei <dw@davidwei.uk>
> Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
> Cc: Marcel Holtmann <marcel@holtmann.org>
> Cc: Xin Long <lucien.xin@gmail.com>
> Cc: Eric Dumazet <edumazet@google.com>
> Cc: Kuniyuki Iwashima <kuniyu@google.com>
> Cc: Paolo Abeni <pabeni@redhat.com>
> Cc: Willem de Bruijn <willemb@google.com>
> Cc: Neal Cardwell <ncardwell@google.com>
> Cc: Jakub Kicinski <kuba@kernel.org>
> Cc: Simon Horman <horms@kernel.org>
> Cc: Aleksa Sarai <cyphar@cyphar.com>
> Cc: Christian Brauner <brauner@kernel.org>
> CC: Kees Cook <keescook@chromium.org>
> Cc: netdev@vger.kernel.org
> Cc: linux-bluetooth@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Stefan Metzmacher <metze@samba.org>
> ---
>  include/linux/uaccess.h | 63 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 63 insertions(+)
> 
> diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
> index 1234b5fa4761..a6cd4f48bb99 100644
> --- a/include/linux/uaccess.h
> +++ b/include/linux/uaccess.h
> @@ -513,6 +513,69 @@ copy_struct_to_user(void __user *dst, size_t usize, const void *src,
>  	return 0;
>  }
>  
> +static __always_inline void
> +__copy_struct_generic_bounce_buffer(void *dst, size_t dstsize,
> +				    const void *src, size_t srcsize,
> +				    bool *ignored_trailing)
> +{
> +	size_t size = min(dstsize, srcsize);
> +	size_t rest = max(dstsize, srcsize) - size;
> +
> +	/* Deal with trailing bytes. */
> +	if (dstsize > srcsize)
> +		memset(dst + size, 0, rest);
> +	if (ignored_trailing)
> +		*ignored_trailing = dstsize < srcsize &&
> +			memchr_inv(src + size, 0, rest) != NULL;
> +	/* Copy the interoperable parts of the struct. */
> +	memcpy(dst, src, size);
> +}

Return 'ignored_trailing' rather than pass by reference.
And this is probably too big to inline.

	David

> +
> +/**
> + * This is like copy_struct_from_user(), but the
> + * src buffer was already copied into a kernel
> + * bounce buffer, so it will never return -EFAULT.
> + */
> +static __always_inline __must_check int
> +copy_struct_from_bounce_buffer(void *dst, size_t dstsize,
> +			       const void *src, size_t srcsize)
> +{
> +	bool ignored_trailing;
> +
> +	/* Double check if ksize is larger than a known object size. */
> +	if (WARN_ON_ONCE(dstsize > __builtin_object_size(dst, 1)))
> +		return -E2BIG;
> +
> +	__copy_struct_generic_bounce_buffer(dst, dstsize,
> +					    src, srcsize,
> +					    &ignored_trailing);
> +	if (unlikely(ignored_trailing))
> +		return -E2BIG;
> +
> +	return 0;
> +}
> +
> +/**
> + * This is like copy_struct_to_user(), but the
> + * dst buffer is a kernel bounce buffer instead
> + * of a direct userspace buffer, so it will never return -EFAULT.
> + */
> +static __always_inline __must_check int
> +copy_struct_to_bounce_buffer(void *dst, size_t dstsize,
> +			     const void *src,
> +			     size_t srcsize,
> +			     bool *ignored_trailing)
> +{
> +	/* Double check if srcsize is larger than a known object size. */
> +	if (WARN_ON_ONCE(srcsize > __builtin_object_size(src, 1)))
> +		return -E2BIG;
> +
> +	__copy_struct_generic_bounce_buffer(dst, dstsize,
> +					    src, srcsize,
> +					    ignored_trailing);
> +	return 0;
> +}
> +
>  bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size);
>  
>  long copy_from_kernel_nofault(void *dst, const void *src, size_t size);


^ permalink raw reply

* Re: [PATCH v9 02/10] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: Jim Mattson @ 2026-04-07 18:40 UTC (permalink / raw)
  To: Pawan Gupta
  Cc: x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin, Josh Poimboeuf,
	David Kaplan, Sean Christopherson, Borislav Petkov, Dave Hansen,
	Peter Zijlstra, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, KP Singh, Jiri Olsa, David S. Miller,
	David Laight, Andy Lutomirski, Thomas Gleixner, Ingo Molnar,
	David Ahern, Martin KaFai Lau, Eduard Zingerman, Song Liu,
	Yonghong Song, John Fastabend, Stanislav Fomichev, Hao Luo,
	Paolo Bonzini, Jonathan Corbet, linux-kernel, kvm, Asit Mallick,
	Tao Zhang, bpf, netdev, linux-doc, chao.gao
In-Reply-To: <20260407171151.2gf2idjbmph35ypb@desk>

On Tue, Apr 7, 2026 at 10:12 AM Pawan Gupta
<pawan.kumar.gupta@linux.intel.com> wrote:
>
> On Tue, Apr 07, 2026 at 09:46:07AM -0700, Jim Mattson wrote:
> > On Tue, Apr 7, 2026 at 9:40 AM Pawan Gupta
> > <pawan.kumar.gupta@linux.intel.com> wrote:
> > >
> > > On Mon, Apr 06, 2026 at 07:23:25AM -0700, Jim Mattson wrote:
> > > > Yes, but the guest needs a way to determine whether the hypervisor
> > > > will do what's necessary to make the short sequence effective. And, in
> > > > particular, no KVM hypervisor today is prepared to do that.
> > > >
> > > > When running under a hypervisor, without BHI_CTRL and without any
> > > > evidence to the contrary, the guest must assume that the longer
> > > > sequence is necessary. At the very least, we need a CPUID or MSR bit
> > > > that says, "the short BHB clearing sequence is adequate for this
> > > > vCPU."
> > >
> > > After discussing this internally, the consensus is that the best path
> > > forward is to add virtual SPEC_CTRL support to KVM, which also aligns with
> > > Intel's guidance. In the long term, virtual SPEC_CTRL can benefit future
> > > mitigations as well. As with many other mitigations (e.g. microcode), the
> > > guest would rely on the host to enforce the appropriate protections.
> >
> > I don't think it's reasonable for the guest to rely on a future
> > implementation to enforce the appropriate protections.
> >
> > This is already a problem today. If a guest sees that BHI_CTRL is
> > unavailable, it will deploy the short BHB clearing sequence and
> > declare that the vulnerability is mitigated. That isn't true if the
> > guest is running on Alder Lake or newer.
>
> In any case, there is a change required in the kernel either for the guest
> or the host, they both are future implementations. Why not implement the
> one that is more future proof.

There will always be old hypervisors. True future-proofing requires
that the guest be able to distinguish an old hypervisor from a new
one.

My proposal is as follows:

1. The (advanced) hypervisor can advertise to the guest (via CPUID bit
or MSR bit) that the short BHB clearing sequence is adequate. This may
mean either that the VM will only be hosted on pre-Alder Lake hardware
or that the hypervisor will set BHI_DIS_S behind the back of the
guest. Presumably, this bit would not be reported if BHI_CTRL is
advertised to the guest.
2. If the guest sees this bit, then it can use the short sequence. If
it doesn't see this bit, it must use the long sequence.

^ permalink raw reply

* Re: [net-next] net: ethernet: ravb: Suspend and resume the transmission flow
From: Niklas Söderlund @ 2026-04-07 18:54 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Yoshihiro Shimoda, Paul Barker, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev,
	linux-renesas-soc
In-Reply-To: <CAMuHMdWnjV=HGE1o08zLhUfTgOSene5fYx1J5GG10mB+Toq8qg@mail.gmail.com>

Hi Geert,

Thanks for your report.

On 2026-04-07 11:03:38 +0200, Geert Uytterhoeven wrote:
> Hi Niklas, Shimoda-san,
> 
> On Wed, 1 Apr 2026 at 20:39, Niklas Söderlund
> <niklas.soderlund+renesas@ragnatech.se> wrote:
> >
> > From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
> >
> > The current driver does not follow the latest datasheet and does not
> > suspend the flow when stopping DMA and resume it when starting. Update
> > the driver to do so.
> >
> > Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
> > [Niklas: Rebase from BSP and reword commit message]
> > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> 
> Thanks for your patch, which is now commit 353d8e7989b6babe ("net:
> ethernet: ravb: Suspend and resume the transmission flow") in
> linux-next/master net-next.
> 
> > --- a/drivers/net/ethernet/renesas/ravb_main.c
> > +++ b/drivers/net/ethernet/renesas/ravb_main.c
> > @@ -694,6 +694,9 @@ static int ravb_dmac_init(struct net_device *ndev)
> >         const struct ravb_hw_info *info = priv->info;
> >         int error;
> >
> > +       /* Clear transmission suspension */
> > +       ravb_modify(ndev, CCC, CCC_DTSR, 0);
> > +
> >         /* Set CONFIG mode */
> >         error = ravb_set_opmode(ndev, CCC_OPC_CONFIG);
> >         if (error)
> > @@ -1103,6 +1106,12 @@ static int ravb_stop_dma(struct net_device *ndev)
> >         if (error)
> >                 return error;
> >
> > +       /* Request for transmission suspension */
> > +       ravb_modify(ndev, CCC, CCC_DTSR, CCC_DTSR);
> > +       error = ravb_wait(ndev, CSR, CSR_DTS, CSR_DTS);
> > +       if (error)
> > +               netdev_err(ndev, "failed to stop AXI BUS\n");
> 
> This error message is printed during resume from s2idle or s2ram on
> e.g. Salvator-XS and Gray Hawk Single.  Ethernet (nfsroot) still works
> fine, though.

I was not able to reproduce this on M3N (r8a77965-salvator-xs.dts) nor 
Sparrow Hawk (r8a779g3-sparrow-hawk.dts). I'm using the following to 
test, is your test-case different?

  # echo enabled > /sys/class/tty/ttySC0/power/wakeup
  # echo s2idle > /sys/power/mem_sleep
  # echo 0 > /sys/module/printk/parameters/console_suspend
  # echo mem > /sys/power/state

Console log from M3N (console wakeup)

[  149.278623] Freezing user space processes
[  149.287951] Freezing user space processes completed (elapsed 0.005 seconds)
[  149.295439] OOM killer disabled.
[  149.298796] Freezing remaining freezable tasks
[  149.305367] Freezing remaining freezable tasks completed (elapsed 0.002 seconds)
[  149.364876] ravb e6800000.ethernet end0: Link is Down
[  149.701951] usb usb2: root hub lost power or was reset
[  149.708295] usb usb1: root hub lost power or was reset
[  149.793871] usb usb3: root hub lost power or was reset
[  149.799174] usb usb4: root hub lost power or was reset
[  149.931707] Micrel KSZ9031 Gigabit PHY e6800000.ethernet-ffffffff:00: attached PHY driver (mii_bus:phy_addr=e6800000.ethernet-ffffffff:00, irq=170)
[  150.008695] OOM killer enabled.
[  150.012003] Restarting tasks: Starting
[  150.020596] Restarting tasks: Done
[  150.024234] random: crng reseeded on system resumption
[  150.036191] PM: suspend exit
[  150.235966] ata1: link resume succeeded after 1 retries
[  150.344391] ata1: SATA link down (SStatus 0 SControl 300)
[  156.499184] ravb e6800000.ethernet end0: Link is Up - 1Gbps/Full - flow control off

Console log from V4H (console wakeup)

[   64.982935] Filesystems sync: 0.000 seconds
[   64.992414] Freezing user space processes
[   64.996111] Freezing user space processes completed (elapsed 0.002 seconds)
[   64.997280] OOM killer disabled.
[   64.997711] Freezing remaining freezable tasks
[   65.000580] Freezing remaining freezable tasks completed (elapsed 0.002 seconds)
[   65.012959] ravb e6800000.ethernet end0: Link is Down
[   65.167569] Micrel KSZ9031 Gigabit PHY e6800000.ethernet-ffffffff:00: attached PHY driver (mii_bus:phy_addr=e6800000.ethernet-ffffffff:00, irq=108)
[   65.190614] OOM killer enabled.
[   65.191058] Restarting tasks: Starting
[   65.195133] Restarting tasks: Done
[   65.195743] random: crng reseeded on system resumption
[   65.201052] PM: suspend exit
[   71.305027] ravb e6800000.ethernet end0: Link is Up - 1Gbps/Full - flow control off

> 
> > +
> >         /* Stop AVB-DMAC process */
> >         return ravb_set_opmode(ndev, CCC_OPC_CONFIG);
> >  }
> 
> Gr{oetje,eeting}s,
> 
>                         Geert
> 
> -- 
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
> 
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
>                                 -- Linus Torvalds

-- 
Kind Regards,
Niklas Söderlund

^ permalink raw reply

* Re: [PATCH net-next v2 1/2] net: phy: c45 scanning: Don't consider -ENODEV fatal
From: Andrew Lunn @ 2026-04-07 18:57 UTC (permalink / raw)
  To: Charles Perry
  Cc: Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Florian Fainelli, Vladimir Oltean,
	netdev, Tim Menninger, Florian Fainelli
In-Reply-To: <adVJ1YpHv1bWCxKL@bby-cbu-swbuild03.eng.microchip.com>

On Tue, Apr 07, 2026 at 11:15:49AM -0700, Charles Perry wrote:
> On Sun, Feb 04, 2024 at 05:14:14PM -0600, Andrew Lunn wrote:
> > When scanning the MDIO bus for C22 devices, the driver returning
> > -ENODEV is not considered fatal, it just indicates the MDIO bus master
> > knows there is no device at that address, maybe because of hardware
> > limitation.
> > 
> > Make the C45 scan code act on -ENODEV the same way, to make C22 and
> > C45 more uniform.
> > 
> > It is expected all reads for a given address will return -ENODEV, so
> > within get_phy_c45_ids() only the first place a read occurs has been
> > changed.
> > 
> > Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
> > Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> > ---
> >  drivers/net/phy/phy_device.c | 8 ++++++--
> >  1 file changed, 6 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
> > index 52828d1c64f7..962ab53c23ff 100644
> > --- a/drivers/net/phy/phy_device.c
> > +++ b/drivers/net/phy/phy_device.c
> > @@ -780,7 +780,7 @@ static int get_phy_c45_devs_in_pkg(struct mii_bus *bus, int addr, int dev_addr,
> >   * and identifiers in @c45_ids.
> >   *
> >   * Returns zero on success, %-EIO on bus access error, or %-ENODEV if
> > - * the "devices in package" is invalid.
> > + * the "devices in package" is invalid or no device responds.
> >   */
> >  static int get_phy_c45_ids(struct mii_bus *bus, int addr,
> >  			   struct phy_c45_device_ids *c45_ids)
> > @@ -803,7 +803,11 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr,
> >  			 */
> >  			ret = phy_c45_probe_present(bus, addr, i);
> >  			if (ret < 0)
> > -				return -EIO;
> > +				/* returning -ENODEV doesn't stop bus
> > +				 * scanning
> > +				 */
> > +				return (phy_reg == -EIO ||
> > +					phy_reg == -ENODEV) ? -ENODEV : -EIO;
> 
> Hello Andrew,
> 
> Did you meant "ret" instead of "phy_reg" here?
> 
> "phy_reg" doesn't make sense here because you would have already returned
> in the previous iteration of the loop.

Hum, good catch. That makes no sense. Looks like i cut/pasted from the
C22 code, and did not fixup the variable name.

	Andrew

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox