diff --git a/fs/lustre/include/lustre_kernelcomm.h b/fs/lustre/include/lustre_kernelcomm.h
index bd5376b2a672..db725ab8a0f9 100644
--- a/fs/lustre/include/lustre_kernelcomm.h
+++ b/fs/lustre/include/lustre_kernelcomm.h
@@ -41,11 +41,46 @@
 /* For declarations shared with userspace */
 #include <uapi/linux/lustre/lustre_kernelcomm.h>
 
+/**
+ * enum lustre_device_attrs	      - Lustre general top-level netlink
+ *					attributes that describe lustre
+ *					'devices'. These values are used
+ *					to piece togther messages for
+ *					sending and receiving.
+ *
+ * @LUSTRE_DEVICE_ATTR_UNSPEC:		unspecified attribute to catch errors
+ *
+ * @LUSTRE_DEVICE_ATTR_HDR:		Netlink group this data is for
+ *					(NLA_NUL_STRING)
+ * @LUSTRE_DEVICE_ATTR_INDEX:		device number used as an index (NLA_U16)
+ * @LUSTRE_DEVICE_ATTR_STATUS:		status of the device (NLA_STRING)
+ * @LUSTRE_DEVICE_ATTR_CLASS:		class the device belongs to (NLA_STRING)
+ * @LUSTRE_DEVICE_ATTR_NAME:		name of the device (NLA_STRING)
+ * @LUSTRE_DEVICE_ATTR_UUID:		UUID of the device (NLA_STRING)
+ * @LUSTRE_DEVICE_ATTR_REFCOUNT:	refcount of the device (NLA_U32)
+ */
+enum lustre_device_attrs {
+	LUSTRE_DEVICE_ATTR_UNSPEC = 0,
+
+	LUSTRE_DEVICE_ATTR_HDR,
+	LUSTRE_DEVICE_ATTR_INDEX,
+	LUSTRE_DEVICE_ATTR_STATUS,
+	LUSTRE_DEVICE_ATTR_CLASS,
+	LUSTRE_DEVICE_ATTR_NAME,
+	LUSTRE_DEVICE_ATTR_UUID,
+	LUSTRE_DEVICE_ATTR_REFCOUNT,
+
+	__LUSTRE_DEVICE_ATTR_MAX_PLUS_ONE
+};
+
+#define LUSTRE_DEVICE_ATTR_MAX (__LUSTRE_DEVICE_ATTR_MAX_PLUS_ONE - 1)
+
 /* prototype for callback function on kuc groups */
 typedef int (*libcfs_kkuc_cb_t)(void *data, void *cb_arg);
 
 /* Kernel methods */
-void libcfs_kkuc_init(void);
+int libcfs_kkuc_init(void);
+void libcfs_kkuc_fini(void);
 int libcfs_kkuc_group_put(const struct obd_uuid *uuid, int group, void *data);
 int libcfs_kkuc_group_add(struct file *fp, const struct obd_uuid *uuid, int uid,
 			  int group, void *data, size_t data_len);
diff --git a/fs/lustre/obdclass/class_obd.c b/fs/lustre/obdclass/class_obd.c
index f455ed752c15..67a94222a664 100644
--- a/fs/lustre/obdclass/class_obd.c
+++ b/fs/lustre/obdclass/class_obd.c
@@ -671,15 +671,17 @@ static int __init obdclass_init(void)
 	if (err)
 		return err;
 
-	libcfs_kkuc_init();
+	err = obd_init_checks();
+	if (err)
+		return err;
 
-	err = obd_zombie_impexp_init();
+	err = libcfs_kkuc_init();
 	if (err)
 		return err;
 
-	err = obd_init_checks();
+	err = obd_zombie_impexp_init();
 	if (err)
-		goto cleanup_zombie_impexp;
+		goto cleanup_kkuc;
 
 	err = class_handle_init();
 	if (err)
@@ -754,6 +756,9 @@ static int __init obdclass_init(void)
 cleanup_zombie_impexp:
 	obd_zombie_impexp_stop();
 
+cleanup_kkuc:
+	libcfs_kkuc_fini();
+
 	return err;
 }
 
@@ -771,6 +776,7 @@ static void obdclass_exit(void)
 	class_handle_cleanup();
 	class_del_uuid(NULL); /* Delete all UUIDs. */
 	obd_zombie_impexp_stop();
+	libcfs_kkuc_fini();
 }
 
 void obd_heat_clear(struct obd_heat_instance *instance, int count)
diff --git a/fs/lustre/obdclass/kernelcomm.c b/fs/lustre/obdclass/kernelcomm.c
index e59b6aadf097..5682d4e1ab53 100644
--- a/fs/lustre/obdclass/kernelcomm.c
+++ b/fs/lustre/obdclass/kernelcomm.c
@@ -38,16 +38,254 @@
 #define DEBUG_SUBSYSTEM S_CLASS
 
 #include <linux/file.h>
-#include <linux/libcfs/libcfs.h>
+#include <linux/glob.h>
+#include <net/genetlink.h>
+#include <net/sock.h>
+
+#include <obd_class.h>
 #include <obd_support.h>
 #include <lustre_kernelcomm.h>
 
+static struct genl_family lustre_family;
+
+static struct ln_key_list device_list = {
+	.lkl_maxattr			= LUSTRE_DEVICE_ATTR_MAX,
+	.lkl_list			= {
+		[LUSTRE_DEVICE_ATTR_HDR]	= {
+			.lkp_value		= "devices",
+			.lkp_key_format		= LNKF_SEQUENCE | LNKF_MAPPING,
+			.lkp_data_type		= NLA_NUL_STRING,
+		},
+		[LUSTRE_DEVICE_ATTR_INDEX]	= {
+			.lkp_value		= "index",
+			.lkp_data_type		= NLA_U16
+		},
+		[LUSTRE_DEVICE_ATTR_STATUS]	= {
+			.lkp_value		= "status",
+			.lkp_data_type		= NLA_STRING
+		},
+		[LUSTRE_DEVICE_ATTR_CLASS]	= {
+			.lkp_value		= "type",
+			.lkp_data_type		= NLA_STRING
+		},
+		[LUSTRE_DEVICE_ATTR_NAME]	= {
+			.lkp_value		= "name",
+			.lkp_data_type		= NLA_STRING
+		},
+		[LUSTRE_DEVICE_ATTR_UUID]	= {
+			.lkp_value		= "uuid",
+			.lkp_data_type		= NLA_STRING
+		},
+		[LUSTRE_DEVICE_ATTR_REFCOUNT]	= {
+			.lkp_value		= "refcount",
+			.lkp_data_type		= NLA_U32
+		},
+	},
+};
+
+struct genl_dev_list {
+	struct obd_device	*gdl_target;
+	unsigned int		gdl_start;
+};
+
+static inline struct genl_dev_list *
+device_dump_ctx(struct netlink_callback *cb)
+{
+	return (struct genl_dev_list *)cb->args[0];
+}
+
+/* generic ->start() handler for GET requests */
+static int lustre_device_list_start(struct netlink_callback *cb)
+{
+	struct genlmsghdr *gnlh = nlmsg_data(cb->nlh);
+	struct netlink_ext_ack *extack = cb->extack;
+	struct genl_dev_list *glist;
+	int msg_len, rc = 0;
+
+	glist = kmalloc(sizeof(*glist), GFP_KERNEL);
+	if (!glist)
+		return -ENOMEM;
+
+	cb->args[0] = (long)glist;
+	glist->gdl_target = NULL;
+	glist->gdl_start = 0;
+
+	msg_len = genlmsg_len(gnlh);
+	if (msg_len > 0) {
+		struct nlattr *params = genlmsg_data(gnlh);
+		struct nlattr *dev;
+		int rem;
+
+		nla_for_each_attr(dev, params, msg_len, rem) {
+			struct nlattr *prop;
+			int rem2;
+
+			nla_for_each_nested(prop, dev, rem2) {
+				char name[MAX_OBD_NAME];
+				struct obd_device *obd;
+
+				if (nla_type(prop) != LN_SCALAR_ATTR_VALUE ||
+				    nla_strcmp(prop, "name") != 0)
+					continue;
+
+				prop = nla_next(prop, &rem2);
+				if (nla_type(prop) != LN_SCALAR_ATTR_VALUE) {
+					rc = -EINVAL;
+					goto report_err;
+				}
+
+				rc = nla_strlcpy(name, prop, sizeof(name));
+				if (rc < 0)
+					goto report_err;
+				rc = 0;
+
+				obd = class_name2obd(name);
+				if (obd)
+					glist->gdl_target = obd;
+			}
+		}
+		if (!glist->gdl_target) {
+			NL_SET_ERR_MSG(extack, "No devices found");
+			rc = -ENOENT;
+		}
+	}
+report_err:
+	if (rc < 0) {
+		kfree(glist);
+		cb->args[0] = 0;
+	}
+	return rc;
+}
+
+static int lustre_device_list_dump(struct sk_buff *msg,
+				   struct netlink_callback *cb)
+{
+	struct genl_dev_list *glist = device_dump_ctx(cb);
+	struct obd_device *filter = glist->gdl_target;
+	struct netlink_ext_ack *extack = cb->extack;
+	int portid = NETLINK_CB(cb->skb).portid;
+	int seq = cb->nlh->nlmsg_seq;
+	int idx, rc = 0;
+
+	if (glist->gdl_start == 0) {
+		const struct ln_key_list *all[] = {
+			&device_list, NULL
+		};
+
+		rc = lnet_genl_send_scalar_list(msg, portid, seq,
+						&lustre_family,
+						NLM_F_CREATE | NLM_F_MULTI,
+						LUSTRE_CMD_DEVICES, all);
+		if (rc < 0) {
+			NL_SET_ERR_MSG(extack, "failed to send key table");
+			return rc;
+		}
+	}
+
+	for (idx = glist->gdl_start; idx < class_devno_max(); idx++) {
+		struct obd_device *obd;
+		const char *status;
+		void *hdr;
+
+		obd = class_num2obd(idx);
+		if (!obd)
+			continue;
+
+		if (filter && filter != obd)
+			continue;
+
+		hdr = genlmsg_put(msg, portid, seq, &lustre_family,
+				  NLM_F_MULTI, LUSTRE_CMD_DEVICES);
+		if (!hdr) {
+			NL_SET_ERR_MSG(extack, "failed to send values");
+			genlmsg_cancel(msg, hdr);
+			rc = -EMSGSIZE;
+			break;
+		}
+
+		if (idx == 0)
+			nla_put_string(msg, LUSTRE_DEVICE_ATTR_HDR, "");
+
+		nla_put_u16(msg, LUSTRE_DEVICE_ATTR_INDEX, obd->obd_minor);
+
+		/* Collect only the index value for a single obd */
+		if (filter) {
+			genlmsg_end(msg, hdr);
+			idx++;
+			break;
+		}
+
+		if (obd->obd_stopping)
+			status = "ST";
+		else if (obd->obd_inactive)
+			status = "IN";
+		else if (obd->obd_set_up)
+			status = "UP";
+		else if (obd->obd_attached)
+			status = "AT";
+		else
+			status = "--";
+
+		nla_put_string(msg, LUSTRE_DEVICE_ATTR_STATUS, status);
+
+		nla_put_string(msg, LUSTRE_DEVICE_ATTR_CLASS,
+			       obd->obd_type->typ_name);
+
+		nla_put_string(msg, LUSTRE_DEVICE_ATTR_NAME,
+			       obd->obd_name);
+
+		nla_put_string(msg, LUSTRE_DEVICE_ATTR_UUID,
+			       obd->obd_uuid.uuid);
+
+		nla_put_u32(msg, LUSTRE_DEVICE_ATTR_REFCOUNT,
+			    atomic_read(&obd->obd_refcount));
+
+		genlmsg_end(msg, hdr);
+	}
+
+	glist->gdl_start = idx;
+	return rc < 0 ? rc : msg->len;
+}
+
+int lustre_device_done(struct netlink_callback *cb)
+{
+	struct genl_dev_list *glist;
+
+	glist = device_dump_ctx(cb);
+	kfree(glist);
+	cb->args[0] = 0;
+
+	return 0;
+}
+
+static const struct genl_multicast_group lustre_mcast_grps[] = {
+	{ .name		= "devices",		},
+};
+
+static const struct genl_ops lustre_genl_ops[] = {
+	{
+		.cmd		= LUSTRE_CMD_DEVICES,
+		.start		= lustre_device_list_start,
+		.dumpit		= lustre_device_list_dump,
+		.done		= lustre_device_done,
+	},
+};
+
+static struct genl_family lustre_family = {
+	.name		= LUSTRE_GENL_NAME,
+	.version	= LUSTRE_GENL_VERSION,
+	.module		= THIS_MODULE,
+	.ops		= lustre_genl_ops,
+	.n_ops		= ARRAY_SIZE(lustre_genl_ops),
+	.mcgrps		= lustre_mcast_grps,
+	.n_mcgrps	= ARRAY_SIZE(lustre_mcast_grps),
+};
+
 /**
  * libcfs_kkuc_msg_put - send an message from kernel to userspace
- *
- * @fp:		to send the message to
- * @payload:	Payload data. First field of payload is always
- *		struct kuc_hdr
+ * @param fp	to send the message to
+ * @param payload Payload data. First field of payload is always
+ *  struct kuc_hdr
  */
 static int libcfs_kkuc_msg_put(struct file *filp, void *payload)
 {
@@ -104,12 +342,19 @@ static inline bool libcfs_kkuc_group_is_valid(unsigned int group)
 	return group < ARRAY_SIZE(kkuc_groups);
 }
 
-void libcfs_kkuc_init(void)
+int libcfs_kkuc_init(void)
 {
 	int group;
 
 	for (group = 0; group < ARRAY_SIZE(kkuc_groups); group++)
 		INIT_LIST_HEAD(&kkuc_groups[group]);
+
+	return genl_register_family(&lustre_family);
+}
+
+void libcfs_kkuc_fini(void)
+{
+	genl_unregister_family(&lustre_family);
 }
 
 /** Add a receiver to a broadcast group
diff --git a/include/uapi/linux/lustre/lustre_kernelcomm.h b/include/uapi/linux/lustre/lustre_kernelcomm.h
index 744eeb674f72..91bb686d33e9 100644
--- a/include/uapi/linux/lustre/lustre_kernelcomm.h
+++ b/include/uapi/linux/lustre/lustre_kernelcomm.h
@@ -39,6 +39,24 @@
 
 #include <linux/types.h>
 
+#define LUSTRE_GENL_NAME		"lustre"
+#define LUSTRE_GENL_VERSION		0x1
+
+/*
+ * enum lustre_commands		      - Supported Lustre Netlink commands
+ *
+ * @LUSTRE_CMD_UNSPEC:			unspecified command to catch errors
+ * @LUSTRE_CMD_DEVICES:			command to manage the Lustre devices
+ */
+enum lustre_commands {
+	LUSTRE_CMD_UNSPEC	= 0,
+	LUSTRE_CMD_DEVICES	= 1,
+
+	__LUSTRE_CMD_MAX_PLUS_ONE
+};
+
+#define LUSTRE_CMD_MAX	(__LUSTRE_CMD_MAX_PLUS_ONE - 1)
+
 /* KUC message header.
  * All current and future KUC messages should use this header.
  * To avoid having to include Lustre headers from libcfs, define this here.
