[Devel] [PATCH RH8] kernfs/sysfs: add ioctl to get fd network namespace tag

Pavel Tikhomirov ptikhomirov at virtuozzo.com
Mon Jul 20 14:21:50 MSK 2020


Sysfs mounts save current netns when mounting and show different set of
network devices in /sys/class/net based on it. But there is currently no
simple way to find out to that netns the sysfs mount is tagged to.

Lets add an ioctl so that we can get open file descriptor to this
namespace on nsfs. Next we would be able to readlink this fd to get
network namespace id.

Sysfs holds only "passive" reference to the net namespace, so the only
guaranty we have is that struct net is not freed yet. So lets add a
helper maybe_get_net_ns to get net only if it's fully alive.

https://jira.sw.ru/browse/PSBM-105161

Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
---
 fs/kernfs/dir.c             |  1 +
 fs/kernfs/file.c            | 23 +++++++++++++++++++++++
 fs/kernfs/kernfs-internal.h |  1 +
 include/linux/kernfs.h      |  5 +++++
 include/linux/socket.h      |  1 +
 net/socket.c                | 12 ++++++++++++
 6 files changed, 43 insertions(+)

diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index e715309f6b9e..f566f3471ed6 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -1690,4 +1690,5 @@ const struct file_operations kernfs_dir_fops = {
 	.iterate_shared	= kernfs_fop_readdir,
 	.release	= kernfs_dir_fop_release,
 	.llseek		= generic_file_llseek,
+	.unlocked_ioctl = kernfs_ioctl,
 };
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index 2015d8c45e4a..1a20f2630cce 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -15,6 +15,10 @@
 #include <linux/pagemap.h>
 #include <linux/sched/mm.h>
 #include <linux/fsnotify.h>
+#include <linux/proc_ns.h>
+#include <linux/magic.h>
+#include <linux/socket.h>
+#include <net/net_namespace.h>
 
 #include "kernfs-internal.h"
 
@@ -949,6 +953,24 @@ void kernfs_notify(struct kernfs_node *kn)
 }
 EXPORT_SYMBOL_GPL(kernfs_notify);
 
+long kernfs_ioctl(struct file *file, unsigned int ioctl,
+		  unsigned long arg)
+{
+	struct dentry *dentry = file->f_path.dentry;
+	const void *ns = kernfs_info(dentry->d_sb)->ns;
+	struct net *net;
+
+	switch (ioctl) {
+	case KERNFS_GET_NS:
+		if (dentry->d_sb->s_magic != SYSFS_MAGIC || !ns)
+			return -ENOTTY;
+		net = (struct net *)ns;
+		return open_related_ns(&net->ns, maybe_get_net_ns);
+	default:
+		return -ENOTTY;
+	}
+}
+
 const struct file_operations kernfs_file_fops = {
 	.read		= kernfs_fop_read,
 	.write		= kernfs_fop_write,
@@ -958,6 +980,7 @@ const struct file_operations kernfs_file_fops = {
 	.release	= kernfs_fop_release,
 	.poll		= kernfs_fop_poll,
 	.fsync		= noop_fsync,
+	.unlocked_ioctl = kernfs_ioctl,
 };
 
 /**
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index b754291000ef..faa90d14025d 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -118,6 +118,7 @@ struct kernfs_node *kernfs_find_and_get_node_by_ino(struct kernfs_root *root,
 extern const struct file_operations kernfs_file_fops;
 
 void kernfs_drain_open_files(struct kernfs_node *kn);
+long kernfs_ioctl(struct file *file, unsigned int ioctl, unsigned long arg);
 
 /*
  * symlink.c
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index 5492e50ded00..a9596411a360 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -16,6 +16,7 @@
 #include <linux/rbtree.h>
 #include <linux/atomic.h>
 #include <linux/wait.h>
+#include <linux/ioctl.h>
 
 #include <linux/rh_kabi.h>
 
@@ -558,4 +559,8 @@ kernfs_mount(struct file_system_type *fs_type, int flags,
 				magic, new_sb_created, NULL);
 }
 
+#define KERNIO 0xb8
+
+#define KERNFS_GET_NS _IO(KERNIO, 0x1)
+
 #endif	/* __LINUX_KERNFS_H */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 7ed4713d5337..922c77730b16 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -386,4 +386,5 @@ extern int __sys_socketpair(int family, int type, int protocol,
 extern int __sys_shutdown(int fd, int how);
 
 extern struct ns_common *get_net_ns(struct ns_common *ns);
+extern struct ns_common *maybe_get_net_ns(struct ns_common *ns);
 #endif /* _LINUX_SOCKET_H */
diff --git a/net/socket.c b/net/socket.c
index 10d4e80991e7..33d015f26c84 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1011,6 +1011,18 @@ struct ns_common *get_net_ns(struct ns_common *ns)
 }
 EXPORT_SYMBOL_GPL(get_net_ns);
 
+struct ns_common *maybe_get_net_ns(struct ns_common *ns)
+{
+	struct net *net;
+
+	net = maybe_get_net(container_of(ns, struct net, ns));
+	if (!net)
+		return ERR_PTR(-ENXIO);
+
+	return &net->ns;
+}
+EXPORT_SYMBOL_GPL(maybe_get_net_ns);
+
 static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 {
 	struct socket *sock;
-- 
2.24.1



More information about the Devel mailing list