static int mounts_open(struct inode *inode, struct file *file)
 {
        struct task_struct *task = get_proc_task(inode);
+       struct nsproxy *nsp;
        struct mnt_namespace *ns = NULL;
        struct proc_mounts *p;
        int ret = -EINVAL;
 
        if (task) {
-               task_lock(task);
-               if (task->nsproxy) {
-                       ns = task->nsproxy->mnt_ns;
+               rcu_read_lock();
+               nsp = task_nsproxy(task);
+               if (nsp) {
+                       ns = nsp->mnt_ns;
                        if (ns)
                                get_mnt_ns(ns);
                }
-               task_unlock(task);
+               rcu_read_unlock();
+
                put_task_struct(task);
        }
 
 
        if (!ret) {
                struct seq_file *m = file->private_data;
+               struct nsproxy *nsp;
                struct mnt_namespace *mnt_ns = NULL;
                struct task_struct *task = get_proc_task(inode);
 
                if (task) {
-                       task_lock(task);
-                       if (task->nsproxy)
-                               mnt_ns = task->nsproxy->mnt_ns;
-                       if (mnt_ns)
-                               get_mnt_ns(mnt_ns);
-                       task_unlock(task);
+                       rcu_read_lock();
+                       nsp = task_nsproxy(task);
+                       if (nsp) {
+                               mnt_ns = nsp->mnt_ns;
+                               if (mnt_ns)
+                                       get_mnt_ns(mnt_ns);
+                       }
+                       rcu_read_unlock();
+
                        put_task_struct(task);
                }
 
 
 };
 extern struct nsproxy init_nsproxy;
 
+/*
+ * the namespaces access rules are:
+ *
+ *  1. only current task is allowed to change tsk->nsproxy pointer or
+ *     any pointer on the nsproxy itself
+ *
+ *  2. when accessing (i.e. reading) current task's namespaces - no
+ *     precautions should be taken - just dereference the pointers
+ *
+ *  3. the access to other task namespaces is performed like this
+ *     rcu_read_lock();
+ *     nsproxy = task_nsproxy(tsk);
+ *     if (nsproxy != NULL) {
+ *             / *
+ *               * work with the namespaces here
+ *               * e.g. get the reference on one of them
+ *               * /
+ *     } / *
+ *         * NULL task_nsproxy() means that this task is
+ *         * almost dead (zombie)
+ *         * /
+ *     rcu_read_unlock();
+ *
+ */
+
+static inline struct nsproxy *task_nsproxy(struct task_struct *tsk)
+{
+       return rcu_dereference(tsk->nsproxy);
+}
+
 int copy_namespaces(unsigned long flags, struct task_struct *tsk);
-void get_task_namespaces(struct task_struct *tsk);
+void exit_task_namespaces(struct task_struct *tsk);
+void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new);
 void free_nsproxy(struct nsproxy *ns);
 int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **,
        struct fs_struct *);
        }
 }
 
-static inline void exit_task_namespaces(struct task_struct *p)
+static inline void get_nsproxy(struct nsproxy *ns)
 {
-       struct nsproxy *ns = p->nsproxy;
-       if (ns) {
-               task_lock(p);
-               p->nsproxy = NULL;
-               task_unlock(p);
-               put_nsproxy(ns);
-       }
+       atomic_inc(&ns->count);
 }
 
 #ifdef CONFIG_CGROUP_NS
 
        current->fs = fs;
        atomic_inc(&fs->count);
 
-       exit_task_namespaces(current);
-       current->nsproxy = init_task.nsproxy;
-       get_task_namespaces(current);
+       if (current->nsproxy != init_task.nsproxy) {
+               get_nsproxy(init_task.nsproxy);
+               switch_task_namespaces(current, init_task.nsproxy);
+       }
 
        exit_files(current);
        current->files = init_task.files;
 
        struct mm_struct *mm, *new_mm = NULL, *active_mm = NULL;
        struct files_struct *fd, *new_fd = NULL;
        struct sem_undo_list *new_ulist = NULL;
-       struct nsproxy *new_nsproxy = NULL, *old_nsproxy = NULL;
+       struct nsproxy *new_nsproxy = NULL;
 
        check_unshare_flags(&unshare_flags);
 
 
        if (new_fs ||  new_mm || new_fd || new_ulist || new_nsproxy) {
 
-               task_lock(current);
-
                if (new_nsproxy) {
-                       old_nsproxy = current->nsproxy;
-                       current->nsproxy = new_nsproxy;
-                       new_nsproxy = old_nsproxy;
+                       switch_task_namespaces(current, new_nsproxy);
+                       new_nsproxy = NULL;
                }
 
+               task_lock(current);
+
                if (new_fs) {
                        fs = current->fs;
                        current->fs = new_fs;
 
 
 struct nsproxy init_nsproxy = INIT_NSPROXY(init_nsproxy);
 
-static inline void get_nsproxy(struct nsproxy *ns)
-{
-       atomic_inc(&ns->count);
-}
-
-void get_task_namespaces(struct task_struct *tsk)
-{
-       struct nsproxy *ns = tsk->nsproxy;
-       if (ns) {
-               get_nsproxy(ns);
-       }
-}
-
 /*
  * creates a copy of "orig" with refcount 1.
  */
        return err;
 }
 
+void switch_task_namespaces(struct task_struct *p, struct nsproxy *new)
+{
+       struct nsproxy *ns;
+
+       might_sleep();
+
+       ns = p->nsproxy;
+
+       rcu_assign_pointer(p->nsproxy, new);
+
+       if (ns && atomic_dec_and_test(&ns->count)) {
+               /*
+                * wait for others to get what they want from this nsproxy.
+                *
+                * cannot release this nsproxy via the call_rcu() since
+                * put_mnt_ns() will want to sleep
+                */
+               synchronize_rcu();
+               free_nsproxy(ns);
+       }
+}
+
+void exit_task_namespaces(struct task_struct *p)
+{
+       switch_task_namespaces(p, NULL);
+}
+
 static int __init nsproxy_cache_init(void)
 {
        nsproxy_cachep = KMEM_CACHE(nsproxy, SLAB_PANIC);
 
        rcu_read_lock();
        tsk = find_task_by_pid(pid);
        if (tsk) {
-               task_lock(tsk);
-               if (tsk->nsproxy)
-                       net = get_net(tsk->nsproxy->net_ns);
-               task_unlock(tsk);
+               struct nsproxy *nsproxy;
+               nsproxy = task_nsproxy(tsk);
+               if (nsproxy)
+                       net = get_net(nsproxy->net_ns);
        }
        rcu_read_unlock();
        return net;