2017-11-27 23:10:23 +03:00
/*
* Copyright ( C ) 2017 Netronome Systems , Inc .
*
* This software is licensed under the GNU General License Version 2 ,
* June 1991 as shown in the file COPYING in the top - level directory of this
* source tree .
*
* THE COPYRIGHT HOLDERS AND / OR OTHER PARTIES PROVIDE THE PROGRAM " AS IS "
* WITHOUT WARRANTY OF ANY KIND , EITHER EXPRESSED OR IMPLIED , INCLUDING ,
* BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE . THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
* OF THE PROGRAM IS WITH YOU . SHOULD THE PROGRAM PROVE DEFECTIVE , YOU ASSUME
* THE COST OF ALL NECESSARY SERVICING , REPAIR OR CORRECTION .
*/
2017-11-03 23:56:17 +03:00
# include <linux/bpf.h>
# include <linux/bpf_verifier.h>
# include <linux/bug.h>
# include <linux/list.h>
# include <linux/netdevice.h>
# include <linux/printk.h>
# include <linux/rtnetlink.h>
/* protected by RTNL */
static LIST_HEAD ( bpf_prog_offload_devs ) ;
int bpf_prog_offload_init ( struct bpf_prog * prog , union bpf_attr * attr )
{
struct net * net = current - > nsproxy - > net_ns ;
struct bpf_dev_offload * offload ;
2017-11-21 02:21:52 +03:00
if ( attr - > prog_type ! = BPF_PROG_TYPE_SCHED_CLS & &
attr - > prog_type ! = BPF_PROG_TYPE_XDP )
return - EINVAL ;
2017-11-03 23:56:17 +03:00
if ( attr - > prog_flags )
return - EINVAL ;
offload = kzalloc ( sizeof ( * offload ) , GFP_USER ) ;
if ( ! offload )
return - ENOMEM ;
offload - > prog = prog ;
init_waitqueue_head ( & offload - > verifier_done ) ;
rtnl_lock ( ) ;
2017-11-21 02:21:53 +03:00
offload - > netdev = __dev_get_by_index ( net , attr - > prog_ifindex ) ;
2017-11-03 23:56:17 +03:00
if ( ! offload - > netdev ) {
rtnl_unlock ( ) ;
kfree ( offload ) ;
return - EINVAL ;
}
prog - > aux - > offload = offload ;
list_add_tail ( & offload - > offloads , & bpf_prog_offload_devs ) ;
rtnl_unlock ( ) ;
return 0 ;
}
static int __bpf_offload_ndo ( struct bpf_prog * prog , enum bpf_netdev_command cmd ,
struct netdev_bpf * data )
{
struct net_device * netdev = prog - > aux - > offload - > netdev ;
ASSERT_RTNL ( ) ;
if ( ! netdev )
return - ENODEV ;
if ( ! netdev - > netdev_ops - > ndo_bpf )
return - EOPNOTSUPP ;
data - > command = cmd ;
return netdev - > netdev_ops - > ndo_bpf ( netdev , data ) ;
}
int bpf_prog_offload_verifier_prep ( struct bpf_verifier_env * env )
{
struct netdev_bpf data = { } ;
int err ;
data . verifier . prog = env - > prog ;
rtnl_lock ( ) ;
err = __bpf_offload_ndo ( env - > prog , BPF_OFFLOAD_VERIFIER_PREP , & data ) ;
if ( err )
goto exit_unlock ;
env - > dev_ops = data . verifier . ops ;
env - > prog - > aux - > offload - > dev_state = true ;
env - > prog - > aux - > offload - > verifier_running = true ;
exit_unlock :
rtnl_unlock ( ) ;
return err ;
}
static void __bpf_prog_offload_destroy ( struct bpf_prog * prog )
{
struct bpf_dev_offload * offload = prog - > aux - > offload ;
struct netdev_bpf data = { } ;
2017-11-21 02:21:51 +03:00
/* Caution - if netdev is destroyed before the program, this function
* will be called twice .
*/
2017-11-03 23:56:17 +03:00
data . offload . prog = prog ;
if ( offload - > verifier_running )
wait_event ( offload - > verifier_done , ! offload - > verifier_running ) ;
if ( offload - > dev_state )
WARN_ON ( __bpf_offload_ndo ( prog , BPF_OFFLOAD_DESTROY , & data ) ) ;
offload - > dev_state = false ;
list_del_init ( & offload - > offloads ) ;
offload - > netdev = NULL ;
}
void bpf_prog_offload_destroy ( struct bpf_prog * prog )
{
struct bpf_dev_offload * offload = prog - > aux - > offload ;
offload - > verifier_running = false ;
wake_up ( & offload - > verifier_done ) ;
rtnl_lock ( ) ;
__bpf_prog_offload_destroy ( prog ) ;
rtnl_unlock ( ) ;
kfree ( offload ) ;
}
static int bpf_prog_offload_translate ( struct bpf_prog * prog )
{
struct bpf_dev_offload * offload = prog - > aux - > offload ;
struct netdev_bpf data = { } ;
int ret ;
data . offload . prog = prog ;
offload - > verifier_running = false ;
wake_up ( & offload - > verifier_done ) ;
rtnl_lock ( ) ;
ret = __bpf_offload_ndo ( prog , BPF_OFFLOAD_TRANSLATE , & data ) ;
rtnl_unlock ( ) ;
return ret ;
}
static unsigned int bpf_prog_warn_on_exec ( const void * ctx ,
const struct bpf_insn * insn )
{
WARN ( 1 , " attempt to execute device eBPF program on the host! " ) ;
return 0 ;
}
int bpf_prog_offload_compile ( struct bpf_prog * prog )
{
prog - > bpf_func = bpf_prog_warn_on_exec ;
return bpf_prog_offload_translate ( prog ) ;
}
const struct bpf_prog_ops bpf_offload_prog_ops = {
} ;
static int bpf_offload_notification ( struct notifier_block * notifier ,
ulong event , void * ptr )
{
struct net_device * netdev = netdev_notifier_info_to_dev ( ptr ) ;
struct bpf_dev_offload * offload , * tmp ;
ASSERT_RTNL ( ) ;
switch ( event ) {
case NETDEV_UNREGISTER :
2017-11-21 02:21:57 +03:00
/* ignore namespace changes */
if ( netdev - > reg_state ! = NETREG_UNREGISTERING )
break ;
2017-11-03 23:56:17 +03:00
list_for_each_entry_safe ( offload , tmp , & bpf_prog_offload_devs ,
offloads ) {
if ( offload - > netdev = = netdev )
__bpf_prog_offload_destroy ( offload - > prog ) ;
}
break ;
default :
break ;
}
return NOTIFY_OK ;
}
static struct notifier_block bpf_offload_notifier = {
. notifier_call = bpf_offload_notification ,
} ;
static int __init bpf_offload_init ( void )
{
register_netdevice_notifier ( & bpf_offload_notifier ) ;
return 0 ;
}
subsys_initcall ( bpf_offload_init ) ;