summaryrefslogtreecommitdiffstats
path: root/net/core/dev.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-16 05:34:03 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-16 05:34:03 +0000
commit967c65a99059fd459b956c1588ce0ba227912c4e (patch)
tree8224d013ff5d255420713d05610c7efebd204d2a /net/core/dev.c
parente20c1cc1656a66a2773bca4591a895cbc12696ff (diff)
Merge with Linux 2.1.72, part 1.
Diffstat (limited to 'net/core/dev.c')
-rw-r--r--net/core/dev.c1301
1 files changed, 635 insertions, 666 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index 5970c5bab..8d94f6817 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -15,6 +15,7 @@
* Florian la Roche <rzsfl@rz.uni-sb.de>
* Alan Cox <gw4pts@gw4pts.ampr.org>
* David Hinds <dhinds@allegro.stanford.edu>
+ * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
*
* Changes:
* Alan Cox : device private ioctl copies fields back.
@@ -61,24 +62,20 @@
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/sockios.h>
-#include <linux/in.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/if_ether.h>
-#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/notifier.h>
-#include <net/ip.h>
-#include <net/route.h>
#include <linux/skbuff.h>
#include <net/sock.h>
-#include <net/arp.h>
+#include <linux/rtnetlink.h>
#include <net/slhc.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <net/br.h>
-#include <linux/net_alias.h>
+#include <net/pkt_sched.h>
#include <linux/init.h>
#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
@@ -90,6 +87,7 @@
extern int plip_init(void);
#endif
+
const char *if_port_text[] = {
"unknown",
"BNC",
@@ -101,12 +99,6 @@ const char *if_port_text[] = {
};
/*
- * The list of devices, that are able to output.
- */
-
-static struct device *dev_up_base;
-
-/*
* The list of packet types we will receive (as opposed to discard)
* and the routines to invoke.
*
@@ -130,16 +122,17 @@ struct packet_type *ptype_base[16]; /* 16 way hashed list */
struct packet_type *ptype_all = NULL; /* Taps */
/*
- * Device list lock
+ * Device list lock. Setting it provides that interface
+ * will not disappear unexpectedly while kernel sleeps.
*/
atomic_t dev_lockct = ATOMIC_INIT(0);
-
+
/*
* Our notifier list
*/
-struct notifier_block *netdev_chain=NULL;
+static struct notifier_block *netdev_chain=NULL;
/*
* Device drivers call our routines to queue packets here. We empty the
@@ -148,14 +141,6 @@ struct notifier_block *netdev_chain=NULL;
static struct sk_buff_head backlog;
-/*
- * We don't overdo the queue or we will thrash memory badly.
- */
-
-static int backlog_size = 0;
-
-
-
/******************************************************************************************
Protocol management and registration routines
@@ -166,7 +151,7 @@ static int backlog_size = 0;
* For efficiency
*/
-static int dev_nit=0;
+int netdev_nit=0;
/*
* Add a protocol ID to the list. Now that the input handler is
@@ -179,7 +164,7 @@ void dev_add_pack(struct packet_type *pt)
int hash;
if(pt->type==htons(ETH_P_ALL))
{
- dev_nit++;
+ netdev_nit++;
pt->next=ptype_all;
ptype_all=pt;
}
@@ -201,7 +186,7 @@ void dev_remove_pack(struct packet_type *pt)
struct packet_type **pt1;
if(pt->type==htons(ETH_P_ALL))
{
- dev_nit--;
+ netdev_nit--;
pt1=&ptype_all;
}
else
@@ -258,7 +243,6 @@ struct device *dev_getbyhwaddr(unsigned short type, char *ha)
for (dev = dev_base; dev != NULL; dev = dev->next)
{
if (dev->type == type &&
- !(dev->flags&(IFF_LOOPBACK|IFF_NOARP)) &&
memcmp(dev->dev_addr, ha, dev->addr_len) == 0)
return(dev);
}
@@ -312,19 +296,20 @@ struct device *dev_alloc(const char *name, int *err)
void dev_load(const char *name)
{
- if(!dev_get(name)) {
-#ifdef CONFIG_NET_ALIAS
- const char *sptr;
-
- for (sptr=name ; *sptr ; sptr++) if(*sptr==':') break;
- if (!(*sptr && *(sptr+1)))
-#endif
+ if(!dev_get(name))
request_module(name);
- }
}
#endif
-
+
+static int
+default_rebuild_header(struct sk_buff *skb)
+{
+ printk(KERN_DEBUG "%s: !skb->arp & !rebuild_header -- BUG!\n", skb->dev->name);
+ kfree_skb(skb, FREE_WRITE);
+ return 1;
+}
+
/*
* Prepare an interface for use.
*/
@@ -334,6 +319,13 @@ int dev_open(struct device *dev)
int ret = 0;
/*
+ * Is it already up?
+ */
+
+ if (dev->flags&IFF_UP)
+ return 0;
+
+ /*
* Call device private open method
*/
@@ -341,29 +333,39 @@ int dev_open(struct device *dev)
ret = dev->open(dev);
/*
- * If it went open OK then set the flags
+ * If it went open OK then:
*/
if (ret == 0)
{
+ /*
+ * nil rebuild_header routine,
+ * that should be never called and used as just bug trap.
+ */
+
+ if (dev->rebuild_header == NULL)
+ dev->rebuild_header = default_rebuild_header;
+
+ /*
+ * Set the flags.
+ */
dev->flags |= (IFF_UP | IFF_RUNNING);
+
/*
- * Initialise multicasting status
+ * Initialize multicasting status
*/
dev_mc_upload(dev);
- notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
-
+
/*
- * Passive non transmitting devices (including
- * aliases) need not be on this chain.
+ * Wakeup transmit queue engine
*/
- if (!net_alias_is(dev) && dev->tx_queue_len)
- {
- cli();
- dev->next_up = dev_up_base;
- dev_up_base = dev;
- sti();
- }
+ dev_activate(dev);
+
+ /*
+ * ... and announce new interface.
+ */
+ notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+
}
return(ret);
}
@@ -375,17 +377,24 @@ int dev_open(struct device *dev)
int dev_close(struct device *dev)
{
- int ct=0;
- struct device **devp;
+ if (!(dev->flags&IFF_UP))
+ return 0;
+
+ dev_deactivate(dev);
+
+ dev_lock_wait();
/*
* Call the device specific close. This cannot fail.
* Only if device is UP
*/
- if ((dev->flags & IFF_UP) && dev->stop)
+ if (dev->stop)
dev->stop(dev);
+ if (dev->start)
+ printk("dev_close: bug %s still running\n", dev->name);
+
/*
* Device is now down.
*/
@@ -397,36 +406,7 @@ int dev_close(struct device *dev)
*/
notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
- /*
- * Flush the multicast chain
- */
- dev_mc_discard(dev);
-
- /*
- * Purge any queued packets when we down the link
- */
- while(ct<DEV_NUMBUFFS)
- {
- struct sk_buff *skb;
- while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
- kfree_skb(skb,FREE_WRITE);
- ct++;
- }
- /*
- * The device is no longer up. Drop it from the list.
- */
-
- devp = &dev_up_base;
- while (*devp)
- {
- if (*devp == dev)
- {
- *devp = dev->next_up;
- break;
- }
- devp = &(*devp)->next_up;
- }
return(0);
}
@@ -451,7 +431,7 @@ int unregister_netdevice_notifier(struct notifier_block *nb)
* taps currently in use.
*/
-static void queue_xmit_nit(struct sk_buff *skb, struct device *dev)
+void dev_queue_xmit_nit(struct sk_buff *skb, struct device *dev)
{
struct packet_type *ptype;
get_fast_time(&skb->stamp);
@@ -467,180 +447,111 @@ static void queue_xmit_nit(struct sk_buff *skb, struct device *dev)
struct sk_buff *skb2;
if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
break;
- skb2->mac.raw = skb2->data;
- skb2->nh.raw = skb2->h.raw = skb2->data + dev->hard_header_len;
- ptype->func(skb2, skb->dev, ptype);
- }
- }
-}
-
-/*
- * Send (or queue for sending) a packet.
- *
- * IMPORTANT: When this is called to resend frames. The caller MUST
- * already have locked the sk_buff. Apart from that we do the
- * rest of the magic.
- */
-
-static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
-{
- unsigned long flags;
- struct sk_buff_head *list;
- int retransmission = 0; /* used to say if the packet should go */
- /* at the front or the back of the */
- /* queue - front is a retransmit try */
- /*
- * Negative priority is used to flag a frame that is being pulled from the
- * queue front as a retransmit attempt. It therefore goes back on the queue
- * start on a failure.
- */
-
- if (pri < 0)
- {
- pri = -pri-1;
- retransmission = 1;
- }
+ /* Code, following below is wrong.
-#ifdef CONFIG_NET_DEBUG
- if (pri >= DEV_NUMBUFFS)
- {
- printk(KERN_WARNING "bad priority in do_dev_queue_xmit.\n");
- pri = 1;
- }
-#endif
-
- /*
- * If we are bridging and this is directly generated output
- * pass the frame via the bridge.
- */
-
-#ifdef CONFIG_BRIDGE
- if(skb->pkt_bridged!=IS_BRIDGED && br_stats.flags & BR_UP)
- {
- if(br_tx_frame(skb))
- return;
- }
-#endif
-
- list = dev->buffs + pri;
-
- save_flags(flags);
-
- /*
- * If this isn't a retransmission, use the first packet instead.
- * Note: We don't do strict priority ordering here. We will in
- * fact kick the queue that is our priority. The dev_tint reload
- * does strict priority queueing. In effect what we are doing here
- * is to add some random jitter to the queues and to do so by
- * saving clocks. Doing a perfect priority queue isn't a good idea
- * as you get some fascinating timing interactions.
- */
+ The only reason, why it does work is that
+ ONLY packet sockets receive outgoing
+ packets. If such a packet will be (occasionally)
+ received by normal packet handler, which expects
+ that mac header is pulled...
+ */
- if (!retransmission)
- {
- /* avoid overrunning the device queue.. */
- if (skb_queue_len(list) > dev->tx_queue_len)
- {
- dev_kfree_skb(skb, FREE_WRITE);
- return;
- }
+ /* More sensible variant. skb->nh should be correctly
+ set by sender, so that the second statement is
+ just protection against buggy protocols.
+ */
+ skb2->mac.raw = skb2->data;
- /* copy outgoing packets to any sniffer packet handlers */
- if (dev_nit)
- queue_xmit_nit(skb,dev);
+ if (skb2->nh.raw < skb2->data || skb2->nh.raw >= skb2->tail) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "protocol %04x is buggy, dev %s\n", skb2->protocol, dev->name);
+ skb2->nh.raw = skb2->data;
+ if (dev->hard_header)
+ skb2->nh.raw += dev->hard_header_len;
+ }
- if (skb_queue_len(list)) {
- cli();
- __skb_queue_tail(list, skb);
- skb = __skb_dequeue(list);
- restore_flags(flags);
+ skb2->h.raw = skb2->nh.raw;
+ skb2->pkt_type = PACKET_OUTGOING;
+ ptype->func(skb2, skb->dev, ptype);
}
}
- if (dev->hard_start_xmit(skb, dev) == 0) {
- /*
- * Packet is now solely the responsibility of the driver
- */
- return;
- }
-
- /*
- * Transmission failed, put skb back into a list. Once on the list it's safe and
- * no longer device locked (it can be freed safely from the device queue)
- */
- cli();
- __skb_queue_head(list,skb);
- restore_flags(flags);
}
/*
- * Entry point for transmitting frames.
+ * Fast path for loopback frames.
*/
+void dev_loopback_xmit(struct sk_buff *skb)
+{
+ struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+ if (newskb==NULL)
+ return;
+
+ skb_pull(newskb, newskb->nh.raw - newskb->data);
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ if (newskb->dst==NULL)
+ printk(KERN_DEBUG "BUG: packet without dst looped back 1\n");
+ netif_rx(newskb);
+}
+
int dev_queue_xmit(struct sk_buff *skb)
{
struct device *dev = skb->dev;
-
- start_bh_atomic();
+ struct Qdisc *q;
/*
* If the address has not been resolved. Call the device header rebuilder.
* This can cover all protocols and technically not just ARP either.
+ *
+ * This call must be moved to protocol layer.
+ * Now it works only for IPv6 and for IPv4 in
+ * some unusual curcumstances (eql device). --ANK
*/
- if (!skb->arp)
- {
- /*
- * FIXME: we should make the printk for no rebuild
- * header a default rebuild_header routine and drop
- * this call. Similarly we should make hard_header
- * have a default NULL operation not check conditions.
- */
- if (dev->rebuild_header)
- {
- if (dev->rebuild_header(skb))
- {
- end_bh_atomic();
- return 0;
- }
- }
- else
- printk(KERN_DEBUG "%s: !skb->arp & !rebuild_header!\n", dev->name);
+ if (!skb->arp && dev->rebuild_header(skb))
+ return 0;
+
+ q = dev->qdisc;
+ if (q->enqueue) {
+ start_bh_atomic();
+ q->enqueue(skb, q);
+ qdisc_wakeup(dev);
+ end_bh_atomic();
+ return 0;
}
- /*
- *
- * If dev is an alias, switch to its main device.
- * "arp" resolution has been made with alias device, so
- * arp entries refer to alias, not main.
- *
- */
-
- if (net_alias_is(dev))
- skb->dev = dev = net_alias_main_dev(dev);
+ /* The device has no queue. Common case for software devices:
+ loopback, all the sorts of tunnels...
- do_dev_queue_xmit(skb, dev, skb->priority);
- end_bh_atomic();
+ Really, it is unlikely that bh protection is necessary here:
+ virtual devices do not generate EOI events.
+ However, it is possible, that they rely on bh protection
+ made by us here.
+ */
+ if (dev->flags&IFF_UP) {
+ start_bh_atomic();
+ if (netdev_nit)
+ dev_queue_xmit_nit(skb,dev);
+ if (dev->hard_start_xmit(skb, dev) == 0) {
+ end_bh_atomic();
+ return 0;
+ }
+ if (net_ratelimit())
+ printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name);
+ end_bh_atomic();
+ }
+ kfree_skb(skb, FREE_WRITE);
return 0;
}
-/*
- * Fast path for loopback frames.
- */
-
-void dev_loopback_xmit(struct sk_buff *skb)
-{
- struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
- if (newskb==NULL)
- return;
- skb_pull(newskb, newskb->nh.raw - newskb->data);
- newskb->ip_summed = CHECKSUM_UNNECESSARY;
- if (newskb->dst==NULL)
- printk(KERN_DEBUG "BUG: packet without dst looped back 1\n");
- netif_rx(newskb);
-}
+/*=======================================================================
+ Receiver rotutines
+ =======================================================================*/
+int netdev_dropping = 0;
+atomic_t netdev_rx_dropped;
/*
* Receive a packet from a device driver and queue it for the upper
@@ -649,15 +560,6 @@ void dev_loopback_xmit(struct sk_buff *skb)
void netif_rx(struct sk_buff *skb)
{
- static int dropping = 0;
-
- /*
- * Any received buffers are un-owned and should be discarded
- * when freed. These will be updated later as the frames get
- * owners.
- */
-
- skb->sk = NULL;
if(skb->stamp.tv_sec==0)
get_fast_time(&skb->stamp);
@@ -665,13 +567,14 @@ void netif_rx(struct sk_buff *skb)
* Check that we aren't overdoing things.
*/
- if (!backlog_size)
- dropping = 0;
- else if (backlog_size > 300)
- dropping = 1;
+ if (!backlog.qlen)
+ netdev_dropping = 0;
+ else if (backlog.qlen > 300)
+ netdev_dropping = 1;
- if (dropping)
+ if (netdev_dropping)
{
+ atomic_inc(&netdev_rx_dropped);
kfree_skb(skb, FREE_READ);
return;
}
@@ -681,7 +584,6 @@ void netif_rx(struct sk_buff *skb)
*/
skb_queue_tail(&backlog,skb);
- backlog_size++;
/*
* If any packet arrived, mark it for processing after the
@@ -692,32 +594,37 @@ void netif_rx(struct sk_buff *skb)
return;
}
-/*
- * This routine causes all interfaces to try to send some data.
- */
-
-static void dev_transmit(void)
+#ifdef CONFIG_BRIDGE
+static inline void handle_bridge(struct skbuff *skb, unsigned short type)
{
- struct device *dev;
-
- for (dev = dev_up_base; dev != NULL; dev = dev->next_up)
+ if (br_stats.flags & BR_UP && br_protocol_ok(ntohs(type)))
{
- if (dev->flags != 0 && !dev->tbusy)
+ /*
+ * We pass the bridge a complete frame. This means
+ * recovering the MAC header first.
+ */
+
+ int offset=skb->data-skb->mac.raw;
+ cli();
+ skb_push(skb,offset); /* Put header back on for bridge */
+ if(br_receive_frame(skb))
{
- /*
- * Kick the device
- */
- dev_tint(dev);
+ sti();
+ continue;
}
+ /*
+ * Pull the MAC header off for the copy going to
+ * the upper layers.
+ */
+ skb_pull(skb,offset);
+ sti();
}
}
+#endif
-
-/**********************************************************************************
-
- Receive Queue Processor
-
-***********************************************************************************/
+#ifdef CONFIG_CPU_IS_SLOW
+int net_cpu_congestion;
+#endif
/*
* When we are called the queue is ready to grab, the interrupts are
@@ -732,7 +639,15 @@ void net_bh(void)
struct packet_type *ptype;
struct packet_type *pt_prev;
unsigned short type;
- int nit = 301;
+ unsigned long start_time = jiffies;
+#ifdef CONFIG_CPU_IS_SLOW
+ static unsigned long start_busy = 0;
+ static unsigned long ave_busy = 0;
+
+ if (start_busy == 0)
+ start_busy = start_time;
+ net_cpu_congestion = ave_busy>>8;
+#endif
/*
* Can we send anything now? We want to clear the
@@ -741,7 +656,8 @@ void net_bh(void)
* latency on a transmit interrupt bh.
*/
- dev_transmit();
+ if (qdisc_head.forw != &qdisc_head)
+ qdisc_run_queues();
/*
* Any data left to process. This may occur because a
@@ -761,55 +677,43 @@ void net_bh(void)
{
struct sk_buff * skb = backlog.next;
+ if (jiffies - start_time > 1) {
+ /* Give chance to other bottom halves to run */
+ mark_bh(NET_BH);
+ return;
+ }
+
/*
* We have a packet. Therefore the queue has shrunk
*/
cli();
__skb_unlink(skb, &backlog);
- backlog_size--;
sti();
- /*
- * We do not want to spin in net_bh infinitely. --ANK
- */
- if (--nit <= 0)
- {
- if (nit == 0)
- printk(KERN_WARNING "net_bh: too many loops, dropping...\n");
+#ifdef CONFIG_CPU_IS_SLOW
+ if (ave_busy > 128*16) {
kfree_skb(skb, FREE_WRITE);
- continue;
+ while ((skb = skb_dequeue(&backlog)) != NULL)
+ kfree_skb(skb, FREE_WRITE);
+ break;
}
+#endif
+
-#ifdef CONFIG_BRIDGE
+ /*
+ * Fetch the packet protocol ID.
+ */
+
+ type = skb->protocol;
+
+#ifdef CONFIG_BRIDGE
/*
* If we are bridging then pass the frame up to the
* bridging code (if this protocol is to be bridged).
* If it is bridged then move on
*/
-
- if (br_stats.flags & BR_UP && br_protocol_ok(ntohs(skb->protocol)))
- {
- /*
- * We pass the bridge a complete frame. This means
- * recovering the MAC header first.
- */
-
- int offset=skb->data-skb->mac.raw;
- cli();
- skb_push(skb,offset); /* Put header back on for bridge */
- if(br_receive_frame(skb))
- {
- sti();
- continue;
- }
- /*
- * Pull the MAC header off for the copy going to
- * the upper layers.
- */
- skb_pull(skb,offset);
- sti();
- }
+ handle_bridge(skb, type);
#endif
/*
@@ -823,12 +727,6 @@ void net_bh(void)
skb->h.raw = skb->nh.raw = skb->data;
/*
- * Fetch the packet protocol ID.
- */
-
- type = skb->protocol;
-
- /*
* We got a packet ID. Now loop over the "known protocols"
* list. There are two lists. The ptype_all list of taps (normally empty)
* and the main protocol list which is hashed perfectly for normal protocols.
@@ -837,15 +735,17 @@ void net_bh(void)
pt_prev = NULL;
for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next)
{
- if(pt_prev)
- {
- struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
- if(skb2)
- pt_prev->func(skb2,skb->dev, pt_prev);
+ if (!ptype->dev || ptype->dev == skb->dev) {
+ if(pt_prev)
+ {
+ struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
+ if(skb2)
+ pt_prev->func(skb2,skb->dev, pt_prev);
+ }
+ pt_prev=ptype;
}
- pt_prev=ptype;
}
-
+
for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next)
{
if (ptype->type == type && (!ptype->dev || ptype->dev==skb->dev))
@@ -872,7 +772,7 @@ void net_bh(void)
pt_prev=ptype;
}
} /* End of protocol list loop */
-
+
/*
* Is there a last item to send to ?
*/
@@ -883,16 +783,9 @@ void net_bh(void)
* Has an unknown packet has been received ?
*/
- else
+ else {
kfree_skb(skb, FREE_WRITE);
- /*
- * Again, see if we can transmit anything now.
- * [Ought to take this out judging by tests it slows
- * us down not speeds us up]
- */
-#ifdef XMIT_EVERY
- dev_transmit();
-#endif
+ }
} /* End of queue loop */
/*
@@ -903,64 +796,47 @@ void net_bh(void)
* One last output flush.
*/
- dev_transmit();
+ if (qdisc_head.forw != &qdisc_head)
+ qdisc_run_queues();
+
+#ifdef CONFIG_CPU_IS_SLOW
+{
+ unsigned long start_idle = jiffies;
+ ave_busy += ((start_idle - start_busy)<<3) - (ave_busy>>4);
+ start_busy = 0;
+}
+#endif
}
+/* Protocol dependent address dumping routines */
-/*
- * This routine is called when an device driver (i.e. an
- * interface) is ready to transmit a packet.
- */
-
-void dev_tint(struct device *dev)
+static int (*gifconf[NPROTO])(struct device *dev, char *bufptr, int len);
+
+int register_gifconf(int family, int (*func)(struct device *dev, char *bufptr, int len))
{
- int i;
- unsigned long flags;
- struct sk_buff_head * head;
-
- /*
- * aliases do not transmit (for now :) )
- */
+ if (family<0 || family>=NPROTO)
+ return -EINVAL;
+ gifconf[family] = func;
+ return 0;
+}
- if (net_alias_is(dev)) {
- printk(KERN_DEBUG "net alias %s transmits\n", dev->name);
- return;
- }
- head = dev->buffs;
- save_flags(flags);
- cli();
+/*
+ This ioctl is wrong by design. It really existed in some
+ old SYSV systems, only was named SIOCGIFNUM.
+ In multiprotocol environment it is just useless.
+ Well, SIOCGIFCONF is wrong too, but we have to preserve
+ it by compatibility reasons.
- /*
- * Work the queues in priority order
- */
- for(i = 0;i < DEV_NUMBUFFS; i++,head++)
- {
+ If someone wants to achieve the same effect, please, use undocumented
+ feature of SIOCGIFCONF: it returns buffer length, if buffer
+ is not supplied.
- while (!skb_queue_empty(head)) {
- struct sk_buff *skb;
+ Let's remove it, until someone started to use it. --ANK
- skb = head->next;
- __skb_unlink(skb, head);
- /*
- * Stop anyone freeing the buffer while we retransmit it
- */
- restore_flags(flags);
- /*
- * Feed them to the output stage and if it fails
- * indicate they re-queue at the front.
- */
- do_dev_queue_xmit(skb,dev,-i - 1);
- /*
- * If we can take no more then stop here.
- */
- if (dev->tbusy)
- return;
- cli();
- }
- }
- restore_flags(flags);
-}
+ In any case, if someone cannot live without it, it should
+ be renamed to SIOCGIFNUM.
+ */
/*
@@ -970,20 +846,26 @@ void dev_tint(struct device *dev)
static int dev_ifcount(unsigned int *arg)
{
struct device *dev;
- int err;
unsigned int count = 0;
for (dev = dev_base; dev != NULL; dev = dev->next)
count++;
- err = copy_to_user(arg, &count, sizeof(unsigned int));
- if (err)
- return -EFAULT;
- return 0;
+ return put_user(count, arg);
}
/*
- * Map an interface index to its name (SIOGIFNAME)
+ * Map an interface index to its name (SIOCGIFNAME)
+ */
+
+/*
+ * This call is useful, but I'd remove it too.
+ *
+ * The reason is purely aestetical, it is the only call
+ * from SIOC* family using struct ifreq in reversed manner.
+ * Besides that, it is pretty silly to put "drawing" facility
+ * to kernel, it is useful only to print ifindices
+ * in readable form, is not it? --ANK
*/
static int dev_ifname(struct ifreq *arg)
@@ -1019,7 +901,6 @@ static int dev_ifname(struct ifreq *arg)
static int dev_ifconf(char *arg)
{
struct ifconf ifc;
- struct ifreq ifr;
struct device *dev;
char *pos;
unsigned int len;
@@ -1031,68 +912,51 @@ static int dev_ifconf(char *arg)
err = copy_from_user(&ifc, arg, sizeof(struct ifconf));
if (err)
- return -EFAULT;
- len = ifc.ifc_len;
+ return -EFAULT;
+
pos = ifc.ifc_buf;
+ if (pos==NULL)
+ ifc.ifc_len=0;
+ len = ifc.ifc_len;
/*
- * We now walk the device list filling each active device
- * into the array.
- */
-
- /*
* Loop over the interfaces, and write an info block for each.
*/
-
- dev_lock_wait();
- dev_lock_list();
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
- /*
- * Have we run out of space here ?
- */
-
- if (len < sizeof(struct ifreq))
- break;
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ int i;
+ for (i=0; i<NPROTO; i++) {
+ int done;
+
+ if (gifconf[i] == NULL)
+ continue;
- memset(&ifr, 0, sizeof(struct ifreq));
- strcpy(ifr.ifr_name, dev->name);
- (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family;
- (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+ done = gifconf[i](dev, pos, len);
+ if (done<0)
+ return -EFAULT;
- /*
- * Write this block to the caller's space.
- */
-
- err = copy_to_user(pos, &ifr, sizeof(struct ifreq));
- if (err)
- return -EFAULT;
- pos += sizeof(struct ifreq);
- len -= sizeof(struct ifreq);
+ len -= done;
+ if (pos)
+ pos += done;
+ }
}
- dev_unlock_list();
-
/*
* All done. Write the updated control block back to the caller.
*/
-
- ifc.ifc_len = (pos - ifc.ifc_buf);
- ifc.ifc_req = (struct ifreq *) ifc.ifc_buf;
- err = copy_to_user(arg, &ifc, sizeof(struct ifconf));
- if (err)
+ ifc.ifc_len -= len;
+
+ if (copy_to_user(arg, &ifc, sizeof(struct ifconf)))
return -EFAULT;
/*
* Report how much was filled in
*/
- return(pos - arg);
+ return ifc.ifc_len;
}
-
/*
* This is invoked by the /proc filesystem handler to display a device
* in detail.
@@ -1105,7 +969,7 @@ static int sprintf_stats(char *buffer, struct device *dev)
int size;
if (stats)
- size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %4lu %8lu %8lu %4lu %4lu %4lu %5lu %4lu\n",
+ size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %4lu %8lu %8lu %4lu %4lu %4lu %5lu %4lu %4lu\n",
dev->name,
stats->rx_bytes,
stats->rx_packets, stats->rx_errors,
@@ -1117,7 +981,8 @@ static int sprintf_stats(char *buffer, struct device *dev)
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_window_errors + stats->tx_heartbeat_errors,
+ stats->multicast);
else
size = sprintf(buffer, "%6s: No statistics available.\n", dev->name);
@@ -1252,272 +1117,216 @@ int dev_get_wireless_info(char * buffer, char **start, off_t offset,
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_NET_RADIO */
+void dev_set_promiscuity(struct device *dev, int inc)
+{
+ unsigned short old_flags = dev->flags;
-/*
- * Perform the SIOCxIFxxx calls.
- *
- * The socket layer has seen an ioctl the address family thinks is
- * for the device. At this point we get invoked to make a decision
- */
-
-static int dev_ifsioc(void *arg, unsigned int getset)
+ dev->flags |= IFF_PROMISC;
+ if ((dev->promiscuity += inc) == 0)
+ dev->flags &= ~IFF_PROMISC;
+ if (dev->flags^old_flags) {
+ dev_mc_upload(dev);
+ printk(KERN_INFO "device %s %s promiscuous mode\n",
+ dev->name, (dev->flags&IFF_PROMISC) ? "entered" : "leaved");
+ }
+}
+
+void dev_set_allmulti(struct device *dev, int inc)
{
- struct ifreq ifr;
- struct device *dev;
- int ret, err;
+ unsigned short old_flags = dev->flags;
+
+ dev->flags |= IFF_ALLMULTI;
+ if ((dev->allmulti += inc) == 0)
+ dev->flags &= ~IFF_ALLMULTI;
+ if (dev->flags^old_flags)
+ dev_mc_upload(dev);
+}
+
+int dev_change_flags(struct device *dev, unsigned flags)
+{
+ int ret;
+ int old_flags = dev->flags;
/*
- * Fetch the caller's info block into kernel space
+ * Set the flags on our device.
*/
-
- err = copy_from_user(&ifr, arg, sizeof(struct ifreq));
- if (err)
- return -EFAULT;
+
+ dev->flags = (flags & (IFF_DEBUG|IFF_NOTRAILERS|IFF_RUNNING|IFF_NOARP|
+ IFF_SLAVE|IFF_MASTER|
+ IFF_MULTICAST|IFF_PORTSEL|IFF_AUTOMEDIA)) |
+ (dev->flags & (IFF_UP|IFF_VOLATILE|IFF_PROMISC));
/*
- * See which interface the caller is talking about.
- */
-
+ * Load in the correct multicast list now the flags have changed.
+ */
+
+ dev_mc_upload(dev);
+
/*
- *
- * net_alias_dev_get(): dev_get() with added alias naming magic.
- * only allow alias creation/deletion if (getset==SIOCSIFADDR)
- *
+ * Have we downed the interface. We handle IFF_UP ourselves
+ * according to user attempts to set it, rather than blindly
+ * setting it.
*/
-
-#ifdef CONFIG_KERNELD
- dev_load(ifr.ifr_name);
-#endif
-#ifdef CONFIG_NET_ALIAS
- if ((dev = net_alias_dev_get(ifr.ifr_name, getset == SIOCSIFADDR, &err, NULL, NULL)) == NULL)
- return(err);
-#else
- if ((dev = dev_get(ifr.ifr_name)) == NULL)
- return(-ENODEV);
-#endif
- switch(getset)
+ ret = 0;
+ if ((old_flags^flags)&IFF_UP) /* Bit is different ? */
+ {
+ if(old_flags&IFF_UP) /* Gone down */
+ ret=dev_close(dev);
+ else /* Come up */
+ ret=dev_open(dev);
+
+ if (ret == 0)
+ dev_mc_upload(dev);
+ }
+
+ if (dev->flags&IFF_UP &&
+ ((old_flags^dev->flags)&~(IFF_UP|IFF_RUNNING|IFF_PROMISC|IFF_VOLATILE))) {
+ printk(KERN_DEBUG "SIFFL %s(%s)\n", dev->name, current->comm);
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
+ }
+
+ if ((flags^dev->gflags)&IFF_PROMISC) {
+ int inc = (flags&IFF_PROMISC) ? +1 : -1;
+ dev->gflags ^= IFF_PROMISC;
+ dev_set_promiscuity(dev, inc);
+ }
+
+ return ret;
+}
+
+/*
+ * Perform the SIOCxIFxxx calls.
+ */
+
+static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
+{
+ struct device *dev;
+ int err;
+
+ if ((dev = dev_get(ifr->ifr_name)) == NULL)
+ return -ENODEV;
+
+ switch(cmd)
{
case SIOCGIFFLAGS: /* Get interface flags */
- ifr.ifr_flags = dev->flags;
- goto rarok;
+ ifr->ifr_flags = (dev->flags&~IFF_PROMISC)|(dev->gflags&IFF_PROMISC);
+ return 0;
case SIOCSIFFLAGS: /* Set interface flags */
- {
- int old_flags = dev->flags;
-
- /*
- * We are not allowed to potentially close/unload
- * a device until we get this lock.
- */
-
- dev_lock_wait();
- dev_lock_list();
-
- /*
- * Set the flags on our device.
- */
-
- dev->flags = (ifr.ifr_flags & (
- IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK | IFF_PORTSEL |
- IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING | IFF_AUTOMEDIA |
- IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER
- | IFF_MULTICAST)) | (dev->flags & IFF_UP);
- /*
- * Load in the correct multicast list now the flags have changed.
- */
-
- dev_mc_upload(dev);
-
- /*
- * Have we downed the interface. We handle IFF_UP ourselves
- * according to user attempts to set it, rather than blindly
- * setting it.
- */
-
- if ((old_flags^ifr.ifr_flags)&IFF_UP) /* Bit is different ? */
- {
- if(old_flags&IFF_UP) /* Gone down */
- ret=dev_close(dev);
- else /* Come up */
- {
- ret=dev_open(dev);
- if(ret<0)
- dev->flags&=~IFF_UP; /* Open failed */
- }
- }
- else
- ret=0;
- /*
- * Load in the correct multicast list now the flags have changed.
- */
-
- dev_mc_upload(dev);
- if ((dev->flags&IFF_UP) && ((old_flags^dev->flags)&~(IFF_UP|IFF_RUNNING|IFF_PROMISC)))
- {
- printk(KERN_DEBUG "SIFFL %s(%s)\n", dev->name, current->comm);
- notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
- }
- if ((dev->flags^old_flags)&IFF_PROMISC) {
- if (dev->flags&IFF_PROMISC)
- printk(KERN_INFO "%s enters promiscuous mode.\n", dev->name);
- else
- printk(KERN_INFO "%s leave promiscuous mode.\n", dev->name);
- }
- dev_unlock_list();
- }
- break;
+ return dev_change_flags(dev, ifr->ifr_flags);
case SIOCGIFMETRIC: /* Get the metric on the interface (currently unused) */
-
- ifr.ifr_metric = dev->metric;
- goto rarok;
+ ifr->ifr_metric = dev->metric;
+ return 0;
case SIOCSIFMETRIC: /* Set the metric on the interface (currently unused) */
- dev->metric = ifr.ifr_metric;
- ret=0;
- break;
+ dev->metric = ifr->ifr_metric;
+ return 0;
case SIOCGIFMTU: /* Get the MTU of a device */
- ifr.ifr_mtu = dev->mtu;
- goto rarok;
+ ifr->ifr_mtu = dev->mtu;
+ return 0;
case SIOCSIFMTU: /* Set the MTU of a device */
-
- if (ifr.ifr_mtu == dev->mtu) {
- ret = 0;
- break;
- }
+ if (ifr->ifr_mtu == dev->mtu)
+ return 0;
/*
* MTU must be positive.
*/
- if(ifr.ifr_mtu<68)
+ if (ifr->ifr_mtu<0)
return -EINVAL;
if (dev->change_mtu)
- ret = dev->change_mtu(dev, ifr.ifr_mtu);
- else
- {
- dev->mtu = ifr.ifr_mtu;
- ret = 0;
+ err = dev->change_mtu(dev, ifr->ifr_mtu);
+ else {
+ dev->mtu = ifr->ifr_mtu;
+ err = 0;
}
- if (!ret && dev->flags&IFF_UP) {
+ if (!err && dev->flags&IFF_UP) {
printk(KERN_DEBUG "SIFMTU %s(%s)\n", dev->name, current->comm);
notifier_call_chain(&netdev_chain, NETDEV_CHANGEMTU, dev);
}
- break;
-
- case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently
- do not support it */
- ret = -EINVAL;
- break;
-
- case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */
- ret = -EINVAL;
- break;
+ return err;
case SIOCGIFHWADDR:
- memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
- ifr.ifr_hwaddr.sa_family=dev->type;
- goto rarok;
+ memcpy(ifr->ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
+ ifr->ifr_hwaddr.sa_family=dev->type;
+ return 0;
case SIOCSIFHWADDR:
if(dev->set_mac_address==NULL)
return -EOPNOTSUPP;
- if(ifr.ifr_hwaddr.sa_family!=dev->type)
+ if(ifr->ifr_hwaddr.sa_family!=dev->type)
return -EINVAL;
- ret=dev->set_mac_address(dev,&ifr.ifr_hwaddr);
- if (!ret)
+ err=dev->set_mac_address(dev,&ifr->ifr_hwaddr);
+ if (!err)
notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
- break;
+ return err;
+ case SIOCSIFHWBROADCAST:
+ if(ifr->ifr_hwaddr.sa_family!=dev->type)
+ return -EINVAL;
+ memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, MAX_ADDR_LEN);
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
+ return 0;
+
case SIOCGIFMAP:
- ifr.ifr_map.mem_start=dev->mem_start;
- ifr.ifr_map.mem_end=dev->mem_end;
- ifr.ifr_map.base_addr=dev->base_addr;
- ifr.ifr_map.irq=dev->irq;
- ifr.ifr_map.dma=dev->dma;
- ifr.ifr_map.port=dev->if_port;
- goto rarok;
+ ifr->ifr_map.mem_start=dev->mem_start;
+ ifr->ifr_map.mem_end=dev->mem_end;
+ ifr->ifr_map.base_addr=dev->base_addr;
+ ifr->ifr_map.irq=dev->irq;
+ ifr->ifr_map.dma=dev->dma;
+ ifr->ifr_map.port=dev->if_port;
+ return 0;
case SIOCSIFMAP:
- if(dev->set_config==NULL)
- return -EOPNOTSUPP;
- return dev->set_config(dev,&ifr.ifr_map);
+ if (dev->set_config)
+ return dev->set_config(dev,&ifr->ifr_map);
+ return -EOPNOTSUPP;
case SIOCADDMULTI:
- if(dev->set_multicast_list==NULL)
- return -EINVAL;
- if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC)
+ if(dev->set_multicast_list==NULL ||
+ ifr->ifr_hwaddr.sa_family!=AF_UNSPEC)
return -EINVAL;
- dev_mc_add(dev,ifr.ifr_hwaddr.sa_data, dev->addr_len, 1);
+ dev_mc_add(dev,ifr->ifr_hwaddr.sa_data, dev->addr_len, 1);
return 0;
case SIOCDELMULTI:
- if(dev->set_multicast_list==NULL)
+ if(dev->set_multicast_list==NULL ||
+ ifr->ifr_hwaddr.sa_family!=AF_UNSPEC)
return -EINVAL;
- if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC)
- return -EINVAL;
- dev_mc_delete(dev,ifr.ifr_hwaddr.sa_data,dev->addr_len, 1);
+ dev_mc_delete(dev,ifr->ifr_hwaddr.sa_data,dev->addr_len, 1);
return 0;
- case SIOGIFINDEX:
- ifr.ifr_ifindex = dev->ifindex;
- goto rarok;
-
+ case SIOCGIFINDEX:
+ ifr->ifr_ifindex = dev->ifindex;
+ return 0;
/*
* Unknown or private ioctl
*/
default:
- if((getset >= SIOCDEVPRIVATE) &&
- (getset <= (SIOCDEVPRIVATE + 15))) {
- if(dev->do_ioctl==NULL)
- return -EOPNOTSUPP;
- ret = dev->do_ioctl(dev, &ifr, getset);
- if (!ret)
- {
- err = copy_to_user(arg,&ifr,sizeof(struct ifreq));
- if (err)
- ret = -EFAULT;
- }
- break;
+ if(cmd >= SIOCDEVPRIVATE &&
+ cmd <= SIOCDEVPRIVATE + 15) {
+ if (dev->do_ioctl)
+ return dev->do_ioctl(dev, ifr, cmd);
+ return -EOPNOTSUPP;
}
#ifdef CONFIG_NET_RADIO
- if((getset >= SIOCIWFIRST) && (getset <= SIOCIWLAST))
- {
- if(dev->do_ioctl==NULL)
- return -EOPNOTSUPP;
- /* Perform the ioctl */
- ret=dev->do_ioctl(dev, &ifr, getset);
- /* If return args... */
- if(IW_IS_GET(getset))
- {
- if (copy_to_user(arg, &ifr,
- sizeof(struct ifreq)))
- {
- ret = -EFAULT;
- }
- }
- break;
+ if(cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+ if (dev->do_ioctl)
+ return dev->do_ioctl(dev, ifr, cmd);
+ return -EOPNOTSUPP;
}
#endif /* CONFIG_NET_RADIO */
- ret = -EINVAL;
}
- return(ret);
-/*
- * The load of calls that return an ifreq and ok (saves memory).
- */
-rarok:
- err = copy_to_user(arg, &ifr, sizeof(struct ifreq));
- if (err)
- err = -EFAULT;
- return err;
+ return -EINVAL;
}
@@ -1528,47 +1337,98 @@ rarok:
int dev_ioctl(unsigned int cmd, void *arg)
{
+ struct ifreq ifr;
+ int ret;
+#ifdef CONFIG_NET_ALIAS
+ char *colon;
+#endif
+
+ /* One special case: SIOCGIFCONF takes ifconf argument
+ and requires shared lock, because it sleeps writing
+ to user space.
+ */
+
+ if (cmd == SIOCGIFCONF) {
+ rtnl_shlock();
+ dev_ifconf((char *) arg);
+ rtnl_shunlock();
+ return 0;
+ }
+ if (cmd == SIOCGIFCOUNT) {
+ return dev_ifcount((unsigned int*)arg);
+ }
+ if (cmd == SIOCGIFNAME) {
+ return dev_ifname((struct ifreq *)arg);
+ }
+
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
+
+ ifr.ifr_name[IFNAMSIZ-1] = 0;
+
+#ifdef CONFIG_NET_ALIAS
+ colon = strchr(ifr.ifr_name, ':');
+ if (colon)
+ *colon = 0;
+#endif
+
+ /*
+ * See which interface the caller is talking about.
+ */
+
+#ifdef CONFIG_KERNELD
+ dev_load(ifr.ifr_name);
+#endif
+
switch(cmd)
{
- case SIOCGIFCONF:
- (void) dev_ifconf((char *) arg);
- return 0;
- case SIOCGIFCOUNT:
- return dev_ifcount((unsigned int *) arg);
- case SIOGIFNAME:
- return dev_ifname((struct ifreq *)arg);
-
/*
- * Ioctl calls that can be done by all.
+ * These ioctl calls:
+ * - can be done by all.
+ * - atomic and do not require locking.
+ * - return a value
*/
case SIOCGIFFLAGS:
case SIOCGIFMETRIC:
case SIOCGIFMTU:
- case SIOCGIFMEM:
case SIOCGIFHWADDR:
case SIOCGIFSLAVE:
case SIOCGIFMAP:
- case SIOGIFINDEX:
- return dev_ifsioc(arg, cmd);
+ case SIOCGIFINDEX:
+ ret = dev_ifsioc(&ifr, cmd);
+ if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ return ret;
/*
- * Ioctl calls requiring the power of a superuser
+ * These ioctl calls:
+ * - require superuser power.
+ * - require strict serialization.
+ * - do not return a value
*/
case SIOCSIFFLAGS:
case SIOCSIFMETRIC:
case SIOCSIFMTU:
- case SIOCSIFMEM:
- case SIOCSIFHWADDR:
case SIOCSIFMAP:
+ case SIOCSIFHWADDR:
case SIOCSIFSLAVE:
case SIOCADDMULTI:
case SIOCDELMULTI:
+ case SIOCSIFHWBROADCAST:
if (!suser())
return -EPERM;
- return dev_ifsioc(arg, cmd);
+ rtnl_lock();
+ ret = dev_ifsioc(&ifr, cmd);
+ rtnl_unlock();
+ return ret;
+ case SIOCGIFMEM:
+ /* Get the per device memory space. We can add this but currently
+ do not support it */
+ case SIOCSIFMEM:
+ /* Set the per device memory buffer space. Not applicable in our case */
case SIOCSIFLINK:
return -EINVAL;
@@ -1577,16 +1437,29 @@ int dev_ioctl(unsigned int cmd, void *arg)
*/
default:
- if((cmd >= SIOCDEVPRIVATE) &&
- (cmd <= (SIOCDEVPRIVATE + 15))) {
- return dev_ifsioc(arg, cmd);
+ if (cmd >= SIOCDEVPRIVATE &&
+ cmd <= SIOCDEVPRIVATE + 15) {
+ rtnl_lock();
+ ret = dev_ifsioc(&ifr, cmd);
+ rtnl_unlock();
+ if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ return ret;
}
#ifdef CONFIG_NET_RADIO
- if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST))
- {
- if((IW_IS_SET(cmd)) && (!suser()))
- return -EPERM;
- return dev_ifsioc(arg, cmd);
+ if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+ if (IW_IS_SET(cmd)) {
+ if (!suser())
+ return -EPERM;
+ rtnl_lock();
+ }
+ ret = dev_ifsioc(&ifr, cmd);
+ if (IW_IS_SET(cmd))
+ rtnl_unlock();
+ if (!ret && IW_IS_GET(cmd) &&
+ copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ return ret;
}
#endif /* CONFIG_NET_RADIO */
return -EINVAL;
@@ -1596,9 +1469,103 @@ int dev_ioctl(unsigned int cmd, void *arg)
int dev_new_index()
{
static int ifindex;
- return ++ifindex;
+ for (;;) {
+ if (++ifindex <= 0)
+ ifindex=1;
+ if (dev_get_by_index(ifindex) == NULL)
+ return ifindex;
+ }
+}
+
+static int dev_boot_phase = 1;
+
+
+int register_netdevice(struct device *dev)
+{
+ struct device *d, **dp;
+
+ if (dev_boot_phase) {
+ printk(KERN_INFO "early initialization of device %s is deferred\n", dev->name);
+
+ /* Check for existence, and append to tail of chain */
+ for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
+ if (d == dev || strcmp(d->name, dev->name) == 0)
+ return -EEXIST;
+ }
+ dev->next = NULL;
+ *dp = dev;
+ return 0;
+ }
+
+ dev->iflink = -1;
+
+ /* Init, if this function is available */
+ if (dev->init && dev->init(dev) != 0)
+ return -EIO;
+
+ /* Check for existence, and append to tail of chain */
+ for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
+ if (d == dev || strcmp(d->name, dev->name) == 0)
+ return -EEXIST;
+ }
+ dev->next = NULL;
+ dev_init_scheduler(dev);
+ dev->ifindex = dev_new_index();
+ if (dev->iflink == -1)
+ dev->iflink = dev->ifindex;
+ *dp = dev;
+
+ /* Notify protocols, that a new device appeared. */
+ notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
+
+ return 0;
+}
+
+int unregister_netdevice(struct device *dev)
+{
+ struct device *d, **dp;
+
+ if (dev_boot_phase == 0) {
+ /* If device is running, close it.
+ It is very bad idea, really we should
+ complain loudly here, but random hackery
+ in linux/drivers/net likes it.
+ */
+ if (dev->flags & IFF_UP)
+ dev_close(dev);
+
+ /* Shutdown queueing discipline. */
+ dev_shutdown(dev);
+
+ /* Notify protocols, that we are about to destroy
+ this device. They should clean all the things.
+ */
+ notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);
+
+ /*
+ * Flush the multicast chain
+ */
+ dev_mc_discard(dev);
+
+ /* To avoid pointers looking to nowhere,
+ we wait for end of critical section */
+ dev_lock_wait();
+ }
+
+ /* And unlink it from device chain. */
+ for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
+ if (d == dev) {
+ *dp = d->next;
+ d->next = NULL;
+ if (dev->destructor)
+ dev->destructor(dev);
+ return 0;
+ }
+ }
+ return -ENODEV;
}
+
/*
* Initialize the DEV module. At boot time this walks the device list and
* unhooks any devices that fail to initialise (normally hardware not
@@ -1606,14 +1573,15 @@ int dev_new_index()
*
*/
extern int lance_init(void);
-extern int pi_init(void);
extern int bpq_init(void);
extern int scc_init(void);
extern void sdla_setup(void);
extern void dlci_setup(void);
-extern int pt_init(void);
+extern int dmascc_init(void);
extern int sm_init(void);
-extern int baycom_init(void);
+extern int baycom_ser_fdx_init(void);
+extern int baycom_ser_hdx_init(void);
+extern int baycom_par_init(void);
extern int lapbeth_init(void);
extern void arcnet_init(void);
@@ -1641,6 +1609,8 @@ __initfunc(int net_dev_init(void))
{
struct device *dev, **dp;
+ pktsched_init();
+
/*
* Initialise the packet receive queue.
*/
@@ -1660,18 +1630,16 @@ __initfunc(int net_dev_init(void))
*
* Some devices want to be initialized early..
*/
+
#if defined(CONFIG_LANCE)
lance_init();
#endif
-#if defined(CONFIG_PI)
- pi_init();
-#endif
#if defined(CONFIG_SCC)
scc_init();
#endif
-#if defined(CONFIG_PT)
- pt_init();
-#endif
+#if defined(CONFIG_DMASCC)
+ dmascc_init();
+#endif
#if defined(CONFIG_BPQETHER)
bpq_init();
#endif
@@ -1681,8 +1649,14 @@ __initfunc(int net_dev_init(void))
#if defined(CONFIG_SDLA)
sdla_setup();
#endif
-#if defined(CONFIG_BAYCOM)
- baycom_init();
+#if defined(CONFIG_BAYCOM_PAR)
+ baycom_par_init();
+#endif
+#if defined(CONFIG_BAYCOM_SER_FDX)
+ baycom_ser_fdx_init();
+#endif
+#if defined(CONFIG_BAYCOM_SER_HDX)
+ baycom_ser_hdx_init();
#endif
#if defined(CONFIG_SOUNDMODEM)
sm_init();
@@ -1706,6 +1680,7 @@ __initfunc(int net_dev_init(void))
slhc_install();
#endif
+
/*
* Add the devices.
* If the call to dev->init fails, the dev is removed
@@ -1716,11 +1691,7 @@ __initfunc(int net_dev_init(void))
dp = &dev_base;
while ((dev = *dp) != NULL)
{
- int i;
- for (i = 0; i < DEV_NUMBUFFS; i++) {
- skb_queue_head_init(dev->buffs + i);
- }
-
+ dev->iflink = -1;
if (dev->init && dev->init(dev))
{
/*
@@ -1732,6 +1703,9 @@ __initfunc(int net_dev_init(void))
{
dp = &dev->next;
dev->ifindex = dev_new_index();
+ if (dev->iflink == -1)
+ dev->iflink = dev->ifindex;
+ dev_init_scheduler(dev);
}
}
@@ -1745,18 +1719,13 @@ __initfunc(int net_dev_init(void))
#endif /* CONFIG_PROC_FS */
#endif /* CONFIG_NET_RADIO */
- /*
- * Initialise net_alias engine
- *
- * - register net_alias device notifier
- * - register proc entries: /proc/net/alias_types
- * /proc/net/aliases
- */
+ init_bh(NET_BH, net_bh);
-#ifdef CONFIG_NET_ALIAS
- net_alias_init();
+ dev_boot_phase = 0;
+
+#ifdef CONFIG_IP_PNP
+ ip_auto_config();
#endif
- init_bh(NET_BH, net_bh);
return 0;
}