2008-02-08 15:18:22 +03:00
/*
* linux / ipc / namespace . c
* Copyright ( C ) 2006 Pavel Emelyanov < xemul @ openvz . org > OpenVZ , SWsoft Inc .
*/
# include <linux/ipc.h>
# include <linux/msg.h>
# include <linux/ipc_namespace.h>
# include <linux/rcupdate.h>
# include <linux/nsproxy.h>
# include <linux/slab.h>
# include "util.h"
static struct ipc_namespace * clone_ipc_ns ( struct ipc_namespace * old_ns )
{
struct ipc_namespace * ns ;
ns = kmalloc ( sizeof ( struct ipc_namespace ) , GFP_KERNEL ) ;
if ( ns = = NULL )
2008-02-08 15:18:57 +03:00
return ERR_PTR ( - ENOMEM ) ;
2008-02-08 15:18:22 +03:00
2008-04-29 12:00:40 +04:00
atomic_inc ( & nr_ipc_ns ) ;
2008-02-08 15:18:57 +03:00
sem_init_ns ( ns ) ;
msg_init_ns ( ns ) ;
shm_init_ns ( ns ) ;
2008-02-08 15:18:22 +03:00
2008-04-29 12:00:44 +04:00
/*
* msgmni has already been computed for the new ipc ns .
* Thus , do the ipcns creation notification before registering that
* new ipcns in the chain .
*/
ipcns_notify ( IPCNS_CREATED ) ;
2008-04-29 12:00:42 +04:00
register_ipcns_notifier ( ns ) ;
2008-02-08 15:18:22 +03:00
kref_init ( & ns - > kref ) ;
return ns ;
}
struct ipc_namespace * copy_ipcs ( unsigned long flags , struct ipc_namespace * ns )
{
struct ipc_namespace * new_ns ;
BUG_ON ( ! ns ) ;
get_ipc_ns ( ns ) ;
if ( ! ( flags & CLONE_NEWIPC ) )
return ns ;
new_ns = clone_ipc_ns ( ns ) ;
put_ipc_ns ( ns ) ;
return new_ns ;
}
2008-02-08 15:18:57 +03:00
/*
* free_ipcs - free all ipcs of one type
* @ ns : the namespace to remove the ipcs from
* @ ids : the table of ipcs to free
* @ free : the function called to free each individual ipc
*
* Called for each kind of ipc when an ipc_namespace exits .
*/
void free_ipcs ( struct ipc_namespace * ns , struct ipc_ids * ids ,
void ( * free ) ( struct ipc_namespace * , struct kern_ipc_perm * ) )
{
struct kern_ipc_perm * perm ;
int next_id ;
int total , in_use ;
down_write ( & ids - > rw_mutex ) ;
in_use = ids - > in_use ;
for ( total = 0 , next_id = 0 ; total < in_use ; next_id + + ) {
perm = idr_find ( & ids - > ipcs_idr , next_id ) ;
if ( perm = = NULL )
continue ;
ipc_lock_by_ptr ( perm ) ;
free ( ns , perm ) ;
total + + ;
}
up_write ( & ids - > rw_mutex ) ;
}
2008-02-08 15:18:22 +03:00
void free_ipc_ns ( struct kref * kref )
{
struct ipc_namespace * ns ;
ns = container_of ( kref , struct ipc_namespace , kref ) ;
2008-04-29 12:00:42 +04:00
/*
* Unregistering the hotplug notifier at the beginning guarantees
* that the ipc namespace won ' t be freed while we are inside the
* callback routine . Since the blocking_notifier_chain_XXX routines
* hold a rw lock on the notifier list , unregister_ipcns_notifier ( )
* won ' t take the rw lock before blocking_notifier_call_chain ( ) has
* released the rd lock .
*/
unregister_ipcns_notifier ( ns ) ;
2008-02-08 15:18:22 +03:00
sem_exit_ns ( ns ) ;
msg_exit_ns ( ns ) ;
shm_exit_ns ( ns ) ;
kfree ( ns ) ;
2008-04-29 12:00:40 +04:00
atomic_dec ( & nr_ipc_ns ) ;
2008-04-29 12:00:44 +04:00
/*
* Do the ipcns removal notification after decrementing nr_ipc_ns in
* order to have a correct value when recomputing msgmni .
*/
ipcns_notify ( IPCNS_REMOVED ) ;
2008-02-08 15:18:22 +03:00
}