Skip to content
Snippets Groups Projects
shm.c 30.2 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>
#include <linux/ipc_namespace.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;
static const struct vm_operations_struct shm_vm_ops;
Linus Torvalds's avatar
Linus Torvalds committed

#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)
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

void shm_init_ns(struct ipc_namespace *ns)
{
	ns->shm_ctlmax = SHMMAX;
	ns->shm_ctlall = SHMALL;
	ns->shm_ctlmni = SHMMNI;
	ns->shm_rmid_forced = 0;
	ns->shm_tot = 0;
WANG Cong's avatar
WANG Cong committed
	ipc_init_ids(&shm_ids(ns));
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 kern_ipc_perm *ipcp)
	struct shmid_kernel *shp;
	shp = container_of(ipcp, struct shmid_kernel, shm_perm);

	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);
}

void shm_exit_ns(struct ipc_namespace *ns)
{
	free_ipcs(ns, &shm_ids(ns), do_shm_rmid);
	idr_destroy(&ns->ids[IPC_SHM_IDS].ipcs_idr);
Linus Torvalds's avatar
Linus Torvalds committed

static int __init ipc_ns_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
{
	shm_init_ns(&init_ipc_ns);
	return 0;
}

pure_initcall(ipc_ns_init);

void __init shm_init (void)
{
	ipc_init_proc_interface("sysvipc/shm",
#if BITS_PER_LONG <= 32
				"       key      shmid perms       size  cpid  lpid nattch   uid   gid  cuid  cgid      atime      dtime      ctime        rss       swap\n",
#else
				"       key      shmid perms                  size  cpid  lpid nattch   uid   gid  cuid  cgid      atime      dtime      ctime                   rss                  swap\n",
#endif
				IPC_SHM_IDS, sysvipc_shm_proc_show);
Nadia Derbey's avatar
Nadia Derbey committed
/*
 * shm_lock_(check_) routines are called in the paths where the rw_mutex
 * is not necessarily held.
Nadia Derbey's avatar
Nadia Derbey committed
 */
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 void shm_lock_by_ptr(struct shmid_kernel *ipcp)
{
	rcu_read_lock();
	ipc_lock_object(&ipcp->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);
/* This is called by fork, once for every shm attach. */
static void shm_open(struct vm_area_struct *vma)
	struct file *file = vma->vm_file;
	struct shm_file_data *sfd = shm_file_data(file);
Linus Torvalds's avatar
Linus Torvalds committed
	struct shmid_kernel *shp;

	shp = shm_lock(sfd->ns, sfd->id);
	BUG_ON(IS_ERR(shp));
Linus Torvalds's avatar
Linus Torvalds committed
	shp->shm_atim = get_seconds();
	shp->shm_lprid = task_tgid_vnr(current);
Linus Torvalds's avatar
Linus Torvalds committed
	shp->shm_nattch++;
	shm_unlock(shp);
}

/*
 * shm_destroy - free the struct shmid_kernel
 *
Nadia Derbey's avatar
Nadia Derbey committed
 * @ns: namespace
Linus Torvalds's avatar
Linus Torvalds committed
 * @shp: struct to free
 *
Nadia Derbey's avatar
Nadia Derbey committed
 * It has to be called with shp and shm_ids.rw_mutex (writer) locked,
Linus Torvalds's avatar
Linus Torvalds committed
 * but returns with shp unlocked and freed.
 */
static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
Linus Torvalds's avatar
Linus Torvalds committed
{
	ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT;
Nadia Derbey's avatar
Nadia Derbey committed
	shm_rmid(ns, shp);
Linus Torvalds's avatar
Linus Torvalds committed
	shm_unlock(shp);
	if (!is_file_hugepages(shp->shm_file))
		shmem_lock(shp->shm_file, 0, shp->mlock_user);
	else if (shp->mlock_user)
Al Viro's avatar
Al Viro committed
		user_shm_unlock(file_inode(shp->shm_file)->i_size,
Linus Torvalds's avatar
Linus Torvalds committed
						shp->mlock_user);
	fput (shp->shm_file);
	security_shm_free(shp);
	ipc_rcu_putref(shp);
Loading
Loading full blame...