[Devel,1/6] proc connector: per-net operations introduced

Submitted by Stanislav Kinsburskiy on Aug. 15, 2017, 12:41 p.m.

Details

Message ID 20170815124154.22445.93070.stgit@localhost.localdomain
State New
Series "proc connector: containerize on per-net basis"
Headers show

Commit Message

Stanislav Kinsburskiy Aug. 15, 2017, 12:41 p.m.
The idea of this patch is to containerize all the static objects, used by proc
connector, on per-net basis.
There are not tha many: 4 objects, defined in per-net "cn_net" structure.
And this structure is allocated and initialized only for initial network
namespace in initial user namespace.

Signed-off-by: Stanislav Kinsburskiy <skinsbursky@virtuozzo.com>
---
 drivers/connector/cn_proc.c   |   35 +++++++++++++
 drivers/connector/connector.c |  114 +++++++++++++++++++++++++++++++++++++++++
 drivers/connector/netns.h     |   22 ++++++++
 3 files changed, 171 insertions(+)
 create mode 100644 drivers/connector/netns.h

Patch hide | download patch | download mbox

diff --git a/drivers/connector/cn_proc.c b/drivers/connector/cn_proc.c
index 3165811..dcd993e 100644
--- a/drivers/connector/cn_proc.c
+++ b/drivers/connector/cn_proc.c
@@ -34,6 +34,8 @@ 
 
 #include <linux/cn_proc.h>
 
+#include "netns.h"
+
 /*
  * Size of a cn_msg followed by a proc_event structure.  Since the
  * sizeof struct cn_msg is a multiple of 4 bytes, but not 8 bytes, we
@@ -391,6 +393,39 @@  static void cn_proc_mcast_ctl(struct cn_msg *msg,
 	cn_proc_ack(err, msg->seq, msg->ack);
 }
 
+int __net_init cn_proc_init_net(struct net *net)
+{
+	struct cn_net *cnn = net_generic(net, cn_net_id);
+	struct cn_dev *dev = cnn->cdev;
+	int err;
+
+	cnn->proc_event_counts = alloc_percpu(u32);
+	if (!cnn->proc_event_counts)
+		return -ENOMEM;
+
+	err = cn_queue_add_callback(dev->cbdev, "cn_proc",
+				    &cn_proc_event_id, cn_proc_mcast_ctl);
+	if (err) {
+		pr_warn("cn_proc failed to register\n");
+		free_percpu(cnn->proc_event_counts);
+		return err;
+	}
+
+	atomic_set(&cnn->proc_event_num_listeners, 0);
+
+	return 0;
+}
+
+void __net_exit cn_proc_exit_net(struct net *net)
+{
+	struct cn_net *cnn = net_generic(net, cn_net_id);
+	struct cn_dev *dev = cnn->cdev;
+
+	cn_queue_del_callback(dev->cbdev, &cn_proc_event_id);
+
+	free_percpu(cnn->proc_event_counts);
+}
+
 /*
  * cn_proc_init - initialization entry point
  *
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index 0daa11e..d0b678f 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -33,6 +33,8 @@ 
 
 #include <net/sock.h>
 
+#include "netns.h"
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
 MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector.");
@@ -253,6 +255,116 @@  static const struct file_operations cn_file_ops = {
 	.release = single_release
 };
 
+static struct cn_dev *cn_init_dev_net(struct net *net)
+{
+	struct cn_dev *dev;
+	struct netlink_kernel_cfg cfg = {
+		.groups	= CN_NETLINK_USERS + 0xf,
+		.input	= cn_rx_skb,
+	};
+	int err;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return ERR_PTR(-ENOMEM);
+
+	dev->nls = netlink_kernel_create(net, NETLINK_CONNECTOR, &cfg);
+	if (!dev->nls) {
+		err = -EIO;
+		goto free_dev;
+	}
+
+	dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
+	if (!dev->cbdev) {
+		err = -EINVAL;
+		goto nl_release;
+	}
+
+	if (!proc_net_create("connector", S_IRUGO, net->proc_net, &cn_file_ops)) {
+		err = -ENOMEM;
+		goto free_cbdev;
+	}
+
+	return dev;
+
+free_cbdev:
+	cn_queue_free_dev(dev->cbdev);
+nl_release:
+	netlink_kernel_release(dev->nls);
+free_dev:
+	kfree(dev);
+	return ERR_PTR(err);
+}
+
+static void cn_fini_dev_net(struct net *net)
+{
+	struct cn_net *cnn = net_generic(net, cn_net_id);
+	struct cn_dev *dev = cnn->cdev;
+
+	remove_proc_entry("connector", net->proc_net);
+
+	cn_queue_free_dev(dev->cbdev);
+	netlink_kernel_release(dev->nls);
+	kfree(dev);
+}
+
+int cn_net_id;
+
+static bool cn_allowed(struct net *net)
+{
+	/* Create device only for containers init user ns */
+	if (!current_user_ns_initial())
+		return false;
+
+	/* Create device only for containers init net */
+	if (net->owner_ve->ve_netns != net)
+		return false;
+
+	return true;
+}
+
+static int __net_init cn_init_net(struct net *net)
+{
+	struct cn_net *cnn = net_generic(net, cn_net_id);
+	int err;
+
+	if (!cn_allowed(net))
+		return 0;
+
+	cnn->cdev = cn_init_dev_net(net);
+	if (IS_ERR(cnn->cdev))
+		return PTR_ERR(cnn->cdev);
+
+	err = cn_proc_init_net(net);
+	if (err) {
+		cn_fini_dev_net(net);
+		return err;
+	}
+
+	cnn->cn_already_initialized = 1;
+	return 0;
+}
+
+static void __net_exit cn_exit_net(struct net *net)
+{
+	struct cn_net *cnn = net_generic(net, cn_net_id);
+
+	if (!cn_allowed(net))
+		return;
+
+	cnn->cn_already_initialized = 0;
+
+	cn_proc_exit_net(net);
+	cn_fini_dev_net(net);
+}
+
+static struct pernet_operations cn_pernet_ops = {
+       .init = cn_init_net,
+       .exit = cn_exit_net,
+       .id = &cn_net_id,
+       .size = sizeof(struct cn_net),
+};
+
 static struct cn_dev cdev = {
 	.input   = cn_rx_skb,
 };
@@ -265,6 +377,8 @@  static int cn_init(void)
 		.input	= dev->input,
 	};
 
+	(void) cn_pernet_ops;
+
 	dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, &cfg);
 	if (!dev->nls)
 		return -EIO;
diff --git a/drivers/connector/netns.h b/drivers/connector/netns.h
new file mode 100644
index 0000000..28387f6
--- /dev/null
+++ b/drivers/connector/netns.h
@@ -0,0 +1,22 @@ 
+#ifndef __CONNECTOR_NETNS__
+#define __CONNECTOR_NETNS__
+
+#include <net/netns/generic.h>
+
+extern int cn_net_id;
+
+struct cn_dev;
+struct cn_net {
+	struct cn_dev	*cdev;
+	int		cn_already_initialized;
+
+	atomic_t	proc_event_num_listeners;
+	u32 __percpu	*proc_event_counts;
+
+};
+
+struct net;
+int cn_proc_init_net(struct net *net);
+void cn_proc_exit_net(struct net *net);
+
+#endif // __CONNECTOR_NETNS__