937 lines · 21.7 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> */ #include <linux/config.h>#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/mutex.h> #include <asm/uaccess.h> #include "util.h" static struct file_operations shm_file_operations;static struct vm_operations_struct shm_vm_ops; static struct ipc_ids shm_ids; #define shm_lock(id)	((struct shmid_kernel*)ipc_lock(&shm_ids,id))#define shm_unlock(shp)	ipc_unlock(&(shp)->shm_perm)#define shm_get(id)	((struct shmid_kernel*)ipc_get(&shm_ids,id))#define shm_buildid(id, seq) \	ipc_buildid(&shm_ids, id, seq) static int newseg (key_t key, int shmflg, size_t size);static void shm_open (struct vm_area_struct *shmd);static void shm_close (struct vm_area_struct *shmd);#ifdef CONFIG_PROC_FSstatic int sysvipc_shm_proc_show(struct seq_file *s, void *it);#endif size_t	shm_ctlmax = SHMMAX;size_t 	shm_ctlall = SHMALL;int 	shm_ctlmni = SHMMNI; static int shm_tot; /* total number of shared memory pages */ void __init shm_init (void){	ipc_init_ids(&shm_ids, 1);	ipc_init_proc_interface("sysvipc/shm",				"       key      shmid perms       size  cpid  lpid nattch   uid   gid  cuid  cgid      atime      dtime      ctime\n",				&shm_ids,				sysvipc_shm_proc_show);} static inline int shm_checkid(struct shmid_kernel *s, int id){	if (ipc_checkid(&shm_ids,&s->shm_perm,id))		return -EIDRM;	return 0;} static inline struct shmid_kernel *shm_rmid(int id){	return (struct shmid_kernel *)ipc_rmid(&shm_ids,id);} static inline int shm_addid(struct shmid_kernel *shp){	return ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni);}   static inline void shm_inc (int id) {	struct shmid_kernel *shp; 	shp = shm_lock(id);	BUG_ON(!shp);	shp->shm_atim = get_seconds();	shp->shm_lprid = current->tgid;	shp->shm_nattch++;	shm_unlock(shp);} /* This is called by fork, once for every shm attach. */static void shm_open (struct vm_area_struct *shmd){	shm_inc (shmd->vm_file->f_dentry->d_inode->i_ino);} /* * shm_destroy - free the struct shmid_kernel * * @shp: struct to free * * It has to be called with shp and shm_ids.mutex locked, * but returns with shp unlocked and freed. */static void shm_destroy (struct shmid_kernel *shp){	shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT;	shm_rmid (shp->id);	shm_unlock(shp);	if (!is_file_hugepages(shp->shm_file))		shmem_lock(shp->shm_file, 0, shp->mlock_user);	else		user_shm_unlock(shp->shm_file->f_dentry->d_inode->i_size,						shp->mlock_user);	fput (shp->shm_file);	security_shm_free(shp);	ipc_rcu_putref(shp);} /* * remove the attach descriptor shmd. * 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 *shmd){	struct file * file = shmd->vm_file;	int id = file->f_dentry->d_inode->i_ino;	struct shmid_kernel *shp; 	mutex_lock(&shm_ids.mutex);	/* remove from the list of attaches of the shm segment */	shp = shm_lock(id);	BUG_ON(!shp);	shp->shm_lprid = current->tgid;	shp->shm_dtim = get_seconds();	shp->shm_nattch--;	if(shp->shm_nattch == 0 &&	   shp->shm_perm.mode & SHM_DEST)		shm_destroy (shp);	else		shm_unlock(shp);	mutex_unlock(&shm_ids.mutex);} static int shm_mmap(struct file * file, struct vm_area_struct * vma){	int ret; 	ret = shmem_mmap(file, vma);	if (ret == 0) {		vma->vm_ops = &shm_vm_ops;		if (!(vma->vm_flags & VM_WRITE))			vma->vm_flags &= ~VM_MAYWRITE;		shm_inc(file->f_dentry->d_inode->i_ino);	} 	return ret;} static struct file_operations shm_file_operations = {	.mmap	= shm_mmap,#ifndef CONFIG_MMU	.get_unmapped_area = shmem_get_unmapped_area,#endif}; static 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 */	.nopage	= shmem_nopage,#if defined(CONFIG_NUMA) && defined(CONFIG_SHMEM)	.set_policy = shmem_set_policy,	.get_policy = shmem_get_policy,#endif}; static int newseg (key_t key, int shmflg, size_t size){	int error;	struct shmid_kernel *shp;	int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;	struct file * file;	char name[13];	int id; 	if (size < SHMMIN || size > shm_ctlmax)		return -EINVAL; 	if (shm_tot + numpages >= shm_ctlall)		return -ENOSPC; 	shp = ipc_rcu_alloc(sizeof(*shp));	if (!shp)		return -ENOMEM; 	shp->shm_perm.key = key;	shp->shm_perm.mode = (shmflg & S_IRWXUGO);	shp->mlock_user = NULL; 	shp->shm_perm.security = NULL;	error = security_shm_alloc(shp);	if (error) {		ipc_rcu_putref(shp);		return error;	} 	if (shmflg & SHM_HUGETLB) {		/* hugetlb_zero_setup takes care of mlock user accounting */		file = hugetlb_zero_setup(size);		shp->mlock_user = current->user;	} else {		int acctflag = VM_ACCOUNT;		/*		 * Do not allow no accounting for OVERCOMMIT_NEVER, even	 	 * if it's asked for.		 */		if  ((shmflg & SHM_NORESERVE) &&				sysctl_overcommit_memory != OVERCOMMIT_NEVER)			acctflag = 0;		sprintf (name, "SYSV%08x", key);		file = shmem_file_setup(name, size, acctflag);	}	error = PTR_ERR(file);	if (IS_ERR(file))		goto no_file; 	error = -ENOSPC;	id = shm_addid(shp);	if(id == -1) 		goto no_id; 	shp->shm_cprid = current->tgid;	shp->shm_lprid = 0;	shp->shm_atim = shp->shm_dtim = 0;	shp->shm_ctim = get_seconds();	shp->shm_segsz = size;	shp->shm_nattch = 0;	shp->id = shm_buildid(id,shp->shm_perm.seq);	shp->shm_file = file;	file->f_dentry->d_inode->i_ino = shp->id; 	/* Hugetlb ops would have already been assigned. */	if (!(shmflg & SHM_HUGETLB))		file->f_op = &shm_file_operations; 	shm_tot += numpages;	shm_unlock(shp);	return shp->id; no_id:	fput(file);no_file:	security_shm_free(shp);	ipc_rcu_putref(shp);	return error;} asmlinkage long sys_shmget (key_t key, size_t size, int shmflg){	struct shmid_kernel *shp;	int err, id = 0; 	mutex_lock(&shm_ids.mutex);	if (key == IPC_PRIVATE) {		err = newseg(key, shmflg, size);	} else if ((id = ipc_findkey(&shm_ids, key)) == -1) {		if (!(shmflg & IPC_CREAT))			err = -ENOENT;		else			err = newseg(key, shmflg, size);	} else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) {		err = -EEXIST;	} else {		shp = shm_lock(id);		BUG_ON(shp==NULL);		if (shp->shm_segsz < size)			err = -EINVAL;		else if (ipcperms(&shp->shm_perm, shmflg))			err = -EACCES;		else {			int shmid = shm_buildid(id, shp->shm_perm.seq);			err = security_shm_associate(shp, shmflg);			if (!err)				err = shmid;		}		shm_unlock(shp);	}	mutex_unlock(&shm_ids.mutex); 	return err;} static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ds *in, int version){	switch(version) {	case IPC_64:		return copy_to_user(buf, in, sizeof(*in));	case IPC_OLD:	    {		struct shmid_ds out; 		ipc64_perm_to_ipc_perm(&in->shm_perm, &out.shm_perm);		out.shm_segsz	= in->shm_segsz;		out.shm_atime	= in->shm_atime;		out.shm_dtime	= in->shm_dtime;		out.shm_ctime	= in->shm_ctime;		out.shm_cpid	= in->shm_cpid;		out.shm_lpid	= in->shm_lpid;		out.shm_nattch	= in->shm_nattch; 		return copy_to_user(buf, &out, sizeof(out));	    }	default:		return -EINVAL;	}} struct shm_setbuf {	uid_t	uid;	gid_t	gid;	mode_t	mode;};	 static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void __user *buf, int version){	switch(version) {	case IPC_64:	    {		struct shmid64_ds tbuf; 		if (copy_from_user(&tbuf, buf, sizeof(tbuf)))			return -EFAULT; 		out->uid	= tbuf.shm_perm.uid;		out->gid	= tbuf.shm_perm.gid;		out->mode	= tbuf.shm_perm.mode; 		return 0;	    }	case IPC_OLD:	    {		struct shmid_ds tbuf_old; 		if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))			return -EFAULT; 		out->uid	= tbuf_old.shm_perm.uid;		out->gid	= tbuf_old.shm_perm.gid;		out->mode	= tbuf_old.shm_perm.mode; 		return 0;	    }	default:		return -EINVAL;	}} static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminfo64 *in, int version){	switch(version) {	case IPC_64:		return copy_to_user(buf, in, sizeof(*in));	case IPC_OLD:	    {		struct shminfo out; 		if(in->shmmax > INT_MAX)			out.shmmax = INT_MAX;		else			out.shmmax = (int)in->shmmax; 		out.shmmin	= in->shmmin;		out.shmmni	= in->shmmni;		out.shmseg	= in->shmseg;		out.shmall	= in->shmall;  		return copy_to_user(buf, &out, sizeof(out));	    }	default:		return -EINVAL;	}} static void shm_get_stat(unsigned long *rss, unsigned long *swp) {	int i; 	*rss = 0;	*swp = 0; 	for (i = 0; i <= shm_ids.max_id; i++) {		struct shmid_kernel *shp;		struct inode *inode; 		shp = shm_get(i);		if(!shp)			continue; 		inode = shp->shm_file->f_dentry->d_inode; 		if (is_file_hugepages(shp->shm_file)) {			struct address_space *mapping = inode->i_mapping;			*rss += (HPAGE_SIZE/PAGE_SIZE)*mapping->nrpages;		} else {			struct shmem_inode_info *info = SHMEM_I(inode);			spin_lock(&info->lock);			*rss += inode->i_mapping->nrpages;			*swp += info->swapped;			spin_unlock(&info->lock);		}	}} asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf){	struct shm_setbuf setbuf;	struct shmid_kernel *shp;	int err, version; 	if (cmd < 0 || shmid < 0) {		err = -EINVAL;		goto out;	} 	version = ipc_parse_version(&cmd); 	switch (cmd) { /* replace with proc interface ? */	case IPC_INFO:	{		struct shminfo64 shminfo; 		err = security_shm_shmctl(NULL, cmd);		if (err)			return err; 		memset(&shminfo,0,sizeof(shminfo));		shminfo.shmmni = shminfo.shmseg = shm_ctlmni;		shminfo.shmmax = shm_ctlmax;		shminfo.shmall = shm_ctlall; 		shminfo.shmmin = SHMMIN;		if(copy_shminfo_to_user (buf, &shminfo, version))			return -EFAULT;		/* reading a integer is always atomic */		err= shm_ids.max_id;		if(err<0)			err = 0;		goto out;	}	case SHM_INFO:	{		struct shm_info shm_info; 		err = security_shm_shmctl(NULL, cmd);		if (err)			return err; 		memset(&shm_info,0,sizeof(shm_info));		mutex_lock(&shm_ids.mutex);		shm_info.used_ids = shm_ids.in_use;		shm_get_stat (&shm_info.shm_rss, &shm_info.shm_swp);		shm_info.shm_tot = shm_tot;		shm_info.swap_attempts = 0;		shm_info.swap_successes = 0;		err = shm_ids.max_id;		mutex_unlock(&shm_ids.mutex);		if(copy_to_user (buf, &shm_info, sizeof(shm_info))) {			err = -EFAULT;			goto out;		} 		err = err < 0 ? 0 : err;		goto out;	}	case SHM_STAT:	case IPC_STAT:	{		struct shmid64_ds tbuf;		int result;		memset(&tbuf, 0, sizeof(tbuf));		shp = shm_lock(shmid);		if(shp==NULL) {			err = -EINVAL;			goto out;		} else if(cmd==SHM_STAT) {

Lines 1–500 of 937.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t