Skip to content
Snippets Groups Projects
dev.c 120 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    			clist = clist->next;
    
    
    			WARN_ON(atomic_read(&skb->users));
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			__kfree_skb(skb);
    		}
    	}
    
    	if (sd->output_queue) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    		local_irq_disable();
    		head = sd->output_queue;
    		sd->output_queue = NULL;
    		local_irq_enable();
    
    		while (head) {
    
    			struct Qdisc *q = head;
    			spinlock_t *root_lock;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			head = head->next_sched;
    
    
    			if (spin_trylock(root_lock)) {
    
    				smp_mb__before_clear_bit();
    				clear_bit(__QDISC_STATE_SCHED,
    					  &q->state);
    
    				qdisc_run(q);
    				spin_unlock(root_lock);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			} else {
    
    				if (!test_bit(__QDISC_STATE_DEACTIVATED,
    
    					__netif_reschedule(q);
    
    				} else {
    					smp_mb__before_clear_bit();
    					clear_bit(__QDISC_STATE_SCHED,
    						  &q->state);
    				}
    
    static inline int deliver_skb(struct sk_buff *skb,
    			      struct packet_type *pt_prev,
    			      struct net_device *orig_dev)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	atomic_inc(&skb->users);
    
    	return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    #if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
    
    /* These hooks defined here for ATM */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    struct net_bridge;
    struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
    						unsigned char *addr);
    
    void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent) __read_mostly;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /*
     * If bridge module is loaded call bridging hook.
     *  returns NULL if packet was consumed.
     */
    struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p,
    					struct sk_buff *skb) __read_mostly;
    static inline struct sk_buff *handle_bridge(struct sk_buff *skb,
    					    struct packet_type **pt_prev, int *ret,
    					    struct net_device *orig_dev)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct net_bridge_port *port;
    
    
    	if (skb->pkt_type == PACKET_LOOPBACK ||
    	    (port = rcu_dereference(skb->dev->br_port)) == NULL)
    		return skb;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	if (*pt_prev) {
    
    		*ret = deliver_skb(skb, *pt_prev, orig_dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		*pt_prev = NULL;
    
    	return br_handle_frame_hook(port, skb);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    #else
    
    #define handle_bridge(skb, pt_prev, ret, orig_dev)	(skb)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #endif
    
    
    #if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE)
    struct sk_buff *(*macvlan_handle_frame_hook)(struct sk_buff *skb) __read_mostly;
    EXPORT_SYMBOL_GPL(macvlan_handle_frame_hook);
    
    static inline struct sk_buff *handle_macvlan(struct sk_buff *skb,
    					     struct packet_type **pt_prev,
    					     int *ret,
    					     struct net_device *orig_dev)
    {
    	if (skb->dev->macvlan_port == NULL)
    		return skb;
    
    	if (*pt_prev) {
    		*ret = deliver_skb(skb, *pt_prev, orig_dev);
    		*pt_prev = NULL;
    	}
    	return macvlan_handle_frame_hook(skb);
    }
    #else
    #define handle_macvlan(skb, pt_prev, ret, orig_dev)	(skb)
    #endif
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #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
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     * the ingress scheduler, you just cant add policies on ingress.
     *
     */
    
    static int ing_filter(struct sk_buff *skb)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct net_device *dev = skb->dev;
    
    	u32 ttl = G_TC_RTTL(skb->tc_verd);
    
    	struct netdev_queue *rxq;
    	int result = TC_ACT_OK;
    	struct Qdisc *q;
    
    	if (MAX_RED_LOOP < ttl++) {
    		printk(KERN_WARNING
    		       "Redir loop detected Dropping packet (%d->%d)\n",
    		       skb->iif, dev->ifindex);
    		return TC_ACT_SHOT;
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	skb->tc_verd = SET_TC_RTTL(skb->tc_verd, ttl);
    	skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_INGRESS);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	q = rxq->qdisc;
    
    	if (q != &noop_qdisc) {
    
    		spin_lock(qdisc_lock(q));
    
    		if (likely(!test_bit(__QDISC_STATE_DEACTIVATED, &q->state)))
    			result = qdisc_enqueue_root(skb, q);
    
    		spin_unlock(qdisc_lock(q));
    	}
    
    static inline struct sk_buff *handle_ing(struct sk_buff *skb,
    					 struct packet_type **pt_prev,
    					 int *ret, struct net_device *orig_dev)
    {
    
    	if (skb->dev->rx_queue.qdisc == &noop_qdisc)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (*pt_prev) {
    		*ret = deliver_skb(skb, *pt_prev, orig_dev);
    		*pt_prev = NULL;
    	} else {
    		/* Huh? Why does turning on AF_PACKET affect this? */
    		skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);
    
    	switch (ing_filter(skb)) {
    	case TC_ACT_SHOT:
    	case TC_ACT_STOLEN:
    		kfree_skb(skb);
    		return NULL;
    	}
    
    out:
    	skb->tc_verd = 0;
    	return skb;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    #endif
    
    
    /*
     * 	netif_nit_deliver - deliver received packets to network taps
     * 	@skb: buffer
     *
     * 	This function is used to deliver incoming packets to network
     * 	taps. It should be used when the normal netif_receive_skb path
     * 	is bypassed, for example because of VLAN acceleration.
     */
    void netif_nit_deliver(struct sk_buff *skb)
    {
    	struct packet_type *ptype;
    
    	if (list_empty(&ptype_all))
    		return;
    
    	skb_reset_network_header(skb);
    	skb_reset_transport_header(skb);
    	skb->mac_len = skb->network_header - skb->mac_header;
    
    	rcu_read_lock();
    	list_for_each_entry_rcu(ptype, &ptype_all, list) {
    		if (!ptype->dev || ptype->dev == skb->dev)
    			deliver_skb(skb, ptype, skb->dev);
    	}
    	rcu_read_unlock();
    }
    
    
    /**
     *	netif_receive_skb - process receive buffer from network
     *	@skb: buffer to process
     *
     *	netif_receive_skb() is the main receive data processing function.
     *	It always succeeds. The buffer may be dropped during processing
     *	for congestion control or by the protocol layers.
     *
     *	This function may only be called from softirq context and interrupts
     *	should be enabled.
     *
     *	Return values (usually ignored):
     *	NET_RX_SUCCESS: no congestion
     *	NET_RX_DROP: packet was dropped
     */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    int netif_receive_skb(struct sk_buff *skb)
    {
    	struct packet_type *ptype, *pt_prev;
    
    	struct net_device *orig_dev;
    
    	struct net_device *null_or_orig;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int ret = NET_RX_DROP;
    
    Al Viro's avatar
    Al Viro committed
    	__be16 type;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (skb->vlan_tci && vlan_hwaccel_do_receive(skb))
    		return NET_RX_SUCCESS;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	/* if we've gotten here through NAPI, check netpoll */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return NET_RX_DROP;
    
    
    	if (!skb->tstamp.tv64)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	if (!skb->iif)
    		skb->iif = skb->dev->ifindex;
    
    	null_or_orig = NULL;
    
    	orig_dev = skb->dev;
    	if (orig_dev->master) {
    
    		if (skb_bond_should_drop(skb))
    			null_or_orig = orig_dev; /* deliver only exact match */
    		else
    			skb->dev = orig_dev->master;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	__get_cpu_var(netdev_rx_stat).total++;
    
    
    	skb_reset_network_header(skb);
    
    	skb_reset_transport_header(skb);
    
    	skb->mac_len = skb->network_header - skb->mac_header;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	pt_prev = NULL;
    
    	rcu_read_lock();
    
    
    	/* Don't receive packets in an exiting network namespace */
    
    	if (!net_alive(dev_net(skb->dev))) {
    		kfree_skb(skb);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #ifdef CONFIG_NET_CLS_ACT
    	if (skb->tc_verd & TC_NCLS) {
    		skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
    		goto ncls;
    	}
    #endif
    
    	list_for_each_entry_rcu(ptype, &ptype_all, list) {
    
    		if (ptype->dev == null_or_orig || ptype->dev == skb->dev ||
    		    ptype->dev == orig_dev) {
    
    				ret = deliver_skb(skb, pt_prev, orig_dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			pt_prev = ptype;
    		}
    	}
    
    #ifdef CONFIG_NET_CLS_ACT
    
    	skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
    	if (!skb)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out;
    ncls:
    #endif
    
    
    	skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);
    
    	if (!skb)
    		goto out;
    	skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out;
    
    	type = skb->protocol;
    
    	list_for_each_entry_rcu(ptype,
    			&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		if (ptype->type == type &&
    
    		    (ptype->dev == null_or_orig || ptype->dev == skb->dev ||
    		     ptype->dev == orig_dev)) {
    
    				ret = deliver_skb(skb, pt_prev, orig_dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			pt_prev = ptype;
    		}
    	}
    
    	if (pt_prev) {
    
    		ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	} else {
    		kfree_skb(skb);
    		/* Jamal, now you will not able to escape explaining
    		 * me how you were going to use this. :-)
    		 */
    		ret = NET_RX_DROP;
    	}
    
    out:
    	rcu_read_unlock();
    	return ret;
    }
    
    
    /* Network device is going away, flush any packets still pending  */
    static void flush_backlog(void *arg)
    {
    	struct net_device *dev = arg;
    	struct softnet_data *queue = &__get_cpu_var(softnet_data);
    	struct sk_buff *skb, *tmp;
    
    	skb_queue_walk_safe(&queue->input_pkt_queue, skb, tmp)
    		if (skb->dev == dev) {
    			__skb_unlink(skb, &queue->input_pkt_queue);
    			kfree_skb(skb);
    		}
    }
    
    
    static int process_backlog(struct napi_struct *napi, int quota)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int work = 0;
    	struct softnet_data *queue = &__get_cpu_var(softnet_data);
    	unsigned long start_time = jiffies;
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		struct sk_buff *skb;
    
    		local_irq_disable();
    		skb = __skb_dequeue(&queue->input_pkt_queue);
    
    		if (!skb) {
    			__napi_complete(napi);
    			local_irq_enable();
    			break;
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		local_irq_enable();
    
    		netif_receive_skb(skb);
    
    	} while (++work < quota && jiffies == start_time);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    /**
     * __napi_schedule - schedule for receive
    
     * @n: entry to schedule
    
     *
     * The entry's receive function will be scheduled to run
     */
    
    void __napi_schedule(struct napi_struct *n)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	local_irq_save(flags);
    	list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);
    	__raise_softirq_irqoff(NET_RX_SOFTIRQ);
    	local_irq_restore(flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    static void net_rx_action(struct softirq_action *h)
    {
    
    	struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
    
    	unsigned long time_limit = jiffies + 2;
    
    	int budget = netdev_budget;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	local_irq_disable();
    
    
    	while (!list_empty(list)) {
    		struct napi_struct *n;
    		int work, weight;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    		/* If softirq window is exhuasted then punt.
    
    		 * Allow this to run for 2 jiffies since which will allow
    		 * an average latency of 1.5/HZ.
    
    		if (unlikely(budget <= 0 || time_after(jiffies, time_limit)))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			goto softnet_break;
    
    		local_irq_enable();
    
    
    		/* Even though interrupts have been re-enabled, this
    		 * access is safe because interrupts can only add new
    		 * entries to the tail of this list, and only ->poll()
    		 * calls can remove this head entry from the list.
    		 */
    		n = list_entry(list->next, struct napi_struct, poll_list);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    		/* This NAPI_STATE_SCHED test is for avoiding a race
    		 * with netpoll's poll_napi().  Only the entity which
    		 * obtains the lock and sees NAPI_STATE_SCHED set will
    		 * actually make the ->poll() call.  Therefore we avoid
    		 * accidently calling ->poll() when NAPI is not scheduled.
    		 */
    		work = 0;
    		if (test_bit(NAPI_STATE_SCHED, &n->state))
    			work = n->poll(n, weight);
    
    
    		WARN_ON_ONCE(work > weight);
    
    		budget -= work;
    
    		local_irq_disable();
    
    		/* Drivers must not modify the NAPI state if they
    		 * consume the entire weight.  In such cases this code
    		 * still "owns" the NAPI instance and therefore can
    		 * move the instance around on the list at-will.
    		 */
    
    		if (unlikely(work == weight)) {
    			if (unlikely(napi_disable_pending(n)))
    				__napi_complete(n);
    			else
    				list_move_tail(&n->poll_list, list);
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    out:
    
    #ifdef CONFIG_NET_DMA
    	/*
    	 * There may not be any more sk_buffs coming right now, so push
    	 * any pending DMA copies to hardware
    	 */
    
    	if (!cpus_empty(net_dma.channel_mask)) {
    		int chan_idx;
    
    		for_each_cpu_mask_nr(chan_idx, net_dma.channel_mask) {
    
    			struct dma_chan *chan = net_dma.channels[chan_idx];
    			if (chan)
    				dma_async_memcpy_issue_pending(chan);
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return;
    
    softnet_break:
    	__get_cpu_var(netdev_rx_stat).time_squeeze++;
    	__raise_softirq_irqoff(NET_RX_SOFTIRQ);
    	goto out;
    }
    
    static gifconf_func_t * gifconf_list [NPROTO];
    
    /**
     *	register_gifconf	-	register a SIOCGIF handler
     *	@family: Address family
     *	@gifconf: Function handler
     *
     *	Register protocol dependent address dumping routines. The handler
     *	that is passed must not be freed or reused until it has been replaced
     *	by another handler.
     */
    int register_gifconf(unsigned int family, gifconf_func_t * gifconf)
    {
    	if (family >= NPROTO)
    		return -EINVAL;
    	gifconf_list[family] = gifconf;
    	return 0;
    }
    
    
    /*
     *	Map an interface index to its name (SIOCGIFNAME)
     */
    
    /*
     *	We need this ioctl for efficient implementation of the
     *	if_indextoname() function required by the IPv6 API.  Without
     *	it, we would have to search all the interfaces to find a
     *	match.  --pb
     */
    
    
    static int dev_ifname(struct net *net, struct ifreq __user *arg)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct net_device *dev;
    	struct ifreq ifr;
    
    	/*
    	 *	Fetch the caller's info block.
    	 */
    
    	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
    		return -EFAULT;
    
    	read_lock(&dev_base_lock);
    
    	dev = __dev_get_by_index(net, ifr.ifr_ifindex);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!dev) {
    		read_unlock(&dev_base_lock);
    		return -ENODEV;
    	}
    
    	strcpy(ifr.ifr_name, dev->name);
    	read_unlock(&dev_base_lock);
    
    	if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
    		return -EFAULT;
    	return 0;
    }
    
    /*
     *	Perform a SIOCGIFCONF call. This structure will change
     *	size eventually, and there is nothing I can do about it.
     *	Thus we will need a 'compatibility mode'.
     */
    
    
    static int dev_ifconf(struct net *net, char __user *arg)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct ifconf ifc;
    	struct net_device *dev;
    	char __user *pos;
    	int len;
    	int total;
    	int i;
    
    	/*
    	 *	Fetch the caller's info block.
    	 */
    
    	if (copy_from_user(&ifc, arg, sizeof(struct ifconf)))
    		return -EFAULT;
    
    	pos = ifc.ifc_buf;
    	len = ifc.ifc_len;
    
    	/*
    	 *	Loop over the interfaces, and write an info block for each.
    	 */
    
    	total = 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		for (i = 0; i < NPROTO; i++) {
    			if (gifconf_list[i]) {
    				int done;
    				if (!pos)
    					done = gifconf_list[i](dev, NULL, 0);
    				else
    					done = gifconf_list[i](dev, pos + total,
    							       len - total);
    				if (done < 0)
    					return -EFAULT;
    				total += done;
    			}
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 *	All done.  Write the updated control block back to the caller.
    	 */
    	ifc.ifc_len = total;
    
    	/*
    	 * 	Both BSD and Solaris return 0 here, so we do too.
    	 */
    	return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0;
    }
    
    #ifdef CONFIG_PROC_FS
    /*
     *	This is invoked by the /proc filesystem handler to display a device
     *	in detail.
     */
    
    void *dev_seq_start(struct seq_file *seq, loff_t *pos)
    
    	__acquires(dev_base_lock)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	struct net *net = seq_file_net(seq);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct net_device *dev;
    
    
    	read_lock(&dev_base_lock);
    	if (!*pos)
    		return SEQ_START_TOKEN;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    		if (off++ == *pos)
    			return dev;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
    {
    
    	struct net *net = seq_file_net(seq);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	++*pos;
    
    	return v == SEQ_START_TOKEN ?
    
    		first_net_device(net) : next_net_device((struct net_device *)v);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    void dev_seq_stop(struct seq_file *seq, void *v)
    
    	__releases(dev_base_lock)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	read_unlock(&dev_base_lock);
    }
    
    static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
    {
    
    	struct net_device_stats *stats = dev->get_stats(dev);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu "
    		   "%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
    		   dev->name, stats->rx_bytes, stats->rx_packets,
    		   stats->rx_errors,
    		   stats->rx_dropped + stats->rx_missed_errors,
    		   stats->rx_fifo_errors,
    		   stats->rx_length_errors + stats->rx_over_errors +
    		    stats->rx_crc_errors + stats->rx_frame_errors,
    		   stats->rx_compressed, stats->multicast,
    		   stats->tx_bytes, stats->tx_packets,
    		   stats->tx_errors, stats->tx_dropped,
    		   stats->tx_fifo_errors, stats->collisions,
    		   stats->tx_carrier_errors +
    		    stats->tx_aborted_errors +
    		    stats->tx_window_errors +
    		    stats->tx_heartbeat_errors,
    		   stats->tx_compressed);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    /*
     *	Called from the PROCfs module. This now uses the new arbitrary sized
     *	/proc/net interface to create /proc/net/dev
     */
    static int dev_seq_show(struct seq_file *seq, void *v)
    {
    	if (v == SEQ_START_TOKEN)
    		seq_puts(seq, "Inter-|   Receive                            "
    			      "                    |  Transmit\n"
    			      " face |bytes    packets errs drop fifo frame "
    			      "compressed multicast|bytes    packets errs "
    			      "drop fifo colls carrier compressed\n");
    	else
    		dev_seq_printf_stats(seq, v);
    	return 0;
    }
    
    static struct netif_rx_stats *softnet_get_online(loff_t *pos)
    {
    	struct netif_rx_stats *rc = NULL;
    
    
    	while (*pos < nr_cpu_ids)
    
    		if (cpu_online(*pos)) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			rc = &per_cpu(netdev_rx_stat, *pos);
    			break;
    		} else
    			++*pos;
    	return rc;
    }
    
    static void *softnet_seq_start(struct seq_file *seq, loff_t *pos)
    {
    	return softnet_get_online(pos);
    }
    
    static void *softnet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
    {
    	++*pos;
    	return softnet_get_online(pos);
    }
    
    static void softnet_seq_stop(struct seq_file *seq, void *v)
    {
    }
    
    static int softnet_seq_show(struct seq_file *seq, void *v)
    {
    	struct netif_rx_stats *s = v;
    
    	seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
    
    		   s->total, s->dropped, s->time_squeeze, 0,
    
    		   0, 0, 0, 0, /* was fastroute */
    		   s->cpu_collision );
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return 0;
    }
    
    
    static const struct seq_operations dev_seq_ops = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	.start = dev_seq_start,
    	.next  = dev_seq_next,
    	.stop  = dev_seq_stop,
    	.show  = dev_seq_show,
    };
    
    static int dev_seq_open(struct inode *inode, struct file *file)
    {
    
    	return seq_open_net(inode, file, &dev_seq_ops,
    			    sizeof(struct seq_net_private));
    
    static const struct file_operations dev_seq_fops = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	.owner	 = THIS_MODULE,
    	.open    = dev_seq_open,
    	.read    = seq_read,
    	.llseek  = seq_lseek,
    
    static const struct seq_operations softnet_seq_ops = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	.start = softnet_seq_start,
    	.next  = softnet_seq_next,
    	.stop  = softnet_seq_stop,
    	.show  = softnet_seq_show,
    };
    
    static int softnet_seq_open(struct inode *inode, struct file *file)
    {
    	return seq_open(file, &softnet_seq_ops);
    }
    
    
    static const struct file_operations softnet_seq_fops = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	.owner	 = THIS_MODULE,
    	.open    = softnet_seq_open,
    	.read    = seq_read,
    	.llseek  = seq_lseek,
    	.release = seq_release,
    };
    
    
    static void *ptype_get_idx(loff_t pos)
    {
    	struct packet_type *pt = NULL;
    	loff_t i = 0;
    	int t;
    
    	list_for_each_entry_rcu(pt, &ptype_all, list) {
    		if (i == pos)
    			return pt;
    		++i;
    	}
    
    
    	for (t = 0; t < PTYPE_HASH_SIZE; t++) {
    
    		list_for_each_entry_rcu(pt, &ptype_base[t], list) {
    			if (i == pos)
    				return pt;
    			++i;
    		}
    	}
    	return NULL;
    }
    
    static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
    
    {
    	rcu_read_lock();
    	return *pos ? ptype_get_idx(*pos - 1) : SEQ_START_TOKEN;
    }
    
    static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
    {
    	struct packet_type *pt;
    	struct list_head *nxt;
    	int hash;
    
    	++*pos;
    	if (v == SEQ_START_TOKEN)
    		return ptype_get_idx(0);
    
    	pt = v;
    	nxt = pt->list.next;
    	if (pt->type == htons(ETH_P_ALL)) {
    		if (nxt != &ptype_all)
    			goto found;
    		hash = 0;
    		nxt = ptype_base[0].next;
    	} else
    
    		hash = ntohs(pt->type) & PTYPE_HASH_MASK;
    
    
    	while (nxt == &ptype_base[hash]) {
    
    		if (++hash >= PTYPE_HASH_SIZE)
    
    			return NULL;
    		nxt = ptype_base[hash].next;
    	}
    found:
    	return list_entry(nxt, struct packet_type, list);
    }
    
    static void ptype_seq_stop(struct seq_file *seq, void *v)
    
    {
    	rcu_read_unlock();
    }
    
    static int ptype_seq_show(struct seq_file *seq, void *v)
    {
    	struct packet_type *pt = v;
    
    	if (v == SEQ_START_TOKEN)
    		seq_puts(seq, "Type Device      Function\n");
    
    	else if (pt->dev == NULL || dev_net(pt->dev) == seq_file_net(seq)) {
    
    		if (pt->type == htons(ETH_P_ALL))
    			seq_puts(seq, "ALL ");
    		else
    			seq_printf(seq, "%04x", ntohs(pt->type));
    
    
    		seq_printf(seq, " %-8s %pF\n",
    			   pt->dev ? pt->dev->name : "", pt->func);
    
    	}
    
    	return 0;
    }
    
    static const struct seq_operations ptype_seq_ops = {
    	.start = ptype_seq_start,
    	.next  = ptype_seq_next,
    	.stop  = ptype_seq_stop,
    	.show  = ptype_seq_show,
    };
    
    static int ptype_seq_open(struct inode *inode, struct file *file)
    {
    
    	return seq_open_net(inode, file, &ptype_seq_ops,
    			sizeof(struct seq_net_private));
    
    }
    
    static const struct file_operations ptype_seq_fops = {
    	.owner	 = THIS_MODULE,
    	.open    = ptype_seq_open,
    	.read    = seq_read,
    	.llseek  = seq_lseek,
    
    	.release = seq_release_net,
    
    static int __net_init dev_proc_net_init(struct net *net)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	int rc = -ENOMEM;
    
    
    	if (!proc_net_fops_create(net, "dev", S_IRUGO, &dev_seq_fops))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out;
    
    	if (!proc_net_fops_create(net, "softnet_stat", S_IRUGO, &softnet_seq_fops))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		goto out_dev;
    
    	if (!proc_net_fops_create(net, "ptype", S_IRUGO, &ptype_seq_fops))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	rc = 0;
    out:
    	return rc;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out_softnet:
    
    	proc_net_remove(net, "softnet_stat");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    out_dev:
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	goto out;
    }
    
    static void __net_exit dev_proc_net_exit(struct net *net)
    
    {
    	wext_proc_exit(net);
    
    	proc_net_remove(net, "ptype");
    	proc_net_remove(net, "softnet_stat");
    	proc_net_remove(net, "dev");
    }
    
    
    static struct pernet_operations __net_initdata dev_proc_ops = {
    
    	.init = dev_proc_net_init,
    	.exit = dev_proc_net_exit,
    };
    
    static int __init dev_proc_init(void)
    {
    	return register_pernet_subsys(&dev_proc_ops);
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #else
    #define dev_proc_init() 0
    #endif	/* CONFIG_PROC_FS */
    
    
    /**
     *	netdev_set_master	-	set up master/slave pair
     *	@slave: slave device
     *	@master: new master device
     *
     *	Changes the master device of the slave. Pass %NULL to break the
     *	bonding. The caller must hold the RTNL semaphore. On a failure
     *	a negative errno code is returned. On success the reference counts
     *	are adjusted, %RTM_NEWLINK is sent to the routing socket and the
     *	function returns zero.
     */
    int netdev_set_master(struct net_device *slave, struct net_device *master)
    {
    	struct net_device *old = slave->master;
    
    	ASSERT_RTNL();
    
    	if (master) {
    		if (old)
    			return -EBUSY;
    		dev_hold(master);
    	}
    
    	slave->master = master;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	synchronize_net();
    
    	if (old)
    		dev_put(old);
    
    	if (master)
    		slave->flags |= IFF_SLAVE;
    	else
    		slave->flags &= ~IFF_SLAVE;
    
    	rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE);
    	return 0;
    }
    
    
    static void dev_change_rx_flags(struct net_device *dev, int flags)
    {
    	if (dev->flags & IFF_UP && dev->change_rx_flags)
    		dev->change_rx_flags(dev, flags);
    }
    
    
    static int __dev_set_promiscuity(struct net_device *dev, int inc)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	unsigned short old_flags = dev->flags;
    
    
    	dev->flags |= IFF_PROMISC;
    	dev->promiscuity += inc;
    	if (dev->promiscuity == 0) {
    		/*
    		 * Avoid overflow.
    		 * If inc causes overflow, untouch promisc and return error.
    		 */
    		if (inc < 0)
    			dev->flags &= ~IFF_PROMISC;
    		else {
    			dev->promiscuity -= inc;
    			printk(KERN_WARNING "%s: promiscuity touches roof, "
    				"set promiscuity failed, promiscuity feature "
    				"of device might be broken.\n", dev->name);
    			return -EOVERFLOW;
    		}
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		printk(KERN_INFO "device %s %s promiscuous mode\n",
    		       dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
    
    		if (audit_enabled)
    			audit_log(current->audit_context, GFP_ATOMIC,
    				AUDIT_ANOM_PROMISCUOUS,
    				"dev=%s prom=%d old_prom=%d auid=%u uid=%u gid=%u ses=%u",
    				dev->name, (dev->flags & IFF_PROMISC),
    				(old_flags & IFF_PROMISC),
    				audit_get_loginuid(current),
    				current->uid, current->gid,
    				audit_get_sessionid(current));
    
    		dev_change_rx_flags(dev, IFF_PROMISC);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    /**
     *	dev_set_promiscuity	- update promiscuity count on a device
     *	@dev: device
     *	@inc: modifier
     *
     *	Add or remove promiscuity from a device. While the count in the device
     *	remains above zero the interface remains promiscuous. Once it hits zero
     *	the device reverts back to normal filtering operation. A negative inc
     *	value is used to drop promiscuity on the device.
    
     *	Return 0 if successful or a negative errno code on error.
    
    int dev_set_promiscuity(struct net_device *dev, int inc)
    
    {
    	unsigned short old_flags = dev->flags;
    
    	err = __dev_set_promiscuity(dev, inc);
    
    	if (dev->flags != old_flags)
    		dev_set_rx_mode(dev);