diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-03-23 02:25:38 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-03-23 02:25:38 +0000 |
commit | 16b5d462f73eb29d1f67fa01cc1ea66afdc72569 (patch) | |
tree | 5407bd573f4840e473ea27cbe61e5c7a07131fcd /ipc | |
parent | ce8a076e11e7e5ee36007f9a3eee5bb3744cb8f6 (diff) |
Merge with Linux 2.3.99-pre2.
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/shm.c | 203 |
1 files changed, 139 insertions, 64 deletions
@@ -18,13 +18,19 @@ * 1) It only can handle one directory. * 2) Because the directory is represented by the SYSV shm array it * can only be mounted one time. - * 3) This again leads to SYSV shm not working properly in a chrooted - * environment - * 4) Read and write are not implemented (should they?) - * 5) No special nodes are supported + * 3) Read and write are not implemented (should they?) + * 4) No special nodes are supported + * + * There are the following mount options: + * - nr_blocks (^= shmall) is the number of blocks of size PAGE_SIZE + * we are allowed to allocate + * - nr_inodes (^= shmmni) is the number of files we are allowed to + * allocate + * - mode is the mode for the root directory (default S_IRWXUGO | S_ISVTX) */ #include <linux/config.h> +#include <linux/module.h> #include <linux/malloc.h> #include <linux/shm.h> #include <linux/swap.h> @@ -48,7 +54,7 @@ static void shm_put_super (struct super_block *); static int shm_remount_fs (struct super_block *, int *, char *); static void shm_read_inode (struct inode *); static void shm_write_inode(struct inode *); -static int shm_statfs (struct super_block *, struct statfs *); +static int shm_statfs (struct super_block *, struct statfs *); static int shm_create (struct inode *,struct dentry *,int); static struct dentry *shm_lookup (struct inode *,struct dentry *); static int shm_unlink (struct inode *,struct dentry *); @@ -57,8 +63,6 @@ static void shm_delete (struct inode *); static int shm_mmap (struct file *, struct vm_area_struct *); static int shm_readdir (struct file *, void *, filldir_t); -char shm_path[256] = "/var/shm"; - #define SHM_NAME_LEN NAME_MAX #define SHM_FMT ".IPC_%08x" #define SHM_FMT_LEN 13 @@ -78,6 +82,7 @@ struct shmid_kernel /* private to the kernel */ time_t ctime; pid_t cpid; pid_t lpid; + int unlinked; int nlen; char nm[0]; } shmem; @@ -95,6 +100,7 @@ struct shmid_kernel /* private to the kernel */ #define shm_lprid permap.shmem.lpid #define shm_namelen permap.shmem.nlen #define shm_name permap.shmem.nm +#define shm_unlinked permap.shmem.unlinked #define zsem permap.zero.sema #define zero_list permap.zero.list @@ -105,7 +111,6 @@ static struct ipc_ids shm_ids; #define shm_lockall() ipc_lockall(&shm_ids) #define shm_unlockall() ipc_unlockall(&shm_ids) #define shm_get(id) ((struct shmid_kernel*)ipc_get(&shm_ids,id)) -#define shm_rmid(id) ((struct shmid_kernel*)ipc_rmid(&shm_ids,id)) #define shm_checkid(s, id) \ ipc_checkid(&shm_ids,&s->shm_perm,id) #define shm_buildid(id, seq) \ @@ -115,6 +120,7 @@ static int newseg (key_t key, const char *name, int namelen, int shmflg, size_t static void killseg_core(struct shmid_kernel *shp, int doacc); static void shm_open (struct vm_area_struct *shmd); static void shm_close (struct vm_area_struct *shmd); +static int shm_remove_name(int id); static struct page * shm_nopage(struct vm_area_struct *, unsigned long, int); static int shm_swapout(struct page *, struct file *); #ifdef CONFIG_PROC_FS @@ -266,7 +272,7 @@ static struct super_block *shm_read_super(struct super_block *s,void *data, struct inode * root_inode; if (shm_sb) { - printk ("shm fs already mounted\n"); + printk(KERN_ERR "shm fs already mounted\n"); return NULL; } @@ -274,7 +280,7 @@ static struct super_block *shm_read_super(struct super_block *s,void *data, shm_ctlmni = SHMMNI; shm_mode = S_IRWXUGO | S_ISVTX; if (shm_parse_options (data)) { - printk ("shm fs invalid option\n"); + printk(KERN_ERR "shm fs invalid option\n"); goto out_unlock; } @@ -297,7 +303,7 @@ static struct super_block *shm_read_super(struct super_block *s,void *data, return s; out_no_root: - printk("proc_read_super: get root inode failed\n"); + printk(KERN_ERR "proc_read_super: get root inode failed\n"); iput(root_inode); out_unlock: return NULL; @@ -310,6 +316,16 @@ static int shm_remount_fs (struct super_block *sb, int *flags, char *data) 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+1); +} + static void shm_put_super(struct super_block *sb) { struct super_block **p = &shm_sb; @@ -329,7 +345,7 @@ static void shm_put_super(struct super_block *sb) if (!(shp = shm_lock (i))) continue; if (shp->shm_nattch) - printk ("shm_nattch = %ld\n", shp->shm_nattch); + printk(KERN_DEBUG "shm_nattch = %ld\n", shp->shm_nattch); shp = shm_rmid(i); shm_unlock(i); killseg_core(shp, 1); @@ -439,7 +455,7 @@ static int shm_readdir (struct file *filp, void *dirent, filldir_t filldir) continue; if (!(shp = shm_get (nr-2))) continue; - if (shp->shm_perm.mode & SHM_DEST) + if (shp->shm_unlinked) continue; if (filldir(dirent, shp->shm_name, shp->shm_namelen, nr, nr) < 0 ) break;; @@ -468,7 +484,7 @@ static struct dentry *shm_lookup (struct inode *dir, struct dentry *dent) continue; if (!(shp = shm_lock(i))) continue; - if (!(shp->shm_perm.mode & SHM_DEST) && + if (!(shp->shm_unlinked) && dent->d_name.len == shp->shm_namelen && strncmp(dent->d_name.name, shp->shm_name, shp->shm_namelen) == 0) goto found; @@ -506,12 +522,21 @@ static int shm_unlink (struct inode *dir, struct dentry *dent) down (&shm_ids.sem); if (!(shp = shm_lock (inode->i_ino))) BUG(); + shp->shm_unlinked = 1; shp->shm_perm.mode |= SHM_DEST; shp->shm_perm.key = IPC_PRIVATE; /* Do not find it any more */ shm_unlock (inode->i_ino); up (&shm_ids.sem); inode->i_nlink -= 1; - d_delete (dent); + /* + * If it's a reserved name we have to drop the dentry instead + * of creating a negative dentry + */ + if (dent->d_name.len == SHM_FMT_LEN && + memcmp (SHM_FMT, dent->d_name.name, SHM_FMT_LEN - 8) == 0) + d_drop (dent); + else + d_delete (dent); return 0; } @@ -637,16 +662,18 @@ set_attr: static inline struct shmid_kernel *newseg_alloc(int numpages, size_t namelen) { struct shmid_kernel *shp; + pte_t **dir; shp = (struct shmid_kernel *) kmalloc (sizeof (*shp) + namelen, GFP_KERNEL); if (!shp) - return 0; + return ERR_PTR(-ENOMEM); - shp->shm_dir = shm_alloc (numpages); - if (!shp->shm_dir) { + dir = shm_alloc (numpages); + if (IS_ERR(dir)) { kfree(shp); - return 0; + return ERR_PTR(PTR_ERR(dir)); } + shp->shm_dir = dir; shp->shm_npages = numpages; shp->shm_nattch = 0; shp->shm_namelen = namelen; @@ -665,12 +692,13 @@ static int newseg (key_t key, const char *name, int namelen, if (size > shm_ctlmax) return -EINVAL; + if (shm_tot + numpages >= shm_ctlall) return -ENOSPC; if (!(shp = newseg_alloc(numpages, namelen ? namelen : SHM_FMT_LEN + 1))) return -ENOMEM; - id = ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni+1); + id = shm_addid(shp); if(id == -1) { shm_free(shp->shm_dir,numpages); kfree(shp); @@ -684,6 +712,7 @@ static int newseg (key_t key, const char *name, int namelen, shp->shm_atim = shp->shm_dtim = 0; shp->shm_ctim = CURRENT_TIME; shp->id = shm_buildid(id,shp->shm_perm.seq); + shp->shm_unlinked = 0; if (namelen != 0) { shp->shm_namelen = namelen; memcpy (shp->shm_name, name, namelen); @@ -877,18 +906,6 @@ static inline unsigned long copy_shminfo_to_user(void *buf, struct shminfo64 *in } } -char * shm_getname(int id) -{ - char *result; - - result = __getname (); - if (IS_ERR(result)) - return result; - - sprintf (result, "%s/" SHM_FMT, shm_path, id); - return result; -} - asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf) { struct shm_setbuf setbuf; @@ -1018,18 +1035,50 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf) } case IPC_RMID: { - char *name; - if ((shmid % SEQ_MULTIPLIER)== zero_id) + /* + * We cannot simply remove the file. The SVID states + * that the block remains until the last person + * detaches from it, then is deleted. A shmat() on + * an RMID segment is legal in older Linux and if + * we change it apps break... + * + * Instead we set a destroyed flag, and then blow + * the name away when the usage hits zero. + */ + if ((shmid % SEQ_MULTIPLIER) == zero_id) return -EINVAL; - name = shm_getname(shmid); - if (IS_ERR(name)) - return PTR_ERR(name); lock_kernel(); - err = do_unlink (name); + down(&shm_ids.sem); + shp = shm_lock(shmid); + if (shp == NULL) { + up(&shm_ids.sem); + unlock_kernel(); + return -EINVAL; + } + err = -EIDRM; + if (shm_checkid(shp, shmid) == 0) { + if (shp->shm_nattch == 0) { + int id=shp->id; + shm_unlock(shmid); + up(&shm_ids.sem); + /* The kernel lock prevents new attaches from + * being happening. We can't hold shm_lock here + * else we will deadlock in shm_lookup when we + * try to recursively grab it. + */ + err = shm_remove_name(id); + unlock_kernel(); + return err; + } + /* Do not find me any more */ + shp->shm_perm.mode |= SHM_DEST; + shp->shm_perm.key = IPC_PRIVATE; /* Do not find it any more */ + err = 0; + } + /* Unlock */ + shm_unlock(shmid); + up(&shm_ids.sem); unlock_kernel(); - putname (name); - if (err == -ENOENT) - err = -EINVAL; return err; } @@ -1108,7 +1157,7 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) struct file * file; int err; int flags; - char *name; + char name[SHM_FMT_LEN+1]; if (!shm_sb || (shmid % SEQ_MULTIPLIER) == zero_id) return -EINVAL; @@ -1125,17 +1174,11 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) } else flags = MAP_SHARED; - name = shm_getname(shmid); - if (IS_ERR (name)) - return PTR_ERR (name); - + sprintf (name, SHM_FMT, shmid); lock_kernel(); - file = filp_open (name, O_RDWR, 0); - putname (name); - if (IS_ERR (file)) { - unlock_kernel(); + file = filp_open(name, O_RDWR, 0, dget(shm_sb->s_root)); + if (IS_ERR (file)) goto bad_file; - } *raddr = do_mmap (file, addr, file->f_dentry->d_inode->i_size, (shmflg & SHM_RDONLY ? PROT_READ : PROT_READ | PROT_WRITE), flags, 0); @@ -1148,6 +1191,7 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr) return err; bad_file: + unlock_kernel(); if ((err = PTR_ERR(file)) == -ENOENT) return -EINVAL; return err; @@ -1160,6 +1204,17 @@ static void shm_open (struct vm_area_struct *shmd) } /* + * Remove a name. Must be called with lock_kernel + */ + +static int shm_remove_name(int id) +{ + char name[SHM_FMT_LEN+1]; + sprintf (name, SHM_FMT, id); + return do_unlink (name, dget(shm_sb->s_root)); +} + +/* * 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 @@ -1170,13 +1225,33 @@ static void shm_close (struct vm_area_struct *shmd) int id = shmd->vm_file->f_dentry->d_inode->i_ino; struct shmid_kernel *shp; + lock_kernel(); + /* remove from the list of attaches of the shm segment */ if(!(shp = shm_lock(id))) BUG(); shp->shm_lprid = current->pid; shp->shm_dtim = CURRENT_TIME; shp->shm_nattch--; - shm_unlock(id); + if(shp->shm_nattch == 0 && shp->shm_perm.mode & SHM_DEST) { + int pid=shp->id; + int err; + shm_unlock(id); + + /* The kernel lock prevents new attaches from + * being happening. We can't hold shm_lock here + * else we will deadlock in shm_lookup when we + * try to recursively grab it. + */ + err = shm_remove_name(pid); + if(err && err != -ENOENT) + printk(KERN_ERR "Unlink of SHM id %d failed (%d).\n", pid, err); + + } else { + shm_unlock(id); + } + + unlock_kernel(); } /* @@ -1214,13 +1289,13 @@ static int shm_swapout(struct page * page, struct file *file) /* * page not present ... go through shm_dir */ -static struct page * shm_nopage_core(struct shmid_kernel *shp, unsigned int idx, int *swp, int *rss) +static struct page * shm_nopage_core(struct shmid_kernel *shp, unsigned int idx, int *swp, int *rss, unsigned long address) { pte_t pte; struct page * page; if (idx >= shp->shm_npages) - goto sigbus; + return NOPAGE_SIGBUS; pte = SHM_ENTRY(shp,idx); if (!pte_present(pte)) { @@ -1232,7 +1307,7 @@ static struct page * shm_nopage_core(struct shmid_kernel *shp, unsigned int idx, page = alloc_page(GFP_HIGHUSER); if (!page) goto oom; - clear_highpage(page); + clear_user_highpage(page, address); if ((shp != shm_lock(shp->id)) && (shp->id != zero_id)) BUG(); } else { @@ -1267,9 +1342,8 @@ static struct page * shm_nopage_core(struct shmid_kernel *shp, unsigned int idx, return pte_page(pte); oom: + shm_lock(shp->id); return NOPAGE_OOM; -sigbus: - return NOPAGE_SIGBUS; } static struct page * shm_nopage(struct vm_area_struct * shmd, unsigned long address, int no_share) @@ -1285,7 +1359,7 @@ static struct page * shm_nopage(struct vm_area_struct * shmd, unsigned long addr down(&inode->i_sem); if(!(shp = shm_lock(inode->i_ino))) BUG(); - page = shm_nopage_core(shp, idx, &shm_swp, &shm_rss); + page = shm_nopage_core(shp, idx, &shm_swp, &shm_rss, address); shm_unlock(inode->i_ino); up(&inode->i_sem); return(page); @@ -1482,8 +1556,8 @@ static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int l continue; shp = shm_lock(i); if(shp!=NULL) { -#define SMALL_STRING "%10d %10d %4o %10u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu %.*s\n" -#define BIG_STRING "%10d %10d %4o %21u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu %.*s\n" +#define SMALL_STRING "%10d %10d %4o %10u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu %.*s%s\n" +#define BIG_STRING "%10d %10d %4o %21u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu %.*s%s\n" char *format; if (sizeof(size_t) <= sizeof(int)) @@ -1506,7 +1580,8 @@ static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int l shp->shm_dtim, shp->shm_ctim, shp->shm_namelen, - shp->shm_name); + shp->shm_name, + shp->shm_unlinked ? " (deleted)" : ""); shm_unlock(i); pos += len; @@ -1595,8 +1670,8 @@ int map_zero_setup(struct vm_area_struct *vma) if (!vm_enough_memory((vma->vm_end - vma->vm_start) >> PAGE_SHIFT)) return -ENOMEM; - if (!(shp = newseg_alloc((vma->vm_end - vma->vm_start) / PAGE_SIZE, 0))) - return -ENOMEM; + if (IS_ERR(shp = newseg_alloc((vma->vm_end - vma->vm_start) / PAGE_SIZE, 0))) + return PTR_ERR(shp); if ((filp = file_setup(vma->vm_file, shp)) == 0) { killseg_core(shp, 0); return -ENOMEM; @@ -1657,7 +1732,7 @@ static struct page * shmzero_nopage(struct vm_area_struct * shmd, unsigned long shp = VMA_TO_SHP(shmd); down(&shp->zsem); shm_lock(zero_id); - page = shm_nopage_core(shp, idx, &dummy, &zshm_rss); + page = shm_nopage_core(shp, idx, &dummy, &zshm_rss, address); shm_unlock(zero_id); up(&shp->zsem); return(page); |