All of lore.kernel.org
 help / color / mirror / Atom feed
From: David Herrmann <dh.herrmann@gmail.com>
To: linux-kernel@vger.kernel.org
Cc: Andy Lutomirski <luto@amacapital.net>,
	Jiri Kosina <jikos@kernel.org>, Greg KH <greg@kroah.com>,
	Hannes Reinecke <hare@suse.com>,
	Steven Rostedt <rostedt@goodmis.org>,
	Arnd Bergmann <arnd@arndb.de>, Tom Gundersen <teg@jklm.no>,
	David Herrmann <dh.herrmann@gmail.com>,
	Josh Triplett <josh@joshtriplett.org>,
	Linus Torvalds <torvalds@linux-foundation.org>,
	Andrew Morton <akpm@linux-foundation.org>
Subject: [RFC v1 07/14] bus1: tracking user contexts
Date: Wed, 26 Oct 2016 21:18:03 +0200	[thread overview]
Message-ID: <20161026191810.12275-8-dh.herrmann@gmail.com> (raw)
In-Reply-To: <20161026191810.12275-1-dh.herrmann@gmail.com>

From: Tom Gundersen <teg@jklm.no>

Different users can communicate via bus1, and many resources are shared
between multiple users. The bus1_user object represents the UID of a
user, like "struct user_struct" does in the kernel core. It is used to
account global resources, apply limits, and calculate quotas if
different UIDs communicate with each other.

All dynamic resources have global per-user limits, which cannot be
exceeded by a user. They prevent a single user from exhausting local
resources. Each peer that is created is always owned by the user that
initialized it. All resources allocated on that peer are accounted on
that pinned user. Additionally to global resources, there are local
limits per peer, that can be controlled by each peer individually
(e.g., specifying a maximum pool size). Those local limits allow a user
to distribute the globally available resources across its peer
instances.

Since bus1 allows communication across UID boundaries, any such
transmission of resources must be properly accounted. Bus1 employs
dynamic quotas to fairly distribute available resources. Those quotas
make sure that available resources of a peer cannot be exhausted by
remote UIDs, but are fairly divided among all communicating peers.

This only implements the user tracking, the resource limits will be
added in follow-up patches.

Signed-off-by: Tom Gundersen <teg@jklm.no>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 ipc/bus1/Makefile |   1 +
 ipc/bus1/main.c   |   3 ++
 ipc/bus1/user.c   | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/bus1/user.h   |  67 ++++++++++++++++++++++++
 4 files changed, 224 insertions(+)
 create mode 100644 ipc/bus1/user.c
 create mode 100644 ipc/bus1/user.h

diff --git a/ipc/bus1/Makefile b/ipc/bus1/Makefile
index 3c90657..94d79e0 100644
--- a/ipc/bus1/Makefile
+++ b/ipc/bus1/Makefile
@@ -1,5 +1,6 @@
 bus1-y :=			\
 	main.o			\
+	user.o			\
 	util/active.o		\
 	util/flist.o		\
 	util/pool.o		\
diff --git a/ipc/bus1/main.c b/ipc/bus1/main.c
index 02412a7..526347d 100644
--- a/ipc/bus1/main.c
+++ b/ipc/bus1/main.c
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 #include "main.h"
 #include "tests.h"
+#include "user.h"
 
 static int bus1_fop_open(struct inode *inode, struct file *file)
 {
@@ -64,6 +65,7 @@ static int __init bus1_modinit(void)
 
 error:
 	debugfs_remove(bus1_debugdir);
+	bus1_user_modexit();
 	return r;
 }
 
@@ -71,6 +73,7 @@ static void __exit bus1_modexit(void)
 {
 	misc_deregister(&bus1_misc);
 	debugfs_remove(bus1_debugdir);
+	bus1_user_modexit();
 	pr_info("unloaded\n");
 }
 
diff --git a/ipc/bus1/user.c b/ipc/bus1/user.c
new file mode 100644
index 0000000..0498ab4
--- /dev/null
+++ b/ipc/bus1/user.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2013-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/err.h>
+#include <linux/idr.h>
+#include <linux/kernel.h>
+#include <linux/kref.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/rcupdate.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uidgid.h>
+#include "user.h"
+
+static DEFINE_MUTEX(bus1_user_lock);
+static DEFINE_IDR(bus1_user_idr);
+
+/**
+ * bus1_user_modexit() - clean up global resources of user accounting
+ *
+ * This function cleans up any remaining global resources that were allocated
+ * by the user accounting helpers. The caller must make sure that no user
+ * object is referenced anymore, before calling this. This function just clears
+ * caches and verifies nothing is leaked.
+ *
+ * This is meant to be called on module-exit.
+ */
+void bus1_user_modexit(void)
+{
+	WARN_ON(!idr_is_empty(&bus1_user_idr));
+	idr_destroy(&bus1_user_idr);
+	idr_init(&bus1_user_idr);
+}
+
+static struct bus1_user *bus1_user_new(void)
+{
+	struct bus1_user *user;
+
+	user = kmalloc(sizeof(*user), GFP_KERNEL);
+	if (!user)
+		return ERR_PTR(-ENOMEM);
+
+	kref_init(&user->ref);
+	user->uid = INVALID_UID;
+	mutex_init(&user->lock);
+
+	return user;
+}
+
+static void bus1_user_free(struct kref *ref)
+{
+	struct bus1_user *user = container_of(ref, struct bus1_user, ref);
+
+	lockdep_assert_held(&bus1_user_lock);
+
+	if (likely(uid_valid(user->uid)))
+		idr_remove(&bus1_user_idr, __kuid_val(user->uid));
+	mutex_destroy(&user->lock);
+	kfree_rcu(user, rcu);
+}
+
+/**
+ * bus1_user_ref_by_uid() - get a user object for a uid
+ * @uid:		uid of the user
+ *
+ * Find and return the user object for the uid if it exists, otherwise create
+ * it first.
+ *
+ * Return: A user object for the given uid, ERR_PTR on failure.
+ */
+struct bus1_user *bus1_user_ref_by_uid(kuid_t uid)
+{
+	struct bus1_user *user;
+	int r;
+
+	if (WARN_ON(!uid_valid(uid)))
+		return ERR_PTR(-ENOTRECOVERABLE);
+
+	/* fast-path: acquire reference via rcu */
+	rcu_read_lock();
+	user = idr_find(&bus1_user_idr, __kuid_val(uid));
+	if (user && !kref_get_unless_zero(&user->ref))
+		user = NULL;
+	rcu_read_unlock();
+	if (user)
+		return user;
+
+	/* slow-path: try again with IDR locked */
+	mutex_lock(&bus1_user_lock);
+	user = idr_find(&bus1_user_idr, __kuid_val(uid));
+	if (likely(!bus1_user_ref(user))) {
+		user = bus1_user_new();
+		if (!IS_ERR(user)) {
+			user->uid = uid;
+			r = idr_alloc(&bus1_user_idr, user, __kuid_val(uid),
+				      __kuid_val(uid) + 1, GFP_KERNEL);
+			if (r < 0) {
+				user->uid = INVALID_UID; /* couldn't insert */
+				kref_put(&user->ref, bus1_user_free);
+				user = ERR_PTR(r);
+			}
+		}
+	}
+	mutex_unlock(&bus1_user_lock);
+
+	return user;
+}
+
+/**
+ * bus1_user_ref() - acquire reference
+ * @user:	user to acquire, or NULL
+ *
+ * Acquire an additional reference to a user-object. The caller must already
+ * own a reference.
+ *
+ * If NULL is passed, this is a no-op.
+ *
+ * Return: @user is returned.
+ */
+struct bus1_user *bus1_user_ref(struct bus1_user *user)
+{
+	if (user)
+		kref_get(&user->ref);
+	return user;
+}
+
+/**
+ * bus1_user_unref() - release reference
+ * @user:	user to release, or NULL
+ *
+ * Release a reference to a user-object.
+ *
+ * If NULL is passed, this is a no-op.
+ *
+ * Return: NULL is returned.
+ */
+struct bus1_user *bus1_user_unref(struct bus1_user *user)
+{
+	if (user) {
+		if (kref_put_mutex(&user->ref, bus1_user_free, &bus1_user_lock))
+			mutex_unlock(&bus1_user_lock);
+	}
+
+	return NULL;
+}
diff --git a/ipc/bus1/user.h b/ipc/bus1/user.h
new file mode 100644
index 0000000..6cdc264
--- /dev/null
+++ b/ipc/bus1/user.h
@@ -0,0 +1,67 @@
+#ifndef __BUS1_USER_H
+#define __BUS1_USER_H
+
+/*
+ * Copyright (C) 2013-2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+/**
+ * DOC: Users
+ *
+ * Different users can communicate via bus1, and many resources are shared
+ * between multiple users. The bus1_user object represents the UID of a user,
+ * like "struct user_struct" does in the kernel core. It is used to account
+ * global resources, apply limits, and calculate quotas if different UIDs
+ * communicate with each other.
+ *
+ * All dynamic resources have global per-user limits, which cannot be exceeded
+ * by a user. They prevent a single user from exhausting local resources. Each
+ * peer that is created is always owned by the user that initialized it. All
+ * resources allocated on that peer are accounted on that pinned user.
+ * Additionally to global resources, there are local limits per peer, that can
+ * be controlled by each peer individually (e.g., specifying a maximum pool
+ * size). Those local limits allow a user to distribute the globally available
+ * resources across its peer instances.
+ *
+ * Since bus1 allows communication across UID boundaries, any such transmission
+ * of resources must be properly accounted. Bus1 employs dynamic quotas to
+ * fairly distribute available resources. Those quotas make sure that available
+ * resources of a peer cannot be exhausted by remote UIDs, but are fairly
+ * divided among all communicating peers.
+ */
+
+#include <linux/atomic.h>
+#include <linux/idr.h>
+#include <linux/kref.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/uidgid.h>
+
+/**
+ * struct bus1_user - resource accounting for users
+ * @ref:		reference counter
+ * @uid:		UID of the user
+ * @lock:		object lock
+ * @rcu:		rcu
+ */
+struct bus1_user {
+	struct kref ref;
+	kuid_t uid;
+	struct mutex lock;
+	struct rcu_head rcu;
+};
+
+/* module cleanup */
+void bus1_user_modexit(void);
+
+/* users */
+struct bus1_user *bus1_user_ref_by_uid(kuid_t uid);
+struct bus1_user *bus1_user_ref(struct bus1_user *user);
+struct bus1_user *bus1_user_unref(struct bus1_user *user);
+
+#endif /* __BUS1_USER_H */
-- 
2.10.1

  parent reply	other threads:[~2016-10-26 19:21 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-10-26 19:17 [RFC v1 00/14] Bus1 Kernel Message Bus David Herrmann
2016-10-26 19:17 ` [RFC v1 01/14] bus1: add bus1(7) man-page David Herrmann
2016-10-27 23:12   ` Kirill A. Shutemov
2016-10-26 19:17 ` [RFC v1 02/14] bus1: provide stub cdev /dev/bus1 David Herrmann
2016-10-26 23:19   ` Andy Lutomirski
2016-10-26 23:54     ` Tom Gundersen
2016-10-27  9:11       ` Arnd Bergmann
2016-10-27 15:25         ` Tom Gundersen
2016-10-27 16:37           ` Linus Torvalds
2016-10-27 16:39             ` Tom Gundersen
2016-10-29 22:13           ` Arnd Bergmann
2016-10-26 19:17 ` [RFC v1 03/14] bus1: util - active reference utility library David Herrmann
2016-10-26 19:18 ` [RFC v1 04/14] bus1: util - fixed list " David Herrmann
2016-10-27 12:37   ` Peter Zijlstra
2016-10-27 12:48     ` David Herrmann
2016-10-27 12:56       ` Arnd Bergmann
2016-10-27 13:31         ` David Herrmann
2016-10-26 19:18 ` [RFC v1 05/14] bus1: util - pool " David Herrmann
2016-10-27 12:54   ` Peter Zijlstra
2016-10-27 12:59   ` Peter Zijlstra
2016-10-27 15:00     ` Peter Zijlstra
2016-10-27 15:14   ` Peter Zijlstra
2016-10-26 19:18 ` [RFC v1 06/14] bus1: util - queue " David Herrmann
2016-10-27 15:27   ` Peter Zijlstra
2016-10-27 16:43   ` Peter Zijlstra
2016-10-28 11:33     ` Tom Gundersen
2016-10-28 13:33       ` Peter Zijlstra
2016-10-28 13:47         ` Tom Gundersen
2016-10-28 13:58           ` Peter Zijlstra
2016-10-28 14:33             ` Tom Gundersen
2016-10-28 16:49               ` Peter Zijlstra
2016-10-26 19:18 ` David Herrmann [this message]
2016-10-26 19:18 ` [RFC v1 08/14] bus1: implement peer management context David Herrmann
2016-10-28 12:06   ` Richard Weinberger
2016-10-28 13:18     ` Tom Gundersen
2016-10-28 13:21       ` Richard Weinberger
2016-10-28 13:05   ` Richard Weinberger
2016-10-28 13:23     ` Tom Gundersen
2016-10-28 13:54       ` Richard Weinberger
2016-10-26 19:18 ` [RFC v1 09/14] bus1: provide transaction context for multicasts David Herrmann
2016-10-28 14:37   ` Peter Zijlstra
2016-10-26 19:18 ` [RFC v1 10/14] bus1: add handle management David Herrmann
2016-10-26 19:18 ` [RFC v1 11/14] bus1: implement message transmission David Herrmann
2016-10-26 19:18 ` [RFC v1 12/14] bus1: hook up file-operations David Herrmann
2016-10-26 19:18 ` [RFC v1 13/14] bus1: limit and protect resources David Herrmann
2016-10-26 19:18 ` [RFC v1 14/14] bus1: basic user-space kselftests David Herrmann
2016-10-26 19:39 ` [RFC v1 00/14] Bus1 Kernel Message Bus Linus Torvalds
2016-10-26 20:34   ` David Herrmann
2016-10-27  0:45     ` Kirill A. Shutemov
2016-10-29 21:04       ` Josh Triplett
2016-11-02 14:45       ` David Herrmann
2017-01-30 22:11     ` Pavel Machek
2016-10-27 11:10 ` Michael Kerrisk
2016-10-28 13:11 ` Richard Weinberger
2016-10-28 13:37   ` Tom Gundersen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20161026191810.12275-8-dh.herrmann@gmail.com \
    --to=dh.herrmann@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=arnd@arndb.de \
    --cc=greg@kroah.com \
    --cc=hare@suse.com \
    --cc=jikos@kernel.org \
    --cc=josh@joshtriplett.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luto@amacapital.net \
    --cc=rostedt@goodmis.org \
    --cc=teg@jklm.no \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.