mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Josef Bacik <josef@toxicpanda.com>
To: Christian Brauner <brauner@kernel.org>,
	 James Bottomley <James.Bottomley@HansenPartnership.com>,
	 Andrew Halaney <andrew@amutable.com>,
	Jeremy Bingham <jbingham@gmail.com>,
	 Tahera Fahimi <taherafahimi@linux.microsoft.com>,
	 Serge Hallyn <sergeh@kernel.org>
Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
	 Josef Bacik <josef@toxicpanda.com>
Subject: [PATCH RFC v3 24/90] cells: add the cell object and cell_create()
Date: Thu, 05 Nov 2026 15:02:16 +0000	[thread overview]
Message-ID: <20261105-b4-cells-v3-24-bc9095937a84@toxicpanda.com> (raw)
In-Reply-To: <20261105-b4-cells-v3-0-bc9095937a84@toxicpanda.com>

Add the cell object in kernel/cell/, a small filesystem called cellfs that
backs its file descriptor, and the system call cell_create().
cell_create() creates a new, empty cell and returns a cellfd. It requires
no privilege. A task of a cell cannot call it, see cell_may_spawn().

A cell goes through the states BUILDING, SEALED, RUNNING, DYING and DEAD,
in that order and never backwards. This patch implements the two ends: a
cell that holds only its user namespace, and the teardown that drops
whatever a cell holds.

The cellfd becomes readable for poll() once the cell is DEAD. read() then
returns the wait status of the first task of the cell. DEAD is only
reported after the budget is empty and the cell has dropped all of its
references.

A cellfd can be passed to another process, but it cannot be reopened
through /proc/<pid>/fd. So when the last cellfd is closed nobody can reach
the cell any more, and the cell is killed. If the process that created the
cell is killed, the cell dies with it, unless the cellfd was passed on. A
cell without a cellfd could not be killed, spawned into or waited for.

Every cell has a 64 bit id that is never reused. It is meant for code that
has to remember a cell for longer than anything pins it. It is also the
inode number of the cellfd. cell_id() is exported, because some of that
code can be built as a module.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
---
 fs/cellfs.c                | 141 ++++++++++++++++++++
 include/linux/cell.h       |  23 ++++
 include/uapi/linux/cell.h  |  24 ++++
 include/uapi/linux/fcntl.h |   1 +
 include/uapi/linux/magic.h |   1 +
 kernel/cell/Makefile       |   2 +
 kernel/cell/core.c         | 312 +++++++++++++++++++++++++++++++++++++++++++++
 kernel/cell/internal.h     |  62 +++++++++
 8 files changed, 566 insertions(+)

diff --git a/fs/cellfs.c b/fs/cellfs.c
new file mode 100644
index 000000000000..744ed39abbba
--- /dev/null
+++ b/fs/cellfs.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cellfs: the pseudo filesystem a cellfd lives on.
+ *
+ * Modelled on pidfs. One inode per cell, numbered by the cell's id, which is
+ * never reused. The inode holds a reference on the cell.
+ */
+#include <linux/cell.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/magic.h>
+#include <linux/mount.h>
+#include <linux/pseudo_fs.h>
+
+#include "internal.h"
+
+static struct vfsmount *cellfs_mnt __ro_after_init;
+
+static void cellfs_evict_inode(struct inode *inode)
+{
+	struct cell *cell = inode->i_private;
+
+	clear_inode(inode);
+	cell_put(cell);
+}
+
+static const struct super_operations cellfs_sops = {
+	.drop_inode	= inode_just_drop,
+	.evict_inode	= cellfs_evict_inode,
+	.statfs		= simple_statfs,
+};
+
+static char *cellfs_dname(struct dentry *dentry, char *buffer, int buflen)
+{
+	return dynamic_dname(buffer, buflen, "cell:[%llu]",
+			     d_inode(dentry)->i_ino);
+}
+
+static const struct dentry_operations cellfs_dentry_operations = {
+	.d_dname	= cellfs_dname,
+	.d_prune	= stashed_dentry_prune,
+};
+
+static int cellfs_setattr(const struct mnt_idmap *idmap, struct dentry *dentry,
+			  struct iattr *attr)
+{
+	return -EOPNOTSUPP;
+}
+
+static const struct inode_operations cellfs_inode_operations = {
+	.setattr	= cellfs_setattr,
+};
+
+static int cellfs_init_inode(struct inode *inode, void *data)
+{
+	struct cell *cell = data;
+
+	inode->i_private = data;
+	inode->i_flags |= S_PRIVATE | S_ANON_INODE;
+	inode->i_mode |= 0700;
+	inode->i_op = &cellfs_inode_operations;
+	inode->i_fop = &cell_file_operations;
+	inode->i_ino = cell_id(cell);
+	return 0;
+}
+
+static void cellfs_put_data(void *data)
+{
+	cell_put(data);
+}
+
+static const struct stashed_operations cellfs_stashed_ops = {
+	.init_inode	= cellfs_init_inode,
+	.put_data	= cellfs_put_data,
+};
+
+static int cellfs_init_fs_context(struct fs_context *fc)
+{
+	struct pseudo_fs_context *ctx;
+
+	ctx = init_pseudo(fc, CELL_FS_MAGIC);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->s_d_flags |= DCACHE_DONTCACHE;
+	ctx->ops = &cellfs_sops;
+	ctx->dops = &cellfs_dentry_operations;
+	fc->s_fs_info = (void *)&cellfs_stashed_ops;
+	return 0;
+}
+
+static struct file_system_type cellfs_type = {
+	.name			= "cellfs",
+	.init_fs_context	= cellfs_init_fs_context,
+	.kill_sb		= kill_anon_super,
+};
+
+/**
+ * cell_from_file - the cell a file is a handle on
+ * @file: any file
+ *
+ * Return: the cell, or NULL if @file is no cellfd. No reference is taken: @file
+ * pins the inode, which holds one.
+ */
+struct cell *cell_from_file(const struct file *file)
+{
+	if (file->f_op != &cell_file_operations)
+		return NULL;
+	return file_inode(file)->i_private;
+}
+
+/**
+ * cellfs_alloc_file - make a new handle on a cell
+ * @cell: the cell. The caller keeps its own reference
+ *
+ * This is the only way to a handle. A cellfd cannot be opened again through
+ * /proc/<pid>/fd.
+ *
+ * Return: a file open for reading and writing, or an ERR_PTR().
+ */
+struct file *cellfs_alloc_file(struct cell *cell)
+{
+	struct path path __free(path_put) = {};
+	int ret;
+
+	ret = path_from_stashed(cell_stashed(cell), cellfs_mnt, cell_get(cell),
+				&path);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	return dentry_open(&path, O_RDWR, current_cred());
+}
+
+static int __init cellfs_init(void)
+{
+	cellfs_mnt = kern_mount(&cellfs_type);
+	if (IS_ERR(cellfs_mnt))
+		panic("Failed to mount cellfs pseudo filesystem");
+	return 0;
+}
+fs_initcall(cellfs_init);
diff --git a/include/linux/cell.h b/include/linux/cell.h
index 37fde4bfea44..75dc59518c91 100644
--- a/include/linux/cell.h
+++ b/include/linux/cell.h
@@ -69,6 +69,20 @@ static inline struct cell *current_cell(void)
 	return cred_cell(current_cred());
 }
 
+/**
+ * cell_may_spawn - may the caller make cells and start tasks in them?
+ *
+ * This is the "spawn" slot of the cell the caller acts in. There is nothing to
+ * fill it with yet, so it is always empty, and the host holds everything. That
+ * no cellfd gets into a cell is not what this rests on.
+ *
+ * Return: true if it may.
+ */
+static inline bool cell_may_spawn(void)
+{
+	return !current_cell();
+}
+
 /**
  * task_cell - the cell a task is in
  * @task: a task
@@ -137,6 +151,7 @@ void cell_put(struct cell *cell);
 static inline struct cell *userns_cell(const struct user_namespace *ns) { return NULL; }
 static inline struct cell *cred_cell(const struct cred *cred) { return NULL; }
 static inline struct cell *current_cell(void) { return NULL; }
+static inline bool cell_may_spawn(void) { return true; }
 static inline struct cell *task_cell(const struct task_struct *t) { return NULL; }
 static inline bool cell_same(const struct cred *a, const struct cred *b) { return true; }
 static inline u64 cred_cell_id(const struct cred *cred) { return 0; }
@@ -152,4 +167,12 @@ void cell_block_release(u32 block);
 static inline void cell_block_release(u32 block) { }
 #endif
 
+/* The cellfd. */
+#ifdef CONFIG_CELLS
+extern const struct file_operations cell_file_operations;
+struct file *cellfs_alloc_file(struct cell *cell);
+struct cell *cell_from_file(const struct file *file);
+struct dentry **cell_stashed(struct cell *cell);
+#endif
+
 #endif /* _LINUX_CELL_H */
diff --git a/include/uapi/linux/cell.h b/include/uapi/linux/cell.h
new file mode 100644
index 000000000000..f3589a23a596
--- /dev/null
+++ b/include/uapi/linux/cell.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_CELL_H
+#define _UAPI_LINUX_CELL_H
+
+#include <linux/types.h>
+
+/*
+ * A cell is a container: one kernel object that a task is born into and never
+ * leaves. It is built from the outside, by filling named slots from file
+ * descriptors the builder holds, and then sealed.
+ */
+
+/* cell_create(): @parent is CELL_SELF, see <linux/fcntl.h>. */
+
+/* Life of a cell. It only ever moves down this list. */
+enum cell_state {
+	CELL_BUILDING		= 0,	/* Slots may be filled */
+	CELL_SEALED		= 1,	/* Immutable, nothing spawned yet */
+	CELL_RUNNING		= 2,	/* Has, or had, its first task */
+	CELL_DYING		= 3,	/* Killed, or its init exited */
+	CELL_DEAD		= 4,	/* Nothing left. The status can be read */
+};
+
+#endif /* _UAPI_LINUX_CELL_H */
diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
index e43e3de3e9ee..951c97e47e99 100644
--- a/include/uapi/linux/fcntl.h
+++ b/include/uapi/linux/fcntl.h
@@ -125,6 +125,7 @@ struct delegation {
 #define FD_PIDFS_ROOT			-10002 /* Root of the pidfs filesystem */
 #define FD_NSFS_ROOT			-10003 /* Root of the nsfs filesystem */
 #define FD_FAILFS_ROOT			-10004 /* Root of the failfs filesystem */
+#define CELL_SELF			-10005 /* The caller's own cell */
 #define FD_INVALID			-10009 /* Invalid file descriptor: -10000 - EBADF = -10009 */
 
 /* Generic flags for the *at(2) family of syscalls. */
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index 66a91c8b1cb9..093e68790b47 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -107,5 +107,6 @@
 #define GUEST_MEMFD_MAGIC	0x474d454d	/* "GMEM" */
 #define NULL_FS_MAGIC		0x4E554C4C	/* "NULL" */
 #define FAIL_FS_MAGIC		0x4641494C	/* "FAIL" */
+#define CELL_FS_MAGIC		0x43454C4C	/* "CELL" */
 
 #endif /* __LINUX_MAGIC_H__ */
diff --git a/kernel/cell/Makefile b/kernel/cell/Makefile
new file mode 100644
index 000000000000..e343e0fa72b0
--- /dev/null
+++ b/kernel/cell/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += core.o
diff --git a/kernel/cell/core.c b/kernel/cell/core.c
new file mode 100644
index 000000000000..d0243779af19
--- /dev/null
+++ b/kernel/cell/core.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cells: a container as one kernel object.
+ *
+ * A task is born in a cell and never leaves it. There is no way to enter one.
+ * A cell is built from the outside: the builder fills named slots from file
+ * descriptors it holds, seals the cell, and spawns the first task into it.
+ * Holding the cellfd is the authority to do all of that.
+ *
+ * Life:  BUILDING -> SEALED -> RUNNING -> DYING -> DEAD
+ *
+ * References. The cell owns a reference on its user namespace until it is torn
+ * down. The user namespace owns a reference on the cell until it is freed, so
+ * that current_cell() can be followed from any credential. Teardown breaks the
+ * cycle, and every cell is torn down: by a kill, by the exit of its first task,
+ * or by the last cellfd going away before anything ran.
+ *
+ * Locking. cell->lock serialises configuration, sealing, spawning and killing.
+ * It is held across copy_process(), which is what closes a kill against a spawn
+ * in flight. It nests outside cgroup_mutex and namespace_sem.
+ */
+#include <linux/cgroup.h>
+#include <linux/fs.h>
+#include <linux/mnt_namespace.h>
+#include <linux/nsproxy.h>
+#include <linux/pid_namespace.h>
+#include <linux/poll.h>
+#include <linux/sched/task.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <net/net_namespace.h>
+#include <uapi/linux/cell.h>
+
+#include "internal.h"
+
+static atomic64_t cell_next_id = ATOMIC64_INIT(0);
+
+/**
+ * cell_id - the id of a cell
+ * @cell: a cell
+ *
+ * Return: the id. It is never 0, which stands for the host, and never reused.
+ */
+u64 cell_id(const struct cell *cell)
+{
+	return cell->id;
+}
+EXPORT_SYMBOL_GPL(cell_id);
+
+/**
+ * cell_stashed - where cellfs keeps the dentry of a cell
+ * @cell: a cell
+ *
+ * Return: the location to hand to path_from_stashed().
+ */
+struct dentry **cell_stashed(struct cell *cell)
+{
+	return &cell->stashed;
+}
+
+/**
+ * cell_get - take a reference on a cell
+ * @cell: a cell the caller already holds a reference on
+ *
+ * A reference keeps the memory of the cell around and nothing else. It neither
+ * keeps the cell alive nor is it a handle on it.
+ *
+ * Return: @cell.
+ */
+struct cell *cell_get(struct cell *cell)
+{
+	refcount_inc(&cell->ref);
+	return cell;
+}
+
+/**
+ * cell_put - drop a reference on a cell
+ * @cell: a cell, or NULL
+ *
+ * Context: Process context. The cell is freed after a grace period.
+ */
+void cell_put(struct cell *cell)
+{
+	if (!cell || !refcount_dec_and_test(&cell->ref))
+		return;
+	if (cell->groups)
+		put_group_info(cell->groups);
+	kfree_rcu(cell, rcu);
+}
+
+/*
+ * Teardown. Drops every reference the cell holds and only then says DEAD. What
+ * that sets off, the release of mounts, of the network namespace and of the
+ * block of ids, completes on its own time.
+ */
+static void cell_teardown(struct cell *cell)
+{
+	struct cgroup *budget = cell->budget;
+
+	lockdep_assert_held(&cell->lock);
+
+	/* cell_may_migrate() looks at this under nothing but RCU. */
+	WRITE_ONCE(cell->budget, NULL);
+
+	if (cell->nsproxy)
+		put_nsproxy(cell->nsproxy);
+	cell->nsproxy = NULL;
+	path_put(&cell->root);
+	memset(&cell->root, 0, sizeof(cell->root));
+	if (cell->mnt_ns)
+		put_mnt_ns(cell->mnt_ns);
+	cell->mnt_ns = NULL;
+	if (cell->pid_ns)
+		put_pid_ns(cell->pid_ns);
+	cell->pid_ns = NULL;
+	if (cell->net)
+		put_net(cell->net);
+	cell->net = NULL;
+	if (budget) {
+		/*
+		 * The ids of the cell were only lent, so nothing may go on
+		 * being owned by them. If that cannot be had, the block is
+		 * never handed out again.
+		 */
+		if (cgroup_cell_budget_put(budget, cell->sealed, cell->budget_uid,
+					   cell->budget_gid))
+			userns_retire_cell_block(cell->user_ns);
+	}
+	put_user_ns(cell->user_ns);
+	cell->user_ns = NULL;
+
+	/* Pairs with the acquire of whoever goes on to read the status. */
+	smp_store_release(&cell->state, CELL_DEAD);
+	wake_up_all(&cell->wait);
+}
+
+static void cell_kill_locked(struct cell *cell)
+{
+	lockdep_assert_held(&cell->lock);
+
+	switch (cell->state) {
+	case CELL_BUILDING:
+	case CELL_SEALED:
+		/* Nothing ever ran. */
+		cell->exit_status = SIGKILL;
+		cell_teardown(cell);
+		break;
+	case CELL_RUNNING:
+		WRITE_ONCE(cell->state, CELL_DYING);
+		fallthrough;
+	case CELL_DYING:
+		/* The exit of the cell's init finishes the job. */
+		cgroup_cell_kill(cell->budget);
+		break;
+	case CELL_DEAD:
+		break;
+	}
+}
+
+static void cell_kill(struct cell *cell)
+{
+	guard(mutex)(&cell->lock);
+	cell_kill_locked(cell);
+}
+
+/* The last cellfd is gone, and there is no way to get another. */
+static void cell_kill_workfn(struct work_struct *work)
+{
+	struct cell *cell = container_of(work, struct cell, kill_work);
+
+	cell_kill(cell);
+	cell_put(cell);
+}
+
+/* The cellfd. */
+
+static int cell_file_open(struct inode *inode, struct file *file)
+{
+	/*
+	 * A handle is handed out, by cellfs_alloc_file(), and passed on. It is
+	 * not opened again through /proc/<pid>/fd: the inode is immutable, so
+	 * may_open() refuses that for writing, and this refuses the rest.
+	 */
+	if (!(file->f_mode & FMODE_WRITE))
+		return -ENXIO;
+
+	return 0;
+}
+
+static int cell_file_release(struct inode *inode, struct file *file)
+{
+	struct cell *cell = inode->i_private;
+
+	/*
+	 * A cell has the one file that cell_create() made, however many file
+	 * descriptors refer to it. Nobody can reach the cell any more, and there
+	 * is no way to get another handle, so it goes: a cell lives for as long
+	 * as somebody holds it. The last fput() may come from anywhere, so only
+	 * owe the kill here.
+	 */
+	cell_get(cell);
+	if (!queue_work(system_dfl_wq, &cell->kill_work))
+		cell_put(cell);
+	return 0;
+}
+
+static bool cell_is_dead(struct cell *cell)
+{
+	/* Pairs with the release in cell_teardown(): the status is there. */
+	return smp_load_acquire(&cell->state) == CELL_DEAD;
+}
+
+static __poll_t cell_file_poll(struct file *file, poll_table *pts)
+{
+	struct cell *cell = cell_from_file(file);
+
+	poll_wait(file, &cell->wait, pts);
+	if (cell_is_dead(cell))
+		return EPOLLIN | EPOLLRDNORM;
+	return 0;
+}
+
+/* Reads the wait status of the cell's first task. Repeatable. */
+static ssize_t cell_file_read(struct file *file, char __user *buf, size_t count,
+			      loff_t *ppos)
+{
+	struct cell *cell = cell_from_file(file);
+	__s32 status;
+	int ret;
+
+	if (count < sizeof(status))
+		return -EINVAL;
+
+	if (!cell_is_dead(cell)) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		ret = wait_event_interruptible(cell->wait, cell_is_dead(cell));
+		if (ret)
+			return ret;
+	}
+
+	status = cell->exit_status;
+	if (copy_to_user(buf, &status, sizeof(status)))
+		return -EFAULT;
+	return sizeof(status);
+}
+
+const struct file_operations cell_file_operations = {
+	.open		= cell_file_open,
+	.release	= cell_file_release,
+	.poll		= cell_file_poll,
+	.read		= cell_file_read,
+	.llseek		= noop_llseek,
+};
+
+/**
+ * sys_cell_create - make a new, empty cell
+ * @parent: CELL_SELF
+ * @flags:  none yet, must be 0
+ *
+ * The cell holds nothing, is in state CELL_BUILDING and can already be killed.
+ * It takes no privilege beyond what making a user namespace takes. A task of a
+ * cell cannot make one.
+ *
+ * Return: a cellfd, which is always close-on-exec, or a negative error.
+ */
+SYSCALL_DEFINE2(cell_create, int, parent, unsigned int, flags)
+{
+	struct cell *cell;
+	struct cred *cred;
+	int ret;
+
+	if (flags)
+		return -EINVAL;
+	if (parent != CELL_SELF)
+		return -EINVAL;
+	if (!cell_may_spawn())
+		return -EPERM;
+
+	cell = kzalloc_obj(struct cell, GFP_KERNEL_ACCOUNT);
+	if (!cell)
+		return -ENOMEM;
+
+	refcount_set(&cell->ref, 1);
+	cell->id = atomic64_inc_return(&cell_next_id);
+	mutex_init(&cell->lock);
+	init_waitqueue_head(&cell->wait);
+	INIT_WORK(&cell->kill_work, cell_kill_workfn);
+	strscpy(cell->hostname, "(none)");
+
+	/* The scratch credential is only here to carry the new namespace. */
+	cred = prepare_creds();
+	if (!cred) {
+		kfree(cell);
+		return -ENOMEM;
+	}
+	ret = create_cell_user_ns(cred, cell);
+	if (ret) {
+		abort_creds(cred);
+		kfree(cell);
+		return ret;
+	}
+	cell->user_ns = get_user_ns(cred->user_ns);
+	cell->groups = get_group_info(cred->group_info);
+	abort_creds(cred);
+
+	ret = FD_ADD(O_CLOEXEC, cellfs_alloc_file(cell));
+	if (ret < 0)
+		cell_kill(cell);
+	cell_put(cell);
+	return ret;
+}
diff --git a/kernel/cell/internal.h b/kernel/cell/internal.h
new file mode 100644
index 000000000000..01a7a8581ff7
--- /dev/null
+++ b/kernel/cell/internal.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _KERNEL_CELL_INTERNAL_H
+#define _KERNEL_CELL_INTERNAL_H
+
+#include <linux/capability.h>
+#include <linux/cell.h>
+#include <linux/file.h>
+#include <linux/mutex.h>
+#include <linux/path.h>
+#include <linux/rcupdate.h>
+#include <linux/refcount.h>
+#include <linux/uidgid.h>
+#include <linux/utsname.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#define CELL_KEY_MAX	64
+
+struct cell {
+	refcount_t		ref;
+	u64			id;
+	struct dentry		*stashed;
+
+	struct mutex		lock;
+	unsigned int		state;		/* enum cell_state */
+
+	/* Gone once the cell is DEAD. */
+	struct user_namespace	*user_ns;
+	u32			ids_count;
+	struct group_info	*groups;	/* of whoever made the cell */
+
+	/* Slots. */
+	struct mnt_namespace	*mnt_ns;
+	struct pid_namespace	*pid_ns;
+	struct net		*net;
+	struct cgroup		*budget;
+	u64			budget_id;
+	kuid_t			budget_uid;	/* who owned it before */
+	kgid_t			budget_gid;
+	bool			sealed;		/* the budget was handed over */
+	char			hostname[__NEW_UTS_LEN + 1];
+	kernel_cap_t		caps;		/* what a task starts with */
+
+	/* What sealing makes of them. */
+	struct nsproxy		*nsproxy;
+	struct path		root;
+
+	int			exit_status;
+	wait_queue_head_t	wait;
+	struct work_struct	kill_work;
+	struct delayed_work	exit_work;
+	struct rcu_head		rcu;
+};
+
+static inline struct cell *cell_from_fd(const struct fd *f)
+{
+	if (fd_empty(*f))
+		return ERR_PTR(-EBADF);
+	return cell_from_file(fd_file(*f)) ?: ERR_PTR(-EINVAL);
+}
+
+#endif /* _KERNEL_CELL_INTERNAL_H */

-- 
2.55.0


  parent reply	other threads:[~2026-11-05 15:08 UTC|newest]

Thread overview: 93+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-05 14:56 [PATCH RFC v3 00/90] cells: a container as one kernel object Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 01/90] Documentation: add a design document for cells Josef Bacik
2026-11-05 17:06   ` Serge E. Hallyn
2026-11-05 18:23     ` Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 02/90] Documentation: add examples " Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 03/90] cells: reserve a window of ids Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 04/90] user_namespace: let a cell own a user namespace Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 05/90] user_namespace: refuse setns() into a cell Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 06/90] capability: give the owner no say over " Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 07/90] ucount: count the id blocks a user holds Josef Bacik
2026-11-05 14:56 ` [PATCH RFC v3 08/90] user_namespace: let the kernel map the ids of a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 09/90] user_namespace: keep legacy maps out of the window Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 10/90] user: add uid_range_in_use() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 11/90] keys: add key_user_range_in_use() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 12/90] fs: add kuid_has_fsmapping() and kgid_has_fsmapping() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 13/90] fs: never store an id that was lent to a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 14/90] fs: take no id of a cell for a mount option Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 15/90] fs: let an idmapping pin the ids of a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 16/90] cgroup: let a cgroup become the budget " Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 17/90] cgroup: add cgroup_cell_chown() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 18/90] cgroup: give the budget of a cell back Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 19/90] cgroup: add cgroup_cell_kill() Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 20/90] cgroup: root a cgroup namespace at a given cgroup Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 21/90] fs: seal a mount namespace for a cell Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 22/90] nsfs: make open_namespace() available outside of fs/ Josef Bacik
2026-11-05 14:57 ` [PATCH RFC v3 23/90] nsproxy: build the nsproxy the tasks of a cell share Josef Bacik
2026-11-05 15:02 ` Josef Bacik [this message]
2026-11-05 15:02 ` [PATCH RFC v3 25/90] cells: add cell_kill() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 26/90] cells: add cell_configure() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 27/90] cells: add the "budget" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 28/90] cells: add the "root" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 29/90] cells: add the "ids" slot, for a single id Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 30/90] cells: lend a cell a block of ids Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 31/90] cells: reserve the "spawn" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 32/90] cells: add CELL_CMD_SEAL Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 33/90] cells: add cell_open() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 34/90] pid: allocate a struct pid ahead of its numbers Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 35/90] fork: let copy_process() place a task in a cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 36/90] cgroup: fork a task of a cell into the budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 37/90] x86: give a task without an mm no user state Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 38/90] uprobes: cope with a new task that has no mm Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 39/90] fork: add tasks that start out with nothing Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 40/90] pidfd: add PIDFD_EMPTY Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 41/90] pidfd: add pidfd_configure() Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 42/90] pidfd: let pidfd_configure() place files Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 43/90] pidfd: let pidfd_configure() set credentials Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 44/90] pidfd: let pidfd_configure() name a cgroup to start in Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 45/90] ptrace: keep everybody off an embryo Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 46/90] cells: end a cell when its init exits Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 47/90] cells: refuse a task of a cell a change of namespaces Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 48/90] fs: refuse a task of a cell a new mount namespace Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 49/90] nsproxy: refuse to enter a namespace of a cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 50/90] cgroup: keep a task of a cell within the budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 51/90] cgroup: let nothing but a spawn into a budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 52/90] kernfs: let a filesystem refuse a change to a node Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 53/90] cgroup: let a cell change nothing outside its budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 54/90] cgroup: leave the limits of a budget to who is above Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 55/90] fs: let a filesystem hold ids that go with the cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 56/90] cgroup: let a cell make cgroups below its budget Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 57/90] resctrl: let a cell change nothing Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 58/90] cred: let no id count across cells Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 59/90] ptrace: refuse PTRACE_TRACEME " Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 60/90] keys: ignore an equal uid or gid " Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 61/90] fs: accept a cellfd for MOUNT_ATTR_IDMAP Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 62/90] fs: let a cell set no set-id bits Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 63/90] fs: drop privileges from what a cell may write to Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 64/90] cells: add the "caps" slot Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 65/90] capabilities: take nothing from a file in a cell Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 66/90] capabilities: let a cell shrink its bounding sets Josef Bacik
2026-11-05 15:02 ` [PATCH RFC v3 67/90] af_unix: deliver ancillary data only within a cell Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 68/90] af_unix: tell a sender that control data will not cross Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 69/90] net: do not identify a peer in another cell Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 70/90] seccomp: let a listener put no file into a cell Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 71/90] umh: run no program for a task of " Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 72/90] coredump: let no core dump leave " Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 73/90] cells: add the "net" slot Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 74/90] net: give a cell without a network no sockets Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 75/90] net: add net_may() Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 76/90] rtnetlink: use net_may() for requests that change state Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 77/90] net: use net_may() in the configuration ioctls Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 78/90] netfilter: use net_may() for configuration Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 79/90] tun: use net_may() to create and attach to devices Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 80/90] net: use net_may() for the net sysctls Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 81/90] net: use net_may() for privileged socket options Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 82/90] net: use net_may() for raw and packet sockets Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 83/90] cells: add the "net-rights" slot Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 84/90] net: honour the rights of a cell in net_may() Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 85/90] tun: ignore an equal uid or gid across cells Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 86/90] cells: add cell_info() Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 87/90] cells: add CONFIG_CELLS Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 88/90] arch: wire up the cell system calls Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 89/90] MAINTAINERS: add an entry for cells Josef Bacik
2026-11-05 15:03 ` [PATCH RFC v3 90/90] selftests: add tests " Josef Bacik

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=20261105-b4-cells-v3-24-bc9095937a84@toxicpanda.com \
    --to=josef@toxicpanda.com \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=andrew@amutable.com \
    --cc=brauner@kernel.org \
    --cc=jbingham@gmail.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sergeh@kernel.org \
    --cc=taherafahimi@linux.microsoft.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®