1419 lines · 33.6 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 <linux/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_idr(&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); 	/*	 * Callers of shm_lock() must validate the status of the returned ipc	 * object pointer (as returned by ipc_lock()), and error out as	 * appropriate.	 */	if (IS_ERR(ipcp))		return (void *)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 kern_ipc_perm *ptr = container_of(head, struct kern_ipc_perm,							rcu);	struct shmid_kernel *shp = container_of(ptr, struct shmid_kernel,							shm_perm);	security_shm_free(shp);	kvfree(shp);} static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s){	list_del(&s->shm_clist);	ipc_rmid(&shm_ids(ns), &s->shm_perm);}  static int __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); 	if (IS_ERR(shp))		return PTR_ERR(shp); 	shp->shm_atim = get_seconds();	shp->shm_lprid = task_tgid_vnr(current);	shp->shm_nattch++;	shm_unlock(shp);	return 0;} /* This is called by fork, once for every shm attach. */static void shm_open(struct vm_area_struct *vma){	int err = __shm_open(vma);	/*	 * We raced in the idr lookup or with shm_destroy().	 * Either way, the ID is busted.	 */	WARN_ON_ONCE(err);} /* * 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(i_size_read(file_inode(shm_file)),				shp->mlock_user);	fput(shm_file);	ipc_rcu_putref(&shp->shm_perm, 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); 	/*	 * We raced in the idr lookup or with shm_destroy().	 * Either way, the ID is busted.	 */	if (WARN_ON_ONCE(IS_ERR(shp)))		goto done; /* no-op */ 	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);done:	up_write(&shm_ids(ns).rwsem);} /* 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);} /* Locking assumes this will only be called with task == current */void exit_shm(struct task_struct *task){	struct ipc_namespace *ns = task->nsproxy->ipc_ns;	struct shmid_kernel *shp, *n; 	if (list_empty(&task->sysvshm.shm_clist))		return; 	/*	 * If kernel.shm_rmid_forced is not set then only keep track of	 * which shmids are orphaned, so that a later set of the sysctl	 * can clean them up.	 */	if (!ns->shm_rmid_forced) {		down_read(&shm_ids(ns).rwsem);		list_for_each_entry(shp, &task->sysvshm.shm_clist, shm_clist)			shp->shm_creator = NULL;		/*		 * Only under read lock but we are only called on current		 * so no entry on the list will be shared.		 */		list_del(&task->sysvshm.shm_clist);		up_read(&shm_ids(ns).rwsem);		return;	} 	/*	 * Destroy all already created segments, that were not yet mapped,	 * and mark any mapped as orphan to cover the sysctl toggling.	 * Destroy is skipped if shm_may_destroy() returns false.	 */	down_write(&shm_ids(ns).rwsem);	list_for_each_entry_safe(shp, n, &task->sysvshm.shm_clist, shm_clist) {		shp->shm_creator = NULL; 		if (shm_may_destroy(ns, shp)) {			shm_lock_by_ptr(shp);			shm_destroy(ns, shp);		}	} 	/* Remove the list head from any segments still attached. */	list_del(&task->sysvshm.shm_clist);	up_write(&shm_ids(ns).rwsem);} static int shm_fault(struct vm_fault *vmf){	struct file *file = vmf->vma->vm_file;	struct shm_file_data *sfd = shm_file_data(file); 	return sfd->vm_ops->fault(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; 	/*	 * In case of remap_file_pages() emulation, the file can represent	 * removed IPC ID: propogate shm_lock() error to caller.	 */	ret = __shm_open(vma);	if (ret)		return ret; 	ret = call_mmap(sfd->file, vma);	if (ret) {		shm_close(vma);		return ret;	}	sfd->vm_ops = vma->vm_ops;#ifdef CONFIG_MMU	WARN_ON(!sfd->vm_ops->fault);#endif	vma->vm_ops = &shm_vm_ops;	return 0;} 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,	.get_unmapped_area	= shm_get_unmapped_area,	.llseek		= noop_llseek,	.fallocate	= shm_fallocate,}; /* * shm_file_operations_huge is now identical to shm_file_operations, * but we keep it distinct for the sake of is_file_shm_hugepages(). */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,}; 

Lines 1–500 of 1419.