Skip to content
Snippets Groups Projects
shm.c 27.7 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/*
 * linux/ipc/shm.c
 * Copyright (C) 1992, 1993 Krishna Balasubramanian
 *	 Many improvements/fixes by Bruno Haible.
 * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994.
 * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli.
 *
 * /proc/sysvipc/shm support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
 * BIGMEM support, Andrea Arcangeli <andrea@suse.de>
 * SMP thread shm, Jean-Luc Boyard <jean-luc.boyard@siemens.fr>
 * HIGHMEM support, Ingo Molnar <mingo@redhat.com>
 * Make shmmax, shmall, shmmni sysctl'able, Christoph Rohland <cr@sap.com>
 * Shared /dev/zero support, Kanoj Sarcar <kanoj@sgi.com>
 * Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr@sap.com>
 *
 * support for audit of ipc object properties and permission changes
 * Dustin Kirkland <dustin.kirkland@us.ibm.com>
 *
 * namespaces support
 * OpenVZ, SWsoft Inc.
 * Pavel Emelianov <xemul@openvz.org>
Linus Torvalds's avatar
Linus Torvalds committed
 */

#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/hugetlb.h>
#include <linux/shm.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/mman.h>
#include <linux/shmem_fs.h>
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/audit.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
Nadia Derbey's avatar
Nadia Derbey committed
#include <linux/rwsem.h>
#include <linux/nsproxy.h>
Linus Torvalds's avatar
Linus Torvalds committed
#include <asm/uaccess.h>

#include "util.h"

struct shm_file_data {
	int id;
	struct ipc_namespace *ns;
	struct file *file;
	const struct vm_operations_struct *vm_ops;
};

#define shm_file_data(file) (*((struct shm_file_data **)&(file)->private_data))

static const struct file_operations shm_file_operations;
Linus Torvalds's avatar
Linus Torvalds committed
static struct vm_operations_struct shm_vm_ops;

static struct ipc_ids init_shm_ids;

#define shm_ids(ns)	(*((ns)->ids[IPC_SHM_IDS]))
Linus Torvalds's avatar
Linus Torvalds committed

#define shm_unlock(shp)			\
	ipc_unlock(&(shp)->shm_perm)
#define shm_buildid(id, seq)	ipc_buildid(id, seq)
Linus Torvalds's avatar
Linus Torvalds committed

static int newseg(struct ipc_namespace *, struct ipc_params *);
static void shm_open(struct vm_area_struct *vma);
static void shm_close(struct vm_area_struct *vma);
static void shm_destroy (struct ipc_namespace *ns, struct shmid_kernel *shp);
Linus Torvalds's avatar
Linus Torvalds committed
#ifdef CONFIG_PROC_FS
static int sysvipc_shm_proc_show(struct seq_file *s, void *it);
Linus Torvalds's avatar
Linus Torvalds committed
#endif

static void __shm_init_ns(struct ipc_namespace *ns, struct ipc_ids *ids)
{
	ns->ids[IPC_SHM_IDS] = ids;
	ns->shm_ctlmax = SHMMAX;
	ns->shm_ctlall = SHMALL;
	ns->shm_ctlmni = SHMMNI;
	ns->shm_tot = 0;
Nadia Derbey's avatar
Nadia Derbey committed
	ipc_init_ids(ids);
Nadia Derbey's avatar
Nadia Derbey committed
/*
Nadia Derbey's avatar
Nadia Derbey committed
 * Called with shm_ids.rw_mutex (writer) and the shp structure locked.
 * Only shm_ids.rw_mutex remains locked on exit.
Nadia Derbey's avatar
Nadia Derbey committed
 */
static void do_shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *shp)
{
	if (shp->shm_nattch){
		shp->shm_perm.mode |= SHM_DEST;
		/* Do not find it any more */
		shp->shm_perm.key = IPC_PRIVATE;
		shm_unlock(shp);
	} else
		shm_destroy(ns, shp);
}

int shm_init_ns(struct ipc_namespace *ns)
{
	struct ipc_ids *ids;

	ids = kmalloc(sizeof(struct ipc_ids), GFP_KERNEL);
	if (ids == NULL)
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed

	__shm_init_ns(ns, ids);
	return 0;
}

void shm_exit_ns(struct ipc_namespace *ns)
{
	struct shmid_kernel *shp;
Nadia Derbey's avatar
Nadia Derbey committed
	int next_id;
	int total, in_use;
Nadia Derbey's avatar
Nadia Derbey committed
	down_write(&shm_ids(ns).rw_mutex);
Nadia Derbey's avatar
Nadia Derbey committed

	in_use = shm_ids(ns).in_use;

	for (total = 0, next_id = 0; total < in_use; next_id++) {
		perm = idr_find(&shm_ids(ns).ipcs_idr, next_id);
		if (perm == NULL)
			continue;
		ipc_lock_by_ptr(perm);
		shp = container_of(perm, struct shmid_kernel, shm_perm);
		do_shm_rmid(ns, shp);
Nadia Derbey's avatar
Nadia Derbey committed
		total++;
Nadia Derbey's avatar
Nadia Derbey committed
	up_write(&shm_ids(ns).rw_mutex);

	kfree(ns->ids[IPC_SHM_IDS]);
	ns->ids[IPC_SHM_IDS] = NULL;
}
Linus Torvalds's avatar
Linus Torvalds committed

void __init shm_init (void)
{
	__shm_init_ns(&init_ipc_ns, &init_shm_ids);
	ipc_init_proc_interface("sysvipc/shm",
				"       key      shmid perms       size  cpid  lpid nattch   uid   gid  cuid  cgid      atime      dtime      ctime\n",
				IPC_SHM_IDS, sysvipc_shm_proc_show);
Nadia Derbey's avatar
Nadia Derbey committed
/*
 * shm_lock_(check_)down routines are called in the paths where the rw_mutex
 * is held to protect access to the idr tree.
 */
static inline struct shmid_kernel *shm_lock_down(struct ipc_namespace *ns,
						int id)
{
	struct kern_ipc_perm *ipcp = ipc_lock_down(&shm_ids(ns), id);

	if (IS_ERR(ipcp))
		return (struct shmid_kernel *)ipcp;

Nadia Derbey's avatar
Nadia Derbey committed
	return container_of(ipcp, struct shmid_kernel, shm_perm);
}

static inline struct shmid_kernel *shm_lock_check_down(
						struct ipc_namespace *ns,
						int id)
{
	struct kern_ipc_perm *ipcp = ipc_lock_check_down(&shm_ids(ns), id);

	if (IS_ERR(ipcp))
		return (struct shmid_kernel *)ipcp;

Nadia Derbey's avatar
Nadia Derbey committed
	return container_of(ipcp, struct shmid_kernel, shm_perm);
}

/*
 * shm_lock_(check_) routines are called in the paths where the rw_mutex
 * is not held.
 */
static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id)
Linus Torvalds's avatar
Linus Torvalds committed
{
Nadia Derbey's avatar
Nadia Derbey committed
	struct kern_ipc_perm *ipcp = ipc_lock(&shm_ids(ns), id);

	if (IS_ERR(ipcp))
		return (struct shmid_kernel *)ipcp;

Nadia Derbey's avatar
Nadia Derbey committed
	return container_of(ipcp, struct shmid_kernel, shm_perm);
}

static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns,
						int id)
{
Nadia Derbey's avatar
Nadia Derbey committed
	struct kern_ipc_perm *ipcp = ipc_lock_check(&shm_ids(ns), id);

	if (IS_ERR(ipcp))
		return (struct shmid_kernel *)ipcp;

Nadia Derbey's avatar
Nadia Derbey committed
	return container_of(ipcp, struct shmid_kernel, shm_perm);
Nadia Derbey's avatar
Nadia Derbey committed
static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s)
Linus Torvalds's avatar
Linus Torvalds committed
{
Nadia Derbey's avatar
Nadia Derbey committed
	ipc_rmid(&shm_ids(ns), &s->shm_perm);
Linus Torvalds's avatar
Linus Torvalds committed
}
Loading
Loading full blame...