#endif
};
+static void free_nsproxy_work(struct work_struct *work);
+
static inline struct nsproxy *create_nsproxy(void)
{
struct nsproxy *nsproxy;
nsproxy = kmem_cache_alloc(nsproxy_cachep, GFP_KERNEL);
- if (nsproxy)
+ if (nsproxy) {
atomic_set(&nsproxy->count, 1);
+ INIT_WORK(&nsproxy->free_nsproxy_work, free_nsproxy_work);
+ }
return nsproxy;
}
void free_nsproxy(struct nsproxy *ns)
{
+ /*
+ * 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();
+
if (ns->mnt_ns)
put_mnt_ns(ns->mnt_ns);
if (ns->uts_ns)
kmem_cache_free(nsproxy_cachep, ns);
}
+static void free_nsproxy_work(struct work_struct *work)
+{
+ struct nsproxy *ns = container_of(work, struct nsproxy,
+ free_nsproxy_work);
+
+ free_nsproxy(ns);
+}
+
/*
* Called from unshare. Unshare all the namespaces part of nsproxy.
* On success, returns the new 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);
- }
+ if (ns && atomic_dec_and_test(&ns->count))
+ schedule_work(&ns->free_nsproxy_work);
}
void exit_task_namespaces(struct task_struct *p)