2011-11-22 05:10:51 +00:00
/*
* net / core / netprio_cgroup . c Priority Control Group
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* Authors : Neil Horman < nhorman @ tuxdriver . com >
*/
2012-05-16 19:58:40 +00:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2011-11-22 05:10:51 +00:00
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/types.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/skbuff.h>
# include <linux/cgroup.h>
# include <linux/rcupdate.h>
# include <linux/atomic.h>
# include <net/rtnetlink.h>
# include <net/pkt_cls.h>
# include <net/sock.h>
# include <net/netprio_cgroup.h>
2012-07-20 10:39:25 +00:00
# include <linux/fdtable.h>
2012-11-22 07:32:46 -08:00
# define PRIOMAP_MIN_SZ 128
2011-11-22 05:10:51 +00:00
2012-11-22 07:32:46 -08:00
/*
2013-12-08 12:15:44 -08:00
* Extend @ dev - > priomap so that it ' s large enough to accommodate
2012-11-22 07:32:46 -08:00
* @ target_idx . @ dev - > priomap . priomap_len > @ target_idx after successful
* return . Must be called under rtnl lock .
*/
static int extend_netdev_table ( struct net_device * dev , u32 target_idx )
2011-11-22 05:10:51 +00:00
{
2012-11-22 07:32:46 -08:00
struct netprio_map * old , * new ;
size_t new_sz , new_len ;
2011-11-22 05:10:51 +00:00
2012-11-22 07:32:46 -08:00
/* is the existing priomap large enough? */
2012-11-22 07:32:46 -08:00
old = rtnl_dereference ( dev - > priomap ) ;
2012-11-22 07:32:46 -08:00
if ( old & & old - > priomap_len > target_idx )
return 0 ;
/*
* Determine the new size . Let ' s keep it power - of - two . We start
* from PRIOMAP_MIN_SZ and double it until it ' s large enough to
* accommodate @ target_idx .
*/
new_sz = PRIOMAP_MIN_SZ ;
while ( true ) {
new_len = ( new_sz - offsetof ( struct netprio_map , priomap ) ) /
sizeof ( new - > priomap [ 0 ] ) ;
if ( new_len > target_idx )
break ;
new_sz * = 2 ;
/* overflowed? */
if ( WARN_ON ( new_sz < PRIOMAP_MIN_SZ ) )
return - ENOSPC ;
}
2011-11-22 05:10:51 +00:00
2012-11-22 07:32:46 -08:00
/* allocate & copy */
new = kzalloc ( new_sz , GFP_KERNEL ) ;
2013-02-04 16:48:16 +00:00
if ( ! new )
net: cgroup: fix access the unallocated memory in netprio cgroup
there are some out of bound accesses in netprio cgroup.
now before accessing the dev->priomap.priomap array,we only check
if the dev->priomap exist.and because we don't want to see
additional bound checkings in fast path, so we should make sure
that dev->priomap is null or array size of dev->priomap.priomap
is equal to max_prioidx + 1;
so in write_priomap logic,we should call extend_netdev_table when
dev->priomap is null and dev->priomap.priomap_len < max_len.
and in cgrp_create->update_netdev_tables logic,we should call
extend_netdev_table only when dev->priomap exist and
dev->priomap.priomap_len < max_len.
and it's not needed to call update_netdev_tables in write_priomap,
we can only allocate the net device's priomap which we change through
net_prio.ifpriomap.
this patch also add a return value for update_netdev_tables &
extend_netdev_table, so when new_priomap is allocated failed,
write_priomap will stop to access the priomap,and return -ENOMEM
back to the userspace to tell the user what happend.
Change From v3:
1. add rtnl protect when reading max_prioidx in write_priomap.
2. only call extend_netdev_table when map->priomap_len < max_len,
this will make sure array size of dev->map->priomap always
bigger than any prioidx.
3. add a function write_update_netdev_table to make codes clear.
Change From v2:
1. protect extend_netdev_table by RTNL.
2. when extend_netdev_table failed,call dev_put to reduce device's refcount.
Signed-off-by: Gao feng <gaofeng@cn.fujitsu.com>
Cc: Neil Horman <nhorman@tuxdriver.com>
Cc: Eric Dumazet <edumazet@google.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-07-11 21:50:15 +00:00
return - ENOMEM ;
2011-11-22 05:10:51 +00:00
2012-11-22 07:32:46 -08:00
if ( old )
memcpy ( new - > priomap , old - > priomap ,
old - > priomap_len * sizeof ( old - > priomap [ 0 ] ) ) ;
2011-11-22 05:10:51 +00:00
2012-11-22 07:32:46 -08:00
new - > priomap_len = new_len ;
2011-11-22 05:10:51 +00:00
2012-11-22 07:32:46 -08:00
/* install the new priomap */
2012-11-22 07:32:46 -08:00
rcu_assign_pointer ( dev - > priomap , new ) ;
if ( old )
kfree_rcu ( old , rcu ) ;
net: cgroup: fix access the unallocated memory in netprio cgroup
there are some out of bound accesses in netprio cgroup.
now before accessing the dev->priomap.priomap array,we only check
if the dev->priomap exist.and because we don't want to see
additional bound checkings in fast path, so we should make sure
that dev->priomap is null or array size of dev->priomap.priomap
is equal to max_prioidx + 1;
so in write_priomap logic,we should call extend_netdev_table when
dev->priomap is null and dev->priomap.priomap_len < max_len.
and in cgrp_create->update_netdev_tables logic,we should call
extend_netdev_table only when dev->priomap exist and
dev->priomap.priomap_len < max_len.
and it's not needed to call update_netdev_tables in write_priomap,
we can only allocate the net device's priomap which we change through
net_prio.ifpriomap.
this patch also add a return value for update_netdev_tables &
extend_netdev_table, so when new_priomap is allocated failed,
write_priomap will stop to access the priomap,and return -ENOMEM
back to the userspace to tell the user what happend.
Change From v3:
1. add rtnl protect when reading max_prioidx in write_priomap.
2. only call extend_netdev_table when map->priomap_len < max_len,
this will make sure array size of dev->map->priomap always
bigger than any prioidx.
3. add a function write_update_netdev_table to make codes clear.
Change From v2:
1. protect extend_netdev_table by RTNL.
2. when extend_netdev_table failed,call dev_put to reduce device's refcount.
Signed-off-by: Gao feng <gaofeng@cn.fujitsu.com>
Cc: Neil Horman <nhorman@tuxdriver.com>
Cc: Eric Dumazet <edumazet@google.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-07-11 21:50:15 +00:00
return 0 ;
}
2012-11-22 07:32:47 -08:00
/**
* netprio_prio - return the effective netprio of a cgroup - net_device pair
2013-08-08 20:11:22 -04:00
* @ css : css part of the target pair
2012-11-22 07:32:47 -08:00
* @ dev : net_device part of the target pair
*
* Should be called under RCU read or rtnl lock .
*/
2013-08-08 20:11:22 -04:00
static u32 netprio_prio ( struct cgroup_subsys_state * css , struct net_device * dev )
2012-11-22 07:32:47 -08:00
{
struct netprio_map * map = rcu_dereference_rtnl ( dev - > priomap ) ;
2013-08-08 20:11:22 -04:00
int id = css - > cgroup - > id ;
2012-11-22 07:32:47 -08:00
2013-08-08 20:11:22 -04:00
if ( map & & id < map - > priomap_len )
return map - > priomap [ id ] ;
2012-11-22 07:32:47 -08:00
return 0 ;
}
/**
* netprio_set_prio - set netprio on a cgroup - net_device pair
2013-08-08 20:11:22 -04:00
* @ css : css part of the target pair
2012-11-22 07:32:47 -08:00
* @ dev : net_device part of the target pair
* @ prio : prio to set
*
2013-08-08 20:11:22 -04:00
* Set netprio to @ prio on @ css - @ dev pair . Should be called under rtnl
2012-11-22 07:32:47 -08:00
* lock and may fail under memory pressure for non - zero @ prio .
*/
2013-08-08 20:11:22 -04:00
static int netprio_set_prio ( struct cgroup_subsys_state * css ,
struct net_device * dev , u32 prio )
2012-11-22 07:32:47 -08:00
{
struct netprio_map * map ;
2013-08-08 20:11:22 -04:00
int id = css - > cgroup - > id ;
2012-11-22 07:32:47 -08:00
int ret ;
/* avoid extending priomap for zero writes */
map = rtnl_dereference ( dev - > priomap ) ;
2013-08-08 20:11:22 -04:00
if ( ! prio & & ( ! map | | map - > priomap_len < = id ) )
2012-11-22 07:32:47 -08:00
return 0 ;
2013-08-08 20:11:22 -04:00
ret = extend_netdev_table ( dev , id ) ;
2012-11-22 07:32:47 -08:00
if ( ret )
return ret ;
map = rtnl_dereference ( dev - > priomap ) ;
2013-08-08 20:11:22 -04:00
map - > priomap [ id ] = prio ;
2012-11-22 07:32:47 -08:00
return 0 ;
}
2013-08-08 20:11:23 -04:00
static struct cgroup_subsys_state *
cgrp_css_alloc ( struct cgroup_subsys_state * parent_css )
2011-11-22 05:10:51 +00:00
{
2013-08-08 20:11:22 -04:00
struct cgroup_subsys_state * css ;
2012-11-22 07:32:47 -08:00
2013-08-08 20:11:22 -04:00
css = kzalloc ( sizeof ( * css ) , GFP_KERNEL ) ;
if ( ! css )
2011-11-22 05:10:51 +00:00
return ERR_PTR ( - ENOMEM ) ;
2013-08-08 20:11:22 -04:00
return css ;
2011-11-22 05:10:51 +00:00
}
2013-08-08 20:11:23 -04:00
static int cgrp_css_online ( struct cgroup_subsys_state * css )
2011-11-22 05:10:51 +00:00
{
2013-08-08 20:11:23 -04:00
struct cgroup_subsys_state * parent_css = css_parent ( css ) ;
2011-11-22 05:10:51 +00:00
struct net_device * dev ;
2012-11-22 07:32:47 -08:00
int ret = 0 ;
2013-08-08 20:11:23 -04:00
if ( ! parent_css )
2012-11-22 07:32:47 -08:00
return 0 ;
2011-11-22 05:10:51 +00:00
rtnl_lock ( ) ;
2012-11-22 07:32:47 -08:00
/*
* Inherit prios from the parent . As all prios are set during
* onlining , there is no need to clear them on offline .
*/
for_each_netdev ( & init_net , dev ) {
2013-08-08 20:11:22 -04:00
u32 prio = netprio_prio ( parent_css , dev ) ;
2012-11-22 07:32:47 -08:00
2013-08-08 20:11:22 -04:00
ret = netprio_set_prio ( css , dev , prio ) ;
2012-11-22 07:32:47 -08:00
if ( ret )
break ;
}
2011-11-22 05:10:51 +00:00
rtnl_unlock ( ) ;
2012-11-22 07:32:47 -08:00
return ret ;
}
2013-08-08 20:11:23 -04:00
static void cgrp_css_free ( struct cgroup_subsys_state * css )
2012-11-22 07:32:47 -08:00
{
2013-08-08 20:11:23 -04:00
kfree ( css ) ;
2011-11-22 05:10:51 +00:00
}
2013-08-08 20:11:24 -04:00
static u64 read_prioidx ( struct cgroup_subsys_state * css , struct cftype * cft )
2011-11-22 05:10:51 +00:00
{
2013-08-08 20:11:24 -04:00
return css - > cgroup - > id ;
2011-11-22 05:10:51 +00:00
}
2013-12-05 12:28:04 -05:00
static int read_priomap ( struct seq_file * sf , void * v )
2011-11-22 05:10:51 +00:00
{
struct net_device * dev ;
rcu_read_lock ( ) ;
2012-11-22 07:32:47 -08:00
for_each_netdev_rcu ( & init_net , dev )
2013-12-05 12:28:04 -05:00
seq_printf ( sf , " %s %u \n " , dev - > name ,
netprio_prio ( seq_css ( sf ) , dev ) ) ;
2011-11-22 05:10:51 +00:00
rcu_read_unlock ( ) ;
return 0 ;
}
2013-08-08 20:11:24 -04:00
static int write_priomap ( struct cgroup_subsys_state * css , struct cftype * cft ,
2014-03-19 10:23:54 -04:00
char * buffer )
2011-11-22 05:10:51 +00:00
{
2012-11-22 07:32:46 -08:00
char devname [ IFNAMSIZ + 1 ] ;
2011-11-22 05:10:51 +00:00
struct net_device * dev ;
2012-11-22 07:32:46 -08:00
u32 prio ;
int ret ;
2011-11-22 05:10:51 +00:00
2012-11-22 07:32:46 -08:00
if ( sscanf ( buffer , " % " __stringify ( IFNAMSIZ ) " s %u " , devname , & prio ) ! = 2 )
return - EINVAL ;
2011-11-22 05:10:51 +00:00
dev = dev_get_by_name ( & init_net , devname ) ;
if ( ! dev )
2012-11-22 07:32:46 -08:00
return - ENODEV ;
2011-11-22 05:10:51 +00:00
2012-08-14 12:34:35 +00:00
rtnl_lock ( ) ;
2012-11-22 07:32:46 -08:00
2013-08-08 20:11:22 -04:00
ret = netprio_set_prio ( css , dev , prio ) ;
net: cgroup: fix access the unallocated memory in netprio cgroup
there are some out of bound accesses in netprio cgroup.
now before accessing the dev->priomap.priomap array,we only check
if the dev->priomap exist.and because we don't want to see
additional bound checkings in fast path, so we should make sure
that dev->priomap is null or array size of dev->priomap.priomap
is equal to max_prioidx + 1;
so in write_priomap logic,we should call extend_netdev_table when
dev->priomap is null and dev->priomap.priomap_len < max_len.
and in cgrp_create->update_netdev_tables logic,we should call
extend_netdev_table only when dev->priomap exist and
dev->priomap.priomap_len < max_len.
and it's not needed to call update_netdev_tables in write_priomap,
we can only allocate the net device's priomap which we change through
net_prio.ifpriomap.
this patch also add a return value for update_netdev_tables &
extend_netdev_table, so when new_priomap is allocated failed,
write_priomap will stop to access the priomap,and return -ENOMEM
back to the userspace to tell the user what happend.
Change From v3:
1. add rtnl protect when reading max_prioidx in write_priomap.
2. only call extend_netdev_table when map->priomap_len < max_len,
this will make sure array size of dev->map->priomap always
bigger than any prioidx.
3. add a function write_update_netdev_table to make codes clear.
Change From v2:
1. protect extend_netdev_table by RTNL.
2. when extend_netdev_table failed,call dev_put to reduce device's refcount.
Signed-off-by: Gao feng <gaofeng@cn.fujitsu.com>
Cc: Neil Horman <nhorman@tuxdriver.com>
Cc: Eric Dumazet <edumazet@google.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-07-11 21:50:15 +00:00
2012-08-14 12:34:35 +00:00
rtnl_unlock ( ) ;
2011-11-22 05:10:51 +00:00
dev_put ( dev ) ;
return ret ;
}
2012-08-21 22:32:06 -04:00
static int update_netprio ( const void * v , struct file * file , unsigned n )
{
int err ;
struct socket * sock = sock_from_file ( file , & err ) ;
if ( sock )
sock - > sk - > sk_cgrp_prioidx = ( u32 ) ( unsigned long ) v ;
return 0 ;
}
2013-08-08 20:11:23 -04:00
static void net_prio_attach ( struct cgroup_subsys_state * css ,
struct cgroup_taskset * tset )
2012-07-20 10:39:25 +00:00
{
struct task_struct * p ;
2013-10-08 11:05:19 +08:00
void * v = ( void * ) ( unsigned long ) css - > cgroup - > id ;
2012-07-20 10:39:25 +00:00
2014-02-13 06:58:41 -05:00
cgroup_taskset_for_each ( p , tset ) {
2012-07-20 10:39:25 +00:00
task_lock ( p ) ;
2012-08-21 22:32:06 -04:00
iterate_fd ( p - > files , 0 , update_netprio , v ) ;
2012-07-20 10:39:25 +00:00
task_unlock ( p ) ;
}
}
2011-11-22 05:10:51 +00:00
static struct cftype ss_files [ ] = {
{
. name = " prioidx " ,
. read_u64 = read_prioidx ,
} ,
{
. name = " ifpriomap " ,
2013-12-05 12:28:04 -05:00
. seq_show = read_priomap ,
2011-11-22 05:10:51 +00:00
. write_string = write_priomap ,
} ,
2012-04-01 12:09:55 -07:00
{ } /* terminate */
2011-11-22 05:10:51 +00:00
} ;
2014-02-08 10:36:58 -05:00
struct cgroup_subsys net_prio_cgrp_subsys = {
2012-11-19 08:13:38 -08:00
. css_alloc = cgrp_css_alloc ,
2012-11-22 07:32:47 -08:00
. css_online = cgrp_css_online ,
2012-11-19 08:13:38 -08:00
. css_free = cgrp_css_free ,
2012-07-20 10:39:25 +00:00
. attach = net_prio_attach ,
2012-04-01 12:09:55 -07:00
. base_cftypes = ss_files ,
2012-04-01 12:09:55 -07:00
} ;
2011-11-22 05:10:51 +00:00
static int netprio_device_event ( struct notifier_block * unused ,
unsigned long event , void * ptr )
{
2013-05-28 01:30:21 +00:00
struct net_device * dev = netdev_notifier_info_to_dev ( ptr ) ;
2011-11-22 05:10:51 +00:00
struct netprio_map * old ;
/*
* Note this is called with rtnl_lock held so we have update side
* protection on our rcu assignments
*/
switch ( event ) {
case NETDEV_UNREGISTER :
old = rtnl_dereference ( dev - > priomap ) ;
2011-11-23 07:09:32 +00:00
RCU_INIT_POINTER ( dev - > priomap , NULL ) ;
2011-11-22 05:10:51 +00:00
if ( old )
kfree_rcu ( old , rcu ) ;
break ;
}
return NOTIFY_DONE ;
}
static struct notifier_block netprio_device_notifier = {
. notifier_call = netprio_device_event
} ;
static int __init init_cgroup_netprio ( void )
{
register_netdevice_notifier ( & netprio_device_notifier ) ;
2014-02-08 10:36:58 -05:00
return 0 ;
2011-11-22 05:10:51 +00:00
}
2014-02-08 10:36:58 -05:00
subsys_initcall ( init_cgroup_netprio ) ;
2011-11-22 05:10:51 +00:00
MODULE_LICENSE ( " GPL v2 " ) ;