fs/pidfs.c

Source file repositories/reference/linux-study-clean/fs/pidfs.c

File Facts

System
Linux kernel
Corpus path
fs/pidfs.c
Extension
.c
Size
30244 bytes
Lines
1157
Domain
Core OS
Bucket
VFS And Filesystem Core
Inferred role
Core OS: operation-table or driver-model contract
Status
pattern implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

static const struct file_operations pidfs_file_operations = {
	.release	= pidfs_file_release,
	.poll		= pidfd_poll,
#ifdef CONFIG_PROC_FS
	.show_fdinfo	= pidfd_show_fdinfo,
#endif
	.unlocked_ioctl	= pidfd_ioctl,
	.compat_ioctl   = compat_ptr_ioctl,
};

struct pid *pidfd_pid(const struct file *file)
{
	if (file->f_op != &pidfs_file_operations)
		return ERR_PTR(-EBADF);
	return file_inode(file)->i_private;
}

/*
 * We're called from release_task(). We know there's at least one
 * reference to struct pid being held that won't be released until the
 * task has been reaped which cannot happen until we're out of
 * release_task().
 *
 * If this struct pid has at least once been referred to by a pidfd then
 * pid->attr will be allocated. If not we mark the struct pid as dead so
 * anyone who is trying to register it with pidfs will fail to do so.
 * Otherwise we would hand out pidfs for reaped tasks without having
 * exit information available.
 *
 * Worst case is that we've filled in the info and the pid gets freed
 * right away in free_pid() when no one holds a pidfd anymore. Since
 * pidfs_exit() currently is placed after exit_task_work() we know that
 * it cannot be us aka the exiting task holding a pidfd to itself.
 */
void pidfs_exit(struct task_struct *tsk)
{
	struct pid *pid = task_pid(tsk);
	struct pidfs_attr *attr;
#ifdef CONFIG_CGROUPS
	struct cgroup *cgrp;
#endif

	might_sleep();

	/* Synchronize with pidfs_register_pid(). */
	scoped_guard(spinlock_irq, &pid->wait_pidfd.lock) {
		attr = pid->attr;
		if (!attr) {
			/*
			 * No one ever held a pidfd for this struct pid.
			 * Mark it as dead so no one can add a pidfs
			 * entry anymore. We're about to be reaped and
			 * so no exit information would be available.
			 */
			pid->attr = PIDFS_PID_DEAD;
			return;
		}
	}

	/*
	 * If @pid->attr is set someone might still legitimately hold a
	 * pidfd to @pid or someone might concurrently still be getting
	 * a reference to an already stashed dentry from @pid->stashed.
	 * So defer cleaning @pid->attr until the last reference to @pid
	 * is put
	 */

#ifdef CONFIG_CGROUPS
	rcu_read_lock();
	cgrp = task_dfl_cgroup(tsk);
	attr->cgroupid = cgroup_id(cgrp);
	rcu_read_unlock();
#endif
	attr->exit_code = tsk->exit_code;

	/* Ensure that PIDFD_GET_INFO sees either all or nothing. */
	smp_wmb();
	set_bit(PIDFS_ATTR_BIT_EXIT, &attr->attr_mask);
}

#ifdef CONFIG_COREDUMP
void pidfs_coredump(const struct coredump_params *cprm)
{
	struct pid *pid = cprm->pid;
	struct pidfs_attr *attr;

	attr = READ_ONCE(pid->attr);

	VFS_WARN_ON_ONCE(!attr);
	VFS_WARN_ON_ONCE(attr == PIDFS_PID_DEAD);

Annotation

Implementation Notes