linux/drivers/net/ifb.c
<<
>>
Prefs
   1/* drivers/net/ifb.c:
   2
   3        The purpose of this driver is to provide a device that allows
   4        for sharing of resources:
   5
   6        1) qdiscs/policies that are per device as opposed to system wide.
   7        ifb allows for a device which can be redirected to thus providing
   8        an impression of sharing.
   9
  10        2) Allows for queueing incoming traffic for shaping instead of
  11        dropping.
  12
  13        The original concept is based on what is known as the IMQ
  14        driver initially written by Martin Devera, later rewritten
  15        by Patrick McHardy and then maintained by Andre Correa.
  16
  17        You need the tc action  mirror or redirect to feed this device
  18        packets.
  19
  20        This program is free software; you can redistribute it and/or
  21        modify it under the terms of the GNU General Public License
  22        as published by the Free Software Foundation; either version
  23        2 of the License, or (at your option) any later version.
  24
  25        Authors:        Jamal Hadi Salim (2005)
  26
  27*/
  28
  29
  30#include <linux/module.h>
  31#include <linux/kernel.h>
  32#include <linux/netdevice.h>
  33#include <linux/etherdevice.h>
  34#include <linux/init.h>
  35#include <linux/moduleparam.h>
  36#include <net/pkt_sched.h>
  37#include <net/net_namespace.h>
  38
  39#define TX_TIMEOUT  (2*HZ)
  40
  41#define TX_Q_LIMIT    32
  42struct ifb_private {
  43        struct tasklet_struct   ifb_tasklet;
  44        int     tasklet_pending;
  45        /* mostly debug stats leave in for now */
  46        unsigned long   st_task_enter; /* tasklet entered */
  47        unsigned long   st_txq_refl_try; /* transmit queue refill attempt */
  48        unsigned long   st_rxq_enter; /* receive queue entered */
  49        unsigned long   st_rx2tx_tran; /* receive to trasmit transfers */
  50        unsigned long   st_rxq_notenter; /*receiveQ not entered, resched */
  51        unsigned long   st_rx_frm_egr; /* received from egress path */
  52        unsigned long   st_rx_frm_ing; /* received from ingress path */
  53        unsigned long   st_rxq_check;
  54        unsigned long   st_rxq_rsch;
  55        struct sk_buff_head     rq;
  56        struct sk_buff_head     tq;
  57};
  58
  59static int numifbs = 2;
  60
  61static void ri_tasklet(unsigned long dev);
  62static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev);
  63static int ifb_open(struct net_device *dev);
  64static int ifb_close(struct net_device *dev);
  65
  66static void ri_tasklet(unsigned long dev)
  67{
  68
  69        struct net_device *_dev = (struct net_device *)dev;
  70        struct ifb_private *dp = netdev_priv(_dev);
  71        struct net_device_stats *stats = &_dev->stats;
  72        struct netdev_queue *txq;
  73        struct sk_buff *skb;
  74
  75        txq = netdev_get_tx_queue(_dev, 0);
  76        dp->st_task_enter++;
  77        if ((skb = skb_peek(&dp->tq)) == NULL) {
  78                dp->st_txq_refl_try++;
  79                if (__netif_tx_trylock(txq)) {
  80                        dp->st_rxq_enter++;
  81                        while ((skb = skb_dequeue(&dp->rq)) != NULL) {
  82                                skb_queue_tail(&dp->tq, skb);
  83                                dp->st_rx2tx_tran++;
  84                        }
  85                        __netif_tx_unlock(txq);
  86                } else {
  87                        /* reschedule */
  88                        dp->st_rxq_notenter++;
  89                        goto resched;
  90                }
  91        }
  92
  93        while ((skb = skb_dequeue(&dp->tq)) != NULL) {
  94                u32 from = G_TC_FROM(skb->tc_verd);
  95
  96                skb->tc_verd = 0;
  97                skb->tc_verd = SET_TC_NCLS(skb->tc_verd);
  98                stats->tx_packets++;
  99                stats->tx_bytes +=skb->len;
 100
 101                skb->dev = dev_get_by_index(&init_net, skb->iif);
 102                if (!skb->dev) {
 103                        dev_kfree_skb(skb);
 104                        stats->tx_dropped++;
 105                        break;
 106                }
 107                dev_put(skb->dev);
 108                skb->iif = _dev->ifindex;
 109
 110                if (from & AT_EGRESS) {
 111                        dp->st_rx_frm_egr++;
 112                        dev_queue_xmit(skb);
 113                } else if (from & AT_INGRESS) {
 114                        dp->st_rx_frm_ing++;
 115                        skb_pull(skb, skb->dev->hard_header_len);
 116                        netif_rx(skb);
 117                } else
 118                        BUG();
 119        }
 120
 121        if (__netif_tx_trylock(txq)) {
 122                dp->st_rxq_check++;
 123                if ((skb = skb_peek(&dp->rq)) == NULL) {
 124                        dp->tasklet_pending = 0;
 125                        if (netif_queue_stopped(_dev))
 126                                netif_wake_queue(_dev);
 127                } else {
 128                        dp->st_rxq_rsch++;
 129                        __netif_tx_unlock(txq);
 130                        goto resched;
 131                }
 132                __netif_tx_unlock(txq);
 133        } else {
 134resched:
 135                dp->tasklet_pending = 1;
 136                tasklet_schedule(&dp->ifb_tasklet);
 137        }
 138
 139}
 140
 141static const struct net_device_ops ifb_netdev_ops = {
 142        .ndo_open       = ifb_open,
 143        .ndo_stop       = ifb_close,
 144        .ndo_start_xmit = ifb_xmit,
 145        .ndo_validate_addr = eth_validate_addr,
 146};
 147
 148static void ifb_setup(struct net_device *dev)
 149{
 150        /* Initialize the device structure. */
 151        dev->destructor = free_netdev;
 152        dev->netdev_ops = &ifb_netdev_ops;
 153
 154        /* Fill in device structure with ethernet-generic values. */
 155        ether_setup(dev);
 156        dev->tx_queue_len = TX_Q_LIMIT;
 157
 158        dev->flags |= IFF_NOARP;
 159        dev->flags &= ~IFF_MULTICAST;
 160        dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
 161        random_ether_addr(dev->dev_addr);
 162}
 163
 164static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
 165{
 166        struct ifb_private *dp = netdev_priv(dev);
 167        struct net_device_stats *stats = &dev->stats;
 168        u32 from = G_TC_FROM(skb->tc_verd);
 169
 170        stats->rx_packets++;
 171        stats->rx_bytes+=skb->len;
 172
 173        if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->iif) {
 174                dev_kfree_skb(skb);
 175                stats->rx_dropped++;
 176                return NETDEV_TX_OK;
 177        }
 178
 179        if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) {
 180                netif_stop_queue(dev);
 181        }
 182
 183        dev->trans_start = jiffies;
 184        skb_queue_tail(&dp->rq, skb);
 185        if (!dp->tasklet_pending) {
 186                dp->tasklet_pending = 1;
 187                tasklet_schedule(&dp->ifb_tasklet);
 188        }
 189
 190        return NETDEV_TX_OK;
 191}
 192
 193static int ifb_close(struct net_device *dev)
 194{
 195        struct ifb_private *dp = netdev_priv(dev);
 196
 197        tasklet_kill(&dp->ifb_tasklet);
 198        netif_stop_queue(dev);
 199        skb_queue_purge(&dp->rq);
 200        skb_queue_purge(&dp->tq);
 201        return 0;
 202}
 203
 204static int ifb_open(struct net_device *dev)
 205{
 206        struct ifb_private *dp = netdev_priv(dev);
 207
 208        tasklet_init(&dp->ifb_tasklet, ri_tasklet, (unsigned long)dev);
 209        skb_queue_head_init(&dp->rq);
 210        skb_queue_head_init(&dp->tq);
 211        netif_start_queue(dev);
 212
 213        return 0;
 214}
 215
 216static int ifb_validate(struct nlattr *tb[], struct nlattr *data[])
 217{
 218        if (tb[IFLA_ADDRESS]) {
 219                if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
 220                        return -EINVAL;
 221                if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
 222                        return -EADDRNOTAVAIL;
 223        }
 224        return 0;
 225}
 226
 227static struct rtnl_link_ops ifb_link_ops __read_mostly = {
 228        .kind           = "ifb",
 229        .priv_size      = sizeof(struct ifb_private),
 230        .setup          = ifb_setup,
 231        .validate       = ifb_validate,
 232};
 233
 234/* Number of ifb devices to be set up by this module. */
 235module_param(numifbs, int, 0);
 236MODULE_PARM_DESC(numifbs, "Number of ifb devices");
 237
 238static int __init ifb_init_one(int index)
 239{
 240        struct net_device *dev_ifb;
 241        int err;
 242
 243        dev_ifb = alloc_netdev(sizeof(struct ifb_private),
 244                                 "ifb%d", ifb_setup);
 245
 246        if (!dev_ifb)
 247                return -ENOMEM;
 248
 249        err = dev_alloc_name(dev_ifb, dev_ifb->name);
 250        if (err < 0)
 251                goto err;
 252
 253        dev_ifb->rtnl_link_ops = &ifb_link_ops;
 254        err = register_netdevice(dev_ifb);
 255        if (err < 0)
 256                goto err;
 257
 258        return 0;
 259
 260err:
 261        free_netdev(dev_ifb);
 262        return err;
 263}
 264
 265static int __init ifb_init_module(void)
 266{
 267        int i, err;
 268
 269        rtnl_lock();
 270        err = __rtnl_link_register(&ifb_link_ops);
 271
 272        for (i = 0; i < numifbs && !err; i++)
 273                err = ifb_init_one(i);
 274        if (err)
 275                __rtnl_link_unregister(&ifb_link_ops);
 276        rtnl_unlock();
 277
 278        return err;
 279}
 280
 281static void __exit ifb_cleanup_module(void)
 282{
 283        rtnl_link_unregister(&ifb_link_ops);
 284}
 285
 286module_init(ifb_init_module);
 287module_exit(ifb_cleanup_module);
 288MODULE_LICENSE("GPL");
 289MODULE_AUTHOR("Jamal Hadi Salim");
 290MODULE_ALIAS_RTNL_LINK("ifb");
 291