X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fnet%2Fifb.c;h=ccbd6554f6eb922e1211f12fdd74bf028287e13d;hb=2aec609fb45e84d65bc8eabc7b650bbecb1cc179;hp=819945e3b33095bfde56e38a205dcd1f1e1c66bd;hpb=62b7ffcaaa4e91ed547fc55758076ac536bd5571;p=linux-2.6 diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index 819945e3b3..ccbd6554f6 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -33,16 +33,14 @@ #include #include #include -#include #include +#include +#include #define TX_TIMEOUT (2*HZ) #define TX_Q_LIMIT 32 struct ifb_private { - struct list_head list; - struct net_device *dev; - struct net_device_stats stats; struct tasklet_struct ifb_tasklet; int tasklet_pending; /* mostly debug stats leave in for now */ @@ -63,7 +61,6 @@ static int numifbs = 2; static void ri_tasklet(unsigned long dev); static int ifb_xmit(struct sk_buff *skb, struct net_device *dev); -static struct net_device_stats *ifb_get_stats(struct net_device *dev); static int ifb_open(struct net_device *dev); static int ifb_close(struct net_device *dev); @@ -72,7 +69,7 @@ static void ri_tasklet(unsigned long dev) struct net_device *_dev = (struct net_device *)dev; struct ifb_private *dp = netdev_priv(_dev); - struct net_device_stats *stats = &dp->stats; + struct net_device_stats *stats = &_dev->stats; struct sk_buff *skb; dp->st_task_enter++; @@ -100,7 +97,7 @@ static void ri_tasklet(unsigned long dev) stats->tx_packets++; stats->tx_bytes +=skb->len; - skb->dev = __dev_get_by_index(skb->iif); + skb->dev = __dev_get_by_index(&init_net, skb->iif); if (!skb->dev) { dev_kfree_skb(skb); stats->tx_dropped++; @@ -139,13 +136,13 @@ resched: } -static void __init ifb_setup(struct net_device *dev) +static void ifb_setup(struct net_device *dev) { /* Initialize the device structure. */ - dev->get_stats = ifb_get_stats; dev->hard_start_xmit = ifb_xmit; dev->open = &ifb_open; dev->stop = &ifb_close; + dev->destructor = free_netdev; /* Fill in device structure with ethernet-generic values. */ ether_setup(dev); @@ -153,14 +150,13 @@ static void __init ifb_setup(struct net_device *dev) dev->change_mtu = NULL; dev->flags |= IFF_NOARP; dev->flags &= ~IFF_MULTICAST; - SET_MODULE_OWNER(dev); random_ether_addr(dev->dev_addr); } static int ifb_xmit(struct sk_buff *skb, struct net_device *dev) { struct ifb_private *dp = netdev_priv(dev); - struct net_device_stats *stats = &dp->stats; + struct net_device_stats *stats = &dev->stats; int ret = 0; u32 from = G_TC_FROM(skb->tc_verd); @@ -187,25 +183,6 @@ static int ifb_xmit(struct sk_buff *skb, struct net_device *dev) return ret; } -static struct net_device_stats *ifb_get_stats(struct net_device *dev) -{ - struct ifb_private *dp = netdev_priv(dev); - struct net_device_stats *stats = &dp->stats; - - pr_debug("tasklets stats %ld:%ld:%ld:%ld:%ld:%ld:%ld:%ld:%ld \n", - dp->st_task_enter, dp->st_txq_refl_try, dp->st_rxq_enter, - dp->st_rx2tx_tran, dp->st_rxq_notenter, dp->st_rx_frm_egr, - dp->st_rx_frm_ing, dp->st_rxq_check, dp->st_rxq_rsch); - - return stats; -} - -static LIST_HEAD(ifbs); - -/* Number of ifb devices to be set up by this module. */ -module_param(numifbs, int, 0); -MODULE_PARM_DESC(numifbs, "Number of ifb devices"); - static int ifb_close(struct net_device *dev) { struct ifb_private *dp = netdev_priv(dev); @@ -229,10 +206,41 @@ static int ifb_open(struct net_device *dev) return 0; } +static int ifb_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + return -EINVAL; + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + return -EADDRNOTAVAIL; + } + return 0; +} + +static struct rtnl_link_ops ifb_link_ops __read_mostly = { + .kind = "ifb", + .priv_size = sizeof(struct ifb_private), + .setup = ifb_setup, + .validate = ifb_validate, +}; + +/* Number of ifb devices to be set up by this module. */ +module_param(numifbs, int, 0); +MODULE_PARM_DESC(numifbs, "Number of ifb devices"); + +/* + * dev_ifb->tx_queue.lock is usually taken after dev->rx_queue.lock, + * reversely to e.g. qdisc_lock_tree(). It should be safe until + * ifb doesn't take dev->tx_queue.lock with dev_ifb->rx_queue.lock. + * But lockdep should know that ifb has different locks from dev. + */ +static struct lock_class_key ifb_tx_queue_lock_key; +static struct lock_class_key ifb_rx_queue_lock_key; + + static int __init ifb_init_one(int index) { struct net_device *dev_ifb; - struct ifb_private *priv; int err; dev_ifb = alloc_netdev(sizeof(struct ifb_private), @@ -241,51 +249,48 @@ static int __init ifb_init_one(int index) if (!dev_ifb) return -ENOMEM; - if ((err = register_netdev(dev_ifb))) { - free_netdev(dev_ifb); - dev_ifb = NULL; - } else { - priv = netdev_priv(dev_ifb); - priv->dev = dev_ifb; - list_add_tail(&priv->list, &ifbs); - } + err = dev_alloc_name(dev_ifb, dev_ifb->name); + if (err < 0) + goto err; - return err; -} + dev_ifb->rtnl_link_ops = &ifb_link_ops; + err = register_netdevice(dev_ifb); + if (err < 0) + goto err; -static void ifb_free_one(struct net_device *dev) -{ - struct ifb_private *priv = netdev_priv(dev); + lockdep_set_class(&dev_ifb->tx_queue.lock, &ifb_tx_queue_lock_key); + lockdep_set_class(&dev_ifb->rx_queue.lock, &ifb_rx_queue_lock_key); + + return 0; - list_del(&priv->list); - unregister_netdev(dev); - free_netdev(dev); +err: + free_netdev(dev_ifb); + return err; } static int __init ifb_init_module(void) { - struct ifb_private *priv, *next; - int i, err = 0; + int i, err; + + rtnl_lock(); + err = __rtnl_link_register(&ifb_link_ops); for (i = 0; i < numifbs && !err; i++) err = ifb_init_one(i); - if (err) { - list_for_each_entry_safe(priv, next, &ifbs, list) - ifb_free_one(priv->dev); - } + if (err) + __rtnl_link_unregister(&ifb_link_ops); + rtnl_unlock(); return err; } static void __exit ifb_cleanup_module(void) { - struct ifb_private *priv, *next; - - list_for_each_entry_safe(priv, next, &ifbs, list) - ifb_free_one(priv->dev); + rtnl_link_unregister(&ifb_link_ops); } module_init(ifb_init_module); module_exit(ifb_cleanup_module); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jamal Hadi Salim"); +MODULE_ALIAS_RTNL_LINK("ifb");