USB: xhci: Set Mult field in endpoint context correctly.
[linux-3.10.git] / ipc / shm.c
index 8d1b2c4..52ed77e 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -54,13 +54,12 @@ struct shm_file_data {
 #define shm_file_data(file) (*((struct shm_file_data **)&(file)->private_data))
 
 static const struct file_operations shm_file_operations;
-static struct vm_operations_struct shm_vm_ops;
+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)
-#define shm_buildid(id, seq)   ipc_buildid(id, seq)
 
 static int newseg(struct ipc_namespace *, struct ipc_params *);
 static void shm_open(struct vm_area_struct *vma);
@@ -76,7 +75,7 @@ void shm_init_ns(struct ipc_namespace *ns)
        ns->shm_ctlall = SHMALL;
        ns->shm_ctlmni = SHMMNI;
        ns->shm_tot = 0;
-       ipc_init_ids(&ns->ids[IPC_SHM_IDS]);
+       ipc_init_ids(&shm_ids(ns));
 }
 
 /*
@@ -101,6 +100,7 @@ static void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
 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);
 }
 #endif
 
@@ -113,35 +113,8 @@ void __init shm_init (void)
 }
 
 /*
- * shm_lock_(check_)down routines are called in the paths where the rw_mutex
- * is held to protect access to the idr tree.
- */
-static inline struct shmid_kernel *shm_lock_down(struct ipc_namespace *ns,
-                                               int id)
-{
-       struct kern_ipc_perm *ipcp = ipc_lock_down(&shm_ids(ns), id);
-
-       if (IS_ERR(ipcp))
-               return (struct shmid_kernel *)ipcp;
-
-       return container_of(ipcp, struct shmid_kernel, shm_perm);
-}
-
-static inline struct shmid_kernel *shm_lock_check_down(
-                                               struct ipc_namespace *ns,
-                                               int id)
-{
-       struct kern_ipc_perm *ipcp = ipc_lock_check_down(&shm_ids(ns), id);
-
-       if (IS_ERR(ipcp))
-               return (struct shmid_kernel *)ipcp;
-
-       return container_of(ipcp, struct shmid_kernel, shm_perm);
-}
-
-/*
  * shm_lock_(check_) routines are called in the paths where the rw_mutex
- * is not held.
+ * is not necessarily held.
  */
 static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id)
 {
@@ -169,12 +142,6 @@ static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s)
        ipc_rmid(&shm_ids(ns), &s->shm_perm);
 }
 
-static inline int shm_addid(struct ipc_namespace *ns, struct shmid_kernel *shp)
-{
-       return ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
-}
-
-
 
 /* This is called by fork, once for every shm attach. */
 static void shm_open(struct vm_area_struct *vma)
@@ -207,7 +174,7 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
        shm_unlock(shp);
        if (!is_file_hugepages(shp->shm_file))
                shmem_lock(shp->shm_file, 0, shp->mlock_user);
-       else
+       else if (shp->mlock_user)
                user_shm_unlock(shp->shm_file->f_path.dentry->d_inode->i_size,
                                                shp->mlock_user);
        fput (shp->shm_file);
@@ -230,7 +197,7 @@ static void shm_close(struct vm_area_struct *vma)
 
        down_write(&shm_ids(ns).rw_mutex);
        /* remove from the list of attaches of the shm segment */
-       shp = shm_lock_down(ns, sfd->id);
+       shp = shm_lock(ns, sfd->id);
        BUG_ON(IS_ERR(shp));
        shp->shm_lprid = task_tgid_vnr(current);
        shp->shm_dtim = get_seconds();
@@ -271,10 +238,9 @@ static struct mempolicy *shm_get_policy(struct vm_area_struct *vma,
 
        if (sfd->vm_ops->get_policy)
                pol = sfd->vm_ops->get_policy(vma, addr);
-       else if (vma->vm_policy) {
+       else if (vma->vm_policy)
                pol = vma->vm_policy;
-               mpol_get(pol);  /* get_vma_policy() expects this */
-       }
+
        return pol;
 }
 #endif
@@ -307,16 +273,13 @@ static int shm_release(struct inode *ino, struct file *file)
        return 0;
 }
 
-static int shm_fsync(struct file *file, struct dentry *dentry, int datasync)
+static int shm_fsync(struct file *file, int datasync)
 {
-       int (*fsync) (struct file *, struct dentry *, int datasync);
        struct shm_file_data *sfd = shm_file_data(file);
-       int ret = -EINVAL;
 
-       fsync = sfd->file->f_op->fsync;
-       if (fsync)
-               ret = fsync(sfd->file, sfd->file->f_path.dentry, datasync);
-       return ret;
+       if (!sfd->file->f_op->fsync)
+               return -EINVAL;
+       return sfd->file->f_op->fsync(sfd->file, datasync);
 }
 
 static unsigned long shm_get_unmapped_area(struct file *file,
@@ -324,29 +287,32 @@ static unsigned long shm_get_unmapped_area(struct file *file,
        unsigned long flags)
 {
        struct shm_file_data *sfd = shm_file_data(file);
-       return get_unmapped_area(sfd->file, addr, len, pgoff, flags);
-}
-
-int is_file_shm_hugepages(struct file *file)
-{
-       int ret = 0;
-
-       if (file->f_op == &shm_file_operations) {
-               struct shm_file_data *sfd;
-               sfd = shm_file_data(file);
-               ret = is_file_hugepages(sfd->file);
-       }
-       return ret;
+       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
 };
 
-static struct vm_operations_struct shm_vm_ops = {
+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,
+};
+
+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,
@@ -375,6 +341,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
        struct file * file;
        char name[13];
        int id;
+       int acctflag = 0;
 
        if (size < SHMMIN || size > ns->shm_ctlmax)
                return -EINVAL;
@@ -399,25 +366,26 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 
        sprintf (name, "SYSV%08x", key);
        if (shmflg & SHM_HUGETLB) {
-               /* hugetlb_file_setup takes care of mlock user accounting */
-               file = hugetlb_file_setup(name, size);
-               shp->mlock_user = current->user;
+               /* hugetlb_file_setup applies strict accounting */
+               if (shmflg & SHM_NORESERVE)
+                       acctflag = VM_NORESERVE;
+               file = hugetlb_file_setup(name, size, acctflag,
+                                       &shp->mlock_user, HUGETLB_SHMFS_INODE);
        } 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;
+                       acctflag = VM_NORESERVE;
                file = shmem_file_setup(name, size, acctflag);
        }
        error = PTR_ERR(file);
        if (IS_ERR(file))
                goto no_file;
 
-       id = shm_addid(ns, shp);
+       id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
        if (id < 0) {
                error = id;
                goto no_id;
@@ -429,7 +397,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
        shp->shm_ctim = get_seconds();
        shp->shm_segsz = size;
        shp->shm_nattch = 0;
-       shp->shm_perm.id = shm_buildid(id, shp->shm_perm.seq);
        shp->shm_file = file;
        /*
         * shmid gets reported as "inode#" in /proc/pid/maps.
@@ -443,6 +410,8 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
        return error;
 
 no_id:
+       if (is_file_hugepages(file) && shp->mlock_user)
+               user_shm_unlock(size, shp->mlock_user);
        fput(file);
 no_file:
        security_shm_free(shp);
@@ -476,7 +445,7 @@ static inline int shm_more_checks(struct kern_ipc_perm *ipcp,
        return 0;
 }
 
-asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
+SYSCALL_DEFINE3(shmget, key_t, key, size_t, size, int, shmflg)
 {
        struct ipc_namespace *ns;
        struct ipc_ops shm_ops;
@@ -520,28 +489,14 @@ static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_
        }
 }
 
-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)
+static inline unsigned long
+copy_shmid_from_user(struct shmid64_ds *out, void __user *buf, int version)
 {
        switch(version) {
        case IPC_64:
-           {
-               struct shmid64_ds tbuf;
-
-               if (copy_from_user(&tbuf, buf, sizeof(tbuf)))
+               if (copy_from_user(out, buf, sizeof(*out)))
                        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;
@@ -549,9 +504,9 @@ static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void __
                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;
+               out->shm_perm.uid       = tbuf_old.shm_perm.uid;
+               out->shm_perm.gid       = tbuf_old.shm_perm.gid;
+               out->shm_perm.mode      = tbuf_old.shm_perm.mode;
 
                return 0;
            }
@@ -601,33 +556,84 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
        in_use = shm_ids(ns).in_use;
 
        for (total = 0, next_id = 0; total < in_use; next_id++) {
+               struct kern_ipc_perm *ipc;
                struct shmid_kernel *shp;
                struct inode *inode;
 
-               shp = idr_find(&shm_ids(ns).ipcs_idr, next_id);
-               if (shp == NULL)
+               ipc = idr_find(&shm_ids(ns).ipcs_idr, next_id);
+               if (ipc == NULL)
                        continue;
+               shp = container_of(ipc, struct shmid_kernel, shm_perm);
 
                inode = shp->shm_file->f_path.dentry->d_inode;
 
                if (is_file_hugepages(shp->shm_file)) {
                        struct address_space *mapping = inode->i_mapping;
-                       *rss += (HPAGE_SIZE/PAGE_SIZE)*mapping->nrpages;
+                       struct hstate *h = hstate_file(shp->shm_file);
+                       *rss += pages_per_huge_page(h) * mapping->nrpages;
                } else {
+#ifdef CONFIG_SHMEM
                        struct shmem_inode_info *info = SHMEM_I(inode);
                        spin_lock(&info->lock);
                        *rss += inode->i_mapping->nrpages;
                        *swp += info->swapped;
                        spin_unlock(&info->lock);
+#else
+                       *rss += inode->i_mapping->nrpages;
+#endif
                }
 
                total++;
        }
 }
 
-asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
+/*
+ * This function handles some shmctl commands which require the rw_mutex
+ * to be held in write mode.
+ * NOTE: no locks must be held, the rw_mutex is taken inside this function.
+ */
+static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
+                      struct shmid_ds __user *buf, int version)
+{
+       struct kern_ipc_perm *ipcp;
+       struct shmid64_ds shmid64;
+       struct shmid_kernel *shp;
+       int err;
+
+       if (cmd == IPC_SET) {
+               if (copy_shmid_from_user(&shmid64, buf, version))
+                       return -EFAULT;
+       }
+
+       ipcp = ipcctl_pre_down(&shm_ids(ns), shmid, cmd, &shmid64.shm_perm, 0);
+       if (IS_ERR(ipcp))
+               return PTR_ERR(ipcp);
+
+       shp = container_of(ipcp, struct shmid_kernel, shm_perm);
+
+       err = security_shm_shmctl(shp, cmd);
+       if (err)
+               goto out_unlock;
+       switch (cmd) {
+       case IPC_RMID:
+               do_shm_rmid(ns, ipcp);
+               goto out_up;
+       case IPC_SET:
+               ipc_update_perm(&shmid64.shm_perm, ipcp);
+               shp->shm_ctim = get_seconds();
+               break;
+       default:
+               err = -EINVAL;
+       }
+out_unlock:
+       shm_unlock(shp);
+out_up:
+       up_write(&shm_ids(ns).rw_mutex);
+       return err;
+}
+
+SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
 {
-       struct shm_setbuf setbuf;
        struct shmid_kernel *shp;
        int err, version;
        struct ipc_namespace *ns;
@@ -649,7 +655,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
                if (err)
                        return err;
 
-               memset(&shminfo,0,sizeof(shminfo));
+               memset(&shminfo, 0, sizeof(shminfo));
                shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni;
                shminfo.shmmax = ns->shm_ctlmax;
                shminfo.shmall = ns->shm_ctlall;
@@ -674,7 +680,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
                if (err)
                        return err;
 
-               memset(&shm_info,0,sizeof(shm_info));
+               memset(&shm_info, 0, sizeof(shm_info));
                down_read(&shm_ids(ns).rw_mutex);
                shm_info.used_ids = shm_ids(ns).in_use;
                shm_get_stat (ns, &shm_info.shm_rss, &shm_info.shm_swp);
@@ -683,7 +689,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
                shm_info.swap_successes = 0;
                err = ipc_get_maxid(&shm_ids(ns));
                up_read(&shm_ids(ns).rw_mutex);
-               if(copy_to_user (buf, &shm_info, sizeof(shm_info))) {
+               if (copy_to_user(buf, &shm_info, sizeof(shm_info))) {
                        err = -EFAULT;
                        goto out;
                }
@@ -697,11 +703,6 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
                struct shmid64_ds tbuf;
                int result;
 
-               if (!buf) {
-                       err = -EFAULT;
-                       goto out;
-               }
-
                if (cmd == SHM_STAT) {
                        shp = shm_lock(ns, shmid);
                        if (IS_ERR(shp)) {
@@ -717,7 +718,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
                        }
                        result = 0;
                }
-               err=-EACCES;
+               err = -EACCES;
                if (ipcperms (&shp->shm_perm, S_IRUGO))
                        goto out_unlock;
                err = security_shm_shmctl(shp, cmd);
@@ -742,23 +743,25 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
        case SHM_LOCK:
        case SHM_UNLOCK:
        {
+               struct file *uninitialized_var(shm_file);
+
+               lru_add_drain_all();  /* drain pagevecs to lru lists */
+
                shp = shm_lock_check(ns, shmid);
                if (IS_ERR(shp)) {
                        err = PTR_ERR(shp);
                        goto out;
                }
 
-               err = audit_ipc_obj(&(shp->shm_perm));
-               if (err)
-                       goto out_unlock;
+               audit_ipc_obj(&(shp->shm_perm));
 
                if (!capable(CAP_IPC_LOCK)) {
+                       uid_t euid = current_euid();
                        err = -EPERM;
-                       if (current->euid != shp->shm_perm.uid &&
-                           current->euid != shp->shm_perm.cuid)
+                       if (euid != shp->shm_perm.uid &&
+                           euid != shp->shm_perm.cuid)
                                goto out_unlock;
-                       if (cmd == SHM_LOCK &&
-                           !current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur)
+                       if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK))
                                goto out_unlock;
                }
 
@@ -767,7 +770,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
                        goto out_unlock;
                
                if(cmd==SHM_LOCK) {
-                       struct user_struct * user = current->user;
+                       struct user_struct *user = current_user();
                        if (!is_file_hugepages(shp->shm_file)) {
                                err = shmem_lock(shp->shm_file, 1, user);
                                if (!err && !(shp->shm_perm.mode & SHM_LOCKED)){
@@ -784,97 +787,13 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
                goto out;
        }
        case IPC_RMID:
-       {
-               /*
-                *      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.
-                */
-               down_write(&shm_ids(ns).rw_mutex);
-               shp = shm_lock_check_down(ns, shmid);
-               if (IS_ERR(shp)) {
-                       err = PTR_ERR(shp);
-                       goto out_up;
-               }
-
-               err = audit_ipc_obj(&(shp->shm_perm));
-               if (err)
-                       goto out_unlock_up;
-
-               if (current->euid != shp->shm_perm.uid &&
-                   current->euid != shp->shm_perm.cuid && 
-                   !capable(CAP_SYS_ADMIN)) {
-                       err=-EPERM;
-                       goto out_unlock_up;
-               }
-
-               err = security_shm_shmctl(shp, cmd);
-               if (err)
-                       goto out_unlock_up;
-
-               do_shm_rmid(ns, &shp->shm_perm);
-               up_write(&shm_ids(ns).rw_mutex);
-               goto out;
-       }
-
        case IPC_SET:
-       {
-               if (!buf) {
-                       err = -EFAULT;
-                       goto out;
-               }
-
-               if (copy_shmid_from_user (&setbuf, buf, version)) {
-                       err = -EFAULT;
-                       goto out;
-               }
-               down_write(&shm_ids(ns).rw_mutex);
-               shp = shm_lock_check_down(ns, shmid);
-               if (IS_ERR(shp)) {
-                       err = PTR_ERR(shp);
-                       goto out_up;
-               }
-               err = audit_ipc_obj(&(shp->shm_perm));
-               if (err)
-                       goto out_unlock_up;
-               err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
-               if (err)
-                       goto out_unlock_up;
-               err=-EPERM;
-               if (current->euid != shp->shm_perm.uid &&
-                   current->euid != shp->shm_perm.cuid && 
-                   !capable(CAP_SYS_ADMIN)) {
-                       goto out_unlock_up;
-               }
-
-               err = security_shm_shmctl(shp, cmd);
-               if (err)
-                       goto out_unlock_up;
-               
-               shp->shm_perm.uid = setbuf.uid;
-               shp->shm_perm.gid = setbuf.gid;
-               shp->shm_perm.mode = (shp->shm_perm.mode & ~S_IRWXUGO)
-                       | (setbuf.mode & S_IRWXUGO);
-               shp->shm_ctim = get_seconds();
-               break;
-       }
-
+               err = shmctl_down(ns, shmid, cmd, buf, version);
+               return err;
        default:
-               err = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
 
-       err = 0;
-out_unlock_up:
-       shm_unlock(shp);
-out_up:
-       up_write(&shm_ids(ns).rw_mutex);
-       goto out;
 out_unlock:
        shm_unlock(shp);
 out:
@@ -902,7 +821,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
        struct ipc_namespace *ns;
        struct shm_file_data *sfd;
        struct path path;
-       mode_t f_mode;
+       fmode_t f_mode;
 
        err = -EINVAL;
        if (shmid < 0)
@@ -958,8 +877,8 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
        if (err)
                goto out_unlock;
 
-       path.dentry = dget(shp->shm_file->f_path.dentry);
-       path.mnt    = shp->shm_file->f_path.mnt;
+       path = shp->shm_file->f_path;
+       path_get(&path);
        shp->shm_nattch++;
        size = i_size_read(path.dentry->d_inode);
        shm_unlock(shp);
@@ -969,9 +888,10 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
        if (!sfd)
                goto out_put_dentry;
 
-       err = -ENOMEM;
-
-       file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
+       file = alloc_file(&path, f_mode,
+                         is_file_hugepages(shp->shm_file) ?
+                               &shm_file_operations_huge :
+                               &shm_file_operations);
        if (!file)
                goto out_free;
 
@@ -1008,7 +928,7 @@ invalid:
 
 out_nattch:
        down_write(&shm_ids(ns).rw_mutex);
-       shp = shm_lock_down(ns, shmid);
+       shp = shm_lock(ns, shmid);
        BUG_ON(IS_ERR(shp));
        shp->shm_nattch--;
        if(shp->shm_nattch == 0 &&
@@ -1028,11 +948,11 @@ out_unlock:
 out_free:
        kfree(sfd);
 out_put_dentry:
-       dput(path.dentry);
+       path_put(&path);
        goto out_nattch;
 }
 
-asmlinkage long sys_shmat(int shmid, char __user *shmaddr, int shmflg)
+SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg)
 {
        unsigned long ret;
        long err;
@@ -1048,13 +968,16 @@ asmlinkage long sys_shmat(int shmid, char __user *shmaddr, int shmflg)
  * detach and kill segment if marked destroyed.
  * The work is done in shm_close.
  */
-asmlinkage long sys_shmdt(char __user *shmaddr)
+SYSCALL_DEFINE1(shmdt, char __user *, shmaddr)
 {
        struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma, *next;
+       struct vm_area_struct *vma;
        unsigned long addr = (unsigned long)shmaddr;
-       loff_t size = 0;
        int retval = -EINVAL;
+#ifdef CONFIG_MMU
+       loff_t size = 0;
+       struct vm_area_struct *next;
+#endif
 
        if (addr & ~PAGE_MASK)
                return retval;
@@ -1083,6 +1006,7 @@ asmlinkage long sys_shmdt(char __user *shmaddr)
         */
        vma = find_vma(mm, addr);
 
+#ifdef CONFIG_MMU
        while (vma) {
                next = vma->vm_next;
 
@@ -1127,6 +1051,17 @@ asmlinkage long sys_shmdt(char __user *shmaddr)
                vma = next;
        }
 
+#else /* CONFIG_MMU */
+       /* under NOMMU conditions, the exact address to be destroyed must be
+        * given */
+       retval = -EINVAL;
+       if (vma->vm_start == addr && vma->vm_ops == &shm_vm_ops) {
+               do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start);
+               retval = 0;
+       }
+
+#endif
+
        up_write(&mm->mmap_sem);
        return retval;
 }
@@ -1135,16 +1070,16 @@ asmlinkage long sys_shmdt(char __user *shmaddr)
 static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
 {
        struct shmid_kernel *shp = it;
-       char *format;
 
-#define SMALL_STRING "%10d %10d  %4o %10u %5u %5u  %5d %5u %5u %5u %5u %10lu %10lu %10lu\n"
-#define BIG_STRING   "%10d %10d  %4o %21u %5u %5u  %5d %5u %5u %5u %5u %10lu %10lu %10lu\n"
+#if BITS_PER_LONG <= 32
+#define SIZE_SPEC "%10lu"
+#else
+#define SIZE_SPEC "%21lu"
+#endif
 
-       if (sizeof(size_t) <= sizeof(int))
-               format = SMALL_STRING;
-       else
-               format = BIG_STRING;
-       return seq_printf(s, format,
+       return seq_printf(s,
+                         "%10d %10d  %4o " SIZE_SPEC " %5u %5u  "
+                         "%5lu %5u %5u %5u %5u %10lu %10lu %10lu\n",
                          shp->shm_perm.key,
                          shp->shm_perm.id,
                          shp->shm_perm.mode,