2014-11-19 16:05:03 +03:00
/*
* Copyright ( c ) 2014 Jiri Pirko < jiri @ resnulli . us >
*
* 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 .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/skbuff.h>
# include <linux/rtnetlink.h>
# include <linux/if_vlan.h>
# include <net/netlink.h>
# include <net/pkt_sched.h>
# include <linux/tc_act/tc_vlan.h>
# include <net/tc_act/tc_vlan.h>
netns: make struct pernet_operations::id unsigned int
Make struct pernet_operations::id unsigned.
There are 2 reasons to do so:
1)
This field is really an index into an zero based array and
thus is unsigned entity. Using negative value is out-of-bound
access by definition.
2)
On x86_64 unsigned 32-bit data which are mixed with pointers
via array indexing or offsets added or subtracted to pointers
are preffered to signed 32-bit data.
"int" being used as an array index needs to be sign-extended
to 64-bit before being used.
void f(long *p, int i)
{
g(p[i]);
}
roughly translates to
movsx rsi, esi
mov rdi, [rsi+...]
call g
MOVSX is 3 byte instruction which isn't necessary if the variable is
unsigned because x86_64 is zero extending by default.
Now, there is net_generic() function which, you guessed it right, uses
"int" as an array index:
static inline void *net_generic(const struct net *net, int id)
{
...
ptr = ng->ptr[id - 1];
...
}
And this function is used a lot, so those sign extensions add up.
Patch snipes ~1730 bytes on allyesconfig kernel (without all junk
messing with code generation):
add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730)
Unfortunately some functions actually grow bigger.
This is a semmingly random artefact of code generation with register
allocator being used differently. gcc decides that some variable
needs to live in new r8+ registers and every access now requires REX
prefix. Or it is shifted into r12, so [r12+0] addressing mode has to be
used which is longer than [r8]
However, overall balance is in negative direction:
add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730)
function old new delta
nfsd4_lock 3886 3959 +73
tipc_link_build_proto_msg 1096 1140 +44
mac80211_hwsim_new_radio 2776 2808 +32
tipc_mon_rcv 1032 1058 +26
svcauth_gss_legacy_init 1413 1429 +16
tipc_bcbase_select_primary 379 392 +13
nfsd4_exchange_id 1247 1260 +13
nfsd4_setclientid_confirm 782 793 +11
...
put_client_renew_locked 494 480 -14
ip_set_sockfn_get 730 716 -14
geneve_sock_add 829 813 -16
nfsd4_sequence_done 721 703 -18
nlmclnt_lookup_host 708 686 -22
nfsd4_lockt 1085 1063 -22
nfs_get_client 1077 1050 -27
tcf_bpf_init 1106 1076 -30
nfsd4_encode_fattr 5997 5930 -67
Total: Before=154856051, After=154854321, chg -0.00%
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-11-17 04:58:21 +03:00
static unsigned int vlan_net_id ;
2016-07-26 02:09:41 +03:00
static struct tc_action_ops act_vlan_ops ;
2016-02-23 02:57:53 +03:00
2018-08-12 16:35:00 +03:00
static int tcf_vlan_act ( struct sk_buff * skb , const struct tc_action * a ,
struct tcf_result * res )
2014-11-19 16:05:03 +03:00
{
2016-07-26 02:09:41 +03:00
struct tcf_vlan * v = to_vlan ( a ) ;
2017-11-07 23:49:05 +03:00
struct tcf_vlan_params * p ;
2014-11-19 16:05:03 +03:00
int action ;
int err ;
2016-09-19 19:11:10 +03:00
u16 tci ;
2014-11-19 16:05:03 +03:00
2016-06-06 13:32:53 +03:00
tcf_lastuse_update ( & v - > tcf_tm ) ;
2017-11-07 23:48:15 +03:00
bstats_cpu_update ( this_cpu_ptr ( v - > common . cpu_bstats ) , skb ) ;
2016-09-29 12:10:40 +03:00
/* Ensure 'data' points at mac_header prior calling vlan manipulating
* functions .
*/
if ( skb_at_tc_ingress ( skb ) )
skb_push_rcsum ( skb , skb - > mac_len ) ;
2017-11-07 23:49:05 +03:00
action = READ_ONCE ( v - > tcf_action ) ;
2018-07-30 15:30:43 +03:00
p = rcu_dereference_bh ( v - > vlan_p ) ;
2017-11-07 23:49:05 +03:00
switch ( p - > tcfv_action ) {
2014-11-19 16:05:03 +03:00
case TCA_VLAN_ACT_POP :
err = skb_vlan_pop ( skb ) ;
if ( err )
goto drop ;
break ;
case TCA_VLAN_ACT_PUSH :
2017-11-07 23:49:05 +03:00
err = skb_vlan_push ( skb , p - > tcfv_push_proto , p - > tcfv_push_vid |
( p - > tcfv_push_prio < < VLAN_PRIO_SHIFT ) ) ;
2014-11-19 16:05:03 +03:00
if ( err )
goto drop ;
break ;
2016-09-19 19:11:10 +03:00
case TCA_VLAN_ACT_MODIFY :
/* No-op if no vlan tag (either hw-accel or in-payload) */
if ( ! skb_vlan_tagged ( skb ) )
2018-07-30 15:30:43 +03:00
goto out ;
2016-09-19 19:11:10 +03:00
/* extract existing tag (and guarantee no hw-accel tag) */
if ( skb_vlan_tag_present ( skb ) ) {
tci = skb_vlan_tag_get ( skb ) ;
skb - > vlan_tci = 0 ;
} else {
/* in-payload vlan tag, pop it */
err = __skb_vlan_pop ( skb , & tci ) ;
if ( err )
goto drop ;
}
/* replace the vid */
2017-11-07 23:49:05 +03:00
tci = ( tci & ~ VLAN_VID_MASK ) | p - > tcfv_push_vid ;
2016-09-19 19:11:10 +03:00
/* replace prio bits, if tcfv_push_prio specified */
2017-11-07 23:49:05 +03:00
if ( p - > tcfv_push_prio ) {
2016-09-19 19:11:10 +03:00
tci & = ~ VLAN_PRIO_MASK ;
2017-11-07 23:49:05 +03:00
tci | = p - > tcfv_push_prio < < VLAN_PRIO_SHIFT ;
2016-09-19 19:11:10 +03:00
}
/* put updated tci as hwaccel tag */
2017-11-07 23:49:05 +03:00
__vlan_hwaccel_put_tag ( skb , p - > tcfv_push_proto , tci ) ;
2016-09-19 19:11:10 +03:00
break ;
2014-11-19 16:05:03 +03:00
default :
BUG ( ) ;
}
2018-07-30 15:30:43 +03:00
out :
2016-09-29 12:10:40 +03:00
if ( skb_at_tc_ingress ( skb ) )
skb_pull_rcsum ( skb , skb - > mac_len ) ;
2014-11-19 16:05:03 +03:00
return action ;
2018-07-30 15:30:43 +03:00
drop :
qstats_drop_inc ( this_cpu_ptr ( v - > common . cpu_qstats ) ) ;
return TC_ACT_SHOT ;
2014-11-19 16:05:03 +03:00
}
static const struct nla_policy vlan_policy [ TCA_VLAN_MAX + 1 ] = {
[ TCA_VLAN_PARMS ] = { . len = sizeof ( struct tc_vlan ) } ,
[ TCA_VLAN_PUSH_VLAN_ID ] = { . type = NLA_U16 } ,
[ TCA_VLAN_PUSH_VLAN_PROTOCOL ] = { . type = NLA_U16 } ,
2016-08-17 13:36:14 +03:00
[ TCA_VLAN_PUSH_VLAN_PRIORITY ] = { . type = NLA_U8 } ,
2014-11-19 16:05:03 +03:00
} ;
static int tcf_vlan_init ( struct net * net , struct nlattr * nla ,
2016-07-26 02:09:41 +03:00
struct nlattr * est , struct tc_action * * a ,
2018-07-05 17:24:25 +03:00
int ovr , int bind , bool rtnl_held ,
struct netlink_ext_ack * extack )
2014-11-19 16:05:03 +03:00
{
2016-02-23 02:57:53 +03:00
struct tc_action_net * tn = net_generic ( net , vlan_net_id ) ;
2014-11-19 16:05:03 +03:00
struct nlattr * tb [ TCA_VLAN_MAX + 1 ] ;
2018-08-10 20:51:51 +03:00
struct tcf_vlan_params * p ;
2014-11-19 16:05:03 +03:00
struct tc_vlan * parm ;
struct tcf_vlan * v ;
int action ;
2018-03-23 21:31:30 +03:00
u16 push_vid = 0 ;
2014-11-19 16:05:03 +03:00
__be16 push_proto = 0 ;
2016-08-17 13:36:14 +03:00
u8 push_prio = 0 ;
2016-06-13 23:46:28 +03:00
bool exists = false ;
int ret = 0 , err ;
2014-11-19 16:05:03 +03:00
if ( ! nla )
return - EINVAL ;
2017-04-12 15:34:07 +03:00
err = nla_parse_nested ( tb , TCA_VLAN_MAX , nla , vlan_policy , NULL ) ;
2014-11-19 16:05:03 +03:00
if ( err < 0 )
return err ;
if ( ! tb [ TCA_VLAN_PARMS ] )
return - EINVAL ;
parm = nla_data ( tb [ TCA_VLAN_PARMS ] ) ;
2018-07-05 17:24:32 +03:00
err = tcf_idr_check_alloc ( tn , & parm - > index , a , bind ) ;
if ( err < 0 )
return err ;
exists = err ;
2016-05-10 23:49:26 +03:00
if ( exists & & bind )
return 0 ;
2014-11-19 16:05:03 +03:00
switch ( parm - > v_action ) {
case TCA_VLAN_ACT_POP :
break ;
case TCA_VLAN_ACT_PUSH :
2016-09-19 19:11:10 +03:00
case TCA_VLAN_ACT_MODIFY :
2016-05-10 23:49:26 +03:00
if ( ! tb [ TCA_VLAN_PUSH_VLAN_ID ] ) {
if ( exists )
2017-08-30 09:31:59 +03:00
tcf_idr_release ( * a , bind ) ;
2018-07-05 17:24:32 +03:00
else
tcf_idr_cleanup ( tn , parm - > index ) ;
2014-11-19 16:05:03 +03:00
return - EINVAL ;
2016-05-10 23:49:26 +03:00
}
2014-11-19 16:05:03 +03:00
push_vid = nla_get_u16 ( tb [ TCA_VLAN_PUSH_VLAN_ID ] ) ;
2016-05-10 23:49:26 +03:00
if ( push_vid > = VLAN_VID_MASK ) {
if ( exists )
2017-08-30 09:31:59 +03:00
tcf_idr_release ( * a , bind ) ;
2018-07-05 17:24:32 +03:00
else
tcf_idr_cleanup ( tn , parm - > index ) ;
2014-11-19 16:05:03 +03:00
return - ERANGE ;
2016-05-10 23:49:26 +03:00
}
2014-11-19 16:05:03 +03:00
if ( tb [ TCA_VLAN_PUSH_VLAN_PROTOCOL ] ) {
push_proto = nla_get_be16 ( tb [ TCA_VLAN_PUSH_VLAN_PROTOCOL ] ) ;
switch ( push_proto ) {
case htons ( ETH_P_8021Q ) :
case htons ( ETH_P_8021AD ) :
break ;
default :
2018-05-16 13:54:29 +03:00
if ( exists )
tcf_idr_release ( * a , bind ) ;
2018-07-05 17:24:32 +03:00
else
tcf_idr_cleanup ( tn , parm - > index ) ;
2014-11-19 16:05:03 +03:00
return - EPROTONOSUPPORT ;
}
} else {
push_proto = htons ( ETH_P_8021Q ) ;
}
2016-08-17 13:36:14 +03:00
if ( tb [ TCA_VLAN_PUSH_VLAN_PRIORITY ] )
push_prio = nla_get_u8 ( tb [ TCA_VLAN_PUSH_VLAN_PRIORITY ] ) ;
2014-11-19 16:05:03 +03:00
break ;
default :
2016-05-10 23:49:26 +03:00
if ( exists )
2017-08-30 09:31:59 +03:00
tcf_idr_release ( * a , bind ) ;
2018-07-05 17:24:32 +03:00
else
tcf_idr_cleanup ( tn , parm - > index ) ;
2014-11-19 16:05:03 +03:00
return - EINVAL ;
}
action = parm - > v_action ;
2016-05-10 23:49:26 +03:00
if ( ! exists ) {
2017-08-30 09:31:59 +03:00
ret = tcf_idr_create ( tn , parm - > index , est , a ,
2017-11-07 23:48:15 +03:00
& act_vlan_ops , bind , true ) ;
2018-07-05 17:24:32 +03:00
if ( ret ) {
tcf_idr_cleanup ( tn , parm - > index ) ;
2014-11-19 16:05:03 +03:00
return ret ;
2018-07-05 17:24:32 +03:00
}
2014-11-19 16:05:03 +03:00
ret = ACT_P_CREATED ;
2018-07-05 17:24:30 +03:00
} else if ( ! ovr ) {
2017-08-30 09:31:59 +03:00
tcf_idr_release ( * a , bind ) ;
2018-07-05 17:24:30 +03:00
return - EEXIST ;
2014-11-19 16:05:03 +03:00
}
2016-07-26 02:09:41 +03:00
v = to_vlan ( * a ) ;
2014-11-19 16:05:03 +03:00
2017-11-07 23:49:05 +03:00
p = kzalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
if ( ! p ) {
2018-07-05 17:24:30 +03:00
tcf_idr_release ( * a , bind ) ;
2017-11-07 23:49:05 +03:00
return - ENOMEM ;
}
2014-11-19 16:05:03 +03:00
2017-11-07 23:49:05 +03:00
p - > tcfv_action = action ;
p - > tcfv_push_vid = push_vid ;
p - > tcfv_push_prio = push_prio ;
p - > tcfv_push_proto = push_proto ;
2018-08-14 21:46:16 +03:00
spin_lock_bh ( & v - > tcf_lock ) ;
2018-08-10 20:51:51 +03:00
v - > tcf_action = parm - > action ;
rcu_swap_protected ( v - > vlan_p , p , lockdep_is_held ( & v - > tcf_lock ) ) ;
2018-08-14 21:46:16 +03:00
spin_unlock_bh ( & v - > tcf_lock ) ;
2017-11-07 23:49:05 +03:00
2018-08-10 20:51:51 +03:00
if ( p )
kfree_rcu ( p , rcu ) ;
2014-11-19 16:05:03 +03:00
if ( ret = = ACT_P_CREATED )
2017-08-30 09:31:59 +03:00
tcf_idr_insert ( tn , * a ) ;
2014-11-19 16:05:03 +03:00
return ret ;
}
2017-12-05 23:53:07 +03:00
static void tcf_vlan_cleanup ( struct tc_action * a )
2017-11-07 23:49:05 +03:00
{
struct tcf_vlan * v = to_vlan ( a ) ;
struct tcf_vlan_params * p ;
p = rcu_dereference_protected ( v - > vlan_p , 1 ) ;
2018-03-16 02:00:53 +03:00
if ( p )
kfree_rcu ( p , rcu ) ;
2017-11-07 23:49:05 +03:00
}
2014-11-19 16:05:03 +03:00
static int tcf_vlan_dump ( struct sk_buff * skb , struct tc_action * a ,
int bind , int ref )
{
unsigned char * b = skb_tail_pointer ( skb ) ;
2016-07-26 02:09:41 +03:00
struct tcf_vlan * v = to_vlan ( a ) ;
2018-08-10 20:51:51 +03:00
struct tcf_vlan_params * p ;
2014-11-19 16:05:03 +03:00
struct tc_vlan opt = {
. index = v - > tcf_index ,
2018-07-05 17:24:24 +03:00
. refcnt = refcount_read ( & v - > tcf_refcnt ) - ref ,
. bindcnt = atomic_read ( & v - > tcf_bindcnt ) - bind ,
2014-11-19 16:05:03 +03:00
} ;
struct tcf_t t ;
2018-08-14 21:46:16 +03:00
spin_lock_bh ( & v - > tcf_lock ) ;
2018-08-10 20:51:51 +03:00
opt . action = v - > tcf_action ;
p = rcu_dereference_protected ( v - > vlan_p , lockdep_is_held ( & v - > tcf_lock ) ) ;
opt . v_action = p - > tcfv_action ;
2014-11-19 16:05:03 +03:00
if ( nla_put ( skb , TCA_VLAN_PARMS , sizeof ( opt ) , & opt ) )
goto nla_put_failure ;
2017-11-07 23:49:05 +03:00
if ( ( p - > tcfv_action = = TCA_VLAN_ACT_PUSH | |
p - > tcfv_action = = TCA_VLAN_ACT_MODIFY ) & &
( nla_put_u16 ( skb , TCA_VLAN_PUSH_VLAN_ID , p - > tcfv_push_vid ) | |
2016-06-05 17:41:32 +03:00
nla_put_be16 ( skb , TCA_VLAN_PUSH_VLAN_PROTOCOL ,
2017-11-07 23:49:05 +03:00
p - > tcfv_push_proto ) | |
2016-08-17 13:36:14 +03:00
( nla_put_u8 ( skb , TCA_VLAN_PUSH_VLAN_PRIORITY ,
2017-11-07 23:49:05 +03:00
p - > tcfv_push_prio ) ) ) )
2014-11-19 16:05:03 +03:00
goto nla_put_failure ;
2016-06-06 13:32:55 +03:00
tcf_tm_dump ( & t , & v - > tcf_tm ) ;
2016-04-26 11:06:18 +03:00
if ( nla_put_64bit ( skb , TCA_VLAN_TM , sizeof ( t ) , & t , TCA_VLAN_PAD ) )
2014-11-19 16:05:03 +03:00
goto nla_put_failure ;
2018-08-14 21:46:16 +03:00
spin_unlock_bh ( & v - > tcf_lock ) ;
2018-08-10 20:51:51 +03:00
2014-11-19 16:05:03 +03:00
return skb - > len ;
nla_put_failure :
2018-08-14 21:46:16 +03:00
spin_unlock_bh ( & v - > tcf_lock ) ;
2014-11-19 16:05:03 +03:00
nlmsg_trim ( skb , b ) ;
return - 1 ;
}
2016-02-23 02:57:53 +03:00
static int tcf_vlan_walker ( struct net * net , struct sk_buff * skb ,
struct netlink_callback * cb , int type ,
2018-02-15 18:54:58 +03:00
const struct tc_action_ops * ops ,
struct netlink_ext_ack * extack )
2016-02-23 02:57:53 +03:00
{
struct tc_action_net * tn = net_generic ( net , vlan_net_id ) ;
2018-02-15 18:54:59 +03:00
return tcf_generic_walker ( tn , skb , cb , type , ops , extack ) ;
2016-02-23 02:57:53 +03:00
}
2018-02-15 18:54:57 +03:00
static int tcf_vlan_search ( struct net * net , struct tc_action * * a , u32 index ,
struct netlink_ext_ack * extack )
2016-02-23 02:57:53 +03:00
{
struct tc_action_net * tn = net_generic ( net , vlan_net_id ) ;
2017-08-30 09:31:59 +03:00
return tcf_idr_search ( tn , a , index ) ;
2016-02-23 02:57:53 +03:00
}
2014-11-19 16:05:03 +03:00
static struct tc_action_ops act_vlan_ops = {
. kind = " vlan " ,
. type = TCA_ACT_VLAN ,
. owner = THIS_MODULE ,
2018-08-12 16:35:00 +03:00
. act = tcf_vlan_act ,
2014-11-19 16:05:03 +03:00
. dump = tcf_vlan_dump ,
. init = tcf_vlan_init ,
2017-11-07 23:49:05 +03:00
. cleanup = tcf_vlan_cleanup ,
2016-02-23 02:57:53 +03:00
. walk = tcf_vlan_walker ,
. lookup = tcf_vlan_search ,
2016-07-26 02:09:41 +03:00
. size = sizeof ( struct tcf_vlan ) ,
2016-02-23 02:57:53 +03:00
} ;
static __net_init int vlan_init_net ( struct net * net )
{
struct tc_action_net * tn = net_generic ( net , vlan_net_id ) ;
2017-11-07 00:47:18 +03:00
return tc_action_net_init ( tn , & act_vlan_ops ) ;
2016-02-23 02:57:53 +03:00
}
2017-12-12 02:35:03 +03:00
static void __net_exit vlan_exit_net ( struct list_head * net_list )
2016-02-23 02:57:53 +03:00
{
2017-12-12 02:35:03 +03:00
tc_action_net_exit ( net_list , vlan_net_id ) ;
2016-02-23 02:57:53 +03:00
}
static struct pernet_operations vlan_net_ops = {
. init = vlan_init_net ,
2017-12-12 02:35:03 +03:00
. exit_batch = vlan_exit_net ,
2016-02-23 02:57:53 +03:00
. id = & vlan_net_id ,
. size = sizeof ( struct tc_action_net ) ,
2014-11-19 16:05:03 +03:00
} ;
static int __init vlan_init_module ( void )
{
2016-02-23 02:57:53 +03:00
return tcf_register_action ( & act_vlan_ops , & vlan_net_ops ) ;
2014-11-19 16:05:03 +03:00
}
static void __exit vlan_cleanup_module ( void )
{
2016-02-23 02:57:53 +03:00
tcf_unregister_action ( & act_vlan_ops , & vlan_net_ops ) ;
2014-11-19 16:05:03 +03:00
}
module_init ( vlan_init_module ) ;
module_exit ( vlan_cleanup_module ) ;
MODULE_AUTHOR ( " Jiri Pirko <jiri@resnulli.us> " ) ;
MODULE_DESCRIPTION ( " vlan manipulation actions " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;