#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/netpoll.h>
+#include <linux/rcupdate.h>
#ifdef CONFIG_NET_RADIO
#include <linux/wireless.h> /* Note : will define WIRELESS_EXT */
#include <net/iw_handler.h>
#endif /* CONFIG_NET_RADIO */
#include <asm/current.h>
+#include <linux/vs_network.h>
/* This define, if set, will randomly drop a packet when congestion
* is more than moderate. It helps fairness in the multi-interface
*/
DEFINE_PER_CPU(struct softnet_data, softnet_data) = { 0, };
-#ifdef CONFIG_NET_FASTROUTE
-int netdev_fastroute;
-int netdev_fastroute_obstacles;
-#endif
-
+#ifdef CONFIG_SYSFS
extern int netdev_sysfs_init(void);
extern int netdev_register_sysfs(struct net_device *);
-extern int netdev_unregister_sysfs(struct net_device *);
+extern void netdev_unregister_sysfs(struct net_device *);
+#else
+#define netdev_sysfs_init() (0)
+#define netdev_register_sysfs(dev) (0)
+#define netdev_unregister_sysfs(dev) do { } while(0)
+#endif
/*******************************************************************************
int hash;
spin_lock_bh(&ptype_lock);
-#ifdef CONFIG_NET_FASTROUTE
- if (pt->af_packet_priv) {
- netdev_fastroute_obstacles++;
- dev_clear_fastroute(pt->dev);
- }
-#endif
if (pt->type == htons(ETH_P_ALL)) {
netdev_nit++;
list_add_rcu(&pt->list, &ptype_all);
list_for_each_entry(pt1, head, list) {
if (pt == pt1) {
-#ifdef CONFIG_NET_FASTROUTE
- if (pt->af_packet_priv)
- netdev_fastroute_obstacles--;
-#endif
list_del_rcu(&pt->list);
goto out;
}
/**
* dev_change_name - change name of a device
* @dev: device
- * @name: name (or format string) must be at least IFNAMSIZ
+ * @newname: name (or format string) must be at least IFNAMSIZ
*
* Change name of a device, can pass format strings "eth%d".
* for wildcarding.
*/
int dev_change_name(struct net_device *dev, char *newname)
{
+ int err = 0;
+
ASSERT_RTNL();
if (dev->flags & IFF_UP)
return -EINVAL;
if (strchr(newname, '%')) {
- int err = dev_alloc_name(dev, newname);
+ err = dev_alloc_name(dev, newname);
if (err < 0)
return err;
strcpy(newname, dev->name);
else
strlcpy(dev->name, newname, IFNAMSIZ);
- hlist_del(&dev->name_hlist);
- hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name));
+ err = class_device_rename(&dev->class_dev, dev->name);
+ if (!err) {
+ hlist_del(&dev->name_hlist);
+ hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name));
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
+ }
- class_device_rename(&dev->class_dev, dev->name);
- notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
- return 0;
+ return err;
}
/**
return ret;
}
-#ifdef CONFIG_NET_FASTROUTE
-
-static void dev_do_clear_fastroute(struct net_device *dev)
-{
- if (dev->accept_fastpath) {
- int i;
-
- for (i = 0; i <= NETDEV_FASTROUTE_HMASK; i++) {
- struct dst_entry *dst;
-
- write_lock_irq(&dev->fastpath_lock);
- dst = dev->fastpath[i];
- dev->fastpath[i] = NULL;
- write_unlock_irq(&dev->fastpath_lock);
-
- dst_release(dst);
- }
- }
-}
-
-void dev_clear_fastroute(struct net_device *dev)
-{
- if (dev) {
- dev_do_clear_fastroute(dev);
- } else {
- read_lock(&dev_base_lock);
- for (dev = dev_base; dev; dev = dev->next)
- dev_do_clear_fastroute(dev);
- read_unlock(&dev_base_lock);
- }
-}
-#endif
-
/**
* dev_close - shutdown an interface.
* @dev: device to shutdown
*/
dev->flags &= ~IFF_UP;
-#ifdef CONFIG_NET_FASTROUTE
- dev_clear_fastroute(dev);
-#endif
/*
* Tell people we are down
rcu_read_unlock();
}
-/* Calculate csum in the case, when packet is misrouted.
- * If it failed by some reason, ignore and send skb with wrong
- * checksum.
+/*
+ * Invalidate hardware checksum when packet is to be mangled, and
+ * complete checksum manually on outgoing path.
*/
-struct sk_buff *skb_checksum_help(struct sk_buff *skb)
+int skb_checksum_help(struct sk_buff **pskb, int inward)
{
unsigned int csum;
- int offset = skb->h.raw - skb->data;
+ int ret = 0, offset = (*pskb)->h.raw - (*pskb)->data;
+
+ if (inward) {
+ (*pskb)->ip_summed = CHECKSUM_NONE;
+ goto out;
+ }
+
+ if (skb_shared(*pskb) || skb_cloned(*pskb)) {
+ struct sk_buff *newskb = skb_copy(*pskb, GFP_ATOMIC);
+ if (!newskb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if ((*pskb)->sk)
+ skb_set_owner_w(newskb, (*pskb)->sk);
+ kfree_skb(*pskb);
+ *pskb = newskb;
+ }
- if (offset > (int)skb->len)
+ if (offset > (int)(*pskb)->len)
BUG();
- csum = skb_checksum(skb, offset, skb->len-offset, 0);
+ csum = skb_checksum(*pskb, offset, (*pskb)->len-offset, 0);
- offset = skb->tail - skb->h.raw;
+ offset = (*pskb)->tail - (*pskb)->h.raw;
if (offset <= 0)
BUG();
- if (skb->csum + 2 > offset)
+ if ((*pskb)->csum + 2 > offset)
BUG();
- *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum);
- skb->ip_summed = CHECKSUM_NONE;
- return skb;
+ *(u16*)((*pskb)->h.raw + (*pskb)->csum) = csum_fold(csum);
+ (*pskb)->ip_summed = CHECKSUM_NONE;
+out:
+ return ret;
}
#ifdef CONFIG_HIGHMEM
return 0;
}
+#define HARD_TX_LOCK_BH(dev, cpu) { \
+ if ((dev->features & NETIF_F_LLTX) == 0) { \
+ spin_lock_bh(&dev->xmit_lock); \
+ dev->xmit_lock_owner = cpu; \
+ } \
+}
+
+#define HARD_TX_UNLOCK_BH(dev) { \
+ if ((dev->features & NETIF_F_LLTX) == 0) { \
+ dev->xmit_lock_owner = -1; \
+ spin_unlock_bh(&dev->xmit_lock); \
+ } \
+}
+
+static inline void qdisc_run(struct net_device *dev)
+{
+ while (!netif_queue_stopped(dev) &&
+ qdisc_restart(dev)<0)
+ /* NOTHING */;
+}
+
/**
* dev_queue_xmit - transmit a buffer
* @skb: buffer to transmit
if (skb->ip_summed == CHECKSUM_HW &&
(!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) &&
(!(dev->features & NETIF_F_IP_CSUM) ||
- skb->protocol != htons(ETH_P_IP)))) {
- if ((skb = skb_checksum_help(skb)) == NULL)
- goto out;
- }
+ skb->protocol != htons(ETH_P_IP))))
+ if (skb_checksum_help(&skb, 0))
+ goto out_kfree_skb;
+
+ rcu_read_lock();
+ /* Updates of qdisc are serialized by queue_lock.
+ * The struct Qdisc which is pointed to by qdisc is now a
+ * rcu structure - it may be accessed without acquiring
+ * a lock (but the structure may be stale.) The freeing of the
+ * qdisc will be deferred until it's known that there are no
+ * more references to it.
+ *
+ * If the qdisc has an enqueue function, we still need to
+ * hold the queue_lock before calling it, since queue_lock
+ * also serializes access to the device queue.
+ */
- /* Grab device queue */
- spin_lock_bh(&dev->queue_lock);
q = dev->qdisc;
+ smp_read_barrier_depends();
+#ifdef CONFIG_NET_CLS_ACT
+ skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS);
+#endif
if (q->enqueue) {
+ /* Grab device queue */
+ spin_lock_bh(&dev->queue_lock);
+
rc = q->enqueue(skb, q);
qdisc_run(dev);
spin_unlock_bh(&dev->queue_lock);
+ rcu_read_unlock();
rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc;
goto out;
}
+ rcu_read_unlock();
/* The device has no queue. Common case for software devices:
loopback, all the sorts of tunnels...
Either shot noqueue qdisc, it is even simpler 8)
*/
if (dev->flags & IFF_UP) {
- int cpu = smp_processor_id();
+ int cpu = get_cpu();
if (dev->xmit_lock_owner != cpu) {
- /*
- * The spin_lock effectivly does a preempt lock, but
- * we are about to drop that...
- */
- preempt_disable();
- spin_unlock(&dev->queue_lock);
- spin_lock(&dev->xmit_lock);
- dev->xmit_lock_owner = cpu;
- preempt_enable();
+
+ HARD_TX_LOCK_BH(dev, cpu);
+ put_cpu();
if (!netif_queue_stopped(dev)) {
if (netdev_nit)
rc = 0;
if (!dev->hard_start_xmit(skb, dev)) {
- dev->xmit_lock_owner = -1;
- spin_unlock_bh(&dev->xmit_lock);
+ HARD_TX_UNLOCK_BH(dev);
goto out;
}
}
- dev->xmit_lock_owner = -1;
- spin_unlock_bh(&dev->xmit_lock);
+ HARD_TX_UNLOCK_BH(dev);
if (net_ratelimit())
printk(KERN_CRIT "Virtual device %s asks to "
"queue packet!\n", dev->name);
goto out_enetdown;
} else {
+ put_cpu();
/* Recursion is detected! It is possible,
* unfortunately */
if (net_ratelimit())
"%s, fix it urgently!\n", dev->name);
}
}
- spin_unlock_bh(&dev->queue_lock);
out_enetdown:
rc = -ENETDOWN;
out_kfree_skb:
return 0;
}
+
+#ifdef CONFIG_NET_CLS_ACT
+/* TODO: Maybe we should just force sch_ingress to be compiled in
+ * when CONFIG_NET_CLS_ACT is? otherwise some useless instructions
+ * a compare and 2 stores extra right now if we dont have it on
+ * but have CONFIG_NET_CLS_ACT
+ * NOTE: This doesnt stop any functionality; if you dont have
+ * the ingress scheduler, you just cant add policies on ingress.
+ *
+ */
+int ing_filter(struct sk_buff *skb)
+{
+ struct Qdisc *q;
+ struct net_device *dev = skb->dev;
+ int result = TC_ACT_OK;
+
+ if (dev->qdisc_ingress) {
+ __u32 ttl = (__u32) G_TC_RTTL(skb->tc_verd);
+ if (MAX_RED_LOOP < ttl++) {
+ printk("Redir loop detected Dropping packet (%s->%s)\n",
+ skb->input_dev?skb->input_dev->name:"??",skb->dev->name);
+ return TC_ACT_SHOT;
+ }
+
+ skb->tc_verd = SET_TC_RTTL(skb->tc_verd,ttl);
+
+ skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_INGRESS);
+ if (NULL == skb->input_dev) {
+ skb->input_dev = skb->dev;
+ printk("ing_filter: fixed %s out %s\n",skb->input_dev->name,skb->dev->name);
+ }
+ spin_lock(&dev->ingress_lock);
+ if ((q = dev->qdisc_ingress) != NULL)
+ result = q->enqueue(skb, q);
+ spin_unlock(&dev->ingress_lock);
+
+ }
+
+ return result;
+}
+#endif
+
int netif_receive_skb(struct sk_buff *skb)
{
struct packet_type *ptype, *pt_prev;
__get_cpu_var(netdev_rx_stat).total++;
-#ifdef CONFIG_NET_FASTROUTE
- if (skb->pkt_type == PACKET_FASTROUTE) {
- __get_cpu_var(netdev_rx_stat).fastroute_deferred_out++;
- return dev_queue_xmit(skb);
- }
-#endif
-
skb->h.raw = skb->nh.raw = skb->data;
skb->mac_len = skb->nh.raw - skb->mac.raw;
pt_prev = NULL;
+#ifdef CONFIG_NET_CLS_ACT
+ if (skb->tc_verd & TC_NCLS) {
+ skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
+ rcu_read_lock();
+ goto ncls;
+ }
+ #endif
+
rcu_read_lock();
list_for_each_entry_rcu(ptype, &ptype_all, list) {
if (!ptype->dev || ptype->dev == skb->dev) {
}
}
+#ifdef CONFIG_NET_CLS_ACT
+ if (pt_prev) {
+ atomic_inc(&skb->users);
+ ret = pt_prev->func(skb, skb->dev, pt_prev);
+ pt_prev = NULL; /* noone else should process this after*/
+ } else {
+ skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);
+ }
+
+ ret = ing_filter(skb);
+
+ if (ret == TC_ACT_SHOT || (ret == TC_ACT_STOLEN)) {
+ kfree_skb(skb);
+ goto out;
+ }
+
+ skb->tc_verd = 0;
+ncls:
+#endif
+
handle_diverter(skb);
if (__handle_bridge(skb, &pt_prev, &ret))
{
struct ifconf ifc;
struct net_device *dev;
- char *pos;
+ char __user *pos;
int len;
int total;
int i;
if ((dev->promiscuity += inc) == 0)
dev->flags &= ~IFF_PROMISC;
if (dev->flags ^ old_flags) {
-#ifdef CONFIG_NET_FASTROUTE
- if (dev->flags & IFF_PROMISC) {
- netdev_fastroute_obstacles++;
- dev_clear_fastroute(dev);
- } else
- netdev_fastroute_obstacles--;
-#endif
dev_mc_upload(dev);
printk(KERN_INFO "device %s %s promiscuous mode\n",
dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
cmd == SIOCGMIIPHY ||
cmd == SIOCGMIIREG ||
cmd == SIOCSMIIREG ||
+ cmd == SIOCBRADDIF ||
+ cmd == SIOCBRDELIF ||
cmd == SIOCWANDEV) {
err = -EOPNOTSUPP;
if (dev->do_ioctl) {
case SIOCBONDSLAVEINFOQUERY:
case SIOCBONDINFOQUERY:
case SIOCBONDCHANGEACTIVE:
+ case SIOCBRADDIF:
+ case SIOCBRDELIF:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
dev_load(ifr.ifr_name);
spin_lock_init(&dev->queue_lock);
spin_lock_init(&dev->xmit_lock);
dev->xmit_lock_owner = -1;
-#ifdef CONFIG_NET_FASTROUTE
- dev->fastpath_lock = RW_LOCK_UNLOCKED;
+#ifdef CONFIG_NET_CLS_ACT
+ spin_lock_init(&dev->ingress_lock);
#endif
ret = alloc_divert_blk(dev);
while (atomic_read(&dev->refcnt) != 0) {
if (time_after(jiffies, rebroadcast_time + 1 * HZ)) {
rtnl_shlock();
- rtnl_exlock();
/* Rebroadcast unregister notification */
notifier_call_chain(&netdev_chain,
linkwatch_run_queue();
}
- rtnl_exunlock();
rtnl_shunlock();
rebroadcast_time = jiffies;
void netdev_run_todo(void)
{
struct list_head list = LIST_HEAD_INIT(list);
+ int err;
- /* Safe outside mutex since we only care about entries that
- * this cpu put into queue while under RTNL.
- */
- if (list_empty(&net_todo_list))
- return;
/* Need to guard against multiple cpu's getting out of order. */
down(&net_todo_run_mutex);
+ /* Not safe to do outside the semaphore. We must not return
+ * until all unregister events invoked by the local processor
+ * have been completed (either by this todo run, or one on
+ * another cpu).
+ */
+ if (list_empty(&net_todo_list))
+ goto out;
+
/* Snapshot list, allow later requests */
spin_lock(&net_todo_list_lock);
list_splice_init(&net_todo_list, &list);
switch(dev->reg_state) {
case NETREG_REGISTERING:
- netdev_register_sysfs(dev);
+ err = netdev_register_sysfs(dev);
+ if (err)
+ printk(KERN_ERR "%s: failed sysfs registration (%d)\n",
+ dev->name, err);
dev->reg_state = NETREG_REGISTERED;
break;
}
}
+out:
up(&net_todo_run_mutex);
}
*/
void free_netdev(struct net_device *dev)
{
+#ifdef CONFIG_SYSFS
/* Compatiablity with error handling in drivers */
if (dev->reg_state == NETREG_UNINITIALIZED) {
kfree((char *)dev - dev->padded);
/* will free via class release */
class_device_put(&dev->class_dev);
+#else
+ kfree((char *)dev - dev->padded);
+#endif
}
/* Synchronize with packet receive processing. */
synchronize_net();
-#ifdef CONFIG_NET_FASTROUTE
- dev_clear_fastroute(dev);
-#endif
-
/* Shutdown queueing discipline. */
dev_shutdown(dev);
/* Finish processing unregister after unlock */
net_set_todo(dev);
+ synchronize_net();
+
dev_put(dev);
return 0;
}
EXPORT_SYMBOL(dev_remove_pack);
EXPORT_SYMBOL(dev_set_allmulti);
EXPORT_SYMBOL(dev_set_promiscuity);
+EXPORT_SYMBOL(dev_change_flags);
+EXPORT_SYMBOL(dev_set_mtu);
EXPORT_SYMBOL(free_netdev);
EXPORT_SYMBOL(netdev_boot_setup_check);
EXPORT_SYMBOL(netdev_set_master);
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
EXPORT_SYMBOL(br_handle_frame_hook);
#endif
-/* for 801q VLAN support */
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
-EXPORT_SYMBOL(dev_change_flags);
-#endif
+
#ifdef CONFIG_KMOD
EXPORT_SYMBOL(dev_load);
#endif
EXPORT_SYMBOL(netdev_register_fc);
EXPORT_SYMBOL(netdev_unregister_fc);
#endif
-#ifdef CONFIG_NET_FASTROUTE
-EXPORT_SYMBOL(netdev_fastroute);
-EXPORT_SYMBOL(netdev_fastroute_obstacles);
+
+#ifdef CONFIG_NET_CLS_ACT
+EXPORT_SYMBOL(ing_filter);
#endif
+
EXPORT_PER_CPU_SYMBOL(softnet_data);