2007-07-09 22:23:51 -07:00
/* sunvnet.c: Sun LDOM Virtual Network Driver.
*
2008-09-01 01:48:52 -07:00
* Copyright ( C ) 2007 , 2008 David S . Miller < davem @ davemloft . net >
2017-03-14 10:24:40 -07:00
* Copyright ( C ) 2016 - 2017 Oracle . All rights reserved .
2007-07-09 22:23:51 -07:00
*/
2010-08-17 07:55:05 +00:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2007-07-09 22:23:51 -07:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/netdevice.h>
# include <linux/ethtool.h>
# include <linux/etherdevice.h>
2007-07-17 22:19:10 -07:00
# include <linux/mutex.h>
2014-12-02 15:31:13 -05:00
# include <linux/highmem.h>
2014-09-29 19:47:59 -04:00
# include <linux/if_vlan.h>
2007-07-09 22:23:51 -07:00
2014-09-29 19:48:24 -04:00
# if IS_ENABLED(CONFIG_IPV6)
# include <linux/icmpv6.h>
# endif
2014-12-02 15:31:04 -05:00
# include <net/ip.h>
2014-09-29 19:48:24 -04:00
# include <net/icmp.h>
# include <net/route.h>
2007-07-09 22:23:51 -07:00
# include <asm/vio.h>
# include <asm/ldc.h>
2016-03-15 11:35:37 -07:00
# include "sunvnet_common.h"
/* length of time before we decide the hardware is borked,
* and dev - > tx_timeout ( ) should be called to fix the problem
*/
# define VNET_TX_TIMEOUT (5 * HZ)
2007-07-09 22:23:51 -07:00
# define DRV_MODULE_NAME "sunvnet"
2017-02-13 10:56:59 -08:00
# define DRV_MODULE_VERSION "2.0"
# define DRV_MODULE_RELDATE "February 3, 2017"
2007-07-09 22:23:51 -07:00
2012-12-03 09:24:02 -05:00
static char version [ ] =
2017-02-13 10:56:59 -08:00
DRV_MODULE_NAME " " DRV_MODULE_VERSION " ( " DRV_MODULE_RELDATE " ) " ;
2007-07-09 22:23:51 -07:00
MODULE_AUTHOR ( " David S. Miller (davem@davemloft.net) " ) ;
MODULE_DESCRIPTION ( " Sun LDOM virtual network driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRV_MODULE_VERSION ) ;
/* Ordered from largest major to lowest */
static struct vio_version vnet_versions [ ] = {
2014-12-02 15:31:04 -05:00
{ . major = 1 , . minor = 8 } ,
{ . major = 1 , . minor = 7 } ,
2014-09-29 19:47:59 -04:00
{ . major = 1 , . minor = 6 } ,
2007-07-09 22:23:51 -07:00
{ . major = 1 , . minor = 0 } ,
} ;
static void vnet_get_drvinfo ( struct net_device * dev ,
struct ethtool_drvinfo * info )
{
2013-01-06 00:44:26 +00:00
strlcpy ( info - > driver , DRV_MODULE_NAME , sizeof ( info - > driver ) ) ;
strlcpy ( info - > version , DRV_MODULE_VERSION , sizeof ( info - > version ) ) ;
2007-07-09 22:23:51 -07:00
}
static u32 vnet_get_msglevel ( struct net_device * dev )
{
struct vnet * vp = netdev_priv ( dev ) ;
2016-03-15 11:35:40 -07:00
2007-07-09 22:23:51 -07:00
return vp - > msg_enable ;
}
static void vnet_set_msglevel ( struct net_device * dev , u32 value )
{
struct vnet * vp = netdev_priv ( dev ) ;
2016-03-15 11:35:40 -07:00
2007-07-09 22:23:51 -07:00
vp - > msg_enable = value ;
}
2017-03-14 10:24:40 -07:00
static const struct {
const char string [ ETH_GSTRING_LEN ] ;
} ethtool_stats_keys [ ] = {
{ " rx_packets " } ,
{ " tx_packets " } ,
{ " rx_bytes " } ,
{ " tx_bytes " } ,
{ " rx_errors " } ,
{ " tx_errors " } ,
{ " rx_dropped " } ,
{ " tx_dropped " } ,
{ " multicast " } ,
{ " rx_length_errors " } ,
{ " rx_frame_errors " } ,
{ " rx_missed_errors " } ,
{ " tx_carrier_errors " } ,
{ " nports " } ,
} ;
static int vnet_get_sset_count ( struct net_device * dev , int sset )
{
struct vnet * vp = ( struct vnet * ) netdev_priv ( dev ) ;
switch ( sset ) {
case ETH_SS_STATS :
return ARRAY_SIZE ( ethtool_stats_keys )
+ ( NUM_VNET_PORT_STATS * vp - > nports ) ;
default :
return - EOPNOTSUPP ;
}
}
static void vnet_get_strings ( struct net_device * dev , u32 stringset , u8 * buf )
{
struct vnet * vp = ( struct vnet * ) netdev_priv ( dev ) ;
struct vnet_port * port ;
char * p = ( char * ) buf ;
switch ( stringset ) {
case ETH_SS_STATS :
memcpy ( buf , & ethtool_stats_keys , sizeof ( ethtool_stats_keys ) ) ;
p + = sizeof ( ethtool_stats_keys ) ;
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( port , & vp - > port_list , list ) {
snprintf ( p , ETH_GSTRING_LEN , " p%u.%s-%pM " ,
port - > q_index , port - > switch_port ? " s " : " q " ,
port - > raddr ) ;
p + = ETH_GSTRING_LEN ;
snprintf ( p , ETH_GSTRING_LEN , " p%u.rx_packets " ,
port - > q_index ) ;
p + = ETH_GSTRING_LEN ;
snprintf ( p , ETH_GSTRING_LEN , " p%u.tx_packets " ,
port - > q_index ) ;
p + = ETH_GSTRING_LEN ;
snprintf ( p , ETH_GSTRING_LEN , " p%u.rx_bytes " ,
port - > q_index ) ;
p + = ETH_GSTRING_LEN ;
snprintf ( p , ETH_GSTRING_LEN , " p%u.tx_bytes " ,
port - > q_index ) ;
p + = ETH_GSTRING_LEN ;
snprintf ( p , ETH_GSTRING_LEN , " p%u.event_up " ,
port - > q_index ) ;
p + = ETH_GSTRING_LEN ;
snprintf ( p , ETH_GSTRING_LEN , " p%u.event_reset " ,
port - > q_index ) ;
p + = ETH_GSTRING_LEN ;
}
rcu_read_unlock ( ) ;
break ;
default :
WARN_ON ( 1 ) ;
break ;
}
}
static void vnet_get_ethtool_stats ( struct net_device * dev ,
struct ethtool_stats * estats , u64 * data )
{
struct vnet * vp = ( struct vnet * ) netdev_priv ( dev ) ;
struct vnet_port * port ;
int i = 0 ;
data [ i + + ] = dev - > stats . rx_packets ;
data [ i + + ] = dev - > stats . tx_packets ;
data [ i + + ] = dev - > stats . rx_bytes ;
data [ i + + ] = dev - > stats . tx_bytes ;
data [ i + + ] = dev - > stats . rx_errors ;
data [ i + + ] = dev - > stats . tx_errors ;
data [ i + + ] = dev - > stats . rx_dropped ;
data [ i + + ] = dev - > stats . tx_dropped ;
data [ i + + ] = dev - > stats . multicast ;
data [ i + + ] = dev - > stats . rx_length_errors ;
data [ i + + ] = dev - > stats . rx_frame_errors ;
data [ i + + ] = dev - > stats . rx_missed_errors ;
data [ i + + ] = dev - > stats . tx_carrier_errors ;
data [ i + + ] = vp - > nports ;
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( port , & vp - > port_list , list ) {
data [ i + + ] = port - > q_index ;
data [ i + + ] = port - > stats . rx_packets ;
data [ i + + ] = port - > stats . tx_packets ;
data [ i + + ] = port - > stats . rx_bytes ;
data [ i + + ] = port - > stats . tx_bytes ;
data [ i + + ] = port - > stats . event_up ;
data [ i + + ] = port - > stats . event_reset ;
}
rcu_read_unlock ( ) ;
}
2007-07-09 22:23:51 -07:00
static const struct ethtool_ops vnet_ethtool_ops = {
. get_drvinfo = vnet_get_drvinfo ,
. get_msglevel = vnet_get_msglevel ,
. set_msglevel = vnet_set_msglevel ,
. get_link = ethtool_op_get_link ,
2017-03-14 10:24:40 -07:00
. get_sset_count = vnet_get_sset_count ,
. get_strings = vnet_get_strings ,
. get_ethtool_stats = vnet_get_ethtool_stats ,
2007-07-09 22:23:51 -07:00
} ;
2007-07-17 22:19:10 -07:00
static LIST_HEAD ( vnet_list ) ;
static DEFINE_MUTEX ( vnet_list_mutex ) ;
2016-03-15 11:35:38 -07:00
static struct vnet_port * __tx_port_find ( struct vnet * vp , struct sk_buff * skb )
{
unsigned int hash = vnet_hashfn ( skb - > data ) ;
struct hlist_head * hp = & vp - > port_hash [ hash ] ;
struct vnet_port * port ;
hlist_for_each_entry_rcu ( port , hp , hash ) {
if ( ! sunvnet_port_is_up_common ( port ) )
continue ;
if ( ether_addr_equal ( port - > raddr , skb - > data ) )
return port ;
}
list_for_each_entry_rcu ( port , & vp - > port_list , list ) {
if ( ! port - > switch_port )
continue ;
if ( ! sunvnet_port_is_up_common ( port ) )
continue ;
return port ;
}
return NULL ;
}
/* func arg to vnet_start_xmit_common() to get the proper tx port */
static struct vnet_port * vnet_tx_port_find ( struct sk_buff * skb ,
struct net_device * dev )
{
struct vnet * vp = netdev_priv ( dev ) ;
return __tx_port_find ( vp , skb ) ;
}
static u16 vnet_select_queue ( struct net_device * dev , struct sk_buff * skb ,
void * accel_priv , select_queue_fallback_t fallback )
{
struct vnet * vp = netdev_priv ( dev ) ;
struct vnet_port * port = __tx_port_find ( vp , skb ) ;
if ( ! port )
return 0 ;
return port - > q_index ;
}
/* Wrappers to common functions */
static int vnet_start_xmit ( struct sk_buff * skb , struct net_device * dev )
{
return sunvnet_start_xmit_common ( skb , dev , vnet_tx_port_find ) ;
}
static void vnet_set_rx_mode ( struct net_device * dev )
{
struct vnet * vp = netdev_priv ( dev ) ;
return sunvnet_set_rx_mode_common ( dev , vp ) ;
}
# ifdef CONFIG_NET_POLL_CONTROLLER
static void vnet_poll_controller ( struct net_device * dev )
{
struct vnet * vp = netdev_priv ( dev ) ;
return sunvnet_poll_controller_common ( dev , vp ) ;
}
# endif
2009-03-20 00:51:22 -07:00
static const struct net_device_ops vnet_ops = {
2016-03-15 11:35:37 -07:00
. ndo_open = sunvnet_open_common ,
. ndo_stop = sunvnet_close_common ,
2016-03-15 11:35:38 -07:00
. ndo_set_rx_mode = vnet_set_rx_mode ,
2016-03-15 11:35:37 -07:00
. ndo_set_mac_address = sunvnet_set_mac_addr_common ,
2009-07-09 17:54:35 +00:00
. ndo_validate_addr = eth_validate_addr ,
2016-03-15 11:35:37 -07:00
. ndo_tx_timeout = sunvnet_tx_timeout_common ,
2016-03-15 11:35:38 -07:00
. ndo_start_xmit = vnet_start_xmit ,
. ndo_select_queue = vnet_select_queue ,
2014-10-25 15:12:12 -04:00
# ifdef CONFIG_NET_POLL_CONTROLLER
2016-03-15 11:35:38 -07:00
. ndo_poll_controller = vnet_poll_controller ,
2014-10-25 15:12:12 -04:00
# endif
2009-03-20 00:51:22 -07:00
} ;
2015-09-18 17:47:55 -04:00
static struct vnet * vnet_new ( const u64 * local_mac ,
struct vio_dev * vdev )
2007-07-17 22:19:10 -07:00
{
struct net_device * dev ;
struct vnet * vp ;
int err , i ;
2014-10-30 12:46:09 -04:00
dev = alloc_etherdev_mqs ( sizeof ( * vp ) , VNET_MAX_TXQS , 1 ) ;
2012-01-29 13:47:52 +00:00
if ( ! dev )
2007-07-17 22:19:10 -07:00
return ERR_PTR ( - ENOMEM ) ;
2014-09-29 19:48:11 -04:00
dev - > needed_headroom = VNET_PACKET_SKIP + 8 ;
dev - > needed_tailroom = 8 ;
2007-07-17 22:19:10 -07:00
for ( i = 0 ; i < ETH_ALEN ; i + + )
dev - > dev_addr [ i ] = ( * local_mac > > ( 5 - i ) * 8 ) & 0xff ;
vp = netdev_priv ( dev ) ;
spin_lock_init ( & vp - > lock ) ;
vp - > dev = dev ;
INIT_LIST_HEAD ( & vp - > port_list ) ;
for ( i = 0 ; i < VNET_PORT_HASH_SIZE ; i + + )
INIT_HLIST_HEAD ( & vp - > port_hash [ i ] ) ;
INIT_LIST_HEAD ( & vp - > list ) ;
vp - > local_mac = * local_mac ;
2009-03-20 00:51:22 -07:00
dev - > netdev_ops = & vnet_ops ;
2007-07-17 22:19:10 -07:00
dev - > ethtool_ops = & vnet_ethtool_ops ;
dev - > watchdog_timeo = VNET_TX_TIMEOUT ;
2014-12-02 15:31:38 -05:00
dev - > hw_features = NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GSO_SOFTWARE |
2014-12-02 15:31:30 -05:00
NETIF_F_HW_CSUM | NETIF_F_SG ;
2014-12-02 15:31:13 -05:00
dev - > features = dev - > hw_features ;
2016-10-17 15:54:10 -04:00
/* MTU range: 68 - 65535 */
dev - > min_mtu = ETH_MIN_MTU ;
dev - > max_mtu = VNET_MAX_MTU ;
2015-09-18 17:47:55 -04:00
SET_NETDEV_DEV ( dev , & vdev - > dev ) ;
2007-07-17 22:19:10 -07:00
err = register_netdev ( dev ) ;
if ( err ) {
2010-08-17 07:55:05 +00:00
pr_err ( " Cannot register net device, aborting \n " ) ;
2007-07-17 22:19:10 -07:00
goto err_out_free_dev ;
}
2010-08-17 07:55:05 +00:00
netdev_info ( dev , " Sun LDOM vnet %pM \n " , dev - > dev_addr ) ;
2007-07-17 22:19:10 -07:00
list_add ( & vp - > list , & vnet_list ) ;
return vp ;
err_out_free_dev :
free_netdev ( dev ) ;
return ERR_PTR ( err ) ;
}
2015-09-18 17:47:55 -04:00
static struct vnet * vnet_find_or_create ( const u64 * local_mac ,
struct vio_dev * vdev )
2007-07-17 22:19:10 -07:00
{
struct vnet * iter , * vp ;
mutex_lock ( & vnet_list_mutex ) ;
vp = NULL ;
list_for_each_entry ( iter , & vnet_list , list ) {
if ( iter - > local_mac = = * local_mac ) {
vp = iter ;
break ;
}
}
if ( ! vp )
2015-09-18 17:47:55 -04:00
vp = vnet_new ( local_mac , vdev ) ;
2007-07-17 22:19:10 -07:00
mutex_unlock ( & vnet_list_mutex ) ;
return vp ;
}
2014-07-16 10:02:26 -04:00
static void vnet_cleanup ( void )
{
struct vnet * vp ;
struct net_device * dev ;
mutex_lock ( & vnet_list_mutex ) ;
while ( ! list_empty ( & vnet_list ) ) {
vp = list_first_entry ( & vnet_list , struct vnet , list ) ;
list_del ( & vp - > list ) ;
dev = vp - > dev ;
/* vio_unregister_driver() should have cleaned up port_list */
BUG_ON ( ! list_empty ( & vp - > port_list ) ) ;
unregister_netdev ( dev ) ;
free_netdev ( dev ) ;
}
mutex_unlock ( & vnet_list_mutex ) ;
}
2007-07-17 22:19:10 -07:00
static const char * local_mac_prop = " local-mac-address " ;
2012-12-03 09:24:02 -05:00
static struct vnet * vnet_find_parent ( struct mdesc_handle * hp ,
2015-09-18 17:47:55 -04:00
u64 port_node ,
struct vio_dev * vdev )
2007-07-17 22:19:10 -07:00
{
const u64 * local_mac = NULL ;
u64 a ;
mdesc_for_each_arc ( a , hp , port_node , MDESC_ARC_TYPE_BACK ) {
u64 target = mdesc_arc_target ( hp , a ) ;
const char * name ;
name = mdesc_get_property ( hp , target , " name " , NULL ) ;
if ( ! name | | strcmp ( name , " network " ) )
continue ;
local_mac = mdesc_get_property ( hp , target ,
local_mac_prop , NULL ) ;
if ( local_mac )
break ;
}
if ( ! local_mac )
return ERR_PTR ( - ENODEV ) ;
2015-09-18 17:47:55 -04:00
return vnet_find_or_create ( local_mac , vdev ) ;
2007-07-17 22:19:10 -07:00
}
2007-07-09 22:23:51 -07:00
static struct ldc_channel_config vnet_ldc_cfg = {
2016-03-15 11:35:37 -07:00
. event = sunvnet_event_common ,
2007-07-09 22:23:51 -07:00
. mtu = 64 ,
. mode = LDC_MODE_UNRELIABLE ,
} ;
static struct vio_driver_ops vnet_vio_ops = {
2016-03-15 11:35:37 -07:00
. send_attr = sunvnet_send_attr_common ,
. handle_attr = sunvnet_handle_attr_common ,
. handshake_complete = sunvnet_handshake_complete_common ,
2007-07-09 22:23:51 -07:00
} ;
const char * remote_macaddr_prop = " remote-mac-address " ;
2012-12-06 14:30:56 +00:00
static int vnet_port_probe ( struct vio_dev * vdev , const struct vio_device_id * id )
2007-07-09 22:23:51 -07:00
{
2007-07-12 13:47:50 -07:00
struct mdesc_handle * hp ;
2007-07-09 22:23:51 -07:00
struct vnet_port * port ;
unsigned long flags ;
struct vnet * vp ;
const u64 * rmac ;
int len , i , err , switch_port ;
2007-07-12 13:47:50 -07:00
hp = mdesc_grab ( ) ;
2015-09-18 17:47:55 -04:00
vp = vnet_find_parent ( hp , vdev - > mp , vdev ) ;
2007-07-17 22:19:10 -07:00
if ( IS_ERR ( vp ) ) {
2010-08-17 07:55:05 +00:00
pr_err ( " Cannot find port parent vnet \n " ) ;
2007-07-17 22:19:10 -07:00
err = PTR_ERR ( vp ) ;
goto err_out_put_mdesc ;
}
2007-07-12 13:47:50 -07:00
rmac = mdesc_get_property ( hp , vdev - > mp , remote_macaddr_prop , & len ) ;
err = - ENODEV ;
2007-07-09 22:23:51 -07:00
if ( ! rmac ) {
2010-08-17 07:55:05 +00:00
pr_err ( " Port lacks %s property \n " , remote_macaddr_prop ) ;
2007-07-12 13:47:50 -07:00
goto err_out_put_mdesc ;
2007-07-09 22:23:51 -07:00
}
port = kzalloc ( sizeof ( * port ) , GFP_KERNEL ) ;
2007-07-12 13:47:50 -07:00
err = - ENOMEM ;
2012-01-29 12:56:23 +00:00
if ( ! port )
2007-07-12 13:47:50 -07:00
goto err_out_put_mdesc ;
2007-07-09 22:23:51 -07:00
for ( i = 0 ; i < ETH_ALEN ; i + + )
port - > raddr [ i ] = ( * rmac > > ( 5 - i ) * 8 ) & 0xff ;
port - > vp = vp ;
2007-07-12 13:47:50 -07:00
err = vio_driver_init ( & port - > vio , vdev , VDEV_NETWORK ,
2007-07-09 22:23:51 -07:00
vnet_versions , ARRAY_SIZE ( vnet_versions ) ,
& vnet_vio_ops , vp - > dev - > name ) ;
if ( err )
goto err_out_free_port ;
err = vio_ldc_alloc ( & port - > vio , & vnet_ldc_cfg , port ) ;
if ( err )
goto err_out_free_port ;
2016-03-15 11:35:37 -07:00
netif_napi_add ( port - > vp - > dev , & port - > napi , sunvnet_poll_common ,
NAPI_POLL_WEIGHT ) ;
2014-10-25 15:12:12 -04:00
2007-07-09 22:23:51 -07:00
INIT_HLIST_NODE ( & port - > hash ) ;
INIT_LIST_HEAD ( & port - > list ) ;
switch_port = 0 ;
2016-03-15 11:35:40 -07:00
if ( mdesc_get_property ( hp , vdev - > mp , " switch-port " , NULL ) )
2007-07-09 22:23:51 -07:00
switch_port = 1 ;
2007-07-20 02:30:25 -07:00
port - > switch_port = switch_port ;
2014-12-02 15:31:38 -05:00
port - > tso = true ;
port - > tsolen = 0 ;
2007-07-09 22:23:51 -07:00
spin_lock_irqsave ( & vp - > lock , flags ) ;
if ( switch_port )
2014-10-25 15:12:20 -04:00
list_add_rcu ( & port - > list , & vp - > port_list ) ;
2007-07-09 22:23:51 -07:00
else
2014-10-25 15:12:20 -04:00
list_add_tail_rcu ( & port - > list , & vp - > port_list ) ;
hlist_add_head_rcu ( & port - > hash ,
& vp - > port_hash [ vnet_hashfn ( port - > raddr ) ] ) ;
2016-03-15 11:35:37 -07:00
sunvnet_port_add_txq_common ( port ) ;
2007-07-09 22:23:51 -07:00
spin_unlock_irqrestore ( & vp - > lock , flags ) ;
dev_set_drvdata ( & vdev - > dev , port ) ;
2010-08-17 07:55:05 +00:00
pr_info ( " %s: PORT ( remote-mac %pM%s ) \n " ,
vp - > dev - > name , port - > raddr , switch_port ? " switch-port " : " " ) ;
2007-07-09 22:23:51 -07:00
2016-03-15 11:35:37 -07:00
setup_timer ( & port - > clean_timer , sunvnet_clean_timer_expire_common ,
2014-09-29 19:48:11 -04:00
( unsigned long ) port ) ;
2014-10-25 15:12:12 -04:00
napi_enable ( & port - > napi ) ;
2007-07-09 22:23:51 -07:00
vio_port_up ( & port - > vio ) ;
2007-07-12 13:47:50 -07:00
mdesc_release ( hp ) ;
2007-07-09 22:23:51 -07:00
return 0 ;
err_out_free_port :
kfree ( port ) ;
2007-07-12 13:47:50 -07:00
err_out_put_mdesc :
mdesc_release ( hp ) ;
2007-07-09 22:23:51 -07:00
return err ;
}
static int vnet_port_remove ( struct vio_dev * vdev )
{
struct vnet_port * port = dev_get_drvdata ( & vdev - > dev ) ;
if ( port ) {
del_timer_sync ( & port - > vio . timer ) ;
2014-10-25 15:12:12 -04:00
napi_disable ( & port - > napi ) ;
2007-07-09 22:23:51 -07:00
2014-10-25 15:12:20 -04:00
list_del_rcu ( & port - > list ) ;
hlist_del_rcu ( & port - > hash ) ;
synchronize_rcu ( ) ;
del_timer_sync ( & port - > clean_timer ) ;
2016-03-15 11:35:37 -07:00
sunvnet_port_rm_txq_common ( port ) ;
2014-10-25 15:12:12 -04:00
netif_napi_del ( & port - > napi ) ;
2016-03-15 11:35:37 -07:00
sunvnet_port_free_tx_bufs_common ( port ) ;
2007-07-09 22:23:51 -07:00
vio_ldc_free ( & port - > vio ) ;
dev_set_drvdata ( & vdev - > dev , NULL ) ;
kfree ( port ) ;
}
return 0 ;
}
2008-09-01 01:48:52 -07:00
static const struct vio_device_id vnet_port_match [ ] = {
2007-07-09 22:23:51 -07:00
{
. type = " vnet-port " ,
} ,
{ } ,
} ;
2007-07-18 14:35:23 -07:00
MODULE_DEVICE_TABLE ( vio , vnet_port_match ) ;
2007-07-09 22:23:51 -07:00
static struct vio_driver vnet_port_driver = {
. id_table = vnet_port_match ,
. probe = vnet_port_probe ,
. remove = vnet_port_remove ,
2012-03-26 19:06:30 +00:00
. name = " vnet_port " ,
2007-07-09 22:23:51 -07:00
} ;
static int __init vnet_init ( void )
{
2017-02-13 10:56:59 -08:00
pr_info ( " %s \n " , version ) ;
2007-07-17 22:19:10 -07:00
return vio_register_driver ( & vnet_port_driver ) ;
2007-07-09 22:23:51 -07:00
}
static void __exit vnet_exit ( void )
{
vio_unregister_driver ( & vnet_port_driver ) ;
2014-07-16 10:02:26 -04:00
vnet_cleanup ( ) ;
2007-07-09 22:23:51 -07:00
}
module_init ( vnet_init ) ;
module_exit ( vnet_exit ) ;