2008-04-29 12:00:10 +04:00
/*
2008-10-19 07:28:07 +04:00
* device_cgroup . c - device cgroup subsystem
2008-04-29 12:00:10 +04:00
*
* Copyright 2007 IBM Corp
*/
# include <linux/device_cgroup.h>
# include <linux/cgroup.h>
# include <linux/ctype.h>
# include <linux/list.h>
# include <linux/uaccess.h>
2008-04-29 12:00:14 +04:00
# include <linux/seq_file.h>
2008-10-19 07:28:07 +04:00
# include <linux/rcupdate.h>
2008-04-29 12:00:10 +04:00
# define ACC_MKNOD 1
# define ACC_READ 2
# define ACC_WRITE 4
# define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)
# define DEV_BLOCK 1
# define DEV_CHAR 2
# define DEV_ALL 4 /* this represents all devices */
/*
* whitelist locking rules :
2008-10-19 07:28:07 +04:00
* hold cgroup_lock ( ) for update / read .
* hold rcu_read_lock ( ) for read .
2008-04-29 12:00:10 +04:00
*/
struct dev_whitelist_item {
u32 major , minor ;
short type ;
short access ;
struct list_head list ;
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
struct rcu_head rcu ;
2008-04-29 12:00:10 +04:00
} ;
struct dev_cgroup {
struct cgroup_subsys_state css ;
struct list_head whitelist ;
} ;
2008-06-06 09:46:24 +04:00
static inline struct dev_cgroup * css_to_devcgroup ( struct cgroup_subsys_state * s )
{
return container_of ( s , struct dev_cgroup , css ) ;
}
2008-04-29 12:00:10 +04:00
static inline struct dev_cgroup * cgroup_to_devcgroup ( struct cgroup * cgroup )
{
2008-06-06 09:46:24 +04:00
return css_to_devcgroup ( cgroup_subsys_state ( cgroup , devices_subsys_id ) ) ;
2008-04-29 12:00:10 +04:00
}
2008-07-25 12:47:03 +04:00
static inline struct dev_cgroup * task_devcgroup ( struct task_struct * task )
{
return css_to_devcgroup ( task_subsys_state ( task , devices_subsys_id ) ) ;
}
2008-04-29 12:00:10 +04:00
struct cgroup_subsys devices_subsys ;
static int devcgroup_can_attach ( struct cgroup_subsys * ss ,
struct cgroup * new_cgroup , struct task_struct * task )
{
if ( current ! = task & & ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
return 0 ;
}
/*
* called under cgroup_lock ( )
*/
static int dev_whitelist_copy ( struct list_head * dest , struct list_head * orig )
{
struct dev_whitelist_item * wh , * tmp , * new ;
list_for_each_entry ( wh , orig , list ) {
2008-10-19 07:28:06 +04:00
new = kmemdup ( wh , sizeof ( * wh ) , GFP_KERNEL ) ;
2008-04-29 12:00:10 +04:00
if ( ! new )
goto free_and_exit ;
list_add_tail ( & new - > list , dest ) ;
}
return 0 ;
free_and_exit :
list_for_each_entry_safe ( wh , tmp , dest , list ) {
list_del ( & wh - > list ) ;
kfree ( wh ) ;
}
return - ENOMEM ;
}
/* Stupid prototype - don't bother combining existing entries */
/*
* called under cgroup_lock ( )
*/
static int dev_whitelist_add ( struct dev_cgroup * dev_cgroup ,
struct dev_whitelist_item * wh )
{
2008-06-06 09:46:28 +04:00
struct dev_whitelist_item * whcopy , * walk ;
2008-04-29 12:00:10 +04:00
2008-10-19 07:28:06 +04:00
whcopy = kmemdup ( wh , sizeof ( * wh ) , GFP_KERNEL ) ;
2008-04-29 12:00:10 +04:00
if ( ! whcopy )
return - ENOMEM ;
2008-06-06 09:46:28 +04:00
list_for_each_entry ( walk , & dev_cgroup - > whitelist , list ) {
if ( walk - > type ! = wh - > type )
continue ;
if ( walk - > major ! = wh - > major )
continue ;
if ( walk - > minor ! = wh - > minor )
continue ;
walk - > access | = wh - > access ;
kfree ( whcopy ) ;
whcopy = NULL ;
}
if ( whcopy ! = NULL )
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
list_add_tail_rcu ( & whcopy - > list , & dev_cgroup - > whitelist ) ;
2008-04-29 12:00:10 +04:00
return 0 ;
}
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
static void whitelist_item_free ( struct rcu_head * rcu )
{
struct dev_whitelist_item * item ;
item = container_of ( rcu , struct dev_whitelist_item , rcu ) ;
kfree ( item ) ;
}
2008-04-29 12:00:10 +04:00
/*
* called under cgroup_lock ( )
*/
static void dev_whitelist_rm ( struct dev_cgroup * dev_cgroup ,
struct dev_whitelist_item * wh )
{
struct dev_whitelist_item * walk , * tmp ;
list_for_each_entry_safe ( walk , tmp , & dev_cgroup - > whitelist , list ) {
if ( walk - > type = = DEV_ALL )
goto remove ;
if ( walk - > type ! = wh - > type )
continue ;
if ( walk - > major ! = ~ 0 & & walk - > major ! = wh - > major )
continue ;
if ( walk - > minor ! = ~ 0 & & walk - > minor ! = wh - > minor )
continue ;
remove :
walk - > access & = ~ wh - > access ;
if ( ! walk - > access ) {
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
list_del_rcu ( & walk - > list ) ;
call_rcu ( & walk - > rcu , whitelist_item_free ) ;
2008-04-29 12:00:10 +04:00
}
}
}
/*
* called from kernel / cgroup . c with cgroup_lock ( ) held .
*/
static struct cgroup_subsys_state * devcgroup_create ( struct cgroup_subsys * ss ,
struct cgroup * cgroup )
{
struct dev_cgroup * dev_cgroup , * parent_dev_cgroup ;
struct cgroup * parent_cgroup ;
int ret ;
dev_cgroup = kzalloc ( sizeof ( * dev_cgroup ) , GFP_KERNEL ) ;
if ( ! dev_cgroup )
return ERR_PTR ( - ENOMEM ) ;
INIT_LIST_HEAD ( & dev_cgroup - > whitelist ) ;
parent_cgroup = cgroup - > parent ;
if ( parent_cgroup = = NULL ) {
struct dev_whitelist_item * wh ;
wh = kmalloc ( sizeof ( * wh ) , GFP_KERNEL ) ;
if ( ! wh ) {
kfree ( dev_cgroup ) ;
return ERR_PTR ( - ENOMEM ) ;
}
wh - > minor = wh - > major = ~ 0 ;
wh - > type = DEV_ALL ;
2008-07-25 12:47:08 +04:00
wh - > access = ACC_MASK ;
2008-04-29 12:00:10 +04:00
list_add ( & wh - > list , & dev_cgroup - > whitelist ) ;
} else {
parent_dev_cgroup = cgroup_to_devcgroup ( parent_cgroup ) ;
ret = dev_whitelist_copy ( & dev_cgroup - > whitelist ,
& parent_dev_cgroup - > whitelist ) ;
if ( ret ) {
kfree ( dev_cgroup ) ;
return ERR_PTR ( ret ) ;
}
}
return & dev_cgroup - > css ;
}
static void devcgroup_destroy ( struct cgroup_subsys * ss ,
struct cgroup * cgroup )
{
struct dev_cgroup * dev_cgroup ;
struct dev_whitelist_item * wh , * tmp ;
dev_cgroup = cgroup_to_devcgroup ( cgroup ) ;
list_for_each_entry_safe ( wh , tmp , & dev_cgroup - > whitelist , list ) {
list_del ( & wh - > list ) ;
kfree ( wh ) ;
}
kfree ( dev_cgroup ) ;
}
# define DEVCG_ALLOW 1
# define DEVCG_DENY 2
2008-04-29 12:00:14 +04:00
# define DEVCG_LIST 3
2008-07-13 23:14:02 +04:00
# define MAJMINLEN 13
2008-04-29 12:00:14 +04:00
# define ACCLEN 4
2008-04-29 12:00:10 +04:00
static void set_access ( char * acc , short access )
{
int idx = 0 ;
2008-04-29 12:00:14 +04:00
memset ( acc , 0 , ACCLEN ) ;
2008-04-29 12:00:10 +04:00
if ( access & ACC_READ )
acc [ idx + + ] = ' r ' ;
if ( access & ACC_WRITE )
acc [ idx + + ] = ' w ' ;
if ( access & ACC_MKNOD )
acc [ idx + + ] = ' m ' ;
}
static char type_to_char ( short type )
{
if ( type = = DEV_ALL )
return ' a ' ;
if ( type = = DEV_CHAR )
return ' c ' ;
if ( type = = DEV_BLOCK )
return ' b ' ;
return ' X ' ;
}
2008-04-29 12:00:14 +04:00
static void set_majmin ( char * str , unsigned m )
2008-04-29 12:00:10 +04:00
{
if ( m = = ~ 0 )
2008-07-25 12:47:08 +04:00
strcpy ( str , " * " ) ;
2008-04-29 12:00:10 +04:00
else
2008-07-25 12:47:08 +04:00
sprintf ( str , " %u " , m ) ;
2008-04-29 12:00:10 +04:00
}
2008-04-29 12:00:14 +04:00
static int devcgroup_seq_read ( struct cgroup * cgroup , struct cftype * cft ,
struct seq_file * m )
2008-04-29 12:00:10 +04:00
{
2008-04-29 12:00:14 +04:00
struct dev_cgroup * devcgroup = cgroup_to_devcgroup ( cgroup ) ;
2008-04-29 12:00:10 +04:00
struct dev_whitelist_item * wh ;
2008-04-29 12:00:14 +04:00
char maj [ MAJMINLEN ] , min [ MAJMINLEN ] , acc [ ACCLEN ] ;
2008-04-29 12:00:10 +04:00
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( wh , & devcgroup - > whitelist , list ) {
2008-04-29 12:00:10 +04:00
set_access ( acc , wh - > access ) ;
2008-04-29 12:00:14 +04:00
set_majmin ( maj , wh - > major ) ;
set_majmin ( min , wh - > minor ) ;
seq_printf ( m , " %c %s:%s %s \n " , type_to_char ( wh - > type ) ,
maj , min , acc ) ;
2008-04-29 12:00:10 +04:00
}
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
rcu_read_unlock ( ) ;
2008-04-29 12:00:10 +04:00
2008-04-29 12:00:14 +04:00
return 0 ;
2008-04-29 12:00:10 +04:00
}
/*
* may_access_whitelist :
* does the access granted to dev_cgroup c contain the access
* requested in whitelist item refwh .
* return 1 if yes , 0 if no .
* call with c - > lock held
*/
static int may_access_whitelist ( struct dev_cgroup * c ,
struct dev_whitelist_item * refwh )
{
struct dev_whitelist_item * whitem ;
list_for_each_entry ( whitem , & c - > whitelist , list ) {
if ( whitem - > type & DEV_ALL )
return 1 ;
if ( ( refwh - > type & DEV_BLOCK ) & & ! ( whitem - > type & DEV_BLOCK ) )
continue ;
if ( ( refwh - > type & DEV_CHAR ) & & ! ( whitem - > type & DEV_CHAR ) )
continue ;
if ( whitem - > major ! = ~ 0 & & whitem - > major ! = refwh - > major )
continue ;
if ( whitem - > minor ! = ~ 0 & & whitem - > minor ! = refwh - > minor )
continue ;
2008-07-13 23:14:04 +04:00
if ( refwh - > access & ( ~ whitem - > access ) )
2008-04-29 12:00:10 +04:00
continue ;
return 1 ;
}
return 0 ;
}
/*
* parent_has_perm :
* when adding a new allow rule to a device whitelist , the rule
* must be allowed in the parent device
*/
2008-07-25 12:47:03 +04:00
static int parent_has_perm ( struct dev_cgroup * childcg ,
2008-04-29 12:00:10 +04:00
struct dev_whitelist_item * wh )
{
2008-07-25 12:47:03 +04:00
struct cgroup * pcg = childcg - > css . cgroup - > parent ;
2008-04-29 12:00:10 +04:00
struct dev_cgroup * parent ;
if ( ! pcg )
return 1 ;
parent = cgroup_to_devcgroup ( pcg ) ;
2008-10-19 07:28:07 +04:00
return may_access_whitelist ( parent , wh ) ;
2008-04-29 12:00:10 +04:00
}
/*
* Modify the whitelist using allow / deny rules .
* CAP_SYS_ADMIN is needed for this . It ' s at least separate from CAP_MKNOD
* so we can give a container CAP_MKNOD to let it create devices but not
* modify the whitelist .
* It seems likely we ' ll want to add a CAP_CONTAINER capability to allow
* us to also grant CAP_SYS_ADMIN to containers without giving away the
* device whitelist controls , but for now we ' ll stick with CAP_SYS_ADMIN
*
* Taking rules away is always allowed ( given CAP_SYS_ADMIN ) . Granting
* new access is only allowed if you ' re in the top - level cgroup , or your
* parent cgroup has the access you ' re asking for .
*/
2008-07-25 12:47:03 +04:00
static int devcgroup_update_access ( struct dev_cgroup * devcgroup ,
int filetype , const char * buffer )
2008-04-29 12:00:10 +04:00
{
2008-07-25 12:47:03 +04:00
const char * b ;
2008-07-25 12:47:08 +04:00
char * endp ;
2008-10-19 07:28:07 +04:00
int count ;
2008-04-29 12:00:10 +04:00
struct dev_whitelist_item wh ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
memset ( & wh , 0 , sizeof ( wh ) ) ;
b = buffer ;
switch ( * b ) {
case ' a ' :
wh . type = DEV_ALL ;
wh . access = ACC_MASK ;
2008-07-04 21:00:07 +04:00
wh . major = ~ 0 ;
wh . minor = ~ 0 ;
2008-04-29 12:00:10 +04:00
goto handle ;
case ' b ' :
wh . type = DEV_BLOCK ;
break ;
case ' c ' :
wh . type = DEV_CHAR ;
break ;
default :
2008-07-25 12:47:03 +04:00
return - EINVAL ;
2008-04-29 12:00:10 +04:00
}
b + + ;
2008-07-25 12:47:03 +04:00
if ( ! isspace ( * b ) )
return - EINVAL ;
2008-04-29 12:00:10 +04:00
b + + ;
if ( * b = = ' * ' ) {
wh . major = ~ 0 ;
b + + ;
} else if ( isdigit ( * b ) ) {
2008-07-25 12:47:08 +04:00
wh . major = simple_strtoul ( b , & endp , 10 ) ;
b = endp ;
2008-04-29 12:00:10 +04:00
} else {
2008-07-25 12:47:03 +04:00
return - EINVAL ;
2008-04-29 12:00:10 +04:00
}
2008-07-25 12:47:03 +04:00
if ( * b ! = ' : ' )
return - EINVAL ;
2008-04-29 12:00:10 +04:00
b + + ;
/* read minor */
if ( * b = = ' * ' ) {
wh . minor = ~ 0 ;
b + + ;
} else if ( isdigit ( * b ) ) {
2008-07-25 12:47:08 +04:00
wh . minor = simple_strtoul ( b , & endp , 10 ) ;
b = endp ;
2008-04-29 12:00:10 +04:00
} else {
2008-07-25 12:47:03 +04:00
return - EINVAL ;
2008-04-29 12:00:10 +04:00
}
2008-07-25 12:47:03 +04:00
if ( ! isspace ( * b ) )
return - EINVAL ;
2008-04-29 12:00:10 +04:00
for ( b + + , count = 0 ; count < 3 ; count + + , b + + ) {
switch ( * b ) {
case ' r ' :
wh . access | = ACC_READ ;
break ;
case ' w ' :
wh . access | = ACC_WRITE ;
break ;
case ' m ' :
wh . access | = ACC_MKNOD ;
break ;
case ' \n ' :
case ' \0 ' :
count = 3 ;
break ;
default :
2008-07-25 12:47:03 +04:00
return - EINVAL ;
2008-04-29 12:00:10 +04:00
}
}
handle :
switch ( filetype ) {
case DEVCG_ALLOW :
2008-07-25 12:47:03 +04:00
if ( ! parent_has_perm ( devcgroup , & wh ) )
return - EPERM ;
return dev_whitelist_add ( devcgroup , & wh ) ;
2008-04-29 12:00:10 +04:00
case DEVCG_DENY :
dev_whitelist_rm ( devcgroup , & wh ) ;
break ;
default :
2008-07-25 12:47:03 +04:00
return - EINVAL ;
2008-04-29 12:00:10 +04:00
}
2008-07-25 12:47:03 +04:00
return 0 ;
}
2008-04-29 12:00:10 +04:00
2008-07-25 12:47:03 +04:00
static int devcgroup_access_write ( struct cgroup * cgrp , struct cftype * cft ,
const char * buffer )
{
int retval ;
if ( ! cgroup_lock_live_group ( cgrp ) )
return - ENODEV ;
retval = devcgroup_update_access ( cgroup_to_devcgroup ( cgrp ) ,
cft - > private , buffer ) ;
2008-04-29 12:00:10 +04:00
cgroup_unlock ( ) ;
return retval ;
}
static struct cftype dev_cgroup_files [ ] = {
{
. name = " allow " ,
2008-07-25 12:47:03 +04:00
. write_string = devcgroup_access_write ,
2008-04-29 12:00:10 +04:00
. private = DEVCG_ALLOW ,
} ,
{
. name = " deny " ,
2008-07-25 12:47:03 +04:00
. write_string = devcgroup_access_write ,
2008-04-29 12:00:10 +04:00
. private = DEVCG_DENY ,
} ,
2008-04-29 12:00:14 +04:00
{
. name = " list " ,
. read_seq_string = devcgroup_seq_read ,
. private = DEVCG_LIST ,
} ,
2008-04-29 12:00:10 +04:00
} ;
static int devcgroup_populate ( struct cgroup_subsys * ss ,
struct cgroup * cgroup )
{
return cgroup_add_files ( cgroup , ss , dev_cgroup_files ,
ARRAY_SIZE ( dev_cgroup_files ) ) ;
}
struct cgroup_subsys devices_subsys = {
. name = " devices " ,
. can_attach = devcgroup_can_attach ,
. create = devcgroup_create ,
. destroy = devcgroup_destroy ,
. populate = devcgroup_populate ,
. subsys_id = devices_subsys_id ,
} ;
int devcgroup_inode_permission ( struct inode * inode , int mask )
{
struct dev_cgroup * dev_cgroup ;
struct dev_whitelist_item * wh ;
dev_t device = inode - > i_rdev ;
if ( ! device )
return 0 ;
if ( ! S_ISBLK ( inode - > i_mode ) & & ! S_ISCHR ( inode - > i_mode ) )
return 0 ;
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
rcu_read_lock ( ) ;
2008-09-03 01:35:52 +04:00
dev_cgroup = task_devcgroup ( current ) ;
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
list_for_each_entry_rcu ( wh , & dev_cgroup - > whitelist , list ) {
2008-04-29 12:00:10 +04:00
if ( wh - > type & DEV_ALL )
goto acc_check ;
if ( ( wh - > type & DEV_BLOCK ) & & ! S_ISBLK ( inode - > i_mode ) )
continue ;
if ( ( wh - > type & DEV_CHAR ) & & ! S_ISCHR ( inode - > i_mode ) )
continue ;
if ( wh - > major ! = ~ 0 & & wh - > major ! = imajor ( inode ) )
continue ;
if ( wh - > minor ! = ~ 0 & & wh - > minor ! = iminor ( inode ) )
continue ;
acc_check :
if ( ( mask & MAY_WRITE ) & & ! ( wh - > access & ACC_WRITE ) )
continue ;
if ( ( mask & MAY_READ ) & & ! ( wh - > access & ACC_READ ) )
continue ;
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
rcu_read_unlock ( ) ;
2008-04-29 12:00:10 +04:00
return 0 ;
}
2008-09-03 01:35:52 +04:00
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
rcu_read_unlock ( ) ;
2008-04-29 12:00:10 +04:00
return - EPERM ;
}
int devcgroup_inode_mknod ( int mode , dev_t dev )
{
struct dev_cgroup * dev_cgroup ;
struct dev_whitelist_item * wh ;
2009-01-08 05:07:46 +03:00
if ( ! S_ISBLK ( mode ) & & ! S_ISCHR ( mode ) )
return 0 ;
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
rcu_read_lock ( ) ;
2008-09-03 01:35:52 +04:00
dev_cgroup = task_devcgroup ( current ) ;
2009-01-08 05:07:45 +03:00
list_for_each_entry_rcu ( wh , & dev_cgroup - > whitelist , list ) {
2008-04-29 12:00:10 +04:00
if ( wh - > type & DEV_ALL )
goto acc_check ;
if ( ( wh - > type & DEV_BLOCK ) & & ! S_ISBLK ( mode ) )
continue ;
if ( ( wh - > type & DEV_CHAR ) & & ! S_ISCHR ( mode ) )
continue ;
if ( wh - > major ! = ~ 0 & & wh - > major ! = MAJOR ( dev ) )
continue ;
if ( wh - > minor ! = ~ 0 & & wh - > minor ! = MINOR ( dev ) )
continue ;
acc_check :
if ( ! ( wh - > access & ACC_MKNOD ) )
continue ;
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
rcu_read_unlock ( ) ;
2008-04-29 12:00:10 +04:00
return 0 ;
}
2008-09-03 01:35:52 +04:00
devcgroup: relax white-list protection down to RCU
Currently this list is protected with a simple spinlock, even for reading
from one. This is OK, but can be better.
Actually I want it to be better very much, since after replacing the
OpenVZ device permissions engine with the cgroup-based one I noticed, that
we set 12 default device permissions for each newly created container (for
/dev/null, full, terminals, ect devices), and people sometimes have up to
20 perms more, so traversing the ~30-40 elements list under a spinlock
doesn't seem very good.
Here's the RCU protection for white-list - dev_whitelist_item-s are added
and removed under the devcg->lock, but are looked up in permissions
checking under the rcu_read_lock.
Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 12:47:07 +04:00
rcu_read_unlock ( ) ;
2008-09-03 01:35:52 +04:00
2008-04-29 12:00:10 +04:00
return - EPERM ;
}