1355 lines · 31.9 KB · C
RawDownload
/* * 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> * * Better ipc lock (kern_ipc_perm.lock) handling * Davidlohr Bueso <davidlohr.bueso@hp.com>, June 2013. */ #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>#include <linux/seq_file.h>#include <linux/rwsem.h>#include <linux/nsproxy.h>#include <linux/mount.h>#include <linux/ipc_namespace.h> #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; #define shm_ids(ns)	((ns)->ids[IPC_SHM_IDS]) #define shm_unlock(shp)			\	ipc_unlock(&(shp)->shm_perm) 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);#ifdef CONFIG_PROC_FSstatic int sysvipc_shm_proc_show(struct seq_file *s, void *it);#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;	ipc_init_ids(&shm_ids(ns));} /* * Called with shm_ids.rwsem (writer) and the shp structure locked. * Only shm_ids.rwsem remains locked on exit. */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);} #ifdef CONFIG_IPC_NSvoid 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);}#endif static int __init ipc_ns_init(void){	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);} static inline struct shmid_kernel *shm_obtain_object(struct ipc_namespace *ns, int id){	struct kern_ipc_perm *ipcp = ipc_obtain_object(&shm_ids(ns), id); 	if (IS_ERR(ipcp))		return ERR_CAST(ipcp); 	return container_of(ipcp, struct shmid_kernel, shm_perm);} static inline struct shmid_kernel *shm_obtain_object_check(struct ipc_namespace *ns, int id){	struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&shm_ids(ns), id); 	if (IS_ERR(ipcp))		return ERR_CAST(ipcp); 	return container_of(ipcp, struct shmid_kernel, shm_perm);} /* * shm_lock_(check_) routines are called in the paths where the rwsem * is not necessarily held. */static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id){	struct kern_ipc_perm *ipcp = ipc_lock(&shm_ids(ns), id); 	if (IS_ERR(ipcp))		return (struct shmid_kernel *)ipcp; 	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 void shm_rcu_free(struct rcu_head *head){	struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);	struct shmid_kernel *shp = ipc_rcu_to_struct(p); 	security_shm_free(shp);	ipc_rcu_free(head);} static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s){	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);	struct shmid_kernel *shp; 	shp = shm_lock(sfd->ns, sfd->id);	BUG_ON(IS_ERR(shp));	shp->shm_atim = get_seconds();	shp->shm_lprid = task_tgid_vnr(current);	shp->shm_nattch++;	shm_unlock(shp);} /* * shm_destroy - free the struct shmid_kernel * * @ns: namespace * @shp: struct to free * * It has to be called with shp and shm_ids.rwsem (writer) locked, * but returns with shp unlocked and freed. */static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp){	struct file *shm_file; 	shm_file = shp->shm_file;	shp->shm_file = NULL;	ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT;	shm_rmid(ns, shp);	shm_unlock(shp);	if (!is_file_hugepages(shm_file))		shmem_lock(shm_file, 0, shp->mlock_user);	else if (shp->mlock_user)		user_shm_unlock(file_inode(shm_file)->i_size, shp->mlock_user);	fput(shm_file);	ipc_rcu_putref(shp, shm_rcu_free);} /* * shm_may_destroy - identifies whether shm segment should be destroyed now * * Returns true if and only if there are no active users of the segment and * one of the following is true: * * 1) shmctl(id, IPC_RMID, NULL) was called for this shp * * 2) sysctl kernel.shm_rmid_forced is set to 1. */static bool shm_may_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp){	return (shp->shm_nattch == 0) &&	       (ns->shm_rmid_forced ||		(shp->shm_perm.mode & SHM_DEST));} /* * remove the attach descriptor vma. * free memory for segment if it is marked destroyed. * The descriptor has already been removed from the current->mm->mmap list * and will later be kfree()d. */static void shm_close(struct vm_area_struct *vma){	struct file * file = vma->vm_file;	struct shm_file_data *sfd = shm_file_data(file);	struct shmid_kernel *shp;	struct ipc_namespace *ns = sfd->ns; 	down_write(&shm_ids(ns).rwsem);	/* remove from the list of attaches of the shm segment */	shp = shm_lock(ns, sfd->id);	BUG_ON(IS_ERR(shp));	shp->shm_lprid = task_tgid_vnr(current);	shp->shm_dtim = get_seconds();	shp->shm_nattch--;	if (shm_may_destroy(ns, shp))		shm_destroy(ns, shp);	else		shm_unlock(shp);	up_write(&shm_ids(ns).rwsem);} /* Called with ns->shm_ids(ns).rwsem locked */static int shm_try_destroy_current(int id, void *p, void *data){	struct ipc_namespace *ns = data;	struct kern_ipc_perm *ipcp = p;	struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm); 	if (shp->shm_creator != current)		return 0; 	/*	 * Mark it as orphaned to destroy the segment when	 * kernel.shm_rmid_forced is changed.	 * It is noop if the following shm_may_destroy() returns true.	 */	shp->shm_creator = NULL; 	/*	 * Don't even try to destroy it.  If shm_rmid_forced=0 and IPC_RMID	 * is not set, it shouldn't be deleted here.	 */	if (!ns->shm_rmid_forced)		return 0; 	if (shm_may_destroy(ns, shp)) {		shm_lock_by_ptr(shp);		shm_destroy(ns, shp);	}	return 0;} /* Called with ns->shm_ids(ns).rwsem locked */static int shm_try_destroy_orphaned(int id, void *p, void *data){	struct ipc_namespace *ns = data;	struct kern_ipc_perm *ipcp = p;	struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm); 	/*	 * We want to destroy segments without users and with already	 * exit'ed originating process.	 *	 * As shp->* are changed under rwsem, it's safe to skip shp locking.	 */	if (shp->shm_creator != NULL)		return 0; 	if (shm_may_destroy(ns, shp)) {		shm_lock_by_ptr(shp);		shm_destroy(ns, shp);	}	return 0;} void shm_destroy_orphaned(struct ipc_namespace *ns){	down_write(&shm_ids(ns).rwsem);	if (shm_ids(ns).in_use)		idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns);	up_write(&shm_ids(ns).rwsem);}  void exit_shm(struct task_struct *task){	struct ipc_namespace *ns = task->nsproxy->ipc_ns; 	if (shm_ids(ns).in_use == 0)		return; 	/* Destroy all already created segments, but not mapped yet */	down_write(&shm_ids(ns).rwsem);	if (shm_ids(ns).in_use)		idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns);	up_write(&shm_ids(ns).rwsem);} static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf){	struct file *file = vma->vm_file;	struct shm_file_data *sfd = shm_file_data(file); 	return sfd->vm_ops->fault(vma, vmf);} #ifdef CONFIG_NUMAstatic int shm_set_policy(struct vm_area_struct *vma, struct mempolicy *new){	struct file *file = vma->vm_file;	struct shm_file_data *sfd = shm_file_data(file);	int err = 0;	if (sfd->vm_ops->set_policy)		err = sfd->vm_ops->set_policy(vma, new);	return err;} static struct mempolicy *shm_get_policy(struct vm_area_struct *vma,					unsigned long addr){	struct file *file = vma->vm_file;	struct shm_file_data *sfd = shm_file_data(file);	struct mempolicy *pol = NULL; 	if (sfd->vm_ops->get_policy)		pol = sfd->vm_ops->get_policy(vma, addr);	else if (vma->vm_policy)		pol = vma->vm_policy; 	return pol;}#endif static int shm_mmap(struct file * file, struct vm_area_struct * vma){	struct shm_file_data *sfd = shm_file_data(file);	int ret; 	ret = sfd->file->f_op->mmap(sfd->file, vma);	if (ret != 0)		return ret;	sfd->vm_ops = vma->vm_ops;#ifdef CONFIG_MMU	BUG_ON(!sfd->vm_ops->fault);#endif	vma->vm_ops = &shm_vm_ops;	shm_open(vma); 	return ret;} static int shm_release(struct inode *ino, struct file *file){	struct shm_file_data *sfd = shm_file_data(file); 	put_ipc_ns(sfd->ns);	shm_file_data(file) = NULL;	kfree(sfd);	return 0;} static int shm_fsync(struct file *file, loff_t start, loff_t end, int datasync){	struct shm_file_data *sfd = shm_file_data(file); 	if (!sfd->file->f_op->fsync)		return -EINVAL;	return sfd->file->f_op->fsync(sfd->file, start, end, datasync);} static long shm_fallocate(struct file *file, int mode, loff_t offset,			  loff_t len){	struct shm_file_data *sfd = shm_file_data(file); 	if (!sfd->file->f_op->fallocate)		return -EOPNOTSUPP;	return sfd->file->f_op->fallocate(file, mode, offset, len);} static unsigned long shm_get_unmapped_area(struct file *file,	unsigned long addr, unsigned long len, unsigned long pgoff,	unsigned long flags){	struct shm_file_data *sfd = shm_file_data(file);	return sfd->file->f_op->get_unmapped_area(sfd->file, addr, len,						pgoff, flags);} static const struct file_operations shm_file_operations = {	.mmap		= shm_mmap,	.fsync		= shm_fsync,	.release	= shm_release,#ifndef CONFIG_MMU	.get_unmapped_area	= shm_get_unmapped_area,#endif	.llseek		= noop_llseek,	.fallocate	= shm_fallocate,}; static const struct file_operations shm_file_operations_huge = {	.mmap		= shm_mmap,	.fsync		= shm_fsync,	.release	= shm_release,	.get_unmapped_area	= shm_get_unmapped_area,	.llseek		= noop_llseek,	.fallocate	= shm_fallocate,}; int is_file_shm_hugepages(struct file *file){	return file->f_op == &shm_file_operations_huge;} static const struct vm_operations_struct shm_vm_ops = {	.open	= shm_open,	/* callback for a new vm-area open */	.close	= shm_close,	/* callback for when the vm-area is released */	.fault	= shm_fault,#if defined(CONFIG_NUMA)	.set_policy = shm_set_policy,	.get_policy = shm_get_policy,#endif}; /** * newseg - Create a new shared memory segment * @ns: namespace * @params: ptr to the structure that contains key, size and shmflg * * Called with shm_ids.rwsem held as a writer. */ static int newseg(struct ipc_namespace *ns, struct ipc_params *params){	key_t key = params->key;	int shmflg = params->flg;	size_t size = params->u.size;	int error;	struct shmid_kernel *shp;	size_t numpages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;	struct file * file;	char name[13];	int id;	vm_flags_t acctflag = 0; 	if (size < SHMMIN || size > ns->shm_ctlmax)		return -EINVAL; 	if (ns->shm_tot + numpages > ns->shm_ctlall)		return -ENOSPC; 	shp = ipc_rcu_alloc(sizeof(*shp));

Lines 1–500 of 1355.