From 5803c5122acb31ebf5f76b1a9925e2c72c4436e1 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Jan 2009 13:01:08 +0000 Subject: arcnet: convert to internal stats Use pre-existing network_device_stats inside network_device rather than own private structure. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/arcdevice.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/arcdevice.h b/include/linux/arcdevice.h index a1916078fd08..ef0d6b7df44c 100644 --- a/include/linux/arcdevice.h +++ b/include/linux/arcdevice.h @@ -235,8 +235,6 @@ struct Outgoing { struct arcnet_local { - struct net_device_stats stats; - uint8_t config, /* current value of CONFIG register */ timeout, /* Extended timeout for COM20020 */ backplane, /* Backplane flag for COM20020 */ -- cgit v1.2.3 From bca5b8939f107e498b3fdc92b3a2d286a868d347 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Jan 2009 13:01:09 +0000 Subject: arcnet: convert to net_device_ops Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/arcnet/arcnet.c | 33 ++++++++++++++++----------------- include/linux/arcdevice.h | 7 ++++++- 2 files changed, 22 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index 34b9a4d0da30..a80d4a30a464 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -95,12 +95,12 @@ EXPORT_SYMBOL(arcnet_unregister_proto); EXPORT_SYMBOL(arcnet_debug); EXPORT_SYMBOL(alloc_arcdev); EXPORT_SYMBOL(arcnet_interrupt); +EXPORT_SYMBOL(arcnet_open); +EXPORT_SYMBOL(arcnet_close); +EXPORT_SYMBOL(arcnet_send_packet); +EXPORT_SYMBOL(arcnet_timeout); /* Internal function prototypes */ -static int arcnet_open(struct net_device *dev); -static int arcnet_close(struct net_device *dev); -static int arcnet_send_packet(struct sk_buff *skb, struct net_device *dev); -static void arcnet_timeout(struct net_device *dev); static int arcnet_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, const void *saddr, unsigned len); @@ -321,11 +321,18 @@ static const struct header_ops arcnet_header_ops = { .rebuild = arcnet_rebuild_header, }; +static const struct net_device_ops arcnet_netdev_ops = { + .ndo_open = arcnet_open, + .ndo_stop = arcnet_close, + .ndo_start_xmit = arcnet_send_packet, + .ndo_tx_timeout = arcnet_timeout, +}; /* Setup a struct device for ARCnet. */ static void arcdev_setup(struct net_device *dev) { dev->type = ARPHRD_ARCNET; + dev->netdev_ops = &arcnet_netdev_ops; dev->header_ops = &arcnet_header_ops; dev->hard_header_len = sizeof(struct archdr); dev->mtu = choose_mtu(); @@ -338,17 +345,9 @@ static void arcdev_setup(struct net_device *dev) /* New-style flags. */ dev->flags = IFF_BROADCAST; - /* - * Put in this stuff here, so we don't have to export the symbols to - * the chipset drivers. - */ - dev->open = arcnet_open; - dev->stop = arcnet_close; - dev->hard_start_xmit = arcnet_send_packet; - dev->tx_timeout = arcnet_timeout; } -struct net_device *alloc_arcdev(char *name) +struct net_device *alloc_arcdev(const char *name) { struct net_device *dev; @@ -370,7 +369,7 @@ struct net_device *alloc_arcdev(char *name) * that "should" only need to be set once at boot, so that there is * non-reboot way to recover if something goes wrong. */ -static int arcnet_open(struct net_device *dev) +int arcnet_open(struct net_device *dev) { struct arcnet_local *lp = netdev_priv(dev); int count, newmtu, error; @@ -470,7 +469,7 @@ static int arcnet_open(struct net_device *dev) /* The inverse routine to arcnet_open - shuts down the card. */ -static int arcnet_close(struct net_device *dev) +int arcnet_close(struct net_device *dev) { struct arcnet_local *lp = netdev_priv(dev); @@ -599,7 +598,7 @@ static int arcnet_rebuild_header(struct sk_buff *skb) /* Called by the kernel in order to transmit a packet. */ -static int arcnet_send_packet(struct sk_buff *skb, struct net_device *dev) +int arcnet_send_packet(struct sk_buff *skb, struct net_device *dev) { struct arcnet_local *lp = netdev_priv(dev); struct archdr *pkt; @@ -718,7 +717,7 @@ static int go_tx(struct net_device *dev) /* Called by the kernel when transmit times out */ -static void arcnet_timeout(struct net_device *dev) +void arcnet_timeout(struct net_device *dev) { unsigned long flags; struct arcnet_local *lp = netdev_priv(dev); diff --git a/include/linux/arcdevice.h b/include/linux/arcdevice.h index ef0d6b7df44c..cd4bcb6989ce 100644 --- a/include/linux/arcdevice.h +++ b/include/linux/arcdevice.h @@ -333,7 +333,12 @@ void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc); void arcnet_unregister_proto(struct ArcProto *proto); irqreturn_t arcnet_interrupt(int irq, void *dev_id); -struct net_device *alloc_arcdev(char *name); +struct net_device *alloc_arcdev(const char *name); + +int arcnet_open(struct net_device *dev); +int arcnet_close(struct net_device *dev); +int arcnet_send_packet(struct sk_buff *skb, struct net_device *dev); +void arcnet_timeout(struct net_device *dev); #endif /* __KERNEL__ */ #endif /* _LINUX_ARCDEVICE_H */ -- cgit v1.2.3 From a1799af4d7deefccdaa9d222a886fa1373dbb49a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Jan 2009 13:01:10 +0000 Subject: com20020: convert to net_devic_ops Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/arcnet/com20020-isa.c | 2 ++ drivers/net/arcnet/com20020-pci.c | 3 +++ drivers/net/arcnet/com20020.c | 10 ++++++++-- include/linux/com20020.h | 1 + 4 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c index ea53a940272f..db08fc24047a 100644 --- a/drivers/net/arcnet/com20020-isa.c +++ b/drivers/net/arcnet/com20020-isa.c @@ -151,6 +151,8 @@ static int __init com20020_init(void) if (node && node != 0xff) dev->dev_addr[0] = node; + dev->netdev_ops = &com20020_netdev_ops; + lp = netdev_priv(dev); lp->backplane = backplane; lp->clockp = clockp & 7; diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index 8b51f632581d..dbf4de39754d 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -72,6 +72,9 @@ static int __devinit com20020pci_probe(struct pci_dev *pdev, const struct pci_de dev = alloc_arcdev(device); if (!dev) return -ENOMEM; + + dev->netdev_ops = &com20020_netdev_ops; + lp = netdev_priv(dev); pci_set_drvdata(pdev, dev); diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c index 103688358fb8..bbe8f2ccdadb 100644 --- a/drivers/net/arcnet/com20020.c +++ b/drivers/net/arcnet/com20020.c @@ -149,6 +149,14 @@ int com20020_check(struct net_device *dev) return 0; } +const struct net_device_ops com20020_netdev_ops = { + .ndo_open = arcnet_open, + .ndo_stop = arcnet_close, + .ndo_start_xmit = arcnet_send_packet, + .ndo_tx_timeout = arcnet_timeout, + .ndo_set_multicast_list = com20020_set_mc_list, +}; + /* Set up the struct net_device associated with this card. Called after * probing succeeds. */ @@ -170,8 +178,6 @@ int com20020_found(struct net_device *dev, int shared) lp->hw.copy_from_card = com20020_copy_from_card; lp->hw.close = com20020_close; - dev->set_multicast_list = com20020_set_mc_list; - if (!dev->dev_addr[0]) dev->dev_addr[0] = inb(ioaddr + BUS_ALIGN*8); /* FIXME: do this some other way! */ diff --git a/include/linux/com20020.h b/include/linux/com20020.h index ac6d9a43e085..350afa773f8f 100644 --- a/include/linux/com20020.h +++ b/include/linux/com20020.h @@ -29,6 +29,7 @@ int com20020_check(struct net_device *dev); int com20020_found(struct net_device *dev, int shared); +const struct net_device_ops com20020_netdev_ops; /* The number of low I/O ports used by the card. */ #define ARCNET_TOTAL_SIZE 8 -- cgit v1.2.3 From 9fd3238e95046b61d518ddacaa767fa09f31b0d0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Jan 2009 13:01:19 +0000 Subject: ibmtr: convert to internal network_device_stats Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/tokenring/ibmtr.c | 29 ++++++----------------------- include/linux/ibmtr.h | 2 +- 2 files changed, 7 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index fa7bce6e0c6d..f195d54ae421 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -200,7 +200,6 @@ static void tr_rx(struct net_device *dev); static void ibmtr_reset_timer(struct timer_list*tmr,struct net_device *dev); static void tok_rerun(unsigned long dev_addr); static void ibmtr_readlog(struct net_device *dev); -static struct net_device_stats *tok_get_stats(struct net_device *dev); static int ibmtr_change_mtu(struct net_device *dev, int mtu); static void find_turbo_adapters(int *iolist); @@ -825,7 +824,6 @@ static int __devinit trdev_init(struct net_device *dev) dev->open = tok_open; dev->stop = tok_close; dev->hard_start_xmit = tok_send_packet; - dev->get_stats = tok_get_stats; dev->set_multicast_list = tok_set_multicast_list; dev->change_mtu = ibmtr_change_mtu; @@ -1460,7 +1458,7 @@ static irqreturn_t tok_interrupt(int irq, void *dev_id) "%02X\n", (int)retcode, (int)readb(ti->ssb + 6)); else - ti->tr_stats.tx_packets++; + dev->stats.tx_packets++; break; case XMIT_XID_CMD: DPRINTK("xmit xid ret_code: %02X\n", @@ -1646,7 +1644,7 @@ static void tr_tx(struct net_device *dev) break; } writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - ti->tr_stats.tx_bytes += ti->current_skb->len; + dev->stats.tx_bytes += ti->current_skb->len; dev_kfree_skb_irq(ti->current_skb); ti->current_skb = NULL; netif_wake_queue(dev); @@ -1722,7 +1720,7 @@ static void tr_rx(struct net_device *dev) if (readb(llc + offsetof(struct trllc, llc)) != UI_CMD) { SET_PAGE(ti->asb_page); writeb(DATA_LOST, ti->asb + RETCODE_OFST); - ti->tr_stats.rx_dropped++; + dev->stats.rx_dropped++; writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); return; } @@ -1757,7 +1755,7 @@ static void tr_rx(struct net_device *dev) if (!(skb = dev_alloc_skb(skb_size))) { DPRINTK("out of memory. frame dropped.\n"); - ti->tr_stats.rx_dropped++; + dev->stats.rx_dropped++; SET_PAGE(ti->asb_page); writeb(DATA_LOST, ti->asb + offsetof(struct asb_rec, ret_code)); writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); @@ -1813,8 +1811,8 @@ static void tr_rx(struct net_device *dev) writeb(RESP_IN_ASB, ti->mmio + ACA_OFFSET + ACA_SET + ISRA_ODD); - ti->tr_stats.rx_bytes += skb->len; - ti->tr_stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + dev->stats.rx_packets++; skb->protocol = tr_type_trans(skb, dev); if (IPv4_p) { @@ -1876,21 +1874,6 @@ static void ibmtr_readlog(struct net_device *dev) /*****************************************************************************/ -/* tok_get_stats(): Basically a scaffold routine which will return - the address of the tr_statistics structure associated with - this device -- the tr.... structure is an ethnet look-alike - so at least for this iteration may suffice. */ - -static struct net_device_stats *tok_get_stats(struct net_device *dev) -{ - - struct tok_info *toki; - toki = netdev_priv(dev); - return (struct net_device_stats *) &toki->tr_stats; -} - -/*****************************************************************************/ - static int ibmtr_change_mtu(struct net_device *dev, int mtu) { struct tok_info *ti = netdev_priv(dev); diff --git a/include/linux/ibmtr.h b/include/linux/ibmtr.h index 1c7a0dd5536a..06695b74d405 100644 --- a/include/linux/ibmtr.h +++ b/include/linux/ibmtr.h @@ -207,7 +207,7 @@ struct tok_info { unsigned short exsap_station_id; unsigned short global_int_enable; struct sk_buff *current_skb; - struct net_device_stats tr_stats; + unsigned char auto_speedsave; open_state open_status, sap_status; enum {MANUAL, AUTOMATIC} open_mode; -- cgit v1.2.3 From 5a7616af604caf0d436a1ed0d4298bb25cd77d67 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 9 Jan 2009 13:01:35 +0000 Subject: hdlcdrv: convert to internal net_device_stats Signed-off-by: Stephen Hemminger Acked-by: Thomas Sailer Signed-off-by: David S. Miller --- drivers/net/hamradio/hdlcdrv.c | 27 +++++++-------------------- include/linux/hdlcdrv.h | 1 - 2 files changed, 7 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index 8eba61a1d4ab..1215a49c38f1 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -154,7 +154,7 @@ static void hdlc_rx_flag(struct net_device *dev, struct hdlcdrv_state *s) pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */ if (!(skb = dev_alloc_skb(pkt_len))) { printk("%s: memory squeeze, dropping packet\n", dev->name); - s->stats.rx_dropped++; + dev->stats.rx_dropped++; return; } cp = skb_put(skb, pkt_len); @@ -162,7 +162,7 @@ static void hdlc_rx_flag(struct net_device *dev, struct hdlcdrv_state *s) memcpy(cp, s->hdlcrx.buffer, pkt_len - 1); skb->protocol = ax25_type_trans(skb, dev); netif_rx(skb); - s->stats.rx_packets++; + dev->stats.rx_packets++; } void hdlcdrv_receiver(struct net_device *dev, struct hdlcdrv_state *s) @@ -326,7 +326,7 @@ void hdlcdrv_transmitter(struct net_device *dev, struct hdlcdrv_state *s) s->hdlctx.len = pkt_len+2; /* the appended CRC */ s->hdlctx.tx_state = 2; s->hdlctx.bitstream = 0; - s->stats.tx_packets++; + dev->stats.tx_packets++; break; case 2: if (!s->hdlctx.len) { @@ -426,19 +426,6 @@ static int hdlcdrv_set_mac_address(struct net_device *dev, void *addr) return 0; } -/* --------------------------------------------------------------------- */ - -static struct net_device_stats *hdlcdrv_get_stats(struct net_device *dev) -{ - struct hdlcdrv_state *sm = netdev_priv(dev); - - /* - * Get the current statistics. This may be called with the - * card open or closed. - */ - return &sm->stats; -} - /* --------------------------------------------------------------------- */ /* * Open/initialize the board. This is called (in the current kernel) @@ -568,10 +555,10 @@ static int hdlcdrv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) bi.data.cs.ptt = hdlcdrv_ptt(s); bi.data.cs.dcd = s->hdlcrx.dcd; bi.data.cs.ptt_keyed = s->ptt_keyed; - bi.data.cs.tx_packets = s->stats.tx_packets; - bi.data.cs.tx_errors = s->stats.tx_errors; - bi.data.cs.rx_packets = s->stats.rx_packets; - bi.data.cs.rx_errors = s->stats.rx_errors; + bi.data.cs.tx_packets = dev->stats.tx_packets; + bi.data.cs.tx_errors = dev->stats.tx_errors; + bi.data.cs.rx_packets = dev->stats.rx_packets; + bi.data.cs.rx_errors = dev->stats.rx_errors; break; case HDLCDRVCTL_OLDGETSTAT: diff --git a/include/linux/hdlcdrv.h b/include/linux/hdlcdrv.h index bf6302f6b5f8..0821bac62b83 100644 --- a/include/linux/hdlcdrv.h +++ b/include/linux/hdlcdrv.h @@ -241,7 +241,6 @@ struct hdlcdrv_state { struct hdlcdrv_bitbuffer bitbuf_hdlc; #endif /* HDLCDRV_DEBUG */ - struct net_device_stats stats; int ptt_keyed; /* queued skb for transmission */ -- cgit v1.2.3 From 7cdc15f5f9db71e9c92422918ab9f8df0d31f81f Mon Sep 17 00:00:00 2001 From: Krzysztof Hałasa Date: Thu, 8 Jan 2009 19:46:54 +0100 Subject: WAN: Generic HDLC now uses IFF_WAN_HDLC private flag. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Krzysztof Hałasa Signed-off-by: David S. Miller --- drivers/net/wan/hdlc.c | 3 ++- include/linux/if.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index 1f2a140c9f7c..d83cd7884e05 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -106,7 +106,7 @@ static int hdlc_device_event(struct notifier_block *this, unsigned long event, if (dev_net(dev) != &init_net) return NOTIFY_DONE; - if (dev->get_stats != hdlc_get_stats) + if (!(dev->priv_flags & IFF_WAN_HDLC)) return NOTIFY_DONE; /* not an HDLC device */ if (event != NETDEV_CHANGE) @@ -235,6 +235,7 @@ static void hdlc_setup_dev(struct net_device *dev) */ dev->get_stats = hdlc_get_stats; dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->priv_flags = IFF_WAN_HDLC; dev->mtu = HDLC_MAX_MTU; dev->type = ARPHRD_RAWHDLC; dev->hard_header_len = 16; diff --git a/include/linux/if.h b/include/linux/if.h index 2a6e29620a96..1108f3e099e3 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -66,6 +66,7 @@ #define IFF_SLAVE_NEEDARP 0x40 /* need ARPs for validation */ #define IFF_ISATAP 0x80 /* ISATAP interface (RFC4214) */ #define IFF_MASTER_ARPMON 0x100 /* bonding master, ARP mon in use */ +#define IFF_WAN_HDLC 0x200 /* WAN HDLC device */ #define IF_GET_IFACE 0x0001 /* for querying only */ #define IF_GET_PROTO 0x0002 -- cgit v1.2.3 From 991990a12de42281f81b4e3a6471586d2d0caf6a Mon Sep 17 00:00:00 2001 From: Krzysztof Hałasa Date: Thu, 8 Jan 2009 22:52:11 +0100 Subject: WAN: Convert generic HDLC drivers to netdev_ops. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also remove unneeded last_rx update from Synclink drivers. Synclink part mostly by Stephen Hemminger. Signed-off-by: Krzysztof Hałasa Signed-off-by: David S. Miller --- drivers/char/pcmcia/synclink_cs.c | 18 +++++++++++------- drivers/char/synclink.c | 18 +++++++++++------- drivers/char/synclink_gt.c | 18 +++++++++++------- drivers/char/synclinkmp.c | 18 +++++++++++------- drivers/net/wan/c101.c | 12 ++++++++---- drivers/net/wan/cosa.c | 14 ++++++++++---- drivers/net/wan/dscc4.c | 18 +++++++++++------- drivers/net/wan/farsync.c | 18 ++++++++++++------ drivers/net/wan/hdlc.c | 14 +++++++++++--- drivers/net/wan/hdlc_cisco.c | 1 - drivers/net/wan/hdlc_fr.c | 28 +++++++++------------------- drivers/net/wan/hdlc_ppp.c | 2 -- drivers/net/wan/hdlc_raw.c | 3 --- drivers/net/wan/hdlc_raw_eth.c | 8 ++------ drivers/net/wan/hdlc_x25.c | 2 +- drivers/net/wan/hostess_sv11.c | 12 +++++++++--- drivers/net/wan/ixp4xx_hss.c | 12 +++++++++--- drivers/net/wan/lmc/lmc_main.c | 19 +++++++++++-------- drivers/net/wan/lmc/lmc_proto.c | 17 +---------------- drivers/net/wan/n2.c | 12 ++++++++---- drivers/net/wan/pc300too.c | 12 ++++++++---- drivers/net/wan/pci200syn.c | 12 ++++++++---- drivers/net/wan/sealevel.c | 12 +++++++++--- drivers/net/wan/wanxl.c | 14 ++++++++++---- include/linux/hdlc.h | 5 +++++ 25 files changed, 186 insertions(+), 133 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index dc073e167abc..5608a1e5a3b3 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -4311,10 +4311,17 @@ static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size) dev->stats.rx_bytes += size; netif_rx(skb); - - dev->last_rx = jiffies; } +static const struct net_device_ops hdlcdev_ops = { + .ndo_open = hdlcdev_open, + .ndo_stop = hdlcdev_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = hdlcdev_ioctl, + .ndo_tx_timeout = hdlcdev_tx_timeout, +}; + /** * called by device driver when adding device instance * do generic HDLC initialization @@ -4341,11 +4348,8 @@ static int hdlcdev_init(MGSLPC_INFO *info) dev->irq = info->irq_level; /* network layer callbacks and settings */ - dev->do_ioctl = hdlcdev_ioctl; - dev->open = hdlcdev_open; - dev->stop = hdlcdev_close; - dev->tx_timeout = hdlcdev_tx_timeout; - dev->watchdog_timeo = 10*HZ; + dev->netdev_ops = &hdlcdev_ops; + dev->watchdog_timeo = 10 * HZ; dev->tx_queue_len = 50; /* generic HDLC layer callbacks and settings */ diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index b8063d4cad32..0057a8f58cb1 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -8007,10 +8007,17 @@ static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size) dev->stats.rx_bytes += size; netif_rx(skb); - - dev->last_rx = jiffies; } +static const struct net_device_ops hdlcdev_ops = { + .ndo_open = hdlcdev_open, + .ndo_stop = hdlcdev_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = hdlcdev_ioctl, + .ndo_tx_timeout = hdlcdev_tx_timeout, +}; + /** * called by device driver when adding device instance * do generic HDLC initialization @@ -8038,11 +8045,8 @@ static int hdlcdev_init(struct mgsl_struct *info) dev->dma = info->dma_level; /* network layer callbacks and settings */ - dev->do_ioctl = hdlcdev_ioctl; - dev->open = hdlcdev_open; - dev->stop = hdlcdev_close; - dev->tx_timeout = hdlcdev_tx_timeout; - dev->watchdog_timeo = 10*HZ; + dev->netdev_ops = &hdlcdev_ops; + dev->watchdog_timeo = 10 * HZ; dev->tx_queue_len = 50; /* generic HDLC layer callbacks and settings */ diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index f329f459817c..efb3dc928a43 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -1763,10 +1763,17 @@ static void hdlcdev_rx(struct slgt_info *info, char *buf, int size) dev->stats.rx_bytes += size; netif_rx(skb); - - dev->last_rx = jiffies; } +static const struct net_device_ops hdlcdev_ops = { + .ndo_open = hdlcdev_open, + .ndo_stop = hdlcdev_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = hdlcdev_ioctl, + .ndo_tx_timeout = hdlcdev_tx_timeout, +}; + /** * called by device driver when adding device instance * do generic HDLC initialization @@ -1794,11 +1801,8 @@ static int hdlcdev_init(struct slgt_info *info) dev->irq = info->irq_level; /* network layer callbacks and settings */ - dev->do_ioctl = hdlcdev_ioctl; - dev->open = hdlcdev_open; - dev->stop = hdlcdev_close; - dev->tx_timeout = hdlcdev_tx_timeout; - dev->watchdog_timeo = 10*HZ; + dev->netdev_ops = &hdlcdev_ops; + dev->watchdog_timeo = 10 * HZ; dev->tx_queue_len = 50; /* generic HDLC layer callbacks and settings */ diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 7b0c5b2dd263..8eb6c89a980e 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -1907,10 +1907,17 @@ static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size) dev->stats.rx_bytes += size; netif_rx(skb); - - dev->last_rx = jiffies; } +static const struct net_device_ops hdlcdev_ops = { + .ndo_open = hdlcdev_open, + .ndo_stop = hdlcdev_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = hdlcdev_ioctl, + .ndo_tx_timeout = hdlcdev_tx_timeout, +}; + /** * called by device driver when adding device instance * do generic HDLC initialization @@ -1938,11 +1945,8 @@ static int hdlcdev_init(SLMP_INFO *info) dev->irq = info->irq_level; /* network layer callbacks and settings */ - dev->do_ioctl = hdlcdev_ioctl; - dev->open = hdlcdev_open; - dev->stop = hdlcdev_close; - dev->tx_timeout = hdlcdev_tx_timeout; - dev->watchdog_timeo = 10*HZ; + dev->netdev_ops = &hdlcdev_ops; + dev->watchdog_timeo = 10 * HZ; dev->tx_queue_len = 50; /* generic HDLC layer callbacks and settings */ diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c index b46897996f7e..9693b0fd323d 100644 --- a/drivers/net/wan/c101.c +++ b/drivers/net/wan/c101.c @@ -296,7 +296,13 @@ static void c101_destroy_card(card_t *card) kfree(card); } - +static const struct net_device_ops c101_ops = { + .ndo_open = c101_open, + .ndo_stop = c101_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = c101_ioctl, +}; static int __init c101_run(unsigned long irq, unsigned long winbase) { @@ -367,9 +373,7 @@ static int __init c101_run(unsigned long irq, unsigned long winbase) dev->mem_start = winbase; dev->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1; dev->tx_queue_len = 50; - dev->do_ioctl = c101_ioctl; - dev->open = c101_open; - dev->stop = c101_close; + dev->netdev_ops = &c101_ops; hdlc->attach = sca_attach; hdlc->xmit = sca_xmit; card->settings.clock_type = CLOCK_EXT; diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index d80b72e22dea..0d7ba117ef60 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -427,6 +427,15 @@ static void __exit cosa_exit(void) } module_exit(cosa_exit); +static const struct net_device_ops cosa_ops = { + .ndo_open = cosa_net_open, + .ndo_stop = cosa_net_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = cosa_net_ioctl, + .ndo_tx_timeout = cosa_net_timeout, +}; + static int cosa_probe(int base, int irq, int dma) { struct cosa_data *cosa = cosa_cards+nr_cards; @@ -575,10 +584,7 @@ static int cosa_probe(int base, int irq, int dma) } dev_to_hdlc(chan->netdev)->attach = cosa_net_attach; dev_to_hdlc(chan->netdev)->xmit = cosa_net_tx; - chan->netdev->open = cosa_net_open; - chan->netdev->stop = cosa_net_close; - chan->netdev->do_ioctl = cosa_net_ioctl; - chan->netdev->tx_timeout = cosa_net_timeout; + chan->netdev->netdev_ops = &cosa_ops; chan->netdev->watchdog_timeo = TX_TIMEOUT; chan->netdev->base_addr = chan->cosa->datareg; chan->netdev->irq = chan->cosa->irq; diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index 888025db2f02..8face5db8f32 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -883,6 +883,15 @@ static inline int dscc4_set_quartz(struct dscc4_dev_priv *dpriv, int hz) return ret; } +static const struct net_device_ops dscc4_ops = { + .ndo_open = dscc4_open, + .ndo_stop = dscc4_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = dscc4_ioctl, + .ndo_tx_timeout = dscc4_tx_timeout, +}; + static int dscc4_found1(struct pci_dev *pdev, void __iomem *ioaddr) { struct dscc4_pci_priv *ppriv; @@ -916,13 +925,8 @@ static int dscc4_found1(struct pci_dev *pdev, void __iomem *ioaddr) hdlc_device *hdlc = dev_to_hdlc(d); d->base_addr = (unsigned long)ioaddr; - d->init = NULL; d->irq = pdev->irq; - d->open = dscc4_open; - d->stop = dscc4_close; - d->set_multicast_list = NULL; - d->do_ioctl = dscc4_ioctl; - d->tx_timeout = dscc4_tx_timeout; + d->netdev_ops = &dscc4_ops; d->watchdog_timeo = TX_TIMEOUT; SET_NETDEV_DEV(d, &pdev->dev); @@ -1048,7 +1052,7 @@ static int dscc4_open(struct net_device *dev) struct dscc4_pci_priv *ppriv; int ret = -EAGAIN; - if ((dscc4_loopback_check(dpriv) < 0) || !dev->hard_start_xmit) + if ((dscc4_loopback_check(dpriv) < 0)) goto err; if ((ret = hdlc_open(dev))) diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 48a2c9d28950..00945f7c1e9b 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -2424,6 +2424,15 @@ fst_init_card(struct fst_card_info *card) type_strings[card->type], card->irq, card->nports); } +static const struct net_device_ops fst_ops = { + .ndo_open = fst_open, + .ndo_stop = fst_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = fst_ioctl, + .ndo_tx_timeout = fst_tx_timeout, +}; + /* * Initialise card when detected. * Returns 0 to indicate success, or errno otherwise. @@ -2565,12 +2574,9 @@ fst_add_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->base_addr = card->pci_conf; dev->irq = card->irq; - dev->tx_queue_len = FST_TX_QUEUE_LEN; - dev->open = fst_open; - dev->stop = fst_close; - dev->do_ioctl = fst_ioctl; - dev->watchdog_timeo = FST_TX_TIMEOUT; - dev->tx_timeout = fst_tx_timeout; + dev->netdev_ops = &fst_ops; + dev->tx_queue_len = FST_TX_QUEUE_LEN; + dev->watchdog_timeo = FST_TX_TIMEOUT; hdlc->attach = fst_attach; hdlc->xmit = fst_start_xmit; } diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index dbc179887f8b..43da8bd72973 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -44,7 +44,7 @@ static const char* version = "HDLC support module revision 1.22"; static struct hdlc_proto *first_proto; -static int hdlc_change_mtu(struct net_device *dev, int new_mtu) +int hdlc_change_mtu(struct net_device *dev, int new_mtu) { if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) return -EINVAL; @@ -66,7 +66,15 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, return hdlc->proto->netif_rx(skb); } +int hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + hdlc_device *hdlc = dev_to_hdlc(dev); + if (hdlc->proto->xmit) + return hdlc->proto->xmit(skb, dev); + + return hdlc->xmit(skb, dev); /* call hardware driver directly */ +} static inline void hdlc_proto_start(struct net_device *dev) { @@ -231,8 +239,6 @@ static void hdlc_setup_dev(struct net_device *dev) dev->hard_header_len = 16; dev->addr_len = 0; dev->header_ops = &hdlc_null_ops; - - dev->change_mtu = hdlc_change_mtu; } static void hdlc_setup(struct net_device *dev) @@ -330,6 +336,8 @@ MODULE_AUTHOR("Krzysztof Halasa "); MODULE_DESCRIPTION("HDLC support module"); MODULE_LICENSE("GPL v2"); +EXPORT_SYMBOL(hdlc_change_mtu); +EXPORT_SYMBOL(hdlc_start_xmit); EXPORT_SYMBOL(hdlc_open); EXPORT_SYMBOL(hdlc_close); EXPORT_SYMBOL(hdlc_ioctl); diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index 44e64b15dbd1..af3fd4fead8a 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -382,7 +382,6 @@ static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr) memcpy(&state(hdlc)->settings, &new_settings, size); spin_lock_init(&state(hdlc)->lock); - dev->hard_start_xmit = hdlc->xmit; dev->header_ops = &cisco_header_ops; dev->type = ARPHRD_CISCO; netif_dormant_on(dev); diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index f1ddd7c3459c..70e57cebc955 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -444,18 +444,6 @@ static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } - - -static int pvc_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - - - static inline void fr_log_dlci_active(pvc_device *pvc) { printk(KERN_INFO "%s: DLCI %d [%s%s%s]%s %s\n", @@ -1068,6 +1056,14 @@ static void pvc_setup(struct net_device *dev) dev->addr_len = 2; } +static const struct net_device_ops pvc_ops = { + .ndo_open = pvc_open, + .ndo_stop = pvc_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = pvc_xmit, + .ndo_do_ioctl = pvc_ioctl, +}; + static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) { hdlc_device *hdlc = dev_to_hdlc(frad); @@ -1104,11 +1100,7 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) *(__be16*)dev->dev_addr = htons(dlci); dlci_to_q922(dev->broadcast, dlci); } - dev->hard_start_xmit = pvc_xmit; - dev->open = pvc_open; - dev->stop = pvc_close; - dev->do_ioctl = pvc_ioctl; - dev->change_mtu = pvc_change_mtu; + dev->netdev_ops = &pvc_ops; dev->mtu = HDLC_MAX_MTU; dev->tx_queue_len = 0; dev->ml_priv = pvc; @@ -1260,8 +1252,6 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr) state(hdlc)->dce_pvc_count = 0; } memcpy(&state(hdlc)->settings, &new_settings, size); - - dev->hard_start_xmit = hdlc->xmit; dev->type = ARPHRD_FRAD; return 0; diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c index 57fe714c1c7f..7b8a5eae201d 100644 --- a/drivers/net/wan/hdlc_ppp.c +++ b/drivers/net/wan/hdlc_ppp.c @@ -558,7 +558,6 @@ out: return NET_RX_DROP; } - static void ppp_timer(unsigned long arg) { struct proto *proto = (struct proto *)arg; @@ -679,7 +678,6 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr) ppp->keepalive_interval = 10; ppp->keepalive_timeout = 60; - dev->hard_start_xmit = hdlc->xmit; dev->hard_header_len = sizeof(struct hdlc_header); dev->header_ops = &ppp_header_ops; dev->type = ARPHRD_PPP; diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c index 8612311748f4..6e92c64ebd0f 100644 --- a/drivers/net/wan/hdlc_raw.c +++ b/drivers/net/wan/hdlc_raw.c @@ -30,8 +30,6 @@ static __be16 raw_type_trans(struct sk_buff *skb, struct net_device *dev) return __constant_htons(ETH_P_IP); } - - static struct hdlc_proto proto = { .type_trans = raw_type_trans, .ioctl = raw_ioctl, @@ -86,7 +84,6 @@ static int raw_ioctl(struct net_device *dev, struct ifreq *ifr) if (result) return result; memcpy(hdlc->state, &new_settings, size); - dev->hard_start_xmit = hdlc->xmit; dev->type = ARPHRD_RAWHDLC; netif_dormant_off(dev); return 0; diff --git a/drivers/net/wan/hdlc_raw_eth.c b/drivers/net/wan/hdlc_raw_eth.c index a13fc3207520..49e68f5ca5f2 100644 --- a/drivers/net/wan/hdlc_raw_eth.c +++ b/drivers/net/wan/hdlc_raw_eth.c @@ -45,6 +45,7 @@ static int eth_tx(struct sk_buff *skb, struct net_device *dev) static struct hdlc_proto proto = { .type_trans = eth_type_trans, + .xmit = eth_tx, .ioctl = raw_eth_ioctl, .module = THIS_MODULE, }; @@ -56,9 +57,7 @@ static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr) const size_t size = sizeof(raw_hdlc_proto); raw_hdlc_proto new_settings; hdlc_device *hdlc = dev_to_hdlc(dev); - int result; - int (*old_ch_mtu)(struct net_device *, int); - int old_qlen; + int result, old_qlen; switch (ifr->ifr_settings.type) { case IF_GET_PROTO: @@ -99,11 +98,8 @@ static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr) if (result) return result; memcpy(hdlc->state, &new_settings, size); - dev->hard_start_xmit = eth_tx; - old_ch_mtu = dev->change_mtu; old_qlen = dev->tx_queue_len; ether_setup(dev); - dev->change_mtu = old_ch_mtu; dev->tx_queue_len = old_qlen; random_ether_addr(dev->dev_addr); netif_dormant_off(dev); diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c index cbcbf6f0414c..b1dc29ed1583 100644 --- a/drivers/net/wan/hdlc_x25.c +++ b/drivers/net/wan/hdlc_x25.c @@ -184,6 +184,7 @@ static struct hdlc_proto proto = { .close = x25_close, .ioctl = x25_ioctl, .netif_rx = x25_rx, + .xmit = x25_xmit, .module = THIS_MODULE, }; @@ -213,7 +214,6 @@ static int x25_ioctl(struct net_device *dev, struct ifreq *ifr) if ((result = attach_hdlc_protocol(dev, &proto, 0))) return result; - dev->hard_start_xmit = x25_xmit; dev->type = ARPHRD_X25; netif_dormant_off(dev); return 0; diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c index af54f0cf1b35..567d4f5062d6 100644 --- a/drivers/net/wan/hostess_sv11.c +++ b/drivers/net/wan/hostess_sv11.c @@ -173,6 +173,14 @@ static int hostess_attach(struct net_device *dev, unsigned short encoding, * Description block for a Comtrol Hostess SV11 card */ +static const struct net_device_ops hostess_ops = { + .ndo_open = hostess_open, + .ndo_stop = hostess_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = hostess_ioctl, +}; + static struct z8530_dev *sv11_init(int iobase, int irq) { struct z8530_dev *sv; @@ -267,9 +275,7 @@ static struct z8530_dev *sv11_init(int iobase, int irq) dev_to_hdlc(netdev)->attach = hostess_attach; dev_to_hdlc(netdev)->xmit = hostess_queue_xmit; - netdev->open = hostess_open; - netdev->stop = hostess_close; - netdev->do_ioctl = hostess_ioctl; + netdev->netdev_ops = &hostess_ops; netdev->base_addr = iobase; netdev->irq = irq; diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index 0dbd85b0162d..7e8bbba2cc1b 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -1230,6 +1230,14 @@ static int hss_hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) * initialization ****************************************************************************/ +static const struct net_device_ops hss_hdlc_ops = { + .ndo_open = hss_hdlc_open, + .ndo_stop = hss_hdlc_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = hss_hdlc_ioctl, +}; + static int __devinit hss_init_one(struct platform_device *pdev) { struct port *port; @@ -1254,9 +1262,7 @@ static int __devinit hss_init_one(struct platform_device *pdev) hdlc = dev_to_hdlc(dev); hdlc->attach = hss_hdlc_attach; hdlc->xmit = hss_hdlc_xmit; - dev->open = hss_hdlc_open; - dev->stop = hss_hdlc_close; - dev->do_ioctl = hss_hdlc_ioctl; + dev->netdev_ops = &hss_hdlc_ops; dev->tx_queue_len = 100; port->clock_type = CLOCK_EXT; port->clock_rate = 2048000; diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index feac3b99f8fe..45b1822c962d 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -806,6 +806,16 @@ static int lmc_attach(struct net_device *dev, unsigned short encoding, return -EINVAL; } +static const struct net_device_ops lmc_ops = { + .ndo_open = lmc_open, + .ndo_stop = lmc_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = lmc_ioctl, + .ndo_tx_timeout = lmc_driver_timeout, + .ndo_get_stats = lmc_get_stats, +}; + static int __devinit lmc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -849,11 +859,7 @@ static int __devinit lmc_init_one(struct pci_dev *pdev, dev->type = ARPHRD_HDLC; dev_to_hdlc(dev)->xmit = lmc_start_xmit; dev_to_hdlc(dev)->attach = lmc_attach; - dev->open = lmc_open; - dev->stop = lmc_close; - dev->get_stats = lmc_get_stats; - dev->do_ioctl = lmc_ioctl; - dev->tx_timeout = lmc_driver_timeout; + dev->netdev_ops = &lmc_ops; dev->watchdog_timeo = HZ; /* 1 second */ dev->tx_queue_len = 100; sc->lmc_device = dev; @@ -1059,9 +1065,6 @@ static int lmc_open(struct net_device *dev) if ((err = lmc_proto_open(sc)) != 0) return err; - dev->do_ioctl = lmc_ioctl; - - netif_start_queue(dev); sc->extra_stats.tx_tbusy0++; diff --git a/drivers/net/wan/lmc/lmc_proto.c b/drivers/net/wan/lmc/lmc_proto.c index 94b4c208b013..044a48175c42 100644 --- a/drivers/net/wan/lmc/lmc_proto.c +++ b/drivers/net/wan/lmc/lmc_proto.c @@ -51,30 +51,15 @@ void lmc_proto_attach(lmc_softc_t *sc) /*FOLD00*/ { lmc_trace(sc->lmc_device, "lmc_proto_attach in"); - switch(sc->if_type){ - case LMC_PPP: - { - struct net_device *dev = sc->lmc_device; - dev->do_ioctl = lmc_ioctl; - } - break; - case LMC_NET: - { + if (sc->if_type == LMC_NET) { struct net_device *dev = sc->lmc_device; /* * They set a few basics because they don't use HDLC */ dev->flags |= IFF_POINTOPOINT; - dev->hard_header_len = 0; dev->addr_len = 0; } - case LMC_RAW: /* Setup the task queue, maybe we should notify someone? */ - { - } - default: - break; - } lmc_trace(sc->lmc_device, "lmc_proto_attach out"); } diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c index 697715ae80f4..83da596e2052 100644 --- a/drivers/net/wan/n2.c +++ b/drivers/net/wan/n2.c @@ -324,7 +324,13 @@ static void n2_destroy_card(card_t *card) kfree(card); } - +static const struct net_device_ops n2_ops = { + .ndo_open = n2_open, + .ndo_stop = n2_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = n2_ioctl, +}; static int __init n2_run(unsigned long io, unsigned long irq, unsigned long winbase, long valid0, long valid1) @@ -460,9 +466,7 @@ static int __init n2_run(unsigned long io, unsigned long irq, dev->mem_start = winbase; dev->mem_end = winbase + USE_WINDOWSIZE - 1; dev->tx_queue_len = 50; - dev->do_ioctl = n2_ioctl; - dev->open = n2_open; - dev->stop = n2_close; + dev->netdev_ops = &n2_ops; hdlc->attach = sca_attach; hdlc->xmit = sca_xmit; port->settings.clock_type = CLOCK_EXT; diff --git a/drivers/net/wan/pc300too.c b/drivers/net/wan/pc300too.c index f247e5d9002a..60ece54bdd94 100644 --- a/drivers/net/wan/pc300too.c +++ b/drivers/net/wan/pc300too.c @@ -287,7 +287,13 @@ static void pc300_pci_remove_one(struct pci_dev *pdev) kfree(card); } - +static const struct net_device_ops pc300_ops = { + .ndo_open = pc300_open, + .ndo_stop = pc300_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = pc300_ioctl, +}; static int __devinit pc300_pci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -448,9 +454,7 @@ static int __devinit pc300_pci_init_one(struct pci_dev *pdev, dev->mem_start = ramphys; dev->mem_end = ramphys + ramsize - 1; dev->tx_queue_len = 50; - dev->do_ioctl = pc300_ioctl; - dev->open = pc300_open; - dev->stop = pc300_close; + dev->netdev_ops = &pc300_ops; hdlc->attach = sca_attach; hdlc->xmit = sca_xmit; port->settings.clock_type = CLOCK_EXT; diff --git a/drivers/net/wan/pci200syn.c b/drivers/net/wan/pci200syn.c index 1104d3a692f7..e035d8c57e11 100644 --- a/drivers/net/wan/pci200syn.c +++ b/drivers/net/wan/pci200syn.c @@ -265,7 +265,13 @@ static void pci200_pci_remove_one(struct pci_dev *pdev) kfree(card); } - +static const struct net_device_ops pci200_ops = { + .ndo_open = pci200_open, + .ndo_stop = pci200_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = pci200_ioctl, +}; static int __devinit pci200_pci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -395,9 +401,7 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev, dev->mem_start = ramphys; dev->mem_end = ramphys + ramsize - 1; dev->tx_queue_len = 50; - dev->do_ioctl = pci200_ioctl; - dev->open = pci200_open; - dev->stop = pci200_close; + dev->netdev_ops = &pci200_ops; hdlc->attach = sca_attach; hdlc->xmit = sca_xmit; port->settings.clock_type = CLOCK_EXT; diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c index 0941a26f6e3f..23b269027453 100644 --- a/drivers/net/wan/sealevel.c +++ b/drivers/net/wan/sealevel.c @@ -169,6 +169,14 @@ static int sealevel_attach(struct net_device *dev, unsigned short encoding, return -EINVAL; } +static const struct net_device_ops sealevel_ops = { + .ndo_open = sealevel_open, + .ndo_stop = sealevel_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = sealevel_ioctl, +}; + static int slvl_setup(struct slvl_device *sv, int iobase, int irq) { struct net_device *dev = alloc_hdlcdev(sv); @@ -177,9 +185,7 @@ static int slvl_setup(struct slvl_device *sv, int iobase, int irq) dev_to_hdlc(dev)->attach = sealevel_attach; dev_to_hdlc(dev)->xmit = sealevel_queue_xmit; - dev->open = sealevel_open; - dev->stop = sealevel_close; - dev->do_ioctl = sealevel_ioctl; + dev->netdev_ops = &sealevel_ops; dev->base_addr = iobase; dev->irq = irq; diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index 4bffb67ebcae..887acb0dc807 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -547,6 +547,15 @@ static void wanxl_pci_remove_one(struct pci_dev *pdev) #include "wanxlfw.inc" +static const struct net_device_ops wanxl_ops = { + .ndo_open = wanxl_open, + .ndo_stop = wanxl_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = wanxl_ioctl, + .ndo_get_stats = wanxl_get_stats, +}; + static int __devinit wanxl_pci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -777,12 +786,9 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev, hdlc = dev_to_hdlc(dev); spin_lock_init(&port->lock); dev->tx_queue_len = 50; - dev->do_ioctl = wanxl_ioctl; - dev->open = wanxl_open; - dev->stop = wanxl_close; + dev->netdev_ops = &wanxl_ops; hdlc->attach = wanxl_attach; hdlc->xmit = wanxl_xmit; - dev->get_stats = wanxl_get_stats; port->card = card; port->node = i; get_status(port)->clocking = CLOCK_EXT; diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h index fd47a151665e..6a6e701f1631 100644 --- a/include/linux/hdlc.h +++ b/include/linux/hdlc.h @@ -38,6 +38,7 @@ struct hdlc_proto { int (*ioctl)(struct net_device *dev, struct ifreq *ifr); __be16 (*type_trans)(struct sk_buff *skb, struct net_device *dev); int (*netif_rx)(struct sk_buff *skb); + int (*xmit)(struct sk_buff *skb, struct net_device *dev); struct module *module; struct hdlc_proto *next; /* next protocol in the list */ }; @@ -102,6 +103,10 @@ static __inline__ void debug_frame(const struct sk_buff *skb) int hdlc_open(struct net_device *dev); /* Must be called by hardware driver when HDLC device is being closed */ void hdlc_close(struct net_device *dev); +/* May be used by hardware driver */ +int hdlc_change_mtu(struct net_device *dev, int new_mtu); +/* Must be pointed to by hw driver's dev->netdev_ops->ndo_start_xmit */ +int hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev); int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, size_t size); -- cgit v1.2.3 From 5ec99fdf8e1a6ee90fce22c2fce94871ab44e8ba Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Wed, 14 Jan 2009 20:34:04 -0800 Subject: sc92031: use device id directly instead of made-up name Instead of making up a name for the device ids, put them directly in the device id table. Also move the vendor id to pci_ids.h. Signed-off-by: Cesar Eduardo Barros Signed-off-by: David S. Miller --- drivers/net/sc92031.c | 8 ++------ include/linux/pci_ids.h | 2 ++ 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c index d24acdcdb157..00dfddbf2f9b 100644 --- a/drivers/net/sc92031.c +++ b/drivers/net/sc92031.c @@ -31,10 +31,6 @@ #include -#define PCI_VENDOR_ID_SILAN 0x1904 -#define PCI_DEVICE_ID_SILAN_SC92031 0x2031 -#define PCI_DEVICE_ID_SILAN_8139D 0x8139 - #define SC92031_NAME "sc92031" /* BAR 0 is MMIO, BAR 1 is PIO */ @@ -1592,8 +1588,8 @@ out: } static struct pci_device_id sc92031_pci_device_id_table[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_SILAN, PCI_DEVICE_ID_SILAN_SC92031) }, - { PCI_DEVICE(PCI_VENDOR_ID_SILAN, PCI_DEVICE_ID_SILAN_8139D) }, + { PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x2031) }, + { PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x8139) }, { 0, } }; MODULE_DEVICE_TABLE(pci, sc92031_pci_device_id_table); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index d56ad9c21c09..302423afa136 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2209,6 +2209,8 @@ #define PCI_VENDOR_ID_TOPSPIN 0x1867 +#define PCI_VENDOR_ID_SILAN 0x1904 + #define PCI_VENDOR_ID_TDI 0x192E #define PCI_DEVICE_ID_TDI_EHCI 0x0101 -- cgit v1.2.3 From 288379f050284087578b77e04f040b57db3db3f8 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 19 Jan 2009 16:43:59 -0800 Subject: net: Remove redundant NAPI functions Following the removal of the unused struct net_device * parameter from the NAPI functions named *netif_rx_* in commit 908a7a1, they are exactly equivalent to the corresponding *napi_* functions and are therefore redundant. Signed-off-by: Ben Hutchings Acked-by: Neil Horman Signed-off-by: David S. Miller --- drivers/infiniband/hw/nes/nes_hw.c | 2 +- drivers/infiniband/hw/nes/nes_nic.c | 2 +- drivers/infiniband/ulp/ipoib/ipoib_ib.c | 6 ++-- drivers/net/8139cp.c | 6 ++-- drivers/net/8139too.c | 6 ++-- drivers/net/amd8111e.c | 6 ++-- drivers/net/arm/ep93xx_eth.c | 8 +++--- drivers/net/arm/ixp4xx_eth.c | 12 ++++---- drivers/net/atl1e/atl1e_main.c | 6 ++-- drivers/net/b44.c | 6 ++-- drivers/net/bnx2.c | 12 ++++---- drivers/net/bnx2x_main.c | 6 ++-- drivers/net/cassini.c | 8 +++--- drivers/net/chelsio/sge.c | 4 +-- drivers/net/cpmac.c | 10 +++---- drivers/net/e100.c | 6 ++-- drivers/net/e1000/e1000_main.c | 10 +++---- drivers/net/e1000e/netdev.c | 14 ++++----- drivers/net/ehea/ehea_main.c | 8 +++--- drivers/net/enic/enic_main.c | 12 ++++---- drivers/net/epic100.c | 6 ++-- drivers/net/forcedeth.c | 10 +++---- drivers/net/fs_enet/fs_enet-main.c | 4 +-- drivers/net/gianfar.c | 6 ++-- drivers/net/ibmveth.c | 8 +++--- drivers/net/igb/igb_main.c | 12 ++++---- drivers/net/ixgb/ixgb_main.c | 6 ++-- drivers/net/ixgbe/ixgbe_main.c | 12 ++++---- drivers/net/ixp2000/ixpdev.c | 4 +-- drivers/net/jme.h | 6 ++-- drivers/net/korina.c | 4 +-- drivers/net/macb.c | 10 +++---- drivers/net/mlx4/en_rx.c | 4 +-- drivers/net/myri10ge/myri10ge.c | 6 ++-- drivers/net/natsemi.c | 6 ++-- drivers/net/netxen/netxen_nic_main.c | 2 +- drivers/net/niu.c | 6 ++-- drivers/net/pasemi_mac.c | 6 ++-- drivers/net/pcnet32.c | 6 ++-- drivers/net/qla3xxx.c | 6 ++-- drivers/net/qlge/qlge_main.c | 6 ++-- drivers/net/r6040.c | 4 +-- drivers/net/r8169.c | 6 ++-- drivers/net/s2io.c | 8 +++--- drivers/net/sb1250-mac.c | 6 ++-- drivers/net/sfc/efx.c | 4 +-- drivers/net/sfc/efx.h | 2 +- drivers/net/skge.c | 6 ++-- drivers/net/smsc911x.c | 8 +++--- drivers/net/smsc9420.c | 4 +-- drivers/net/spider_net.c | 12 ++++---- drivers/net/starfire.c | 6 ++-- drivers/net/sungem.c | 6 ++-- drivers/net/tc35815.c | 6 ++-- drivers/net/tehuti.c | 6 ++-- drivers/net/tg3.c | 14 ++++----- drivers/net/tsi108_eth.c | 8 +++--- drivers/net/tulip/interrupt.c | 10 +++---- drivers/net/typhoon.c | 6 ++-- drivers/net/ucc_geth.c | 6 ++-- drivers/net/via-rhine.c | 4 +-- drivers/net/virtio_net.c | 12 ++++---- drivers/net/wan/hd64572.c | 4 +-- drivers/net/wan/ixp4xx_hss.c | 12 ++++---- drivers/net/xen-netfront.c | 8 +++--- include/linux/netdevice.h | 50 --------------------------------- 66 files changed, 227 insertions(+), 277 deletions(-) (limited to 'include/linux') diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c index 5d139db1b771..53df9de23423 100644 --- a/drivers/infiniband/hw/nes/nes_hw.c +++ b/drivers/infiniband/hw/nes/nes_hw.c @@ -2541,7 +2541,7 @@ static void nes_nic_napi_ce_handler(struct nes_device *nesdev, struct nes_hw_nic { struct nes_vnic *nesvnic = container_of(cq, struct nes_vnic, nic_cq); - netif_rx_schedule(&nesvnic->napi); + napi_schedule(&nesvnic->napi); } diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index 57a47cf7e513..f5484ad1279b 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -111,7 +111,7 @@ static int nes_netdev_poll(struct napi_struct *napi, int budget) nes_nic_ce_handler(nesdev, nescq); if (nescq->cqes_pending == 0) { - netif_rx_complete(napi); + napi_complete(napi); /* clear out completed cqes and arm */ nes_write32(nesdev->regs+NES_CQE_ALLOC, NES_CQE_ALLOC_NOTIFY_NEXT | nescq->cq_number | (nescq->cqe_allocs_pending << 16)); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index a1925810be3c..da6082739839 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -446,11 +446,11 @@ poll_more: if (dev->features & NETIF_F_LRO) lro_flush_all(&priv->lro.lro_mgr); - netif_rx_complete(napi); + napi_complete(napi); if (unlikely(ib_req_notify_cq(priv->recv_cq, IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS)) && - netif_rx_reschedule(napi)) + napi_reschedule(napi)) goto poll_more; } @@ -462,7 +462,7 @@ void ipoib_ib_completion(struct ib_cq *cq, void *dev_ptr) struct net_device *dev = dev_ptr; struct ipoib_dev_priv *priv = netdev_priv(dev); - netif_rx_schedule(&priv->napi); + napi_schedule(&priv->napi); } static void drain_tx_cq(struct net_device *dev) diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 4e19ae3ce6be..35517b06ec3f 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -604,7 +604,7 @@ rx_next: spin_lock_irqsave(&cp->lock, flags); cpw16_f(IntrMask, cp_intr_mask); - __netif_rx_complete(napi); + __napi_complete(napi); spin_unlock_irqrestore(&cp->lock, flags); } @@ -641,9 +641,9 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance) } if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr)) - if (netif_rx_schedule_prep(&cp->napi)) { + if (napi_schedule_prep(&cp->napi)) { cpw16_f(IntrMask, cp_norx_intr_mask); - __netif_rx_schedule(&cp->napi); + __napi_schedule(&cp->napi); } if (status & (TxOK | TxErr | TxEmpty | SWInt)) diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index a5b24202d564..5341da604e84 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -2128,7 +2128,7 @@ static int rtl8139_poll(struct napi_struct *napi, int budget) */ spin_lock_irqsave(&tp->lock, flags); RTL_W16_F(IntrMask, rtl8139_intr_mask); - __netif_rx_complete(napi); + __napi_complete(napi); spin_unlock_irqrestore(&tp->lock, flags); } spin_unlock(&tp->rx_lock); @@ -2178,9 +2178,9 @@ static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance) /* Receive packets are processed by poll routine. If not running start it now. */ if (status & RxAckBits){ - if (netif_rx_schedule_prep(&tp->napi)) { + if (napi_schedule_prep(&tp->napi)) { RTL_W16_F (IntrMask, rtl8139_norx_intr_mask); - __netif_rx_schedule(&tp->napi); + __napi_schedule(&tp->napi); } } diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c index 7709992bb6bf..cb9c95d3ed0a 100644 --- a/drivers/net/amd8111e.c +++ b/drivers/net/amd8111e.c @@ -831,7 +831,7 @@ static int amd8111e_rx_poll(struct napi_struct *napi, int budget) if (rx_pkt_limit > 0) { /* Receive descriptor is empty now */ spin_lock_irqsave(&lp->lock, flags); - __netif_rx_complete(napi); + __napi_complete(napi); writel(VAL0|RINTEN0, mmio + INTEN0); writel(VAL2 | RDMD0, mmio + CMD0); spin_unlock_irqrestore(&lp->lock, flags); @@ -1170,11 +1170,11 @@ static irqreturn_t amd8111e_interrupt(int irq, void *dev_id) /* Check if Receive Interrupt has occurred. */ if (intr0 & RINT0) { - if (netif_rx_schedule_prep(&lp->napi)) { + if (napi_schedule_prep(&lp->napi)) { /* Disable receive interupts */ writel(RINTEN0, mmio + INTEN0); /* Schedule a polling routine */ - __netif_rx_schedule(&lp->napi); + __napi_schedule(&lp->napi); } else if (intren0 & RINTEN0) { printk("************Driver bug! \ interrupt while in poll\n"); diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c index 3ec20cc18b0c..cc7708775da0 100644 --- a/drivers/net/arm/ep93xx_eth.c +++ b/drivers/net/arm/ep93xx_eth.c @@ -298,7 +298,7 @@ poll_some_more: int more = 0; spin_lock_irq(&ep->rx_lock); - __netif_rx_complete(napi); + __napi_complete(napi); wrl(ep, REG_INTEN, REG_INTEN_TX | REG_INTEN_RX); if (ep93xx_have_more_rx(ep)) { wrl(ep, REG_INTEN, REG_INTEN_TX); @@ -307,7 +307,7 @@ poll_some_more: } spin_unlock_irq(&ep->rx_lock); - if (more && netif_rx_reschedule(napi)) + if (more && napi_reschedule(napi)) goto poll_some_more; } @@ -415,9 +415,9 @@ static irqreturn_t ep93xx_irq(int irq, void *dev_id) if (status & REG_INTSTS_RX) { spin_lock(&ep->rx_lock); - if (likely(netif_rx_schedule_prep(&ep->napi))) { + if (likely(napi_schedule_prep(&ep->napi))) { wrl(ep, REG_INTEN, REG_INTEN_TX); - __netif_rx_schedule(&ep->napi); + __napi_schedule(&ep->napi); } spin_unlock(&ep->rx_lock); } diff --git a/drivers/net/arm/ixp4xx_eth.c b/drivers/net/arm/ixp4xx_eth.c index 5fce1d5c1a1a..5fe17d5eaa54 100644 --- a/drivers/net/arm/ixp4xx_eth.c +++ b/drivers/net/arm/ixp4xx_eth.c @@ -473,7 +473,7 @@ static void eth_rx_irq(void *pdev) printk(KERN_DEBUG "%s: eth_rx_irq\n", dev->name); #endif qmgr_disable_irq(port->plat->rxq); - netif_rx_schedule(&port->napi); + napi_schedule(&port->napi); } static int eth_poll(struct napi_struct *napi, int budget) @@ -498,16 +498,16 @@ static int eth_poll(struct napi_struct *napi, int budget) if ((n = queue_get_desc(rxq, port, 0)) < 0) { #if DEBUG_RX - printk(KERN_DEBUG "%s: eth_poll netif_rx_complete\n", + printk(KERN_DEBUG "%s: eth_poll napi_complete\n", dev->name); #endif - netif_rx_complete(napi); + napi_complete(napi); qmgr_enable_irq(rxq); if (!qmgr_stat_empty(rxq) && - netif_rx_reschedule(napi)) { + napi_reschedule(napi)) { #if DEBUG_RX printk(KERN_DEBUG "%s: eth_poll" - " netif_rx_reschedule successed\n", + " napi_reschedule successed\n", dev->name); #endif qmgr_disable_irq(rxq); @@ -1036,7 +1036,7 @@ static int eth_open(struct net_device *dev) } ports_open++; /* we may already have RX data, enables IRQ */ - netif_rx_schedule(&port->napi); + napi_schedule(&port->napi); return 0; } diff --git a/drivers/net/atl1e/atl1e_main.c b/drivers/net/atl1e/atl1e_main.c index bb9094d4cbc9..c758884728a5 100644 --- a/drivers/net/atl1e/atl1e_main.c +++ b/drivers/net/atl1e/atl1e_main.c @@ -1326,9 +1326,9 @@ static irqreturn_t atl1e_intr(int irq, void *data) AT_WRITE_REG(hw, REG_IMR, IMR_NORMAL_MASK & ~ISR_RX_EVENT); AT_WRITE_FLUSH(hw); - if (likely(netif_rx_schedule_prep( + if (likely(napi_schedule_prep( &adapter->napi))) - __netif_rx_schedule(&adapter->napi); + __napi_schedule(&adapter->napi); } } while (--max_ints > 0); /* re-enable Interrupt*/ @@ -1514,7 +1514,7 @@ static int atl1e_clean(struct napi_struct *napi, int budget) /* If no Tx and not enough Rx work done, exit the polling mode */ if (work_done < budget) { quit_polling: - netif_rx_complete(napi); + napi_complete(napi); imr_data = AT_READ_REG(&adapter->hw, REG_IMR); AT_WRITE_REG(&adapter->hw, REG_IMR, imr_data | ISR_RX_EVENT); /* test debug */ diff --git a/drivers/net/b44.c b/drivers/net/b44.c index c38512ebcea6..92aaaa1ee9f1 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -874,7 +874,7 @@ static int b44_poll(struct napi_struct *napi, int budget) } if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); b44_enable_ints(bp); } @@ -906,13 +906,13 @@ static irqreturn_t b44_interrupt(int irq, void *dev_id) goto irq_ack; } - if (netif_rx_schedule_prep(&bp->napi)) { + if (napi_schedule_prep(&bp->napi)) { /* NOTE: These writes are posted by the readback of * the ISTAT register below. */ bp->istat = istat; __b44_disable_ints(bp); - __netif_rx_schedule(&bp->napi); + __napi_schedule(&bp->napi); } else { printk(KERN_ERR PFX "%s: Error, poll already scheduled\n", dev->name); diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index d4a3dac21dcf..e817802b2483 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -3053,7 +3053,7 @@ bnx2_msi(int irq, void *dev_instance) if (unlikely(atomic_read(&bp->intr_sem) != 0)) return IRQ_HANDLED; - netif_rx_schedule(&bnapi->napi); + napi_schedule(&bnapi->napi); return IRQ_HANDLED; } @@ -3070,7 +3070,7 @@ bnx2_msi_1shot(int irq, void *dev_instance) if (unlikely(atomic_read(&bp->intr_sem) != 0)) return IRQ_HANDLED; - netif_rx_schedule(&bnapi->napi); + napi_schedule(&bnapi->napi); return IRQ_HANDLED; } @@ -3106,9 +3106,9 @@ bnx2_interrupt(int irq, void *dev_instance) if (unlikely(atomic_read(&bp->intr_sem) != 0)) return IRQ_HANDLED; - if (netif_rx_schedule_prep(&bnapi->napi)) { + if (napi_schedule_prep(&bnapi->napi)) { bnapi->last_status_idx = sblk->status_idx; - __netif_rx_schedule(&bnapi->napi); + __napi_schedule(&bnapi->napi); } return IRQ_HANDLED; @@ -3218,7 +3218,7 @@ static int bnx2_poll_msix(struct napi_struct *napi, int budget) rmb(); if (likely(!bnx2_has_fast_work(bnapi))) { - netif_rx_complete(napi); + napi_complete(napi); REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num | BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | bnapi->last_status_idx); @@ -3251,7 +3251,7 @@ static int bnx2_poll(struct napi_struct *napi, int budget) rmb(); if (likely(!bnx2_has_work(bnapi))) { - netif_rx_complete(napi); + napi_complete(napi); if (likely(bp->flags & BNX2_FLAG_USING_MSI_OR_MSIX)) { REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 074374ff93f3..21764bfc048e 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -1647,7 +1647,7 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie) prefetch(&fp->status_blk->c_status_block.status_block_index); prefetch(&fp->status_blk->u_status_block.status_block_index); - netif_rx_schedule(&bnx2x_fp(bp, index, napi)); + napi_schedule(&bnx2x_fp(bp, index, napi)); return IRQ_HANDLED; } @@ -1686,7 +1686,7 @@ static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance) prefetch(&fp->status_blk->c_status_block.status_block_index); prefetch(&fp->status_blk->u_status_block.status_block_index); - netif_rx_schedule(&bnx2x_fp(bp, 0, napi)); + napi_schedule(&bnx2x_fp(bp, 0, napi)); status &= ~mask; } @@ -9339,7 +9339,7 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) #ifdef BNX2X_STOP_ON_ERROR poll_panic: #endif - netif_rx_complete(napi); + napi_complete(napi); bnx2x_ack_sb(bp, FP_SB_ID(fp), USTORM_ID, le16_to_cpu(fp->fp_u_idx), IGU_INT_NOP, 1); diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 840b3d1a22f5..bb46be275339 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -2506,7 +2506,7 @@ static irqreturn_t cas_interruptN(int irq, void *dev_id) if (status & INTR_RX_DONE_ALT) { /* handle rx separately */ #ifdef USE_NAPI cas_mask_intr(cp); - netif_rx_schedule(&cp->napi); + napi_schedule(&cp->napi); #else cas_rx_ringN(cp, ring, 0); #endif @@ -2557,7 +2557,7 @@ static irqreturn_t cas_interrupt1(int irq, void *dev_id) if (status & INTR_RX_DONE_ALT) { /* handle rx separately */ #ifdef USE_NAPI cas_mask_intr(cp); - netif_rx_schedule(&cp->napi); + napi_schedule(&cp->napi); #else cas_rx_ringN(cp, 1, 0); #endif @@ -2613,7 +2613,7 @@ static irqreturn_t cas_interrupt(int irq, void *dev_id) if (status & INTR_RX_DONE) { #ifdef USE_NAPI cas_mask_intr(cp); - netif_rx_schedule(&cp->napi); + napi_schedule(&cp->napi); #else cas_rx_ringN(cp, 0, 0); #endif @@ -2691,7 +2691,7 @@ rx_comp: #endif spin_unlock_irqrestore(&cp->lock, flags); if (enable_intr) { - netif_rx_complete(napi); + napi_complete(napi); cas_unmask_intr(cp); } return credits; diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c index d984b7995763..840da83fb3cf 100644 --- a/drivers/net/chelsio/sge.c +++ b/drivers/net/chelsio/sge.c @@ -1612,7 +1612,7 @@ int t1_poll(struct napi_struct *napi, int budget) int work_done = process_responses(adapter, budget); if (likely(work_done < budget)) { - netif_rx_complete(napi); + napi_complete(napi); writel(adapter->sge->respQ.cidx, adapter->regs + A_SG_SLEEPING); } @@ -1630,7 +1630,7 @@ irqreturn_t t1_interrupt(int irq, void *data) if (napi_schedule_prep(&adapter->napi)) { if (process_pure_responses(adapter)) - __netif_rx_schedule(&adapter->napi); + __napi_schedule(&adapter->napi); else { /* no data, no NAPI needed */ writel(sge->respQ.cidx, adapter->regs + A_SG_SLEEPING); diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index f66548751c38..4dad04e91f6d 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -428,7 +428,7 @@ static int cpmac_poll(struct napi_struct *napi, int budget) printk(KERN_WARNING "%s: rx: polling, but no queue\n", priv->dev->name); spin_unlock(&priv->rx_lock); - netif_rx_complete(napi); + napi_complete(napi); return 0; } @@ -514,7 +514,7 @@ static int cpmac_poll(struct napi_struct *napi, int budget) if (processed == 0) { /* we ran out of packets to read, * revert to interrupt-driven mode */ - netif_rx_complete(napi); + napi_complete(napi); cpmac_write(priv->regs, CPMAC_RX_INT_ENABLE, 1); return 0; } @@ -536,7 +536,7 @@ fatal_error: } spin_unlock(&priv->rx_lock); - netif_rx_complete(napi); + napi_complete(napi); netif_tx_stop_all_queues(priv->dev); napi_disable(&priv->napi); @@ -802,9 +802,9 @@ static irqreturn_t cpmac_irq(int irq, void *dev_id) if (status & MAC_INT_RX) { queue = (status >> 8) & 7; - if (netif_rx_schedule_prep(&priv->napi)) { + if (napi_schedule_prep(&priv->napi)) { cpmac_write(priv->regs, CPMAC_RX_INT_CLEAR, 1 << queue); - __netif_rx_schedule(&priv->napi); + __napi_schedule(&priv->napi); } } diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 86bb876fb123..861d2eeaa43c 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -1944,9 +1944,9 @@ static irqreturn_t e100_intr(int irq, void *dev_id) if (stat_ack & stat_ack_rnr) nic->ru_running = RU_SUSPENDED; - if (likely(netif_rx_schedule_prep(&nic->napi))) { + if (likely(napi_schedule_prep(&nic->napi))) { e100_disable_irq(nic); - __netif_rx_schedule(&nic->napi); + __napi_schedule(&nic->napi); } return IRQ_HANDLED; @@ -1962,7 +1962,7 @@ static int e100_poll(struct napi_struct *napi, int budget) /* If budget not fully consumed, exit the polling mode */ if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); e100_enable_irq(nic); } diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 26474c92193f..ffe466e0afb9 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -3687,12 +3687,12 @@ static irqreturn_t e1000_intr_msi(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - if (likely(netif_rx_schedule_prep(&adapter->napi))) { + if (likely(napi_schedule_prep(&adapter->napi))) { adapter->total_tx_bytes = 0; adapter->total_tx_packets = 0; adapter->total_rx_bytes = 0; adapter->total_rx_packets = 0; - __netif_rx_schedule(&adapter->napi); + __napi_schedule(&adapter->napi); } else e1000_irq_enable(adapter); @@ -3747,12 +3747,12 @@ static irqreturn_t e1000_intr(int irq, void *data) ew32(IMC, ~0); E1000_WRITE_FLUSH(); } - if (likely(netif_rx_schedule_prep(&adapter->napi))) { + if (likely(napi_schedule_prep(&adapter->napi))) { adapter->total_tx_bytes = 0; adapter->total_tx_packets = 0; adapter->total_rx_bytes = 0; adapter->total_rx_packets = 0; - __netif_rx_schedule(&adapter->napi); + __napi_schedule(&adapter->napi); } else /* this really should not happen! if it does it is basically a * bug, but not a hard error, so enable ints and continue */ @@ -3793,7 +3793,7 @@ static int e1000_clean(struct napi_struct *napi, int budget) if (work_done < budget) { if (likely(adapter->itr_setting & 3)) e1000_set_itr(adapter); - netif_rx_complete(napi); + napi_complete(napi); e1000_irq_enable(adapter); } diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 91817d0afcaf..ff5b66adfc42 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -1179,12 +1179,12 @@ static irqreturn_t e1000_intr_msi(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - if (netif_rx_schedule_prep(&adapter->napi)) { + if (napi_schedule_prep(&adapter->napi)) { adapter->total_tx_bytes = 0; adapter->total_tx_packets = 0; adapter->total_rx_bytes = 0; adapter->total_rx_packets = 0; - __netif_rx_schedule(&adapter->napi); + __napi_schedule(&adapter->napi); } return IRQ_HANDLED; @@ -1246,12 +1246,12 @@ static irqreturn_t e1000_intr(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - if (netif_rx_schedule_prep(&adapter->napi)) { + if (napi_schedule_prep(&adapter->napi)) { adapter->total_tx_bytes = 0; adapter->total_tx_packets = 0; adapter->total_rx_bytes = 0; adapter->total_rx_packets = 0; - __netif_rx_schedule(&adapter->napi); + __napi_schedule(&adapter->napi); } return IRQ_HANDLED; @@ -1320,10 +1320,10 @@ static irqreturn_t e1000_intr_msix_rx(int irq, void *data) adapter->rx_ring->set_itr = 0; } - if (netif_rx_schedule_prep(&adapter->napi)) { + if (napi_schedule_prep(&adapter->napi)) { adapter->total_rx_bytes = 0; adapter->total_rx_packets = 0; - __netif_rx_schedule(&adapter->napi); + __napi_schedule(&adapter->napi); } return IRQ_HANDLED; } @@ -2028,7 +2028,7 @@ clean_rx: if (work_done < budget) { if (adapter->itr_setting & 3) e1000_set_itr(adapter); - netif_rx_complete(napi); + napi_complete(napi); if (adapter->msix_entries) ew32(IMS, adapter->rx_ring->ims_val); else diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index dfe92264e825..8dc2047da5c0 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -830,7 +830,7 @@ static int ehea_poll(struct napi_struct *napi, int budget) while ((rx != budget) || force_irq) { pr->poll_counter = 0; force_irq = 0; - netif_rx_complete(napi); + napi_complete(napi); ehea_reset_cq_ep(pr->recv_cq); ehea_reset_cq_ep(pr->send_cq); ehea_reset_cq_n1(pr->recv_cq); @@ -841,7 +841,7 @@ static int ehea_poll(struct napi_struct *napi, int budget) if (!cqe && !cqe_skb) return rx; - if (!netif_rx_reschedule(napi)) + if (!napi_reschedule(napi)) return rx; cqe_skb = ehea_proc_cqes(pr, EHEA_POLL_MAX_CQES); @@ -859,7 +859,7 @@ static void ehea_netpoll(struct net_device *dev) int i; for (i = 0; i < port->num_def_qps; i++) - netif_rx_schedule(&port->port_res[i].napi); + napi_schedule(&port->port_res[i].napi); } #endif @@ -867,7 +867,7 @@ static irqreturn_t ehea_recv_irq_handler(int irq, void *param) { struct ehea_port_res *pr = param; - netif_rx_schedule(&pr->napi); + napi_schedule(&pr->napi); return IRQ_HANDLED; } diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c index 7d60551d538f..4617956821cd 100644 --- a/drivers/net/enic/enic_main.c +++ b/drivers/net/enic/enic_main.c @@ -411,8 +411,8 @@ static irqreturn_t enic_isr_legacy(int irq, void *data) } if (ENIC_TEST_INTR(pba, ENIC_INTX_WQ_RQ)) { - if (netif_rx_schedule_prep(&enic->napi)) - __netif_rx_schedule(&enic->napi); + if (napi_schedule_prep(&enic->napi)) + __napi_schedule(&enic->napi); } else { vnic_intr_unmask(&enic->intr[ENIC_INTX_WQ_RQ]); } @@ -440,7 +440,7 @@ static irqreturn_t enic_isr_msi(int irq, void *data) * writes). */ - netif_rx_schedule(&enic->napi); + napi_schedule(&enic->napi); return IRQ_HANDLED; } @@ -450,7 +450,7 @@ static irqreturn_t enic_isr_msix_rq(int irq, void *data) struct enic *enic = data; /* schedule NAPI polling for RQ cleanup */ - netif_rx_schedule(&enic->napi); + napi_schedule(&enic->napi); return IRQ_HANDLED; } @@ -1068,7 +1068,7 @@ static int enic_poll(struct napi_struct *napi, int budget) if (netdev->features & NETIF_F_LRO) lro_flush_all(&enic->lro_mgr); - netif_rx_complete(napi); + napi_complete(napi); vnic_intr_unmask(&enic->intr[ENIC_MSIX_RQ]); } @@ -1112,7 +1112,7 @@ static int enic_poll_msix(struct napi_struct *napi, int budget) if (netdev->features & NETIF_F_LRO) lro_flush_all(&enic->lro_mgr); - netif_rx_complete(napi); + napi_complete(napi); vnic_intr_unmask(&enic->intr[ENIC_MSIX_RQ]); } diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index a539bc3163cf..b60e27dfcfa7 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -1114,9 +1114,9 @@ static irqreturn_t epic_interrupt(int irq, void *dev_instance) if ((status & EpicNapiEvent) && !ep->reschedule_in_poll) { spin_lock(&ep->napi_lock); - if (netif_rx_schedule_prep(&ep->napi)) { + if (napi_schedule_prep(&ep->napi)) { epic_napi_irq_off(dev, ep); - __netif_rx_schedule(&ep->napi); + __napi_schedule(&ep->napi); } else ep->reschedule_in_poll++; spin_unlock(&ep->napi_lock); @@ -1293,7 +1293,7 @@ rx_action: more = ep->reschedule_in_poll; if (!more) { - __netif_rx_complete(napi); + __napi_complete(napi); outl(EpicNapiEvent, ioaddr + INTSTAT); epic_napi_irq_on(dev, ep); } else diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 5b910cf63740..875509d7d86b 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -1760,7 +1760,7 @@ static void nv_do_rx_refill(unsigned long data) struct fe_priv *np = netdev_priv(dev); /* Just reschedule NAPI rx processing */ - netif_rx_schedule(&np->napi); + napi_schedule(&np->napi); } #else static void nv_do_rx_refill(unsigned long data) @@ -3406,7 +3406,7 @@ static irqreturn_t nv_nic_irq(int foo, void *data) #ifdef CONFIG_FORCEDETH_NAPI if (events & NVREG_IRQ_RX_ALL) { spin_lock(&np->lock); - netif_rx_schedule(&np->napi); + napi_schedule(&np->napi); /* Disable furthur receive irq's */ np->irqmask &= ~NVREG_IRQ_RX_ALL; @@ -3523,7 +3523,7 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data) #ifdef CONFIG_FORCEDETH_NAPI if (events & NVREG_IRQ_RX_ALL) { spin_lock(&np->lock); - netif_rx_schedule(&np->napi); + napi_schedule(&np->napi); /* Disable furthur receive irq's */ np->irqmask &= ~NVREG_IRQ_RX_ALL; @@ -3680,7 +3680,7 @@ static int nv_napi_poll(struct napi_struct *napi, int budget) /* re-enable receive interrupts */ spin_lock_irqsave(&np->lock, flags); - __netif_rx_complete(napi); + __napi_complete(napi); np->irqmask |= NVREG_IRQ_RX_ALL; if (np->msi_flags & NV_MSI_X_ENABLED) @@ -3706,7 +3706,7 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data) writel(NVREG_IRQ_RX_ALL, base + NvRegMSIXIrqStatus); if (events) { - netif_rx_schedule(&np->napi); + napi_schedule(&np->napi); /* disable receive interrupts on the nic */ writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask); pci_push(base); diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index ce900e54d8d1..b037ce9857bf 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -209,7 +209,7 @@ static int fs_enet_rx_napi(struct napi_struct *napi, int budget) if (received < budget) { /* done */ - netif_rx_complete(napi); + napi_complete(napi); (*fep->ops->napi_enable_rx)(dev); } return received; @@ -478,7 +478,7 @@ fs_enet_interrupt(int irq, void *dev_id) /* NOTE: it is possible for FCCs in NAPI mode */ /* to submit a spurious interrupt while in poll */ if (napi_ok) - __netif_rx_schedule(&fep->napi); + __napi_schedule(&fep->napi); } } diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index ea530673236e..2e76699f8104 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -1627,9 +1627,9 @@ static void gfar_schedule_cleanup(struct net_device *dev) spin_lock_irqsave(&priv->txlock, flags); spin_lock(&priv->rxlock); - if (netif_rx_schedule_prep(&priv->napi)) { + if (napi_schedule_prep(&priv->napi)) { gfar_write(&priv->regs->imask, IMASK_RTX_DISABLED); - __netif_rx_schedule(&priv->napi); + __napi_schedule(&priv->napi); } spin_unlock(&priv->rxlock); @@ -1886,7 +1886,7 @@ static int gfar_poll(struct napi_struct *napi, int budget) return budget; if (rx_cleaned < budget) { - netif_rx_complete(napi); + napi_complete(napi); /* Clear the halt bit in RSTAT */ gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT); diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index dfa6348ac1dc..5c6315df86b9 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -1028,10 +1028,10 @@ static int ibmveth_poll(struct napi_struct *napi, int budget) ibmveth_assert(lpar_rc == H_SUCCESS); - netif_rx_complete(napi); + napi_complete(napi); if (ibmveth_rxq_pending_buffer(adapter) && - netif_rx_reschedule(napi)) { + napi_reschedule(napi)) { lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE); goto restart_poll; @@ -1047,11 +1047,11 @@ static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance) struct ibmveth_adapter *adapter = netdev_priv(netdev); unsigned long lpar_rc; - if (netif_rx_schedule_prep(&adapter->napi)) { + if (napi_schedule_prep(&adapter->napi)) { lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE); ibmveth_assert(lpar_rc == H_SUCCESS); - __netif_rx_schedule(&adapter->napi); + __napi_schedule(&adapter->napi); } return IRQ_HANDLED; } diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index b82b0fb2056c..3806bb9d8bfa 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -3386,8 +3386,8 @@ static irqreturn_t igb_msix_rx(int irq, void *data) igb_write_itr(rx_ring); - if (netif_rx_schedule_prep(&rx_ring->napi)) - __netif_rx_schedule(&rx_ring->napi); + if (napi_schedule_prep(&rx_ring->napi)) + __napi_schedule(&rx_ring->napi); #ifdef CONFIG_IGB_DCA if (rx_ring->adapter->flags & IGB_FLAG_DCA_ENABLED) @@ -3539,7 +3539,7 @@ static irqreturn_t igb_intr_msi(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - netif_rx_schedule(&adapter->rx_ring[0].napi); + napi_schedule(&adapter->rx_ring[0].napi); return IRQ_HANDLED; } @@ -3577,7 +3577,7 @@ static irqreturn_t igb_intr(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - netif_rx_schedule(&adapter->rx_ring[0].napi); + napi_schedule(&adapter->rx_ring[0].napi); return IRQ_HANDLED; } @@ -3612,7 +3612,7 @@ static int igb_poll(struct napi_struct *napi, int budget) !netif_running(netdev)) { if (adapter->itr_setting & 3) igb_set_itr(adapter); - netif_rx_complete(napi); + napi_complete(napi); if (!test_bit(__IGB_DOWN, &adapter->state)) igb_irq_enable(adapter); return 0; @@ -3638,7 +3638,7 @@ static int igb_clean_rx_ring_msix(struct napi_struct *napi, int budget) /* If not enough Rx work done, exit the polling mode */ if ((work_done == 0) || !netif_running(netdev)) { - netif_rx_complete(napi); + napi_complete(napi); if (adapter->itr_setting & 3) { if (adapter->num_rx_queues == 1) diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c index eee28d395682..e2ef16b29700 100644 --- a/drivers/net/ixgb/ixgb_main.c +++ b/drivers/net/ixgb/ixgb_main.c @@ -1721,14 +1721,14 @@ ixgb_intr(int irq, void *data) if (!test_bit(__IXGB_DOWN, &adapter->flags)) mod_timer(&adapter->watchdog_timer, jiffies); - if (netif_rx_schedule_prep(&adapter->napi)) { + if (napi_schedule_prep(&adapter->napi)) { /* Disable interrupts and register for poll. The flush of the posted write is intentionally left out. */ IXGB_WRITE_REG(&adapter->hw, IMC, ~0); - __netif_rx_schedule(&adapter->napi); + __napi_schedule(&adapter->napi); } return IRQ_HANDLED; } @@ -1749,7 +1749,7 @@ ixgb_clean(struct napi_struct *napi, int budget) /* If budget not fully consumed, exit the polling mode */ if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); if (!test_bit(__IXGB_DOWN, &adapter->flags)) ixgb_irq_enable(adapter); } diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index d2f4d5f508b7..7489094bbbc8 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -1015,7 +1015,7 @@ static irqreturn_t ixgbe_msix_clean_rx(int irq, void *data) rx_ring = &(adapter->rx_ring[r_idx]); /* disable interrupts on this vector only */ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, rx_ring->v_idx); - netif_rx_schedule(&q_vector->napi); + napi_schedule(&q_vector->napi); return IRQ_HANDLED; } @@ -1056,7 +1056,7 @@ static int ixgbe_clean_rxonly(struct napi_struct *napi, int budget) /* If all Rx work done, exit the polling mode */ if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); if (adapter->itr_setting & 3) ixgbe_set_itr_msix(q_vector); if (!test_bit(__IXGBE_DOWN, &adapter->state)) @@ -1105,7 +1105,7 @@ static int ixgbe_clean_rxonly_many(struct napi_struct *napi, int budget) rx_ring = &(adapter->rx_ring[r_idx]); /* If all Rx work done, exit the polling mode */ if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); if (adapter->itr_setting & 3) ixgbe_set_itr_msix(q_vector); if (!test_bit(__IXGBE_DOWN, &adapter->state)) @@ -1381,13 +1381,13 @@ static irqreturn_t ixgbe_intr(int irq, void *data) ixgbe_check_fan_failure(adapter, eicr); - if (netif_rx_schedule_prep(&adapter->q_vector[0].napi)) { + if (napi_schedule_prep(&adapter->q_vector[0].napi)) { adapter->tx_ring[0].total_packets = 0; adapter->tx_ring[0].total_bytes = 0; adapter->rx_ring[0].total_packets = 0; adapter->rx_ring[0].total_bytes = 0; /* would disable interrupts here but EIAM disabled it */ - __netif_rx_schedule(&adapter->q_vector[0].napi); + __napi_schedule(&adapter->q_vector[0].napi); } return IRQ_HANDLED; @@ -2317,7 +2317,7 @@ static int ixgbe_poll(struct napi_struct *napi, int budget) /* If budget not fully consumed, exit the polling mode */ if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); if (adapter->itr_setting & 3) ixgbe_set_itr(adapter); if (!test_bit(__IXGBE_DOWN, &adapter->state)) diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c index 014745720560..d3bf2f017cc2 100644 --- a/drivers/net/ixp2000/ixpdev.c +++ b/drivers/net/ixp2000/ixpdev.c @@ -141,7 +141,7 @@ static int ixpdev_poll(struct napi_struct *napi, int budget) break; } while (ixp2000_reg_read(IXP2000_IRQ_THD_RAW_STATUS_A_0) & 0x00ff); - netif_rx_complete(napi); + napi_complete(napi); ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_SET_A_0, 0x00ff); return rx; @@ -204,7 +204,7 @@ static irqreturn_t ixpdev_interrupt(int irq, void *dev_id) ixp2000_reg_wrb(IXP2000_IRQ_THD_ENABLE_CLEAR_A_0, 0x00ff); if (likely(napi_schedule_prep(&ip->napi))) { - __netif_rx_schedule(&ip->napi); + __napi_schedule(&ip->napi); } else { printk(KERN_CRIT "ixp2000: irq while polling!!\n"); } diff --git a/drivers/net/jme.h b/drivers/net/jme.h index 5154411b5e6b..e321c678b11c 100644 --- a/drivers/net/jme.h +++ b/drivers/net/jme.h @@ -398,15 +398,15 @@ struct jme_ring { #define JME_NAPI_WEIGHT(w) int w #define JME_NAPI_WEIGHT_VAL(w) w #define JME_NAPI_WEIGHT_SET(w, r) -#define JME_RX_COMPLETE(dev, napis) netif_rx_complete(napis) +#define JME_RX_COMPLETE(dev, napis) napi_complete(napis) #define JME_NAPI_ENABLE(priv) napi_enable(&priv->napi); #define JME_NAPI_DISABLE(priv) \ if (!napi_disable_pending(&priv->napi)) \ napi_disable(&priv->napi); #define JME_RX_SCHEDULE_PREP(priv) \ - netif_rx_schedule_prep(&priv->napi) + napi_schedule_prep(&priv->napi) #define JME_RX_SCHEDULE(priv) \ - __netif_rx_schedule(&priv->napi); + __napi_schedule(&priv->napi); /* * Jmac Adapter Private data diff --git a/drivers/net/korina.c b/drivers/net/korina.c index 75010cac76ac..38d6649a29c4 100644 --- a/drivers/net/korina.c +++ b/drivers/net/korina.c @@ -334,7 +334,7 @@ static irqreturn_t korina_rx_dma_interrupt(int irq, void *dev_id) DMA_STAT_HALT | DMA_STAT_ERR), &lp->rx_dma_regs->dmasm); - netif_rx_schedule(&lp->napi); + napi_schedule(&lp->napi); if (dmas & DMA_STAT_ERR) printk(KERN_ERR DRV_NAME "%s: DMA error\n", dev->name); @@ -468,7 +468,7 @@ static int korina_poll(struct napi_struct *napi, int budget) work_done = korina_rx(dev, budget); if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); writel(readl(&lp->rx_dma_regs->dmasm) & ~(DMA_STAT_DONE | DMA_STAT_HALT | DMA_STAT_ERR), diff --git a/drivers/net/macb.c b/drivers/net/macb.c index f6c4936e2fa8..dc33d51213d7 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -527,7 +527,7 @@ static int macb_poll(struct napi_struct *napi, int budget) * this function was called last time, and no packets * have been received since. */ - netif_rx_complete(napi); + napi_complete(napi); goto out; } @@ -538,13 +538,13 @@ static int macb_poll(struct napi_struct *napi, int budget) dev_warn(&bp->pdev->dev, "No RX buffers complete, status = %02lx\n", (unsigned long)status); - netif_rx_complete(napi); + napi_complete(napi); goto out; } work_done = macb_rx(bp, budget); if (work_done < budget) - netif_rx_complete(napi); + napi_complete(napi); /* * We've done what we can to clean the buffers. Make sure we @@ -579,7 +579,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) } if (status & MACB_RX_INT_FLAGS) { - if (netif_rx_schedule_prep(&bp->napi)) { + if (napi_schedule_prep(&bp->napi)) { /* * There's no point taking any more interrupts * until we have processed the buffers @@ -587,7 +587,7 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) macb_writel(bp, IDR, MACB_RX_INT_FLAGS); dev_dbg(&bp->pdev->dev, "scheduling RX softirq\n"); - __netif_rx_schedule(&bp->napi); + __napi_schedule(&bp->napi); } } diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c index c61b0bdca1a4..ac55ebd2f146 100644 --- a/drivers/net/mlx4/en_rx.c +++ b/drivers/net/mlx4/en_rx.c @@ -814,7 +814,7 @@ void mlx4_en_rx_irq(struct mlx4_cq *mcq) struct mlx4_en_priv *priv = netdev_priv(cq->dev); if (priv->port_up) - netif_rx_schedule(&cq->napi); + napi_schedule(&cq->napi); else mlx4_en_arm_cq(priv, cq); } @@ -834,7 +834,7 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) INC_PERF_COUNTER(priv->pstats.napi_quota); else { /* Done for now */ - netif_rx_complete(napi); + napi_complete(napi); mlx4_en_arm_cq(priv, cq); } return done; diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index e9c1296b267e..2dacb8852dc3 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -1514,7 +1514,7 @@ static int myri10ge_poll(struct napi_struct *napi, int budget) work_done = myri10ge_clean_rx_done(ss, budget); if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); put_be32(htonl(3), ss->irq_claim); } return work_done; @@ -1532,7 +1532,7 @@ static irqreturn_t myri10ge_intr(int irq, void *arg) /* an interrupt on a non-zero receive-only slice is implicitly * valid since MSI-X irqs are not shared */ if ((mgp->dev->real_num_tx_queues == 1) && (ss != mgp->ss)) { - netif_rx_schedule(&ss->napi); + napi_schedule(&ss->napi); return (IRQ_HANDLED); } @@ -1543,7 +1543,7 @@ static irqreturn_t myri10ge_intr(int irq, void *arg) /* low bit indicates receives are present, so schedule * napi poll handler */ if (stats->valid & 1) - netif_rx_schedule(&ss->napi); + napi_schedule(&ss->napi); if (!mgp->msi_enabled && !mgp->msix_enabled) { put_be32(0, mgp->irq_deassert); diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index c5dec54251bf..c23a58624a33 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -2198,10 +2198,10 @@ static irqreturn_t intr_handler(int irq, void *dev_instance) prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]); - if (netif_rx_schedule_prep(&np->napi)) { + if (napi_schedule_prep(&np->napi)) { /* Disable interrupts and register for poll */ natsemi_irq_disable(dev); - __netif_rx_schedule(&np->napi); + __napi_schedule(&np->napi); } else printk(KERN_WARNING "%s: Ignoring interrupt, status %#08x, mask %#08x.\n", @@ -2253,7 +2253,7 @@ static int natsemi_poll(struct napi_struct *napi, int budget) np->intr_status = readl(ioaddr + IntrStatus); } while (np->intr_status); - netif_rx_complete(napi); + napi_complete(napi); /* Reenable interrupts providing nothing is trying to shut * the chip down. */ diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index d854f07ef4d3..1139e637f5da 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -1631,7 +1631,7 @@ static int netxen_nic_poll(struct napi_struct *napi, int budget) } if ((work_done < budget) && tx_complete) { - netif_rx_complete(&adapter->napi); + napi_complete(&adapter->napi); netxen_nic_enable_int(adapter); } diff --git a/drivers/net/niu.c b/drivers/net/niu.c index 0c0b752315ca..4a5a089fa301 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -3669,7 +3669,7 @@ static int niu_poll(struct napi_struct *napi, int budget) work_done = niu_poll_core(np, lp, budget); if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); niu_ldg_rearm(np, lp, 1); } return work_done; @@ -4088,12 +4088,12 @@ static void __niu_fastpath_interrupt(struct niu *np, int ldg, u64 v0) static void niu_schedule_napi(struct niu *np, struct niu_ldg *lp, u64 v0, u64 v1, u64 v2) { - if (likely(netif_rx_schedule_prep(&lp->napi))) { + if (likely(napi_schedule_prep(&lp->napi))) { lp->v0 = v0; lp->v1 = v1; lp->v2 = v2; __niu_fastpath_interrupt(np, lp->ldg_num, v0); - __netif_rx_schedule(&lp->napi); + __napi_schedule(&lp->napi); } } diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c index d0349e7d73ea..5eeb5a87b738 100644 --- a/drivers/net/pasemi_mac.c +++ b/drivers/net/pasemi_mac.c @@ -970,7 +970,7 @@ static irqreturn_t pasemi_mac_rx_intr(int irq, void *data) if (*chan->status & PAS_STATUS_ERROR) reg |= PAS_IOB_DMA_RXCH_RESET_DINTC; - netif_rx_schedule(&mac->napi); + napi_schedule(&mac->napi); write_iob_reg(PAS_IOB_DMA_RXCH_RESET(chan->chno), reg); @@ -1010,7 +1010,7 @@ static irqreturn_t pasemi_mac_tx_intr(int irq, void *data) mod_timer(&txring->clean_timer, jiffies + (TX_CLEAN_INTERVAL)*2); - netif_rx_schedule(&mac->napi); + napi_schedule(&mac->napi); if (reg) write_iob_reg(PAS_IOB_DMA_TXCH_RESET(chan->chno), reg); @@ -1639,7 +1639,7 @@ static int pasemi_mac_poll(struct napi_struct *napi, int budget) pkts = pasemi_mac_clean_rx(rx_ring(mac), budget); if (pkts < budget) { /* all done, no more packets present */ - netif_rx_complete(napi); + napi_complete(napi); pasemi_mac_restart_rx_intr(mac); pasemi_mac_restart_tx_intr(mac); diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 665a4286da39..80124fac65fa 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -1397,7 +1397,7 @@ static int pcnet32_poll(struct napi_struct *napi, int budget) if (work_done < budget) { spin_lock_irqsave(&lp->lock, flags); - __netif_rx_complete(napi); + __napi_complete(napi); /* clear interrupt masks */ val = lp->a.read_csr(ioaddr, CSR3); @@ -2592,14 +2592,14 @@ pcnet32_interrupt(int irq, void *dev_id) dev->name, csr0); /* unlike for the lance, there is no restart needed */ } - if (netif_rx_schedule_prep(&lp->napi)) { + if (napi_schedule_prep(&lp->napi)) { u16 val; /* set interrupt masks */ val = lp->a.read_csr(ioaddr, CSR3); val |= 0x5f00; lp->a.write_csr(ioaddr, CSR3, val); mmiowb(); - __netif_rx_schedule(&lp->napi); + __napi_schedule(&lp->napi); break; } csr0 = lp->a.read_csr(ioaddr, CSR0); diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index 189ec29ac7a4..8b2823c8dccf 100644 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -2292,7 +2292,7 @@ static int ql_poll(struct napi_struct *napi, int budget) if (tx_cleaned + rx_cleaned != budget) { spin_lock_irqsave(&qdev->hw_lock, hw_flags); - __netif_rx_complete(napi); + __napi_complete(napi); ql_update_small_bufq_prod_index(qdev); ql_update_lrg_bufq_prod_index(qdev); writel(qdev->rsp_consumer_index, @@ -2351,8 +2351,8 @@ static irqreturn_t ql3xxx_isr(int irq, void *dev_id) spin_unlock(&qdev->adapter_lock); } else if (value & ISP_IMR_DISABLE_CMPL_INT) { ql_disable_interrupts(qdev); - if (likely(netif_rx_schedule_prep(&qdev->napi))) { - __netif_rx_schedule(&qdev->napi); + if (likely(napi_schedule_prep(&qdev->napi))) { + __napi_schedule(&qdev->napi); } } else { return IRQ_NONE; diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index 45421c8b6010..16eb9dd85286 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c @@ -1642,7 +1642,7 @@ static int ql_napi_poll_msix(struct napi_struct *napi, int budget) rx_ring->cq_id); if (work_done < budget) { - __netif_rx_complete(napi); + __napi_complete(napi); ql_enable_completion_interrupt(qdev, rx_ring->irq); } return work_done; @@ -1727,7 +1727,7 @@ static irqreturn_t qlge_msix_tx_isr(int irq, void *dev_id) static irqreturn_t qlge_msix_rx_isr(int irq, void *dev_id) { struct rx_ring *rx_ring = dev_id; - netif_rx_schedule(&rx_ring->napi); + napi_schedule(&rx_ring->napi); return IRQ_HANDLED; } @@ -1813,7 +1813,7 @@ static irqreturn_t qlge_isr(int irq, void *dev_id) &rx_ring->rx_work, 0); else - netif_rx_schedule(&rx_ring->napi); + napi_schedule(&rx_ring->napi); work_done++; } } diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index 72fd9e97c190..cc0f886b0c29 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c @@ -677,7 +677,7 @@ static int r6040_poll(struct napi_struct *napi, int budget) work_done = r6040_rx(dev, budget); if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); /* Enable RX interrupt */ iowrite16(ioread16(ioaddr + MIER) | RX_INTS, ioaddr + MIER); } @@ -714,7 +714,7 @@ static irqreturn_t r6040_interrupt(int irq, void *dev_id) /* Mask off RX interrupt */ misr &= ~RX_INTS; - netif_rx_schedule(&lp->napi); + napi_schedule(&lp->napi); } /* TX interrupt request */ diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 2c73ca606b35..1c4a980253fe 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -3581,8 +3581,8 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) RTL_W16(IntrMask, tp->intr_event & ~tp->napi_event); tp->intr_mask = ~tp->napi_event; - if (likely(netif_rx_schedule_prep(&tp->napi))) - __netif_rx_schedule(&tp->napi); + if (likely(napi_schedule_prep(&tp->napi))) + __napi_schedule(&tp->napi); else if (netif_msg_intr(tp)) { printk(KERN_INFO "%s: interrupt %04x in poll\n", dev->name, status); @@ -3603,7 +3603,7 @@ static int rtl8169_poll(struct napi_struct *napi, int budget) rtl8169_tx_interrupt(dev, tp, ioaddr); if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); tp->intr_mask = 0xffff; /* * 20040426: the barrier is not strictly required but the diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index f5c57c059bca..2a96a10fd0cf 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -2852,7 +2852,7 @@ static int s2io_poll_msix(struct napi_struct *napi, int budget) s2io_chk_rx_buffers(nic, ring); if (pkts_processed < budget_org) { - netif_rx_complete(napi); + napi_complete(napi); /*Re Enable MSI-Rx Vector*/ addr = (u8 __iomem *)&bar0->xmsi_mask_reg; addr += 7 - ring->ring_no; @@ -2889,7 +2889,7 @@ static int s2io_poll_inta(struct napi_struct *napi, int budget) break; } if (pkts_processed < budget_org) { - netif_rx_complete(napi); + napi_complete(napi); /* Re enable the Rx interrupts for the ring */ writeq(0, &bar0->rx_traffic_mask); readl(&bar0->rx_traffic_mask); @@ -4342,7 +4342,7 @@ static irqreturn_t s2io_msix_ring_handle(int irq, void *dev_id) val8 = (ring->ring_no == 0) ? 0x7f : 0xff; writeb(val8, addr); val8 = readb(addr); - netif_rx_schedule(&ring->napi); + napi_schedule(&ring->napi); } else { rx_intr_handler(ring, 0); s2io_chk_rx_buffers(sp, ring); @@ -4789,7 +4789,7 @@ static irqreturn_t s2io_isr(int irq, void *dev_id) if (config->napi) { if (reason & GEN_INTR_RXTRAFFIC) { - netif_rx_schedule(&sp->napi); + napi_schedule(&sp->napi); writeq(S2IO_MINUS_ONE, &bar0->rx_traffic_mask); writeq(S2IO_MINUS_ONE, &bar0->rx_traffic_int); readl(&bar0->rx_traffic_int); diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index 31e38fae017f..3e11c1d6d792 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -2039,9 +2039,9 @@ static irqreturn_t sbmac_intr(int irq,void *dev_instance) sbdma_tx_process(sc,&(sc->sbm_txdma), 0); if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) { - if (netif_rx_schedule_prep(&sc->napi)) { + if (napi_schedule_prep(&sc->napi)) { __raw_writeq(0, sc->sbm_imr); - __netif_rx_schedule(&sc->napi); + __napi_schedule(&sc->napi); /* Depend on the exit from poll to reenable intr */ } else { @@ -2667,7 +2667,7 @@ static int sbmac_poll(struct napi_struct *napi, int budget) sbdma_tx_process(sc, &(sc->sbm_txdma), 1); if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); #ifdef CONFIG_SBMAC_COALESCE __raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) | diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 7673fd92eaf5..77aca5d67b57 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -225,11 +225,11 @@ static int efx_poll(struct napi_struct *napi, int budget) if (rx_packets < budget) { /* There is no race here; although napi_disable() will - * only wait for netif_rx_complete(), this isn't a problem + * only wait for napi_complete(), this isn't a problem * since efx_channel_processed() will have no effect if * interrupts have already been disabled. */ - netif_rx_complete(napi); + napi_complete(napi); efx_channel_processed(channel); } diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index 0dd7a532c78a..fb1ac0e63c0b 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h @@ -77,7 +77,7 @@ static inline void efx_schedule_channel(struct efx_channel *channel) channel->channel, raw_smp_processor_id()); channel->work_pending = true; - netif_rx_schedule(&channel->napi_str); + napi_schedule(&channel->napi_str); } #endif /* EFX_EFX_H */ diff --git a/drivers/net/skge.c b/drivers/net/skge.c index c9dbb06f8c94..952d37ffee51 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -3214,7 +3214,7 @@ static int skge_poll(struct napi_struct *napi, int to_do) unsigned long flags; spin_lock_irqsave(&hw->hw_lock, flags); - __netif_rx_complete(napi); + __napi_complete(napi); hw->intr_mask |= napimask[skge->port]; skge_write32(hw, B0_IMSK, hw->intr_mask); skge_read32(hw, B0_IMSK); @@ -3377,7 +3377,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id) if (status & (IS_XA1_F|IS_R1_F)) { struct skge_port *skge = netdev_priv(hw->dev[0]); hw->intr_mask &= ~(IS_XA1_F|IS_R1_F); - netif_rx_schedule(&skge->napi); + napi_schedule(&skge->napi); } if (status & IS_PA_TO_TX1) @@ -3397,7 +3397,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id) if (status & (IS_XA2_F|IS_R2_F)) { hw->intr_mask &= ~(IS_XA2_F|IS_R2_F); - netif_rx_schedule(&skge->napi); + napi_schedule(&skge->napi); } if (status & IS_PA_TO_RX2) { diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index f513bdf1c887..d271ae39c6f3 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -984,7 +984,7 @@ static int smsc911x_poll(struct napi_struct *napi, int budget) /* We processed all packets available. Tell NAPI it can * stop polling then re-enable rx interrupts */ smsc911x_reg_write(pdata, INT_STS, INT_STS_RSFL_); - netif_rx_complete(napi); + napi_complete(napi); temp = smsc911x_reg_read(pdata, INT_EN); temp |= INT_EN_RSFL_EN_; smsc911x_reg_write(pdata, INT_EN, temp); @@ -1485,16 +1485,16 @@ static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id) } if (likely(intsts & inten & INT_STS_RSFL_)) { - if (likely(netif_rx_schedule_prep(&pdata->napi))) { + if (likely(napi_schedule_prep(&pdata->napi))) { /* Disable Rx interrupts */ temp = smsc911x_reg_read(pdata, INT_EN); temp &= (~INT_EN_RSFL_EN_); smsc911x_reg_write(pdata, INT_EN, temp); /* Schedule a NAPI poll */ - __netif_rx_schedule(&pdata->napi); + __napi_schedule(&pdata->napi); } else { SMSC_WARNING(RX_ERR, - "netif_rx_schedule_prep failed"); + "napi_schedule_prep failed"); } serviced = IRQ_HANDLED; } diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c index c14a4c6452c7..79f4c228b030 100644 --- a/drivers/net/smsc9420.c +++ b/drivers/net/smsc9420.c @@ -666,7 +666,7 @@ static irqreturn_t smsc9420_isr(int irq, void *dev_id) smsc9420_pci_flush_write(pd); ints_to_clear |= (DMAC_STS_RX_ | DMAC_STS_NIS_); - netif_rx_schedule(&pd->napi); + napi_schedule(&pd->napi); } if (ints_to_clear) @@ -889,7 +889,7 @@ static int smsc9420_rx_poll(struct napi_struct *napi, int budget) smsc9420_pci_flush_write(pd); if (work_done < budget) { - netif_rx_complete(&pd->napi); + napi_complete(&pd->napi); /* re-enable RX DMA interrupts */ dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA); diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 88d2c67788df..7f6b4a4052ee 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -1301,7 +1301,7 @@ static int spider_net_poll(struct napi_struct *napi, int budget) /* if all packets are in the stack, enable interrupts and return 0 */ /* if not, return 1 */ if (packets_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); spider_net_rx_irq_on(card); card->ignore_rx_ramfull = 0; } @@ -1528,7 +1528,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg, spider_net_refill_rx_chain(card); spider_net_enable_rxdmac(card); card->num_rx_ints ++; - netif_rx_schedule(&card->napi); + napi_schedule(&card->napi); } show_error = 0; break; @@ -1548,7 +1548,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg, spider_net_refill_rx_chain(card); spider_net_enable_rxdmac(card); card->num_rx_ints ++; - netif_rx_schedule(&card->napi); + napi_schedule(&card->napi); show_error = 0; break; @@ -1562,7 +1562,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg, spider_net_refill_rx_chain(card); spider_net_enable_rxdmac(card); card->num_rx_ints ++; - netif_rx_schedule(&card->napi); + napi_schedule(&card->napi); show_error = 0; break; @@ -1656,11 +1656,11 @@ spider_net_interrupt(int irq, void *ptr) if (status_reg & SPIDER_NET_RXINT ) { spider_net_rx_irq_off(card); - netif_rx_schedule(&card->napi); + napi_schedule(&card->napi); card->num_rx_ints ++; } if (status_reg & SPIDER_NET_TXINT) - netif_rx_schedule(&card->napi); + napi_schedule(&card->napi); if (status_reg & SPIDER_NET_LINKINT) spider_net_link_reset(netdev); diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index da3a76b18eff..98fe79515bab 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -1342,8 +1342,8 @@ static irqreturn_t intr_handler(int irq, void *dev_instance) if (intr_status & (IntrRxDone | IntrRxEmpty)) { u32 enable; - if (likely(netif_rx_schedule_prep(&np->napi))) { - __netif_rx_schedule(&np->napi); + if (likely(napi_schedule_prep(&np->napi))) { + __napi_schedule(&np->napi); enable = readl(ioaddr + IntrEnable); enable &= ~(IntrRxDone | IntrRxEmpty); writel(enable, ioaddr + IntrEnable); @@ -1587,7 +1587,7 @@ static int netdev_poll(struct napi_struct *napi, int budget) intr_status = readl(ioaddr + IntrStatus); } while (intr_status & (IntrRxDone | IntrRxEmpty)); - netif_rx_complete(napi); + napi_complete(napi); intr_status = readl(ioaddr + IntrEnable); intr_status |= IntrRxDone | IntrRxEmpty; writel(intr_status, ioaddr + IntrEnable); diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 86c765d83de1..4942059109f3 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -921,7 +921,7 @@ static int gem_poll(struct napi_struct *napi, int budget) gp->status = readl(gp->regs + GREG_STAT); } while (gp->status & GREG_STAT_NAPI); - __netif_rx_complete(napi); + __napi_complete(napi); gem_enable_ints(gp); spin_unlock_irqrestore(&gp->lock, flags); @@ -944,7 +944,7 @@ static irqreturn_t gem_interrupt(int irq, void *dev_id) spin_lock_irqsave(&gp->lock, flags); - if (netif_rx_schedule_prep(&gp->napi)) { + if (napi_schedule_prep(&gp->napi)) { u32 gem_status = readl(gp->regs + GREG_STAT); if (gem_status == 0) { @@ -954,7 +954,7 @@ static irqreturn_t gem_interrupt(int irq, void *dev_id) } gp->status = gem_status; gem_disable_ints(gp); - __netif_rx_schedule(&gp->napi); + __napi_schedule(&gp->napi); } spin_unlock_irqrestore(&gp->lock, flags); diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index bcd0e60cbda9..f42c67e93bf4 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -1609,8 +1609,8 @@ static irqreturn_t tc35815_interrupt(int irq, void *dev_id) if (!(dmactl & DMA_IntMask)) { /* disable interrupts */ tc_writel(dmactl | DMA_IntMask, &tr->DMA_Ctl); - if (netif_rx_schedule_prep(&lp->napi)) - __netif_rx_schedule(&lp->napi); + if (napi_schedule_prep(&lp->napi)) + __napi_schedule(&lp->napi); else { printk(KERN_ERR "%s: interrupt taken in poll\n", dev->name); @@ -1919,7 +1919,7 @@ static int tc35815_poll(struct napi_struct *napi, int budget) spin_unlock(&lp->lock); if (received < budget) { - netif_rx_complete(napi); + napi_complete(napi); /* enable interrupts */ tc_writel(tc_readl(&tr->DMA_Ctl) & ~DMA_IntMask, &tr->DMA_Ctl); } diff --git a/drivers/net/tehuti.c b/drivers/net/tehuti.c index a7a4dc4d6313..be9f38f8f0bf 100644 --- a/drivers/net/tehuti.c +++ b/drivers/net/tehuti.c @@ -265,8 +265,8 @@ static irqreturn_t bdx_isr_napi(int irq, void *dev) bdx_isr_extra(priv, isr); if (isr & (IR_RX_DESC_0 | IR_TX_FREE_0)) { - if (likely(netif_rx_schedule_prep(&priv->napi))) { - __netif_rx_schedule(&priv->napi); + if (likely(napi_schedule_prep(&priv->napi))) { + __napi_schedule(&priv->napi); RET(IRQ_HANDLED); } else { /* NOTE: we get here if intr has slipped into window @@ -302,7 +302,7 @@ static int bdx_poll(struct napi_struct *napi, int budget) * device lock and allow waiting tasks (eg rmmod) to advance) */ priv->napi_stop = 0; - netif_rx_complete(napi); + napi_complete(napi); bdx_enable_interrupts(priv); } return work_done; diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 8b3f84685387..5fa65acb68e5 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -4460,7 +4460,7 @@ static int tg3_poll(struct napi_struct *napi, int budget) sblk->status &= ~SD_STATUS_UPDATED; if (likely(!tg3_has_work(tp))) { - netif_rx_complete(napi); + napi_complete(napi); tg3_restart_ints(tp); break; } @@ -4470,7 +4470,7 @@ static int tg3_poll(struct napi_struct *napi, int budget) tx_recovery: /* work_done is guaranteed to be less than budget. */ - netif_rx_complete(napi); + napi_complete(napi); schedule_work(&tp->reset_task); return work_done; } @@ -4519,7 +4519,7 @@ static irqreturn_t tg3_msi_1shot(int irq, void *dev_id) prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); if (likely(!tg3_irq_sync(tp))) - netif_rx_schedule(&tp->napi); + napi_schedule(&tp->napi); return IRQ_HANDLED; } @@ -4544,7 +4544,7 @@ static irqreturn_t tg3_msi(int irq, void *dev_id) */ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); if (likely(!tg3_irq_sync(tp))) - netif_rx_schedule(&tp->napi); + napi_schedule(&tp->napi); return IRQ_RETVAL(1); } @@ -4586,7 +4586,7 @@ static irqreturn_t tg3_interrupt(int irq, void *dev_id) sblk->status &= ~SD_STATUS_UPDATED; if (likely(tg3_has_work(tp))) { prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); - netif_rx_schedule(&tp->napi); + napi_schedule(&tp->napi); } else { /* No work, shared interrupt perhaps? re-enable * interrupts, and flush that PCI write @@ -4632,7 +4632,7 @@ static irqreturn_t tg3_interrupt_tagged(int irq, void *dev_id) tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); if (tg3_irq_sync(tp)) goto out; - if (netif_rx_schedule_prep(&tp->napi)) { + if (napi_schedule_prep(&tp->napi)) { prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); /* Update last_tag to mark that this status has been * seen. Because interrupt may be shared, we may be @@ -4640,7 +4640,7 @@ static irqreturn_t tg3_interrupt_tagged(int irq, void *dev_id) * if tg3_poll() is not scheduled. */ tp->last_tag = sblk->status_tag; - __netif_rx_schedule(&tp->napi); + __napi_schedule(&tp->napi); } out: return IRQ_RETVAL(handled); diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c index 75461dbd4876..1138782e5611 100644 --- a/drivers/net/tsi108_eth.c +++ b/drivers/net/tsi108_eth.c @@ -888,7 +888,7 @@ static int tsi108_poll(struct napi_struct *napi, int budget) if (num_received < budget) { data->rxpending = 0; - netif_rx_complete(napi); + napi_complete(napi); TSI_WRITE(TSI108_EC_INTMASK, TSI_READ(TSI108_EC_INTMASK) @@ -915,11 +915,11 @@ static void tsi108_rx_int(struct net_device *dev) * * This can happen if this code races with tsi108_poll(), which masks * the interrupts after tsi108_irq_one() read the mask, but before - * netif_rx_schedule is called. It could also happen due to calls + * napi_schedule is called. It could also happen due to calls * from tsi108_check_rxring(). */ - if (netif_rx_schedule_prep(&data->napi)) { + if (napi_schedule_prep(&data->napi)) { /* Mask, rather than ack, the receive interrupts. The ack * will happen in tsi108_poll(). */ @@ -930,7 +930,7 @@ static void tsi108_rx_int(struct net_device *dev) | TSI108_INT_RXTHRESH | TSI108_INT_RXOVERRUN | TSI108_INT_RXERROR | TSI108_INT_RXWAIT); - __netif_rx_schedule(&data->napi); + __napi_schedule(&data->napi); } else { if (!netif_running(dev)) { /* This can happen if an interrupt occurs while the diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c index 6c3428a37c0b..9f946d421088 100644 --- a/drivers/net/tulip/interrupt.c +++ b/drivers/net/tulip/interrupt.c @@ -103,7 +103,7 @@ void oom_timer(unsigned long data) { struct net_device *dev = (struct net_device *)data; struct tulip_private *tp = netdev_priv(dev); - netif_rx_schedule(&tp->napi); + napi_schedule(&tp->napi); } int tulip_poll(struct napi_struct *napi, int budget) @@ -300,7 +300,7 @@ int tulip_poll(struct napi_struct *napi, int budget) /* Remove us from polling list and enable RX intr. */ - netif_rx_complete(napi); + napi_complete(napi); iowrite32(tulip_tbl[tp->chip_id].valid_intrs, tp->base_addr+CSR7); /* The last op happens after poll completion. Which means the following: @@ -333,10 +333,10 @@ int tulip_poll(struct napi_struct *napi, int budget) /* Think: timer_pending() was an explicit signature of bug. * Timer can be pending now but fired and completed - * before we did netif_rx_complete(). See? We would lose it. */ + * before we did napi_complete(). See? We would lose it. */ /* remove ourselves from the polling list */ - netif_rx_complete(napi); + napi_complete(napi); return work_done; } @@ -519,7 +519,7 @@ irqreturn_t tulip_interrupt(int irq, void *dev_instance) rxd++; /* Mask RX intrs and add the device to poll list. */ iowrite32(tulip_tbl[tp->chip_id].valid_intrs&~RxPollInt, ioaddr + CSR7); - netif_rx_schedule(&tp->napi); + napi_schedule(&tp->napi); if (!(csr5&~(AbnormalIntr|NormalIntr|RxPollInt|TPLnkPass))) break; diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index 3af9a9516ccb..dcff5ade6d08 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -1783,7 +1783,7 @@ typhoon_poll(struct napi_struct *napi, int budget) } if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); iowrite32(TYPHOON_INTR_NONE, tp->ioaddr + TYPHOON_REG_INTR_MASK); typhoon_post_pci_writes(tp->ioaddr); @@ -1806,10 +1806,10 @@ typhoon_interrupt(int irq, void *dev_instance) iowrite32(intr_status, ioaddr + TYPHOON_REG_INTR_STATUS); - if (netif_rx_schedule_prep(&tp->napi)) { + if (napi_schedule_prep(&tp->napi)) { iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK); typhoon_post_pci_writes(ioaddr); - __netif_rx_schedule(&tp->napi); + __napi_schedule(&tp->napi); } else { printk(KERN_ERR "%s: Error, poll already scheduled\n", dev->name); diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 11441225bf41..6def6f826a54 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -3251,7 +3251,7 @@ static int ucc_geth_poll(struct napi_struct *napi, int budget) howmany += ucc_geth_rx(ugeth, i, budget - howmany); if (howmany < budget) { - netif_rx_complete(napi); + napi_complete(napi); setbits32(ugeth->uccf->p_uccm, UCCE_RX_EVENTS); } @@ -3282,10 +3282,10 @@ static irqreturn_t ucc_geth_irq_handler(int irq, void *info) /* check for receive events that require processing */ if (ucce & UCCE_RX_EVENTS) { - if (netif_rx_schedule_prep(&ugeth->napi)) { + if (napi_schedule_prep(&ugeth->napi)) { uccm &= ~UCCE_RX_EVENTS; out_be32(uccf->p_uccm, uccm); - __netif_rx_schedule(&ugeth->napi); + __napi_schedule(&ugeth->napi); } } diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 3b8e63254277..4671436ecf0e 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -589,7 +589,7 @@ static int rhine_napipoll(struct napi_struct *napi, int budget) work_done = rhine_rx(dev, budget); if (work_done < budget) { - netif_rx_complete(napi); + napi_complete(napi); iowrite16(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow | IntrRxDropped | IntrRxNoBuf | IntrTxAborted | @@ -1319,7 +1319,7 @@ static irqreturn_t rhine_interrupt(int irq, void *dev_instance) IntrPCIErr | IntrStatsMax | IntrLinkChange, ioaddr + IntrEnable); - netif_rx_schedule(&rp->napi); + napi_schedule(&rp->napi); } if (intr_status & (IntrTxErrSummary | IntrTxDone)) { diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 43f6523c40be..30ae6d9a12af 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -374,9 +374,9 @@ static void skb_recv_done(struct virtqueue *rvq) { struct virtnet_info *vi = rvq->vdev->priv; /* Schedule NAPI, Suppress further interrupts if successful. */ - if (netif_rx_schedule_prep(&vi->napi)) { + if (napi_schedule_prep(&vi->napi)) { rvq->vq_ops->disable_cb(rvq); - __netif_rx_schedule(&vi->napi); + __napi_schedule(&vi->napi); } } @@ -402,11 +402,11 @@ again: /* Out of packets? */ if (received < budget) { - netif_rx_complete(napi); + napi_complete(napi); if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq)) && napi_schedule_prep(napi)) { vi->rvq->vq_ops->disable_cb(vi->rvq); - __netif_rx_schedule(napi); + __napi_schedule(napi); goto again; } } @@ -580,9 +580,9 @@ static int virtnet_open(struct net_device *dev) * won't get another interrupt, so process any outstanding packets * now. virtnet_poll wants re-enable the queue, so we disable here. * We synchronize against interrupts via NAPI_STATE_SCHED */ - if (netif_rx_schedule_prep(&vi->napi)) { + if (napi_schedule_prep(&vi->napi)) { vi->rvq->vq_ops->disable_cb(vi->rvq); - __netif_rx_schedule(&vi->napi); + __napi_schedule(&vi->napi); } return 0; } diff --git a/drivers/net/wan/hd64572.c b/drivers/net/wan/hd64572.c index 08b3536944fe..497b003d7239 100644 --- a/drivers/net/wan/hd64572.c +++ b/drivers/net/wan/hd64572.c @@ -341,7 +341,7 @@ static int sca_poll(struct napi_struct *napi, int budget) received = sca_rx_done(port, budget); if (received < budget) { - netif_rx_complete(napi); + napi_complete(napi); enable_intr(port); } @@ -359,7 +359,7 @@ static irqreturn_t sca_intr(int irq, void *dev_id) if (port && (isr0 & (i ? 0x08002200 : 0x00080022))) { handled = 1; disable_intr(port); - netif_rx_schedule(&port->napi); + napi_schedule(&port->napi); } } diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index 7e8bbba2cc1b..3bf7d3f447db 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -622,7 +622,7 @@ static void hss_hdlc_rx_irq(void *pdev) printk(KERN_DEBUG "%s: hss_hdlc_rx_irq\n", dev->name); #endif qmgr_disable_irq(queue_ids[port->id].rx); - netif_rx_schedule(&port->napi); + napi_schedule(&port->napi); } static int hss_hdlc_poll(struct napi_struct *napi, int budget) @@ -649,15 +649,15 @@ static int hss_hdlc_poll(struct napi_struct *napi, int budget) if ((n = queue_get_desc(rxq, port, 0)) < 0) { #if DEBUG_RX printk(KERN_DEBUG "%s: hss_hdlc_poll" - " netif_rx_complete\n", dev->name); + " napi_complete\n", dev->name); #endif - netif_rx_complete(napi); + napi_complete(napi); qmgr_enable_irq(rxq); if (!qmgr_stat_empty(rxq) && - netif_rx_reschedule(napi)) { + napi_reschedule(napi)) { #if DEBUG_RX printk(KERN_DEBUG "%s: hss_hdlc_poll" - " netif_rx_reschedule succeeded\n", + " napi_reschedule succeeded\n", dev->name); #endif qmgr_disable_irq(rxq); @@ -1069,7 +1069,7 @@ static int hss_hdlc_open(struct net_device *dev) hss_start_hdlc(port); /* we may already have RX data, enables IRQ */ - netif_rx_schedule(&port->napi); + napi_schedule(&port->napi); return 0; err_unlock: diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index cd6184ee08ee..9f102a6535c4 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -196,7 +196,7 @@ static void rx_refill_timeout(unsigned long data) { struct net_device *dev = (struct net_device *)data; struct netfront_info *np = netdev_priv(dev); - netif_rx_schedule(&np->napi); + napi_schedule(&np->napi); } static int netfront_tx_slot_available(struct netfront_info *np) @@ -328,7 +328,7 @@ static int xennet_open(struct net_device *dev) xennet_alloc_rx_buffers(dev); np->rx.sring->rsp_event = np->rx.rsp_cons + 1; if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx)) - netif_rx_schedule(&np->napi); + napi_schedule(&np->napi); } spin_unlock_bh(&np->rx_lock); @@ -979,7 +979,7 @@ err: RING_FINAL_CHECK_FOR_RESPONSES(&np->rx, more_to_do); if (!more_to_do) - __netif_rx_complete(napi); + __napi_complete(napi); local_irq_restore(flags); } @@ -1317,7 +1317,7 @@ static irqreturn_t xennet_interrupt(int irq, void *dev_id) xennet_tx_buf_gc(dev); /* Under tx_lock: protects access to rx shared-ring indexes. */ if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx)) - netif_rx_schedule(&np->napi); + napi_schedule(&np->napi); } spin_unlock_irqrestore(&np->tx_lock, flags); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ec54785d34f9..dd8a35b3e8b2 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1574,56 +1574,6 @@ static inline u32 netif_msg_init(int debug_value, int default_msg_enable_bits) return (1 << debug_value) - 1; } -/* Test if receive needs to be scheduled but only if up */ -static inline int netif_rx_schedule_prep(struct napi_struct *napi) -{ - return napi_schedule_prep(napi); -} - -/* Add interface to tail of rx poll list. This assumes that _prep has - * already been called and returned 1. - */ -static inline void __netif_rx_schedule(struct napi_struct *napi) -{ - __napi_schedule(napi); -} - -/* Try to reschedule poll. Called by irq handler. */ - -static inline void netif_rx_schedule(struct napi_struct *napi) -{ - if (netif_rx_schedule_prep(napi)) - __netif_rx_schedule(napi); -} - -/* Try to reschedule poll. Called by dev->poll() after netif_rx_complete(). */ -static inline int netif_rx_reschedule(struct napi_struct *napi) -{ - if (napi_schedule_prep(napi)) { - __netif_rx_schedule(napi); - return 1; - } - return 0; -} - -/* same as netif_rx_complete, except that local_irq_save(flags) - * has already been issued - */ -static inline void __netif_rx_complete(struct napi_struct *napi) -{ - __napi_complete(napi); -} - -/* Remove interface from poll list: it must be in the poll list - * on current cpu. This primitive is called by dev->poll(), when - * it completes the work. The device cannot be out of poll list at this - * moment, it is BUG(). - */ -static inline void netif_rx_complete(struct napi_struct *napi) -{ - napi_complete(napi); -} - static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu) { spin_lock(&txq->_xmit_lock); -- cgit v1.2.3 From f90f92eed74251034f251e3cdf4fa5c4c1f09df0 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Fri, 16 Jan 2009 23:36:30 +0000 Subject: dccp: Initialisation framework for feature negotiation This initialises feature negotiation from two tables, which are in turn are initialised from sysctls. As a novel feature, specifics of the implementation (e.g. that short seqnos and ECN are not yet available) are advertised for robustness. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: David S. Miller --- include/linux/dccp.h | 19 --------------- net/dccp/feat.c | 65 ++++++++++++++++++++++++++++++++++++++++++++-------- net/dccp/feat.h | 2 +- 3 files changed, 57 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 61734e27abb7..990e97fa1f07 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -369,28 +369,9 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) * Will be used to pass the state from dccp_request_sock to dccp_sock. * * @dccpms_sequence_window - Sequence Window Feature (section 7.5.2) - * @dccpms_pending - List of features being negotiated - * @dccpms_conf - */ struct dccp_minisock { __u64 dccpms_sequence_window; - struct list_head dccpms_pending; - struct list_head dccpms_conf; -}; - -struct dccp_opt_conf { - __u8 *dccpoc_val; - __u8 dccpoc_len; -}; - -struct dccp_opt_pend { - struct list_head dccpop_node; - __u8 dccpop_type; - __u8 dccpop_feat; - __u8 *dccpop_val; - __u8 dccpop_len; - int dccpop_conf; - struct dccp_opt_conf *dccpop_sc; }; extern void dccp_minisock_init(struct dccp_minisock *dmsk); diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 4152308958ab..67ffac9905f8 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -1115,23 +1115,70 @@ int dccp_feat_parse_options(struct sock *sk, struct dccp_request_sock *dreq, return 0; /* ignore FN options in all other states */ } +/** + * dccp_feat_init - Seed feature negotiation with host-specific defaults + * This initialises global defaults, depending on the value of the sysctls. + * These can later be overridden by registering changes via setsockopt calls. + * The last link in the chain is finalise_settings, to make sure that between + * here and the start of actual feature negotiation no inconsistencies enter. + * + * All features not appearing below use either defaults or are otherwise + * later adjusted through dccp_feat_finalise_settings(). + */ int dccp_feat_init(struct sock *sk) { - struct dccp_sock *dp = dccp_sk(sk); - struct dccp_minisock *dmsk = dccp_msk(sk); + struct list_head *fn = &dccp_sk(sk)->dccps_featneg; + u8 on = 1, off = 0; int rc; + struct { + u8 *val; + u8 len; + } tx, rx; + + /* Non-negotiable (NN) features */ + rc = __feat_register_nn(fn, DCCPF_SEQUENCE_WINDOW, 0, + sysctl_dccp_feat_sequence_window); + if (rc) + return rc; + + /* Server-priority (SP) features */ + + /* Advertise that short seqnos are not supported (7.6.1) */ + rc = __feat_register_sp(fn, DCCPF_SHORT_SEQNOS, true, true, &off, 1); + if (rc) + return rc; - INIT_LIST_HEAD(&dmsk->dccpms_pending); /* XXX no longer used */ - INIT_LIST_HEAD(&dmsk->dccpms_conf); /* XXX no longer used */ + /* RFC 4340 12.1: "If a DCCP is not ECN capable, ..." */ + rc = __feat_register_sp(fn, DCCPF_ECN_INCAPABLE, true, true, &on, 1); + if (rc) + return rc; + + /* + * We advertise the available list of CCIDs and reorder according to + * preferences, to avoid failure resulting from negotiating different + * singleton values (which always leads to failure). + * These settings can still (later) be overridden via sockopts. + */ + if (ccid_get_builtin_ccids(&tx.val, &tx.len) || + ccid_get_builtin_ccids(&rx.val, &rx.len)) + return -ENOBUFS; - /* Ack ratio */ - rc = __feat_register_nn(&dp->dccps_featneg, DCCPF_ACK_RATIO, 0, - dp->dccps_l_ack_ratio); + if (!dccp_feat_prefer(sysctl_dccp_feat_tx_ccid, tx.val, tx.len) || + !dccp_feat_prefer(sysctl_dccp_feat_rx_ccid, rx.val, rx.len)) + goto free_ccid_lists; + + rc = __feat_register_sp(fn, DCCPF_CCID, true, false, tx.val, tx.len); + if (rc) + goto free_ccid_lists; + + rc = __feat_register_sp(fn, DCCPF_CCID, false, false, rx.val, rx.len); + +free_ccid_lists: + kfree(tx.val); + kfree(rx.val); return rc; } -EXPORT_SYMBOL_GPL(dccp_feat_init); - int dccp_feat_activate_values(struct sock *sk, struct list_head *fn_list) { struct dccp_sock *dp = dccp_sk(sk); diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 9b46e2a7866e..5e7b8481cd04 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h @@ -113,13 +113,13 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val) #define dccp_feat_debug(type, feat, val) #endif /* CONFIG_IP_DCCP_DEBUG */ +extern int dccp_feat_init(struct sock *sk); extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, u8 const *list, u8 len); extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, u8 mand, u8 opt, u8 feat, u8 *val, u8 len); extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); -extern int dccp_feat_init(struct sock *sk); /* * Encoding variable-length options and their maximum length. -- cgit v1.2.3 From 792b48780e8b6435d017cef4b5c304876a48653e Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Fri, 16 Jan 2009 23:36:31 +0000 Subject: dccp: Implement both feature-local and feature-remote Sequence Window feature This adds full support for local/remote Sequence Window feature, from which the * sequence-number-validity (W) and * acknowledgment-number-validity (W') windows derive as specified in RFC 4340, 7.5.3. Specifically, the following is contained in this patch: * integrated new socket fields into dccp_sk; * updated the update_gsr/gss routines with regard to these fields; * updated handler code: the Sequence Window feature is located at the TX side, so the local feature is meant if the handler-rx flag is false; * the initialisation of `rcv_wnd' in reqsk is removed, since - rcv_wnd is not used by the code anywhere; - sequence number checks are not done in the LISTEN state (cf. 7.5.3); - dccp_check_req checks the Ack number validity more rigorously; * the `struct dccp_minisock' became empty and is now removed. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: David S. Miller --- Documentation/networking/dccp.txt | 3 ++- include/linux/dccp.h | 24 ++++-------------------- net/dccp/dccp.h | 16 +++++++--------- net/dccp/feat.c | 13 +++++++++++-- net/dccp/minisocks.c | 11 ----------- net/dccp/proto.c | 2 -- 6 files changed, 24 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt index 7a3bb1abb830..b132e4a3cf0f 100644 --- a/Documentation/networking/dccp.txt +++ b/Documentation/networking/dccp.txt @@ -141,7 +141,8 @@ rx_ccid = 2 Default CCID for the receiver-sender half-connection; see tx_ccid. seq_window = 100 - The initial sequence window (sec. 7.5.2). + The initial sequence window (sec. 7.5.2) of the sender. This influences + the local ackno validity and the remote seqno validity windows (7.5.1). tx_qlen = 5 The size of the transmit buffer in packets. A value of 0 corresponds diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 990e97fa1f07..7a0502ab383a 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -363,19 +363,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) /* FIXME: for now we're default to 1 but it should really be 0 */ #define DCCPF_INITIAL_SEND_NDP_COUNT 1 -/** - * struct dccp_minisock - Minimal DCCP connection representation - * - * Will be used to pass the state from dccp_request_sock to dccp_sock. - * - * @dccpms_sequence_window - Sequence Window Feature (section 7.5.2) - */ -struct dccp_minisock { - __u64 dccpms_sequence_window; -}; - -extern void dccp_minisock_init(struct dccp_minisock *dmsk); - /** * struct dccp_request_sock - represent DCCP-specific connection request * @dreq_inet_rsk: structure inherited from @@ -464,13 +451,14 @@ struct dccp_ackvec; * @dccps_timestamp_time - time of receiving latest @dccps_timestamp_echo * @dccps_l_ack_ratio - feature-local Ack Ratio * @dccps_r_ack_ratio - feature-remote Ack Ratio + * @dccps_l_seq_win - local Sequence Window (influences ack number validity) + * @dccps_r_seq_win - remote Sequence Window (influences seq number validity) * @dccps_pcslen - sender partial checksum coverage (via sockopt) * @dccps_pcrlen - receiver partial checksum coverage (via sockopt) * @dccps_send_ndp_count - local Send NDP Count feature (7.7.2) * @dccps_ndp_count - number of Non Data Packets since last data packet * @dccps_mss_cache - current value of MSS (path MTU minus header sizes) * @dccps_rate_last - timestamp for rate-limiting DCCP-Sync (RFC 4340, 7.5.4) - * @dccps_minisock - associated minisock (accessed via dccp_msk) * @dccps_featneg - tracks feature-negotiation state (mostly during handshake) * @dccps_hc_rx_ackvec - rx half connection ack vector * @dccps_hc_rx_ccid - CCID used for the receiver (or receiving half-connection) @@ -504,12 +492,13 @@ struct dccp_sock { __u32 dccps_timestamp_time; __u16 dccps_l_ack_ratio; __u16 dccps_r_ack_ratio; + __u64 dccps_l_seq_win:48; + __u64 dccps_r_seq_win:48; __u8 dccps_pcslen:4; __u8 dccps_pcrlen:4; __u8 dccps_send_ndp_count:1; __u64 dccps_ndp_count:48; unsigned long dccps_rate_last; - struct dccp_minisock dccps_minisock; struct list_head dccps_featneg; struct dccp_ackvec *dccps_hc_rx_ackvec; struct ccid *dccps_hc_rx_ccid; @@ -527,11 +516,6 @@ static inline struct dccp_sock *dccp_sk(const struct sock *sk) return (struct dccp_sock *)sk; } -static inline struct dccp_minisock *dccp_msk(const struct sock *sk) -{ - return (struct dccp_minisock *)&dccp_sk(sk)->dccps_minisock; -} - static inline const char *dccp_role(const struct sock *sk) { switch (dccp_sk(sk)->dccps_role) { diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index f2230fc168e1..04ae91898a68 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -409,23 +409,21 @@ static inline void dccp_hdr_set_ack(struct dccp_hdr_ack_bits *dhack, static inline void dccp_update_gsr(struct sock *sk, u64 seq) { struct dccp_sock *dp = dccp_sk(sk); - const struct dccp_minisock *dmsk = dccp_msk(sk); dp->dccps_gsr = seq; - dccp_set_seqno(&dp->dccps_swl, - dp->dccps_gsr + 1 - (dmsk->dccpms_sequence_window / 4)); - dccp_set_seqno(&dp->dccps_swh, - dp->dccps_gsr + (3 * dmsk->dccpms_sequence_window) / 4); + /* Sequence validity window depends on remote Sequence Window (7.5.1) */ + dp->dccps_swl = SUB48(ADD48(dp->dccps_gsr, 1), dp->dccps_r_seq_win / 4); + dp->dccps_swh = ADD48(dp->dccps_gsr, (3 * dp->dccps_r_seq_win) / 4); } static inline void dccp_update_gss(struct sock *sk, u64 seq) { struct dccp_sock *dp = dccp_sk(sk); - dp->dccps_awh = dp->dccps_gss = seq; - dccp_set_seqno(&dp->dccps_awl, - (dp->dccps_gss - - dccp_msk(sk)->dccpms_sequence_window + 1)); + dp->dccps_gss = seq; + /* Ack validity window depends on local Sequence Window value (7.5.1) */ + dp->dccps_awl = SUB48(ADD48(dp->dccps_gss, 1), dp->dccps_l_seq_win); + dp->dccps_awh = dp->dccps_gss; } static inline int dccp_ack_pending(const struct sock *sk) diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 67ffac9905f8..7303f79705d2 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -51,8 +51,17 @@ static int dccp_hdlr_ccid(struct sock *sk, u64 ccid, bool rx) static int dccp_hdlr_seq_win(struct sock *sk, u64 seq_win, bool rx) { - if (!rx) - dccp_msk(sk)->dccpms_sequence_window = seq_win; + struct dccp_sock *dp = dccp_sk(sk); + + if (rx) { + dp->dccps_r_seq_win = seq_win; + /* propagate changes to update SWL/SWH */ + dccp_update_gsr(sk, dp->dccps_gsr); + } else { + dp->dccps_l_seq_win = seq_win; + /* propagate changes to update AWL */ + dccp_update_gss(sk, dp->dccps_gss); + } return 0; } diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 6821ae33dd37..5ca49cec95f5 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -42,11 +42,6 @@ struct inet_timewait_death_row dccp_death_row = { EXPORT_SYMBOL_GPL(dccp_death_row); -void dccp_minisock_init(struct dccp_minisock *dmsk) -{ - dmsk->dccpms_sequence_window = sysctl_dccp_feat_sequence_window; -} - void dccp_time_wait(struct sock *sk, int state, int timeo) { struct inet_timewait_sock *tw = NULL; @@ -110,7 +105,6 @@ struct sock *dccp_create_openreq_child(struct sock *sk, struct dccp_request_sock *dreq = dccp_rsk(req); struct inet_connection_sock *newicsk = inet_csk(newsk); struct dccp_sock *newdp = dccp_sk(newsk); - struct dccp_minisock *newdmsk = dccp_msk(newsk); newdp->dccps_role = DCCP_ROLE_SERVER; newdp->dccps_hc_rx_ackvec = NULL; @@ -128,10 +122,6 @@ struct sock *dccp_create_openreq_child(struct sock *sk, * Initialize S.GAR := S.ISS * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies */ - - /* See dccp_v4_conn_request */ - newdmsk->dccpms_sequence_window = req->rcv_wnd; - newdp->dccps_gar = newdp->dccps_iss = dreq->dreq_iss; dccp_update_gss(newsk, dreq->dreq_iss); @@ -290,7 +280,6 @@ int dccp_reqsk_init(struct request_sock *req, inet_rsk(req)->rmt_port = dccp_hdr(skb)->dccph_sport; inet_rsk(req)->loc_port = dccp_hdr(skb)->dccph_dport; inet_rsk(req)->acked = 0; - req->rcv_wnd = sysctl_dccp_feat_sequence_window; dreq->dreq_timestamp_echo = 0; /* inherit feature negotiation options from listening socket */ diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 945b4d5d23b3..314a1b5c033c 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -174,8 +174,6 @@ int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized) struct dccp_sock *dp = dccp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); - dccp_minisock_init(&dp->dccps_minisock); - icsk->icsk_rto = DCCP_TIMEOUT_INIT; icsk->icsk_syn_retries = sysctl_dccp_request_retries; sk->sk_state = DCCP_CLOSED; -- cgit v1.2.3 From 883ca833e5fb814fb03426c9d35e5489ce43e8da Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Fri, 16 Jan 2009 23:36:32 +0000 Subject: dccp: Initialisation and type-checking of feature sysctls This patch takes care of initialising and type-checking sysctls related to feature negotiation. Type checking is important since some of the sysctls now directly impact the feature-negotiation process. The sysctls are initialised with the known default values for each feature. For the type-checking the value constraints from RFC 4340 are used: * Sequence Window uses the specified Wmin=32, the maximum is ulong (4 bytes), tested and confirmed that it works up to 4294967295 - for Gbps speed; * Ack Ratio is between 0 .. 0xffff (2-byte unsigned integer); * CCIDs are between 0 .. 255; * request_retries, retries1, retries2 also between 0..255 for good measure; * tx_qlen is checked to be non-negative; * sync_ratelimit remains as before. Notes: ------ 1. Die s@sysctl_dccp_feat@sysctl_dccp@g since the sysctls are now in feat.c. 2. As pointed out by Arnaldo, the pattern of type-checking repeats itself in other places, sometimes with exactly the same kind of definitions (e.g. "static int zero;"). It may be a good idea (kernel janitors?) to consolidate type checking. For the sake of keeping the changeset small and in order not to affect other subsystems, I have not strived to generalise here. Signed-off-by: Gerrit Renker Acked-by: Ian McDonald Signed-off-by: David S. Miller --- include/linux/dccp.h | 8 -------- net/dccp/dccp.h | 3 --- net/dccp/feat.c | 11 ++++++++--- net/dccp/feat.h | 8 ++++++++ net/dccp/options.c | 4 ---- net/dccp/sysctl.c | 43 ++++++++++++++++++++++++++++++------------- 6 files changed, 46 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 7a0502ab383a..7434a8353e23 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -355,14 +355,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) return __dccp_hdr_len(dccp_hdr(skb)); } - -/* initial values for each feature */ -#define DCCPF_INITIAL_SEQUENCE_WINDOW 100 -#define DCCPF_INITIAL_ACK_RATIO 2 -#define DCCPF_INITIAL_CCID DCCPC_CCID2 -/* FIXME: for now we're default to 1 but it should really be 0 */ -#define DCCPF_INITIAL_SEND_NDP_COUNT 1 - /** * struct dccp_request_sock - represent DCCP-specific connection request * @dreq_inet_rsk: structure inherited from diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 04ae91898a68..44a5bc6f6785 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -95,9 +95,6 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo); extern int sysctl_dccp_request_retries; extern int sysctl_dccp_retries1; extern int sysctl_dccp_retries2; -extern int sysctl_dccp_feat_sequence_window; -extern int sysctl_dccp_feat_rx_ccid; -extern int sysctl_dccp_feat_tx_ccid; extern int sysctl_dccp_tx_qlen; extern int sysctl_dccp_sync_ratelimit; diff --git a/net/dccp/feat.c b/net/dccp/feat.c index 7303f79705d2..12006e9b2472 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -25,6 +25,11 @@ #include "ccid.h" #include "feat.h" +/* feature-specific sysctls - initialised to the defaults from RFC 4340, 6.4 */ +unsigned long sysctl_dccp_sequence_window __read_mostly = 100; +int sysctl_dccp_rx_ccid __read_mostly = 2, + sysctl_dccp_tx_ccid __read_mostly = 2; + /* * Feature activation handlers. * @@ -1146,7 +1151,7 @@ int dccp_feat_init(struct sock *sk) /* Non-negotiable (NN) features */ rc = __feat_register_nn(fn, DCCPF_SEQUENCE_WINDOW, 0, - sysctl_dccp_feat_sequence_window); + sysctl_dccp_sequence_window); if (rc) return rc; @@ -1172,8 +1177,8 @@ int dccp_feat_init(struct sock *sk) ccid_get_builtin_ccids(&rx.val, &rx.len)) return -ENOBUFS; - if (!dccp_feat_prefer(sysctl_dccp_feat_tx_ccid, tx.val, tx.len) || - !dccp_feat_prefer(sysctl_dccp_feat_rx_ccid, rx.val, rx.len)) + if (!dccp_feat_prefer(sysctl_dccp_tx_ccid, tx.val, tx.len) || + !dccp_feat_prefer(sysctl_dccp_rx_ccid, rx.val, rx.len)) goto free_ccid_lists; rc = __feat_register_sp(fn, DCCPF_CCID, true, false, tx.val, tx.len); diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 5e7b8481cd04..40aa7a10bd5f 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h @@ -100,6 +100,13 @@ struct ccid_dependency { u8 val; }; +/* + * Sysctls to seed defaults for feature negotiation + */ +extern unsigned long sysctl_dccp_sequence_window; +extern int sysctl_dccp_rx_ccid; +extern int sysctl_dccp_tx_ccid; + #ifdef CONFIG_IP_DCCP_DEBUG extern const char *dccp_feat_typename(const u8 type); extern const char *dccp_feat_name(const u8 feat); @@ -114,6 +121,7 @@ static inline void dccp_feat_debug(const u8 type, const u8 feat, const u8 val) #endif /* CONFIG_IP_DCCP_DEBUG */ extern int dccp_feat_init(struct sock *sk); +extern void dccp_feat_initialise_sysctls(void); extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, u8 const *list, u8 len); extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); diff --git a/net/dccp/options.c b/net/dccp/options.c index 7b1165c21f51..3e2726c7182d 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -23,10 +23,6 @@ #include "dccp.h" #include "feat.h" -int sysctl_dccp_feat_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW; -int sysctl_dccp_feat_rx_ccid = DCCPF_INITIAL_CCID; -int sysctl_dccp_feat_tx_ccid = DCCPF_INITIAL_CCID; - u64 dccp_decode_value_var(const u8 *bf, const u8 len) { u64 value = 0; diff --git a/net/dccp/sysctl.c b/net/dccp/sysctl.c index 018e210875e1..a5a1856234e7 100644 --- a/net/dccp/sysctl.c +++ b/net/dccp/sysctl.c @@ -18,55 +18,72 @@ #error This file should not be compiled without CONFIG_SYSCTL defined #endif +/* Boundary values */ +static int zero = 0, + u8_max = 0xFF; +static unsigned long seqw_min = 32; + static struct ctl_table dccp_default_table[] = { { .procname = "seq_window", - .data = &sysctl_dccp_feat_sequence_window, - .maxlen = sizeof(sysctl_dccp_feat_sequence_window), + .data = &sysctl_dccp_sequence_window, + .maxlen = sizeof(sysctl_dccp_sequence_window), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_doulongvec_minmax, + .extra1 = &seqw_min, /* RFC 4340, 7.5.2 */ }, { .procname = "rx_ccid", - .data = &sysctl_dccp_feat_rx_ccid, - .maxlen = sizeof(sysctl_dccp_feat_rx_ccid), + .data = &sysctl_dccp_rx_ccid, + .maxlen = sizeof(sysctl_dccp_rx_ccid), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &u8_max, /* RFC 4340, 10. */ }, { .procname = "tx_ccid", - .data = &sysctl_dccp_feat_tx_ccid, - .maxlen = sizeof(sysctl_dccp_feat_tx_ccid), + .data = &sysctl_dccp_tx_ccid, + .maxlen = sizeof(sysctl_dccp_tx_ccid), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &u8_max, /* RFC 4340, 10. */ }, { .procname = "request_retries", .data = &sysctl_dccp_request_retries, .maxlen = sizeof(sysctl_dccp_request_retries), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &u8_max, }, { .procname = "retries1", .data = &sysctl_dccp_retries1, .maxlen = sizeof(sysctl_dccp_retries1), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &u8_max, }, { .procname = "retries2", .data = &sysctl_dccp_retries2, .maxlen = sizeof(sysctl_dccp_retries2), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &u8_max, }, { .procname = "tx_qlen", .data = &sysctl_dccp_tx_qlen, .maxlen = sizeof(sysctl_dccp_tx_qlen), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, }, { .procname = "sync_ratelimit", -- cgit v1.2.3 From 9f4d26d0f3016cf8813977d624751b94465fa317 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Mon, 19 Jan 2009 17:09:49 -0800 Subject: virtio_net: add link status handling Allow the host to inform us that the link is down by adding a VIRTIO_NET_F_STATUS which indicates that device status is available in virtio_net config. This is currently useful for simulating link down conditions (e.g. using proposed qemu 'set_link' monitor command) but would also be needed if we were to support device assignment via virtio. Signed-off-by: Mark McLoughlin Signed-off-by: Rusty Russell (added future masking) Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 43 ++++++++++++++++++++++++++++++++++++++++++- include/linux/virtio_net.h | 5 +++++ 2 files changed, 47 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 30ae6d9a12af..9b33d6ebf542 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -42,6 +42,7 @@ struct virtnet_info struct virtqueue *rvq, *svq; struct net_device *dev; struct napi_struct napi; + unsigned int status; /* The skb we couldn't send because buffers were full. */ struct sk_buff *last_xmit_skb; @@ -611,6 +612,7 @@ static struct ethtool_ops virtnet_ethtool_ops = { .set_tx_csum = virtnet_set_tx_csum, .set_sg = ethtool_op_set_sg, .set_tso = ethtool_op_set_tso, + .get_link = ethtool_op_get_link, }; #define MIN_MTU 68 @@ -636,6 +638,41 @@ static const struct net_device_ops virtnet_netdev = { #endif }; +static void virtnet_update_status(struct virtnet_info *vi) +{ + u16 v; + + if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) + return; + + vi->vdev->config->get(vi->vdev, + offsetof(struct virtio_net_config, status), + &v, sizeof(v)); + + /* Ignore unknown (future) status bits */ + v &= VIRTIO_NET_S_LINK_UP; + + if (vi->status == v) + return; + + vi->status = v; + + if (vi->status & VIRTIO_NET_S_LINK_UP) { + netif_carrier_on(vi->dev); + netif_wake_queue(vi->dev); + } else { + netif_carrier_off(vi->dev); + netif_stop_queue(vi->dev); + } +} + +static void virtnet_config_changed(struct virtio_device *vdev) +{ + struct virtnet_info *vi = vdev->priv; + + virtnet_update_status(vi); +} + static int virtnet_probe(struct virtio_device *vdev) { int err; @@ -738,6 +775,9 @@ static int virtnet_probe(struct virtio_device *vdev) goto unregister; } + vi->status = VIRTIO_NET_S_LINK_UP; + virtnet_update_status(vi); + pr_debug("virtnet: registered device %s\n", dev->name); return 0; @@ -793,7 +833,7 @@ static unsigned int features[] = { VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_ECN, /* We don't yet handle UFO input. */ - VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_F_NOTIFY_ON_EMPTY, }; @@ -805,6 +845,7 @@ static struct virtio_driver virtio_net = { .id_table = id_table, .probe = virtnet_probe, .remove = __devexit_p(virtnet_remove), + .config_changed = virtnet_config_changed, }; static int __init init(void) diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 5cdd0aa8bde9..f76bd4a753ef 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -21,11 +21,16 @@ #define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ #define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ #define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ +#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ + +#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ struct virtio_net_config { /* The config defining mac address (if VIRTIO_NET_F_MAC) */ __u8 mac[6]; + /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ + __u16 status; } __attribute__((packed)); /* This is the first element of the scatter-gather list. If you don't -- cgit v1.2.3 From 273ec51dd7ceaa76e038875d85061ec856d8905e Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 21 Jan 2009 15:55:35 -0800 Subject: net: ppp_generic - introduce net-namespace functionality v2 - Each namespace contains ppp channels and units separately with appropriate locks Signed-off-by: Cyrill Gorcunov Signed-off-by: David S. Miller --- drivers/net/ppp_generic.c | 275 +++++++++++++++++++++++++++++++------------- include/linux/ppp_channel.h | 4 + 2 files changed, 202 insertions(+), 77 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 7b2728b8f1b7..4405a76ed3da 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -49,6 +49,10 @@ #include #include +#include +#include +#include + #define PPP_VERSION "2.4.2" /* @@ -131,6 +135,7 @@ struct ppp { struct sock_filter *active_filter;/* filter for pkts to reset idle */ unsigned pass_len, active_len; #endif /* CONFIG_PPP_FILTER */ + struct net *ppp_net; /* the net we belong to */ }; /* @@ -155,6 +160,7 @@ struct channel { struct rw_semaphore chan_sem; /* protects `chan' during chan ioctl */ spinlock_t downl; /* protects `chan', file.xq dequeue */ struct ppp *ppp; /* ppp unit we're connected to */ + struct net *chan_net; /* the net channel belongs to */ struct list_head clist; /* link in list of channels per unit */ rwlock_t upl; /* protects `ppp' */ #ifdef CONFIG_PPP_MULTILINK @@ -173,26 +179,35 @@ struct channel { * channel.downl. */ -/* - * all_ppp_mutex protects the all_ppp_units mapping. - * It also ensures that finding a ppp unit in the all_ppp_units map - * and updating its file.refcnt field is atomic. - */ -static DEFINE_MUTEX(all_ppp_mutex); static atomic_t ppp_unit_count = ATOMIC_INIT(0); -static DEFINE_IDR(ppp_units_idr); - -/* - * all_channels_lock protects all_channels and last_channel_index, - * and the atomicity of find a channel and updating its file.refcnt - * field. - */ -static DEFINE_SPINLOCK(all_channels_lock); -static LIST_HEAD(all_channels); -static LIST_HEAD(new_channels); -static int last_channel_index; static atomic_t channel_count = ATOMIC_INIT(0); +/* per-net private data for this module */ +static unsigned int ppp_net_id; +struct ppp_net { + /* units to ppp mapping */ + struct idr units_idr; + + /* + * all_ppp_mutex protects the units_idr mapping. + * It also ensures that finding a ppp unit in the units_idr + * map and updating its file.refcnt field is atomic. + */ + struct mutex all_ppp_mutex; + + /* channels */ + struct list_head all_channels; + struct list_head new_channels; + int last_channel_index; + + /* + * all_channels_lock protects all_channels and + * last_channel_index, and the atomicity of find + * a channel and updating its file.refcnt field. + */ + spinlock_t all_channels_lock; +}; + /* Get the PPP protocol number from a skb */ #define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1]) @@ -216,8 +231,8 @@ static atomic_t channel_count = ATOMIC_INIT(0); #define seq_after(a, b) ((s32)((a) - (b)) > 0) /* Prototypes. */ -static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file, - unsigned int cmd, unsigned long arg); +static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, + struct file *file, unsigned int cmd, unsigned long arg); static void ppp_xmit_process(struct ppp *ppp); static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb); static void ppp_push(struct ppp *ppp); @@ -240,12 +255,12 @@ static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound); static void ppp_ccp_closed(struct ppp *ppp); static struct compressor *find_compressor(int type); static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st); -static struct ppp *ppp_create_interface(int unit, int *retp); +static struct ppp *ppp_create_interface(struct net *net, int unit, int *retp); static void init_ppp_file(struct ppp_file *pf, int kind); static void ppp_shutdown_interface(struct ppp *ppp); static void ppp_destroy_interface(struct ppp *ppp); -static struct ppp *ppp_find_unit(int unit); -static struct channel *ppp_find_channel(int unit); +static struct ppp *ppp_find_unit(struct ppp_net *pn, int unit); +static struct channel *ppp_find_channel(struct ppp_net *pn, int unit); static int ppp_connect_channel(struct channel *pch, int unit); static int ppp_disconnect_channel(struct channel *pch); static void ppp_destroy_channel(struct channel *pch); @@ -256,6 +271,14 @@ static void *unit_find(struct idr *p, int n); static struct class *ppp_class; +/* per net-namespace data */ +static inline struct ppp_net *ppp_pernet(struct net *net) +{ + BUG_ON(!net); + + return net_generic(net, ppp_net_id); +} + /* Translates a PPP protocol number to a NP index (NP == network protocol) */ static inline int proto_to_npindex(int proto) { @@ -544,7 +567,8 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) int __user *p = argp; if (!pf) - return ppp_unattached_ioctl(pf, file, cmd, arg); + return ppp_unattached_ioctl(current->nsproxy->net_ns, + pf, file, cmd, arg); if (cmd == PPPIOCDETACH) { /* @@ -763,12 +787,13 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return err; } -static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file, - unsigned int cmd, unsigned long arg) +static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, + struct file *file, unsigned int cmd, unsigned long arg) { int unit, err = -EFAULT; struct ppp *ppp; struct channel *chan; + struct ppp_net *pn; int __user *p = (int __user *)arg; lock_kernel(); @@ -777,7 +802,7 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file, /* Create a new ppp unit */ if (get_user(unit, p)) break; - ppp = ppp_create_interface(unit, &err); + ppp = ppp_create_interface(net, unit, &err); if (!ppp) break; file->private_data = &ppp->file; @@ -792,29 +817,31 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file, /* Attach to an existing ppp unit */ if (get_user(unit, p)) break; - mutex_lock(&all_ppp_mutex); err = -ENXIO; - ppp = ppp_find_unit(unit); + pn = ppp_pernet(net); + mutex_lock(&pn->all_ppp_mutex); + ppp = ppp_find_unit(pn, unit); if (ppp) { atomic_inc(&ppp->file.refcnt); file->private_data = &ppp->file; err = 0; } - mutex_unlock(&all_ppp_mutex); + mutex_unlock(&pn->all_ppp_mutex); break; case PPPIOCATTCHAN: if (get_user(unit, p)) break; - spin_lock_bh(&all_channels_lock); err = -ENXIO; - chan = ppp_find_channel(unit); + pn = ppp_pernet(net); + spin_lock_bh(&pn->all_channels_lock); + chan = ppp_find_channel(pn, unit); if (chan) { atomic_inc(&chan->file.refcnt); file->private_data = &chan->file; err = 0; } - spin_unlock_bh(&all_channels_lock); + spin_unlock_bh(&pn->all_channels_lock); break; default: @@ -834,6 +861,51 @@ static const struct file_operations ppp_device_fops = { .release = ppp_release }; +static __net_init int ppp_init_net(struct net *net) +{ + struct ppp_net *pn; + int err; + + pn = kzalloc(sizeof(*pn), GFP_KERNEL); + if (!pn) + return -ENOMEM; + + idr_init(&pn->units_idr); + mutex_init(&pn->all_ppp_mutex); + + INIT_LIST_HEAD(&pn->all_channels); + INIT_LIST_HEAD(&pn->new_channels); + + spin_lock_init(&pn->all_channels_lock); + + err = net_assign_generic(net, ppp_net_id, pn); + if (err) { + kfree(pn); + return err; + } + + return 0; +} + +static __net_exit void ppp_exit_net(struct net *net) +{ + struct ppp_net *pn; + + pn = net_generic(net, ppp_net_id); + idr_destroy(&pn->units_idr); + /* + * if someone has cached our net then + * further net_generic call will return NULL + */ + net_assign_generic(net, ppp_net_id, NULL); + kfree(pn); +} + +static __net_initdata struct pernet_operations ppp_net_ops = { + .init = ppp_init_net, + .exit = ppp_exit_net, +}; + #define PPP_MAJOR 108 /* Called at boot time if ppp is compiled into the kernel, @@ -843,25 +915,36 @@ static int __init ppp_init(void) int err; printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n"); - err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); - if (!err) { - ppp_class = class_create(THIS_MODULE, "ppp"); - if (IS_ERR(ppp_class)) { - err = PTR_ERR(ppp_class); - goto out_chrdev; - } - device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, - "ppp"); + + err = register_pernet_gen_device(&ppp_net_id, &ppp_net_ops); + if (err) { + printk(KERN_ERR "failed to register PPP pernet device (%d)\n", err); + goto out; } -out: - if (err) + err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); + if (err) { printk(KERN_ERR "failed to register PPP device (%d)\n", err); - return err; + goto out_net; + } + + ppp_class = class_create(THIS_MODULE, "ppp"); + if (IS_ERR(ppp_class)) { + err = PTR_ERR(ppp_class); + goto out_chrdev; + } + + /* not a big deal if we fail here :-) */ + device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), NULL, "ppp"); + + return 0; out_chrdev: unregister_chrdev(PPP_MAJOR, "ppp"); - goto out; +out_net: + unregister_pernet_gen_device(ppp_net_id, &ppp_net_ops); +out: + return err; } /* @@ -969,6 +1052,7 @@ static void ppp_setup(struct net_device *dev) dev->tx_queue_len = 3; dev->type = ARPHRD_PPP; dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->features |= NETIF_F_NETNS_LOCAL; } /* @@ -1986,19 +2070,27 @@ ppp_mp_reconstruct(struct ppp *ppp) * Channel interface. */ -/* - * Create a new, unattached ppp channel. - */ -int -ppp_register_channel(struct ppp_channel *chan) +/* Create a new, unattached ppp channel. */ +int ppp_register_channel(struct ppp_channel *chan) +{ + return ppp_register_net_channel(current->nsproxy->net_ns, chan); +} + +/* Create a new, unattached ppp channel for specified net. */ +int ppp_register_net_channel(struct net *net, struct ppp_channel *chan) { struct channel *pch; + struct ppp_net *pn; pch = kzalloc(sizeof(struct channel), GFP_KERNEL); if (!pch) return -ENOMEM; + + pn = ppp_pernet(net); + pch->ppp = NULL; pch->chan = chan; + pch->chan_net = net; chan->ppp = pch; init_ppp_file(&pch->file, CHANNEL); pch->file.hdrlen = chan->hdrlen; @@ -2008,11 +2100,13 @@ ppp_register_channel(struct ppp_channel *chan) init_rwsem(&pch->chan_sem); spin_lock_init(&pch->downl); rwlock_init(&pch->upl); - spin_lock_bh(&all_channels_lock); - pch->file.index = ++last_channel_index; - list_add(&pch->list, &new_channels); + + spin_lock_bh(&pn->all_channels_lock); + pch->file.index = ++pn->last_channel_index; + list_add(&pch->list, &pn->new_channels); atomic_inc(&channel_count); - spin_unlock_bh(&all_channels_lock); + spin_unlock_bh(&pn->all_channels_lock); + return 0; } @@ -2053,9 +2147,11 @@ void ppp_unregister_channel(struct ppp_channel *chan) { struct channel *pch = chan->ppp; + struct ppp_net *pn; if (!pch) return; /* should never happen */ + chan->ppp = NULL; /* @@ -2068,9 +2164,12 @@ ppp_unregister_channel(struct ppp_channel *chan) spin_unlock_bh(&pch->downl); up_write(&pch->chan_sem); ppp_disconnect_channel(pch); - spin_lock_bh(&all_channels_lock); + + pn = ppp_pernet(pch->chan_net); + spin_lock_bh(&pn->all_channels_lock); list_del(&pch->list); - spin_unlock_bh(&all_channels_lock); + spin_unlock_bh(&pn->all_channels_lock); + pch->file.dead = 1; wake_up_interruptible(&pch->file.rwait); if (atomic_dec_and_test(&pch->file.refcnt)) @@ -2395,9 +2494,10 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st) * unit == -1 means allocate a new number. */ static struct ppp * -ppp_create_interface(int unit, int *retp) +ppp_create_interface(struct net *net, int unit, int *retp) { struct ppp *ppp; + struct ppp_net *pn; struct net_device *dev = NULL; int ret = -ENOMEM; int i; @@ -2406,6 +2506,8 @@ ppp_create_interface(int unit, int *retp) if (!dev) goto out1; + pn = ppp_pernet(net); + ppp = netdev_priv(dev); ppp->dev = dev; ppp->mru = PPP_MRU; @@ -2421,17 +2523,23 @@ ppp_create_interface(int unit, int *retp) skb_queue_head_init(&ppp->mrq); #endif /* CONFIG_PPP_MULTILINK */ + /* + * drum roll: don't forget to set + * the net device is belong to + */ + dev_net_set(dev, net); + ret = -EEXIST; - mutex_lock(&all_ppp_mutex); + mutex_lock(&pn->all_ppp_mutex); if (unit < 0) { - unit = unit_get(&ppp_units_idr, ppp); + unit = unit_get(&pn->units_idr, ppp); if (unit < 0) { *retp = unit; goto out2; } } else { - if (unit_find(&ppp_units_idr, unit)) + if (unit_find(&pn->units_idr, unit)) goto out2; /* unit already exists */ /* * if caller need a specified unit number @@ -2442,7 +2550,7 @@ ppp_create_interface(int unit, int *retp) * fair but at least pppd will ask us to allocate * new unit in this case so user is happy :) */ - unit = unit_set(&ppp_units_idr, ppp, unit); + unit = unit_set(&pn->units_idr, ppp, unit); if (unit < 0) goto out2; } @@ -2453,20 +2561,22 @@ ppp_create_interface(int unit, int *retp) ret = register_netdev(dev); if (ret != 0) { - unit_put(&ppp_units_idr, unit); + unit_put(&pn->units_idr, unit); printk(KERN_ERR "PPP: couldn't register device %s (%d)\n", dev->name, ret); goto out2; } + ppp->ppp_net = net; + atomic_inc(&ppp_unit_count); - mutex_unlock(&all_ppp_mutex); + mutex_unlock(&pn->all_ppp_mutex); *retp = 0; return ppp; out2: - mutex_unlock(&all_ppp_mutex); + mutex_unlock(&pn->all_ppp_mutex); free_netdev(dev); out1: *retp = ret; @@ -2492,7 +2602,11 @@ init_ppp_file(struct ppp_file *pf, int kind) */ static void ppp_shutdown_interface(struct ppp *ppp) { - mutex_lock(&all_ppp_mutex); + struct ppp_net *pn; + + pn = ppp_pernet(ppp->ppp_net); + mutex_lock(&pn->all_ppp_mutex); + /* This will call dev_close() for us. */ ppp_lock(ppp); if (!ppp->closing) { @@ -2502,11 +2616,12 @@ static void ppp_shutdown_interface(struct ppp *ppp) } else ppp_unlock(ppp); - unit_put(&ppp_units_idr, ppp->file.index); + unit_put(&pn->units_idr, ppp->file.index); ppp->file.dead = 1; ppp->owner = NULL; wake_up_interruptible(&ppp->file.rwait); - mutex_unlock(&all_ppp_mutex); + + mutex_unlock(&pn->all_ppp_mutex); } /* @@ -2554,9 +2669,9 @@ static void ppp_destroy_interface(struct ppp *ppp) * The caller should have locked the all_ppp_mutex. */ static struct ppp * -ppp_find_unit(int unit) +ppp_find_unit(struct ppp_net *pn, int unit) { - return unit_find(&ppp_units_idr, unit); + return unit_find(&pn->units_idr, unit); } /* @@ -2568,20 +2683,22 @@ ppp_find_unit(int unit) * when we have a lot of channels in use. */ static struct channel * -ppp_find_channel(int unit) +ppp_find_channel(struct ppp_net *pn, int unit) { struct channel *pch; - list_for_each_entry(pch, &new_channels, list) { + list_for_each_entry(pch, &pn->new_channels, list) { if (pch->file.index == unit) { - list_move(&pch->list, &all_channels); + list_move(&pch->list, &pn->all_channels); return pch; } } - list_for_each_entry(pch, &all_channels, list) { + + list_for_each_entry(pch, &pn->all_channels, list) { if (pch->file.index == unit) return pch; } + return NULL; } @@ -2592,11 +2709,14 @@ static int ppp_connect_channel(struct channel *pch, int unit) { struct ppp *ppp; + struct ppp_net *pn; int ret = -ENXIO; int hdrlen; - mutex_lock(&all_ppp_mutex); - ppp = ppp_find_unit(unit); + pn = ppp_pernet(pch->chan_net); + + mutex_lock(&pn->all_ppp_mutex); + ppp = ppp_find_unit(pn, unit); if (!ppp) goto out; write_lock_bh(&pch->upl); @@ -2620,7 +2740,7 @@ ppp_connect_channel(struct channel *pch, int unit) outl: write_unlock_bh(&pch->upl); out: - mutex_unlock(&all_ppp_mutex); + mutex_unlock(&pn->all_ppp_mutex); return ret; } @@ -2677,7 +2797,7 @@ static void __exit ppp_cleanup(void) unregister_chrdev(PPP_MAJOR, "ppp"); device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0)); class_destroy(ppp_class); - idr_destroy(&ppp_units_idr); + unregister_pernet_gen_device(ppp_net_id, &ppp_net_ops); } /* @@ -2743,6 +2863,7 @@ static void *unit_find(struct idr *p, int n) module_init(ppp_init); module_exit(ppp_cleanup); +EXPORT_SYMBOL(ppp_register_net_channel); EXPORT_SYMBOL(ppp_register_channel); EXPORT_SYMBOL(ppp_unregister_channel); EXPORT_SYMBOL(ppp_channel_index); diff --git a/include/linux/ppp_channel.h b/include/linux/ppp_channel.h index a942892d6dfe..9d64bdf14770 100644 --- a/include/linux/ppp_channel.h +++ b/include/linux/ppp_channel.h @@ -22,6 +22,7 @@ #include #include #include +#include struct ppp_channel; @@ -56,6 +57,9 @@ extern void ppp_input(struct ppp_channel *, struct sk_buff *); that we may have missed a packet. */ extern void ppp_input_error(struct ppp_channel *, int code); +/* Attach a channel to a given PPP unit in specified net. */ +extern int ppp_register_net_channel(struct net *, struct ppp_channel *); + /* Attach a channel to a given PPP unit. */ extern int ppp_register_channel(struct ppp_channel *); -- cgit v1.2.3 From 5c0a66f5f3c9c59e2c341400048e2cff768e67a9 Mon Sep 17 00:00:00 2001 From: Benjamin Thery Date: Thu, 22 Jan 2009 04:56:17 +0000 Subject: netns: ipmr: store netns in struct mfc_cache This patch stores into struct mfc_cache the network namespace each mfc_cache belongs to. The new member is mfc_net. mfc_net is assigned at cache allocation and doesn't change during the rest of the cache entry life. A new net parameter is added to ipmr_cache_alloc/ipmr_cache_alloc_unres. This will help to retrieve the current netns around the IPv4 multicast routing code. At the moment, all mfc_cache are allocated in init_net. Signed-off-by: Benjamin Thery Signed-off-by: David S. Miller --- include/linux/mroute.h | 15 +++++++++++++++ net/ipv4/ipmr.c | 26 +++++++++++++++++--------- 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index 8a455694d682..beaf3aae8aaf 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -193,6 +193,9 @@ struct vif_device struct mfc_cache { struct mfc_cache *next; /* Next entry on cache line */ +#ifdef CONFIG_NET_NS + struct net *mfc_net; +#endif __be32 mfc_mcastgrp; /* Group the entry belongs to */ __be32 mfc_origin; /* Source of packet */ vifi_t mfc_parent; /* Source interface */ @@ -215,6 +218,18 @@ struct mfc_cache } mfc_un; }; +static inline +struct net *mfc_net(const struct mfc_cache *mfc) +{ + return read_pnet(&mfc->mfc_net); +} + +static inline +void mfc_net_set(struct mfc_cache *mfc, struct net *net) +{ + write_pnet(&mfc->mfc_net, hold_net(net)); +} + #define MFC_STATIC 1 #define MFC_NOTIFY 2 diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 75a5f79cc226..8428a0fb5c10 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -327,6 +327,12 @@ static int vif_delete(int vifi, int notify) return 0; } +static inline void ipmr_cache_free(struct mfc_cache *c) +{ + release_net(mfc_net(c)); + kmem_cache_free(mrt_cachep, c); +} + /* Destroy an unresolved cache entry, killing queued skbs and reporting error to netlink readers. */ @@ -353,7 +359,7 @@ static void ipmr_destroy_unres(struct mfc_cache *c) kfree_skb(skb); } - kmem_cache_free(mrt_cachep, c); + ipmr_cache_free(c); } @@ -528,22 +534,24 @@ static struct mfc_cache *ipmr_cache_find(__be32 origin, __be32 mcastgrp) /* * Allocate a multicast cache entry */ -static struct mfc_cache *ipmr_cache_alloc(void) +static struct mfc_cache *ipmr_cache_alloc(struct net *net) { struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); if (c == NULL) return NULL; c->mfc_un.res.minvif = MAXVIFS; + mfc_net_set(c, net); return c; } -static struct mfc_cache *ipmr_cache_alloc_unres(void) +static struct mfc_cache *ipmr_cache_alloc_unres(struct net *net) { struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); if (c == NULL) return NULL; skb_queue_head_init(&c->mfc_un.unres.unresolved); c->mfc_un.unres.expires = jiffies + 10*HZ; + mfc_net_set(c, net); return c; } @@ -695,7 +703,7 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) */ if (atomic_read(&cache_resolve_queue_len) >= 10 || - (c=ipmr_cache_alloc_unres())==NULL) { + (c = ipmr_cache_alloc_unres(&init_net)) == NULL) { spin_unlock_bh(&mfc_unres_lock); kfree_skb(skb); @@ -718,7 +726,7 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) */ spin_unlock_bh(&mfc_unres_lock); - kmem_cache_free(mrt_cachep, c); + ipmr_cache_free(c); kfree_skb(skb); return err; } @@ -763,7 +771,7 @@ static int ipmr_mfc_delete(struct mfcctl *mfc) *cp = c->next; write_unlock_bh(&mrt_lock); - kmem_cache_free(mrt_cachep, c); + ipmr_cache_free(c); return 0; } } @@ -796,7 +804,7 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) return -EINVAL; - c = ipmr_cache_alloc(); + c = ipmr_cache_alloc(&init_net); if (c == NULL) return -ENOMEM; @@ -831,7 +839,7 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) if (uc) { ipmr_cache_resolve(uc, c); - kmem_cache_free(mrt_cachep, uc); + ipmr_cache_free(uc); } return 0; } @@ -868,7 +876,7 @@ static void mroute_clean_tables(struct sock *sk) *cp = c->next; write_unlock_bh(&mrt_lock); - kmem_cache_free(mrt_cachep, c); + ipmr_cache_free(c); } } -- cgit v1.2.3 From 4feb88e5c694bfe414cbc3ce0e383f7f7038f90b Mon Sep 17 00:00:00 2001 From: Benjamin Thery Date: Thu, 22 Jan 2009 04:56:23 +0000 Subject: netns: ipmr: enable namespace support in ipv4 multicast routing code This last patch makes the appropriate changes to use and propagate the network namespace where needed in IPv4 multicast routing code. This consists mainly in replacing all the remaining init_net occurences with current netns pointer retrieved from sockets, net devices or mfc_caches depending on the routines' contexts. Some routines receive a new 'struct net' parameter to propagate the current netns: * vif_add/vif_delete * ipmr_new_tunnel * mroute_clean_tables * ipmr_cache_find * ipmr_cache_report * ipmr_cache_unresolved * ipmr_mfc_add/ipmr_mfc_delete * ipmr_get_route * rt_fill_info (in route.c) Signed-off-by: Benjamin Thery Signed-off-by: David S. Miller --- include/linux/mroute.h | 3 +- net/ipv4/ipmr.c | 243 +++++++++++++++++++++++++++---------------------- net/ipv4/route.c | 11 ++- 3 files changed, 143 insertions(+), 114 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index beaf3aae8aaf..0d45b4e8d367 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -256,7 +256,8 @@ void mfc_net_set(struct mfc_cache *mfc, struct net *net) #ifdef __KERNEL__ struct rtmsg; -extern int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait); +extern int ipmr_get_route(struct net *net, struct sk_buff *skb, + struct rtmsg *rtm, int nowait); #endif #endif diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index a4fd97f1920c..21a6dc710f20 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -95,7 +95,8 @@ static DEFINE_SPINLOCK(mfc_unres_lock); static struct kmem_cache *mrt_cachep __read_mostly; static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local); -static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert); +static int ipmr_cache_report(struct net *net, + struct sk_buff *pkt, vifi_t vifi, int assert); static int ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm); #ifdef CONFIG_IP_PIMSM_V2 @@ -108,9 +109,11 @@ static struct timer_list ipmr_expire_timer; static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v) { + struct net *net = dev_net(dev); + dev_close(dev); - dev = __dev_get_by_name(&init_net, "tunl0"); + dev = __dev_get_by_name(net, "tunl0"); if (dev) { const struct net_device_ops *ops = dev->netdev_ops; struct ifreq ifr; @@ -136,11 +139,11 @@ static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v) } static -struct net_device *ipmr_new_tunnel(struct vifctl *v) +struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v) { struct net_device *dev; - dev = __dev_get_by_name(&init_net, "tunl0"); + dev = __dev_get_by_name(net, "tunl0"); if (dev) { const struct net_device_ops *ops = dev->netdev_ops; @@ -169,7 +172,8 @@ struct net_device *ipmr_new_tunnel(struct vifctl *v) dev = NULL; - if (err == 0 && (dev = __dev_get_by_name(&init_net, p.name)) != NULL) { + if (err == 0 && + (dev = __dev_get_by_name(net, p.name)) != NULL) { dev->flags |= IFF_MULTICAST; in_dev = __in_dev_get_rtnl(dev); @@ -199,10 +203,13 @@ failure: static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) { + struct net *net = dev_net(dev); + read_lock(&mrt_lock); dev->stats.tx_bytes += skb->len; dev->stats.tx_packets++; - ipmr_cache_report(skb, init_net.ipv4.mroute_reg_vif_num, IGMPMSG_WHOLEPKT); + ipmr_cache_report(net, skb, net->ipv4.mroute_reg_vif_num, + IGMPMSG_WHOLEPKT); read_unlock(&mrt_lock); kfree_skb(skb); return 0; @@ -269,16 +276,16 @@ failure: * @notify: Set to 1, if the caller is a notifier_call */ -static int vif_delete(int vifi, int notify) +static int vif_delete(struct net *net, int vifi, int notify) { struct vif_device *v; struct net_device *dev; struct in_device *in_dev; - if (vifi < 0 || vifi >= init_net.ipv4.maxvif) + if (vifi < 0 || vifi >= net->ipv4.maxvif) return -EADDRNOTAVAIL; - v = &init_net.ipv4.vif_table[vifi]; + v = &net->ipv4.vif_table[vifi]; write_lock_bh(&mrt_lock); dev = v->dev; @@ -290,17 +297,17 @@ static int vif_delete(int vifi, int notify) } #ifdef CONFIG_IP_PIMSM - if (vifi == init_net.ipv4.mroute_reg_vif_num) - init_net.ipv4.mroute_reg_vif_num = -1; + if (vifi == net->ipv4.mroute_reg_vif_num) + net->ipv4.mroute_reg_vif_num = -1; #endif - if (vifi+1 == init_net.ipv4.maxvif) { + if (vifi+1 == net->ipv4.maxvif) { int tmp; for (tmp=vifi-1; tmp>=0; tmp--) { - if (VIF_EXISTS(&init_net, tmp)) + if (VIF_EXISTS(net, tmp)) break; } - init_net.ipv4.maxvif = tmp+1; + net->ipv4.maxvif = tmp+1; } write_unlock_bh(&mrt_lock); @@ -333,8 +340,9 @@ static void ipmr_destroy_unres(struct mfc_cache *c) { struct sk_buff *skb; struct nlmsgerr *e; + struct net *net = mfc_net(c); - atomic_dec(&init_net.ipv4.cache_resolve_queue_len); + atomic_dec(&net->ipv4.cache_resolve_queue_len); while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) { if (ip_hdr(skb)->version == 0) { @@ -346,7 +354,7 @@ static void ipmr_destroy_unres(struct mfc_cache *c) e->error = -ETIMEDOUT; memset(&e->msg, 0, sizeof(e->msg)); - rtnl_unicast(skb, &init_net, NETLINK_CB(skb).pid); + rtnl_unicast(skb, net, NETLINK_CB(skb).pid); } else kfree_skb(skb); } @@ -401,13 +409,14 @@ out: static void ipmr_update_thresholds(struct mfc_cache *cache, unsigned char *ttls) { int vifi; + struct net *net = mfc_net(cache); cache->mfc_un.res.minvif = MAXVIFS; cache->mfc_un.res.maxvif = 0; memset(cache->mfc_un.res.ttls, 255, MAXVIFS); - for (vifi = 0; vifi < init_net.ipv4.maxvif; vifi++) { - if (VIF_EXISTS(&init_net, vifi) && + for (vifi = 0; vifi < net->ipv4.maxvif; vifi++) { + if (VIF_EXISTS(net, vifi) && ttls[vifi] && ttls[vifi] < 255) { cache->mfc_un.res.ttls[vifi] = ttls[vifi]; if (cache->mfc_un.res.minvif > vifi) @@ -418,16 +427,16 @@ static void ipmr_update_thresholds(struct mfc_cache *cache, unsigned char *ttls) } } -static int vif_add(struct vifctl *vifc, int mrtsock) +static int vif_add(struct net *net, struct vifctl *vifc, int mrtsock) { int vifi = vifc->vifc_vifi; - struct vif_device *v = &init_net.ipv4.vif_table[vifi]; + struct vif_device *v = &net->ipv4.vif_table[vifi]; struct net_device *dev; struct in_device *in_dev; int err; /* Is vif busy ? */ - if (VIF_EXISTS(&init_net, vifi)) + if (VIF_EXISTS(net, vifi)) return -EADDRINUSE; switch (vifc->vifc_flags) { @@ -437,7 +446,7 @@ static int vif_add(struct vifctl *vifc, int mrtsock) * Special Purpose VIF in PIM * All the packets will be sent to the daemon */ - if (init_net.ipv4.mroute_reg_vif_num >= 0) + if (net->ipv4.mroute_reg_vif_num >= 0) return -EADDRINUSE; dev = ipmr_reg_vif(); if (!dev) @@ -451,7 +460,7 @@ static int vif_add(struct vifctl *vifc, int mrtsock) break; #endif case VIFF_TUNNEL: - dev = ipmr_new_tunnel(vifc); + dev = ipmr_new_tunnel(net, vifc); if (!dev) return -ENOBUFS; err = dev_set_allmulti(dev, 1); @@ -462,7 +471,7 @@ static int vif_add(struct vifctl *vifc, int mrtsock) } break; case 0: - dev = ip_dev_find(&init_net, vifc->vifc_lcl_addr.s_addr); + dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr); if (!dev) return -EADDRNOTAVAIL; err = dev_set_allmulti(dev, 1); @@ -503,20 +512,22 @@ static int vif_add(struct vifctl *vifc, int mrtsock) v->dev = dev; #ifdef CONFIG_IP_PIMSM if (v->flags&VIFF_REGISTER) - init_net.ipv4.mroute_reg_vif_num = vifi; + net->ipv4.mroute_reg_vif_num = vifi; #endif - if (vifi+1 > init_net.ipv4.maxvif) - init_net.ipv4.maxvif = vifi+1; + if (vifi+1 > net->ipv4.maxvif) + net->ipv4.maxvif = vifi+1; write_unlock_bh(&mrt_lock); return 0; } -static struct mfc_cache *ipmr_cache_find(__be32 origin, __be32 mcastgrp) +static struct mfc_cache *ipmr_cache_find(struct net *net, + __be32 origin, + __be32 mcastgrp) { int line = MFC_HASH(mcastgrp, origin); struct mfc_cache *c; - for (c = init_net.ipv4.mfc_cache_array[line]; c; c = c->next) { + for (c = net->ipv4.mfc_cache_array[line]; c; c = c->next) { if (c->mfc_origin==origin && c->mfc_mcastgrp==mcastgrp) break; } @@ -576,7 +587,7 @@ static void ipmr_cache_resolve(struct mfc_cache *uc, struct mfc_cache *c) memset(&e->msg, 0, sizeof(e->msg)); } - rtnl_unicast(skb, &init_net, NETLINK_CB(skb).pid); + rtnl_unicast(skb, mfc_net(c), NETLINK_CB(skb).pid); } else ip_mr_forward(skb, c, 0); } @@ -589,7 +600,8 @@ static void ipmr_cache_resolve(struct mfc_cache *uc, struct mfc_cache *c) * Called under mrt_lock. */ -static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert) +static int ipmr_cache_report(struct net *net, + struct sk_buff *pkt, vifi_t vifi, int assert) { struct sk_buff *skb; const int ihl = ip_hdrlen(pkt); @@ -621,7 +633,7 @@ static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert) memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); msg->im_msgtype = IGMPMSG_WHOLEPKT; msg->im_mbz = 0; - msg->im_vif = init_net.ipv4.mroute_reg_vif_num; + msg->im_vif = net->ipv4.mroute_reg_vif_num; ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + sizeof(struct iphdr)); @@ -653,7 +665,7 @@ static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert) skb->transport_header = skb->network_header; } - if (init_net.ipv4.mroute_sk == NULL) { + if (net->ipv4.mroute_sk == NULL) { kfree_skb(skb); return -EINVAL; } @@ -661,7 +673,7 @@ static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert) /* * Deliver to mrouted */ - ret = sock_queue_rcv_skb(init_net.ipv4.mroute_sk, skb); + ret = sock_queue_rcv_skb(net->ipv4.mroute_sk, skb); if (ret < 0) { if (net_ratelimit()) printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n"); @@ -676,7 +688,7 @@ static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert) */ static int -ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) +ipmr_cache_unresolved(struct net *net, vifi_t vifi, struct sk_buff *skb) { int err; struct mfc_cache *c; @@ -684,7 +696,7 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) spin_lock_bh(&mfc_unres_lock); for (c=mfc_unres_queue; c; c=c->next) { - if (net_eq(mfc_net(c), &init_net) && + if (net_eq(mfc_net(c), net) && c->mfc_mcastgrp == iph->daddr && c->mfc_origin == iph->saddr) break; @@ -695,8 +707,8 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) * Create a new entry if allowable */ - if (atomic_read(&init_net.ipv4.cache_resolve_queue_len) >= 10 || - (c = ipmr_cache_alloc_unres(&init_net)) == NULL) { + if (atomic_read(&net->ipv4.cache_resolve_queue_len) >= 10 || + (c = ipmr_cache_alloc_unres(net)) == NULL) { spin_unlock_bh(&mfc_unres_lock); kfree_skb(skb); @@ -713,7 +725,8 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) /* * Reflect first query at mrouted. */ - if ((err = ipmr_cache_report(skb, vifi, IGMPMSG_NOCACHE))<0) { + err = ipmr_cache_report(net, skb, vifi, IGMPMSG_NOCACHE); + if (err < 0) { /* If the report failed throw the cache entry out - Brad Parker */ @@ -724,7 +737,7 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) return err; } - atomic_inc(&init_net.ipv4.cache_resolve_queue_len); + atomic_inc(&net->ipv4.cache_resolve_queue_len); c->next = mfc_unres_queue; mfc_unres_queue = c; @@ -750,14 +763,14 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) * MFC cache manipulation by user space mroute daemon */ -static int ipmr_mfc_delete(struct mfcctl *mfc) +static int ipmr_mfc_delete(struct net *net, struct mfcctl *mfc) { int line; struct mfc_cache *c, **cp; line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); - for (cp = &init_net.ipv4.mfc_cache_array[line]; + for (cp = &net->ipv4.mfc_cache_array[line]; (c = *cp) != NULL; cp = &c->next) { if (c->mfc_origin == mfc->mfcc_origin.s_addr && c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) { @@ -772,14 +785,14 @@ static int ipmr_mfc_delete(struct mfcctl *mfc) return -ENOENT; } -static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) +static int ipmr_mfc_add(struct net *net, struct mfcctl *mfc, int mrtsock) { int line; struct mfc_cache *uc, *c, **cp; line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); - for (cp = &init_net.ipv4.mfc_cache_array[line]; + for (cp = &net->ipv4.mfc_cache_array[line]; (c = *cp) != NULL; cp = &c->next) { if (c->mfc_origin == mfc->mfcc_origin.s_addr && c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) @@ -799,7 +812,7 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) return -EINVAL; - c = ipmr_cache_alloc(&init_net); + c = ipmr_cache_alloc(net); if (c == NULL) return -ENOMEM; @@ -811,8 +824,8 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) c->mfc_flags |= MFC_STATIC; write_lock_bh(&mrt_lock); - c->next = init_net.ipv4.mfc_cache_array[line]; - init_net.ipv4.mfc_cache_array[line] = c; + c->next = net->ipv4.mfc_cache_array[line]; + net->ipv4.mfc_cache_array[line] = c; write_unlock_bh(&mrt_lock); /* @@ -822,11 +835,11 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) spin_lock_bh(&mfc_unres_lock); for (cp = &mfc_unres_queue; (uc=*cp) != NULL; cp = &uc->next) { - if (net_eq(mfc_net(uc), &init_net) && + if (net_eq(mfc_net(uc), net) && uc->mfc_origin == c->mfc_origin && uc->mfc_mcastgrp == c->mfc_mcastgrp) { *cp = uc->next; - atomic_dec(&init_net.ipv4.cache_resolve_queue_len); + atomic_dec(&net->ipv4.cache_resolve_queue_len); break; } } @@ -845,16 +858,16 @@ static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) * Close the multicast socket, and clear the vif tables etc */ -static void mroute_clean_tables(struct sock *sk) +static void mroute_clean_tables(struct net *net) { int i; /* * Shut down all active vif entries */ - for (i = 0; i < init_net.ipv4.maxvif; i++) { - if (!(init_net.ipv4.vif_table[i].flags&VIFF_STATIC)) - vif_delete(i, 0); + for (i = 0; i < net->ipv4.maxvif; i++) { + if (!(net->ipv4.vif_table[i].flags&VIFF_STATIC)) + vif_delete(net, i, 0); } /* @@ -863,7 +876,7 @@ static void mroute_clean_tables(struct sock *sk) for (i=0; iipv4.mfc_cache_array[i]; while ((c = *cp) != NULL) { if (c->mfc_flags&MFC_STATIC) { cp = &c->next; @@ -877,13 +890,13 @@ static void mroute_clean_tables(struct sock *sk) } } - if (atomic_read(&init_net.ipv4.cache_resolve_queue_len) != 0) { + if (atomic_read(&net->ipv4.cache_resolve_queue_len) != 0) { struct mfc_cache *c, **cp; spin_lock_bh(&mfc_unres_lock); cp = &mfc_unres_queue; while ((c = *cp) != NULL) { - if (!net_eq(mfc_net(c), &init_net)) { + if (!net_eq(mfc_net(c), net)) { cp = &c->next; continue; } @@ -897,15 +910,17 @@ static void mroute_clean_tables(struct sock *sk) static void mrtsock_destruct(struct sock *sk) { + struct net *net = sock_net(sk); + rtnl_lock(); - if (sk == init_net.ipv4.mroute_sk) { - IPV4_DEVCONF_ALL(sock_net(sk), MC_FORWARDING)--; + if (sk == net->ipv4.mroute_sk) { + IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; write_lock_bh(&mrt_lock); - init_net.ipv4.mroute_sk = NULL; + net->ipv4.mroute_sk = NULL; write_unlock_bh(&mrt_lock); - mroute_clean_tables(sk); + mroute_clean_tables(net); } rtnl_unlock(); } @@ -922,9 +937,10 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int int ret; struct vifctl vif; struct mfcctl mfc; + struct net *net = sock_net(sk); if (optname != MRT_INIT) { - if (sk != init_net.ipv4.mroute_sk && !capable(CAP_NET_ADMIN)) + if (sk != net->ipv4.mroute_sk && !capable(CAP_NET_ADMIN)) return -EACCES; } @@ -937,7 +953,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int return -ENOPROTOOPT; rtnl_lock(); - if (init_net.ipv4.mroute_sk) { + if (net->ipv4.mroute_sk) { rtnl_unlock(); return -EADDRINUSE; } @@ -945,15 +961,15 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int ret = ip_ra_control(sk, 1, mrtsock_destruct); if (ret == 0) { write_lock_bh(&mrt_lock); - init_net.ipv4.mroute_sk = sk; + net->ipv4.mroute_sk = sk; write_unlock_bh(&mrt_lock); - IPV4_DEVCONF_ALL(sock_net(sk), MC_FORWARDING)++; + IPV4_DEVCONF_ALL(net, MC_FORWARDING)++; } rtnl_unlock(); return ret; case MRT_DONE: - if (sk != init_net.ipv4.mroute_sk) + if (sk != net->ipv4.mroute_sk) return -EACCES; return ip_ra_control(sk, 0, NULL); case MRT_ADD_VIF: @@ -966,9 +982,9 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int return -ENFILE; rtnl_lock(); if (optname == MRT_ADD_VIF) { - ret = vif_add(&vif, sk == init_net.ipv4.mroute_sk); + ret = vif_add(net, &vif, sk == net->ipv4.mroute_sk); } else { - ret = vif_delete(vif.vifc_vifi, 0); + ret = vif_delete(net, vif.vifc_vifi, 0); } rtnl_unlock(); return ret; @@ -985,9 +1001,9 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int return -EFAULT; rtnl_lock(); if (optname == MRT_DEL_MFC) - ret = ipmr_mfc_delete(&mfc); + ret = ipmr_mfc_delete(net, &mfc); else - ret = ipmr_mfc_add(&mfc, sk == init_net.ipv4.mroute_sk); + ret = ipmr_mfc_add(net, &mfc, sk == net->ipv4.mroute_sk); rtnl_unlock(); return ret; /* @@ -998,7 +1014,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int int v; if (get_user(v,(int __user *)optval)) return -EFAULT; - init_net.ipv4.mroute_do_assert = (v) ? 1 : 0; + net->ipv4.mroute_do_assert = (v) ? 1 : 0; return 0; } #ifdef CONFIG_IP_PIMSM @@ -1012,11 +1028,11 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int rtnl_lock(); ret = 0; - if (v != init_net.ipv4.mroute_do_pim) { - init_net.ipv4.mroute_do_pim = v; - init_net.ipv4.mroute_do_assert = v; + if (v != net->ipv4.mroute_do_pim) { + net->ipv4.mroute_do_pim = v; + net->ipv4.mroute_do_assert = v; #ifdef CONFIG_IP_PIMSM_V2 - if (init_net.ipv4.mroute_do_pim) + if (net->ipv4.mroute_do_pim) ret = inet_add_protocol(&pim_protocol, IPPROTO_PIM); else @@ -1047,6 +1063,7 @@ int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int { int olr; int val; + struct net *net = sock_net(sk); if (optname != MRT_VERSION && #ifdef CONFIG_IP_PIMSM @@ -1068,10 +1085,10 @@ int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int val = 0x0305; #ifdef CONFIG_IP_PIMSM else if (optname == MRT_PIM) - val = init_net.ipv4.mroute_do_pim; + val = net->ipv4.mroute_do_pim; #endif else - val = init_net.ipv4.mroute_do_assert; + val = net->ipv4.mroute_do_assert; if (copy_to_user(optval, &val, olr)) return -EFAULT; return 0; @@ -1087,16 +1104,17 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) struct sioc_vif_req vr; struct vif_device *vif; struct mfc_cache *c; + struct net *net = sock_net(sk); switch (cmd) { case SIOCGETVIFCNT: if (copy_from_user(&vr, arg, sizeof(vr))) return -EFAULT; - if (vr.vifi >= init_net.ipv4.maxvif) + if (vr.vifi >= net->ipv4.maxvif) return -EINVAL; read_lock(&mrt_lock); - vif = &init_net.ipv4.vif_table[vr.vifi]; - if (VIF_EXISTS(&init_net, vr.vifi)) { + vif = &net->ipv4.vif_table[vr.vifi]; + if (VIF_EXISTS(net, vr.vifi)) { vr.icount = vif->pkt_in; vr.ocount = vif->pkt_out; vr.ibytes = vif->bytes_in; @@ -1114,7 +1132,7 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) return -EFAULT; read_lock(&mrt_lock); - c = ipmr_cache_find(sr.src.s_addr, sr.grp.s_addr); + c = ipmr_cache_find(net, sr.src.s_addr, sr.grp.s_addr); if (c) { sr.pktcnt = c->mfc_un.res.pkt; sr.bytecnt = c->mfc_un.res.bytes; @@ -1136,18 +1154,19 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = ptr; + struct net *net = dev_net(dev); struct vif_device *v; int ct; - if (!net_eq(dev_net(dev), &init_net)) + if (!net_eq(dev_net(dev), net)) return NOTIFY_DONE; if (event != NETDEV_UNREGISTER) return NOTIFY_DONE; - v = &init_net.ipv4.vif_table[0]; - for (ct = 0; ct < init_net.ipv4.maxvif; ct++, v++) { + v = &net->ipv4.vif_table[0]; + for (ct = 0; ct < net->ipv4.maxvif; ct++, v++) { if (v->dev == dev) - vif_delete(ct, 1); + vif_delete(net, ct, 1); } return NOTIFY_DONE; } @@ -1207,8 +1226,9 @@ static inline int ipmr_forward_finish(struct sk_buff *skb) static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi) { + struct net *net = mfc_net(c); const struct iphdr *iph = ip_hdr(skb); - struct vif_device *vif = &init_net.ipv4.vif_table[vifi]; + struct vif_device *vif = &net->ipv4.vif_table[vifi]; struct net_device *dev; struct rtable *rt; int encap = 0; @@ -1222,7 +1242,7 @@ static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi) vif->bytes_out += skb->len; vif->dev->stats.tx_bytes += skb->len; vif->dev->stats.tx_packets++; - ipmr_cache_report(skb, vifi, IGMPMSG_WHOLEPKT); + ipmr_cache_report(net, skb, vifi, IGMPMSG_WHOLEPKT); kfree_skb(skb); return; } @@ -1235,7 +1255,7 @@ static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi) .saddr = vif->local, .tos = RT_TOS(iph->tos) } }, .proto = IPPROTO_IPIP }; - if (ip_route_output_key(&init_net, &rt, &fl)) + if (ip_route_output_key(net, &rt, &fl)) goto out_free; encap = sizeof(struct iphdr); } else { @@ -1244,7 +1264,7 @@ static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi) { .daddr = iph->daddr, .tos = RT_TOS(iph->tos) } }, .proto = IPPROTO_IPIP }; - if (ip_route_output_key(&init_net, &rt, &fl)) + if (ip_route_output_key(net, &rt, &fl)) goto out_free; } @@ -1308,9 +1328,10 @@ out_free: static int ipmr_find_vif(struct net_device *dev) { + struct net *net = dev_net(dev); int ct; - for (ct = init_net.ipv4.maxvif-1; ct >= 0; ct--) { - if (init_net.ipv4.vif_table[ct].dev == dev) + for (ct = net->ipv4.maxvif-1; ct >= 0; ct--) { + if (net->ipv4.vif_table[ct].dev == dev) break; } return ct; @@ -1322,6 +1343,7 @@ static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local { int psend = -1; int vif, ct; + struct net *net = mfc_net(cache); vif = cache->mfc_parent; cache->mfc_un.res.pkt++; @@ -1330,7 +1352,7 @@ static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local /* * Wrong interface: drop packet and (maybe) send PIM assert. */ - if (init_net.ipv4.vif_table[vif].dev != skb->dev) { + if (net->ipv4.vif_table[vif].dev != skb->dev) { int true_vifi; if (skb->rtable->fl.iif == 0) { @@ -1351,24 +1373,24 @@ static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local cache->mfc_un.res.wrong_if++; true_vifi = ipmr_find_vif(skb->dev); - if (true_vifi >= 0 && init_net.ipv4.mroute_do_assert && + if (true_vifi >= 0 && net->ipv4.mroute_do_assert && /* pimsm uses asserts, when switching from RPT to SPT, so that we cannot check that packet arrived on an oif. It is bad, but otherwise we would need to move pretty large chunk of pimd to kernel. Ough... --ANK */ - (init_net.ipv4.mroute_do_pim || + (net->ipv4.mroute_do_pim || cache->mfc_un.res.ttls[true_vifi] < 255) && time_after(jiffies, cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) { cache->mfc_un.res.last_assert = jiffies; - ipmr_cache_report(skb, true_vifi, IGMPMSG_WRONGVIF); + ipmr_cache_report(net, skb, true_vifi, IGMPMSG_WRONGVIF); } goto dont_forward; } - init_net.ipv4.vif_table[vif].pkt_in++; - init_net.ipv4.vif_table[vif].bytes_in += skb->len; + net->ipv4.vif_table[vif].pkt_in++; + net->ipv4.vif_table[vif].bytes_in += skb->len; /* * Forward the frame @@ -1408,6 +1430,7 @@ dont_forward: int ip_mr_input(struct sk_buff *skb) { struct mfc_cache *cache; + struct net *net = dev_net(skb->dev); int local = skb->rtable->rt_flags&RTCF_LOCAL; /* Packet is looped back after forward, it should not be @@ -1428,9 +1451,9 @@ int ip_mr_input(struct sk_buff *skb) that we can forward NO IGMP messages. */ read_lock(&mrt_lock); - if (init_net.ipv4.mroute_sk) { + if (net->ipv4.mroute_sk) { nf_reset(skb); - raw_rcv(init_net.ipv4.mroute_sk, skb); + raw_rcv(net->ipv4.mroute_sk, skb); read_unlock(&mrt_lock); return 0; } @@ -1439,7 +1462,7 @@ int ip_mr_input(struct sk_buff *skb) } read_lock(&mrt_lock); - cache = ipmr_cache_find(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); + cache = ipmr_cache_find(net, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); /* * No usable cache entry @@ -1459,7 +1482,7 @@ int ip_mr_input(struct sk_buff *skb) vif = ipmr_find_vif(skb->dev); if (vif >= 0) { - int err = ipmr_cache_unresolved(vif, skb); + int err = ipmr_cache_unresolved(net, vif, skb); read_unlock(&mrt_lock); return err; @@ -1490,6 +1513,7 @@ static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen) { struct net_device *reg_dev = NULL; struct iphdr *encap; + struct net *net = dev_net(skb->dev); encap = (struct iphdr *)(skb_transport_header(skb) + pimlen); /* @@ -1504,8 +1528,8 @@ static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen) return 1; read_lock(&mrt_lock); - if (init_net.ipv4.mroute_reg_vif_num >= 0) - reg_dev = init_net.ipv4.vif_table[init_net.ipv4.mroute_reg_vif_num].dev; + if (net->ipv4.mroute_reg_vif_num >= 0) + reg_dev = net->ipv4.vif_table[net->ipv4.mroute_reg_vif_num].dev; if (reg_dev) dev_hold(reg_dev); read_unlock(&mrt_lock); @@ -1540,13 +1564,14 @@ static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen) int pim_rcv_v1(struct sk_buff * skb) { struct igmphdr *pim; + struct net *net = dev_net(skb->dev); if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) goto drop; pim = igmp_hdr(skb); - if (!init_net.ipv4.mroute_do_pim || + if (!net->ipv4.mroute_do_pim || pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) goto drop; @@ -1586,7 +1611,8 @@ ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm) { int ct; struct rtnexthop *nhp; - struct net_device *dev = init_net.ipv4.vif_table[c->mfc_parent].dev; + struct net *net = mfc_net(c); + struct net_device *dev = net->ipv4.vif_table[c->mfc_parent].dev; u8 *b = skb_tail_pointer(skb); struct rtattr *mp_head; @@ -1602,7 +1628,7 @@ ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm) nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp))); nhp->rtnh_flags = 0; nhp->rtnh_hops = c->mfc_un.res.ttls[ct]; - nhp->rtnh_ifindex = init_net.ipv4.vif_table[ct].dev->ifindex; + nhp->rtnh_ifindex = net->ipv4.vif_table[ct].dev->ifindex; nhp->rtnh_len = sizeof(*nhp); } } @@ -1616,14 +1642,15 @@ rtattr_failure: return -EMSGSIZE; } -int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait) +int ipmr_get_route(struct net *net, + struct sk_buff *skb, struct rtmsg *rtm, int nowait) { int err; struct mfc_cache *cache; struct rtable *rt = skb->rtable; read_lock(&mrt_lock); - cache = ipmr_cache_find(rt->rt_src, rt->rt_dst); + cache = ipmr_cache_find(net, rt->rt_src, rt->rt_dst); if (cache == NULL) { struct sk_buff *skb2; @@ -1654,7 +1681,7 @@ int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait) iph->saddr = rt->rt_src; iph->daddr = rt->rt_dst; iph->version = 0; - err = ipmr_cache_unresolved(vif, skb2); + err = ipmr_cache_unresolved(net, vif, skb2); read_unlock(&mrt_lock); return err; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 97f71153584f..6a9e204c8024 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2779,7 +2779,8 @@ int ip_route_output_key(struct net *net, struct rtable **rp, struct flowi *flp) return ip_route_output_flow(net, rp, flp, NULL, 0); } -static int rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, +static int rt_fill_info(struct net *net, + struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait, unsigned int flags) { struct rtable *rt = skb->rtable; @@ -2844,8 +2845,8 @@ static int rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, __be32 dst = rt->rt_dst; if (ipv4_is_multicast(dst) && !ipv4_is_local_multicast(dst) && - IPV4_DEVCONF_ALL(&init_net, MC_FORWARDING)) { - int err = ipmr_get_route(skb, r, nowait); + IPV4_DEVCONF_ALL(net, MC_FORWARDING)) { + int err = ipmr_get_route(net, skb, r, nowait); if (err <= 0) { if (!nowait) { if (err == 0) @@ -2950,7 +2951,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void if (rtm->rtm_flags & RTM_F_NOTIFY) rt->rt_flags |= RTCF_NOTIFY; - err = rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, + err = rt_fill_info(net, skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, RTM_NEWROUTE, 0, 0); if (err <= 0) goto errout_free; @@ -2988,7 +2989,7 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb) if (rt_is_expired(rt)) continue; skb->dst = dst_clone(&rt->u.dst); - if (rt_fill_info(skb, NETLINK_CB(cb->skb).pid, + if (rt_fill_info(net, skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1, NLM_F_MULTI) <= 0) { dst_release(xchg(&skb->dst, NULL)); -- cgit v1.2.3 From 0db155de988031f925096a7df1bf9633790a2c18 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 23 Jan 2009 22:28:48 -0800 Subject: com20020: Fix allyesconfig build failure. Reported by Stephen Rothwell. Due to missing 'extern' in the com20020_netdev_ops declaration, each file that includes linux/com20020.h gets another copy defined in it's resulting object file. Signed-off-by: David S. Miller --- include/linux/com20020.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/com20020.h b/include/linux/com20020.h index 350afa773f8f..5dcfb944b6ce 100644 --- a/include/linux/com20020.h +++ b/include/linux/com20020.h @@ -29,7 +29,7 @@ int com20020_check(struct net_device *dev); int com20020_found(struct net_device *dev, int shared); -const struct net_device_ops com20020_netdev_ops; +extern const struct net_device_ops com20020_netdev_ops; /* The number of low I/O ports used by the card. */ #define ARCNET_TOTAL_SIZE 8 -- cgit v1.2.3 From d5a9e24afb4ab38110ebb777588ea0bd0eacbd0a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 27 Jan 2009 16:22:11 -0800 Subject: net: Allow RX queue selection to seed TX queue hashing. The idea is that drivers which implement multiqueue RX pre-seed the SKB by recording the RX queue selected by the hardware. If such a seed is found on TX, we'll use that to select the outgoing TX queue. This helps get more consistent load balancing on router and firewall loads. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 15 +++++++++++++++ net/core/dev.c | 8 ++++++++ 2 files changed, 23 insertions(+) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index cf2cb50f77d1..a2c2378a9c58 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1904,6 +1904,21 @@ static inline void skb_copy_queue_mapping(struct sk_buff *to, const struct sk_bu to->queue_mapping = from->queue_mapping; } +static inline void skb_record_rx_queue(struct sk_buff *skb, u16 rx_queue) +{ + skb->queue_mapping = rx_queue + 1; +} + +static inline u16 skb_get_rx_queue(struct sk_buff *skb) +{ + return skb->queue_mapping - 1; +} + +static inline bool skb_rx_queue_recorded(struct sk_buff *skb) +{ + return (skb->queue_mapping != 0); +} + #ifdef CONFIG_XFRM static inline struct sec_path *skb_sec_path(struct sk_buff *skb) { diff --git a/net/core/dev.c b/net/core/dev.c index 5379b0c1190a..b21ad0b47aae 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1722,6 +1722,13 @@ static u16 simple_tx_hash(struct net_device *dev, struct sk_buff *skb) simple_tx_hashrnd_initialized = 1; } + if (skb_rx_queue_recorded(skb)) { + u32 val = skb_get_rx_queue(skb); + + hash = jhash_1word(val, simple_tx_hashrnd); + goto out; + } + switch (skb->protocol) { case htons(ETH_P_IP): if (!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET))) @@ -1759,6 +1766,7 @@ static u16 simple_tx_hash(struct net_device *dev, struct sk_buff *skb) hash = jhash_3words(addr1, addr2, ports, simple_tx_hashrnd); +out: return (u16) (((u64) hash * dev->real_num_tx_queues) >> 32); } -- cgit v1.2.3 From 9ee677c2276bfcbcf68042ec2718a504af0c5fd7 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Tue, 23 Dec 2008 14:03:38 +0000 Subject: wireless: Add channel/frequency conversions to ieee80211.h Added mappings for FHSS, DSSS and OFDM channels - with macros to point HR DSSS and ERP to the DSSS mappings. Currently just static inline functions. Use the new functions in the older fullmac drivers. This eliminates a number of const static buffers and removes a couple of range checks that are now redundant. Signed-off-by: David Kilroy Acked-by: Arnaldo Carvalho de Melo Acked-by: Richard Farina Acked-by: Jeroen Vreeken Signed-off-by: John W. Linville --- drivers/net/wireless/airo.c | 25 ++----- drivers/net/wireless/atmel.c | 20 +++--- drivers/net/wireless/orinoco/orinoco.c | 33 ++++------ drivers/net/wireless/rndis_wlan.c | 13 ++-- drivers/net/wireless/wl3501_cs.c | 9 +-- drivers/net/wireless/zd1201.c | 7 +- include/linux/ieee80211.h | 116 +++++++++++++++++++++++++++++++++ 7 files changed, 155 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index fc4322ca669f..2ff588bb0a7c 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1070,10 +1070,6 @@ static WifiCtlHdr wifictlhdr8023 = { } }; -// Frequency list (map channels to frequencies) -static const long frequency_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, - 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; - // A few details needed for WEP (Wireless Equivalent Privacy) #define MAX_KEY_SIZE 13 // 128 (?) bits #define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP @@ -5725,16 +5721,12 @@ static int airo_set_freq(struct net_device *dev, int rc = -EINPROGRESS; /* Call commit handler */ /* If setting by frequency, convert to a channel */ - if((fwrq->e == 1) && - (fwrq->m >= (int) 2.412e8) && - (fwrq->m <= (int) 2.487e8)) { + if(fwrq->e == 1) { int f = fwrq->m / 100000; - int c = 0; - while((c < 14) && (f != frequency_list[c])) - c++; + /* Hack to fall through... */ fwrq->e = 0; - fwrq->m = c + 1; + fwrq->m = ieee80211_freq_to_dsss_chan(f); } /* Setting by channel number */ if((fwrq->m > 1000) || (fwrq->e > 0)) @@ -5778,7 +5770,7 @@ static int airo_get_freq(struct net_device *dev, ch = le16_to_cpu(status_rid.channel); if((ch > 0) && (ch < 15)) { - fwrq->m = frequency_list[ch - 1] * 100000; + fwrq->m = ieee80211_dsss_chan_to_freq(ch) * 100000; fwrq->e = 1; } else { fwrq->m = ch; @@ -6795,8 +6787,8 @@ static int airo_get_range(struct net_device *dev, k = 0; for(i = 0; i < 14; i++) { range->freq[k].i = i + 1; /* List index */ - range->freq[k].m = frequency_list[i] * 100000; - range->freq[k++].e = 1; /* Values in table in MHz -> * 10^5 * 10 */ + range->freq[k].m = ieee80211_dsss_chan_to_freq(i + 1) * 100000; + range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */ } range->num_frequency = k; @@ -7189,10 +7181,7 @@ static inline char *airo_translate_scan(struct net_device *dev, /* Add frequency */ iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = le16_to_cpu(bss->dsChannel); - /* iwe.u.freq.m containt the channel (starting 1), our - * frequency_list array start at index 0... - */ - iwe.u.freq.m = frequency_list[iwe.u.freq.m - 1] * 100000; + iwe.u.freq.m = ieee80211_dsss_chan_to_freq(iwe.u.freq.m) * 100000; iwe.u.freq.e = 1; current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index 4223672c4432..91930a2c3c6b 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -2204,9 +2204,6 @@ static int atmel_get_frag(struct net_device *dev, return 0; } -static const long frequency_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, - 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; - static int atmel_set_freq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *fwrq, @@ -2216,16 +2213,12 @@ static int atmel_set_freq(struct net_device *dev, int rc = -EINPROGRESS; /* Call commit handler */ /* If setting by frequency, convert to a channel */ - if ((fwrq->e == 1) && - (fwrq->m >= (int) 241200000) && - (fwrq->m <= (int) 248700000)) { + if (fwrq->e == 1) { int f = fwrq->m / 100000; - int c = 0; - while ((c < 14) && (f != frequency_list[c])) - c++; + /* Hack to fall through... */ fwrq->e = 0; - fwrq->m = c + 1; + fwrq->m = ieee80211_freq_to_dsss_chan(f); } /* Setting by channel number */ if ((fwrq->m > 1000) || (fwrq->e > 0)) @@ -2384,8 +2377,11 @@ static int atmel_get_range(struct net_device *dev, if (range->num_channels != 0) { for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) { range->freq[k].i = i; /* List index */ - range->freq[k].m = frequency_list[i - 1] * 100000; - range->freq[k++].e = 1; /* Values in table in MHz -> * 10^5 * 10 */ + + /* Values in MHz -> * 10^5 * 10 */ + range->freq[k].m = (ieee80211_dsss_chan_to_freq(i) * + 100000); + range->freq[k++].e = 1; } range->num_frequency = k; } diff --git a/drivers/net/wireless/orinoco/orinoco.c b/drivers/net/wireless/orinoco/orinoco.c index 45a04faa7818..beb4d1f8c184 100644 --- a/drivers/net/wireless/orinoco/orinoco.c +++ b/drivers/net/wireless/orinoco/orinoco.c @@ -178,12 +178,7 @@ static const struct ethtool_ops orinoco_ethtool_ops; /* Data tables */ /********************************************************************/ -/* The frequency of each channel in MHz */ -static const long channel_frequency[] = { - 2412, 2417, 2422, 2427, 2432, 2437, 2442, - 2447, 2452, 2457, 2462, 2467, 2472, 2484 -}; -#define NUM_CHANNELS ARRAY_SIZE(channel_frequency) +#define NUM_CHANNELS 14 /* This tables gives the actual meanings of the bitrate IDs returned * by the firmware. */ @@ -3742,13 +3737,13 @@ static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, return err; } -static long orinoco_hw_get_freq(struct orinoco_private *priv) +static int orinoco_hw_get_freq(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; u16 channel; - long freq = 0; + int freq = 0; unsigned long flags; if (orinoco_lock(priv, &flags) != 0) @@ -3771,7 +3766,7 @@ static long orinoco_hw_get_freq(struct orinoco_private *priv) goto out; } - freq = channel_frequency[channel-1] * 100000; + freq = ieee80211_dsss_chan_to_freq(channel); out: orinoco_unlock(priv, &flags); @@ -3998,7 +3993,8 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, for (i = 0; i < NUM_CHANNELS; i++) { if (priv->channel_mask & (1 << i)) { range->freq[k].i = i + 1; - range->freq[k].m = channel_frequency[i] * 100000; + range->freq[k].m = (ieee80211_dsss_chan_to_freq(i + 1) * + 100000); range->freq[k].e = 1; k++; } @@ -4346,16 +4342,15 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, /* Setting by channel number */ chan = frq->m; } else { - /* Setting by frequency - search the table */ - int mult = 1; + /* Setting by frequency */ + int denom = 1; int i; + /* Calculate denominator to rescale to MHz */ for (i = 0; i < (6 - frq->e); i++) - mult *= 10; + denom *= 10; - for (i = 0; i < NUM_CHANNELS; i++) - if (frq->m == (channel_frequency[i] * mult)) - chan = i+1; + chan = ieee80211_freq_to_dsss_chan(frq->m / denom); } if ( (chan < 1) || (chan > NUM_CHANNELS) || @@ -4392,7 +4387,7 @@ static int orinoco_ioctl_getfreq(struct net_device *dev, return tmp; } - frq->m = tmp; + frq->m = tmp * 100000; frq->e = 1; return 0; @@ -5609,7 +5604,7 @@ static inline char *orinoco_translate_scan(struct net_device *dev, current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); - iwe.u.freq.m = channel_frequency[channel-1] * 100000; + iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000; iwe.u.freq.e = 1; current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); @@ -5760,7 +5755,7 @@ static inline char *orinoco_translate_ext_scan(struct net_device *dev, current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); - iwe.u.freq.m = channel_frequency[channel-1] * 100000; + iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000; iwe.u.freq.e = 1; current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index ed93ac41297f..105f214e21f4 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -369,9 +369,6 @@ struct rndis_wext_private { }; -static const int freq_chan[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, - 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; - static const int rates_80211g[8] = { 6, 9, 12, 18, 24, 36, 48, 54 }; static const int bcm4320_power_output[4] = { 25, 50, 75, 100 }; @@ -640,8 +637,8 @@ static void dsconfig_to_freq(unsigned int dsconfig, struct iw_freq *freq) static int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig) { if (freq->m < 1000 && freq->e == 0) { - if (freq->m >= 1 && freq->m <= ARRAY_SIZE(freq_chan)) - *dsconfig = freq_chan[freq->m - 1] * 1000; + if (freq->m >= 1 && freq->m <= 14) + *dsconfig = ieee80211_dsss_chan_to_freq(freq->m) * 1000; else return -1; } else { @@ -1178,11 +1175,11 @@ static int rndis_iw_get_range(struct net_device *dev, range->throughput = 11 * 1000 * 1000 / 2; } - range->num_channels = ARRAY_SIZE(freq_chan); + range->num_channels = 14; - for (i = 0; i < ARRAY_SIZE(freq_chan) && i < IW_MAX_FREQUENCIES; i++) { + for (i = 0; (i < 14) && (i < IW_MAX_FREQUENCIES); i++) { range->freq[i].i = i + 1; - range->freq[i].m = freq_chan[i] * 100000; + range->freq[i].m = ieee80211_dsss_chan_to_freq(i + 1) * 100000; range->freq[i].e = 1; } range->num_frequency = i; diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index c99a1b6b948f..c8d5c34e8ddf 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -111,12 +112,6 @@ static void wl3501_release(struct pcmcia_device *link); */ static dev_info_t wl3501_dev_info = "wl3501_cs"; -static int wl3501_chan2freq[] = { - [0] = 2412, [1] = 2417, [2] = 2422, [3] = 2427, [4] = 2432, - [5] = 2437, [6] = 2442, [7] = 2447, [8] = 2452, [9] = 2457, - [10] = 2462, [11] = 2467, [12] = 2472, [13] = 2477, -}; - static const struct { int reg_domain; int min, max, deflt; @@ -1510,7 +1505,7 @@ static int wl3501_get_freq(struct net_device *dev, struct iw_request_info *info, { struct wl3501_card *this = netdev_priv(dev); - wrqu->freq.m = wl3501_chan2freq[this->chan - 1] * 100000; + wrqu->freq.m = ieee80211_dsss_chan_to_freq(this->chan) * 100000; wrqu->freq.e = 1; return 0; } diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index b45c27d42fd8..6226ac2357f8 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -919,10 +919,9 @@ static int zd1201_set_freq(struct net_device *dev, if (freq->e == 0) channel = freq->m; else { - if (freq->m >= 2482) - channel = 14; - if (freq->m >= 2407) - channel = (freq->m-2407)/5; + channel = ieee80211_freq_to_dsss_chan(freq->m); + if (channel < 0) + channel = 0; } err = zd1201_setconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, channel); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index c4e6ca1a6306..cade2556af0e 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1185,4 +1185,120 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) return hdr->addr1; } +/** + * ieee80211_fhss_chan_to_freq - get channel frequency + * @channel: the FHSS channel + * + * Convert IEEE802.11 FHSS channel to frequency (MHz) + * Ref IEEE 802.11-2007 section 14.6 + */ +static inline int ieee80211_fhss_chan_to_freq(int channel) +{ + if ((channel > 1) && (channel < 96)) + return channel + 2400; + else + return -1; +} + +/** + * ieee80211_freq_to_fhss_chan - get channel + * @freq: the channels frequency + * + * Convert frequency (MHz) to IEEE802.11 FHSS channel + * Ref IEEE 802.11-2007 section 14.6 + */ +static inline int ieee80211_freq_to_fhss_chan(int freq) +{ + if ((freq > 2401) && (freq < 2496)) + return freq - 2400; + else + return -1; +} + +/** + * ieee80211_dsss_chan_to_freq - get channel center frequency + * @channel: the DSSS channel + * + * Convert IEEE802.11 DSSS channel to the center frequency (MHz). + * Ref IEEE 802.11-2007 section 15.6 + */ +static inline int ieee80211_dsss_chan_to_freq(int channel) +{ + if ((channel > 0) && (channel < 14)) + return 2407 + (channel * 5); + else if (channel == 14) + return 2484; + else + return -1; +} + +/** + * ieee80211_freq_to_dsss_chan - get channel + * @freq: the frequency + * + * Convert frequency (MHz) to IEEE802.11 DSSS channel + * Ref IEEE 802.11-2007 section 15.6 + * + * This routine selects the channel with the closest center frequency. + */ +static inline int ieee80211_freq_to_dsss_chan(int freq) +{ + if ((freq >= 2410) && (freq < 2475)) + return (freq - 2405) / 5; + else if ((freq >= 2482) && (freq < 2487)) + return 14; + else + return -1; +} + +/* Convert IEEE802.11 HR DSSS channel to frequency (MHz) and back + * Ref IEEE 802.11-2007 section 18.4.6.2 + * + * The channels and frequencies are the same as those defined for DSSS + */ +#define ieee80211_hr_chan_to_freq(chan) ieee80211_dsss_chan_to_freq(chan) +#define ieee80211_freq_to_hr_chan(freq) ieee80211_freq_to_dsss_chan(freq) + +/* Convert IEEE802.11 ERP channel to frequency (MHz) and back + * Ref IEEE 802.11-2007 section 19.4.2 + */ +#define ieee80211_erp_chan_to_freq(chan) ieee80211_hr_chan_to_freq(chan) +#define ieee80211_freq_to_erp_chan(freq) ieee80211_freq_to_hr_chan(freq) + +/** + * ieee80211_ofdm_chan_to_freq - get channel center frequency + * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz + * @channel: the OFDM channel + * + * Convert IEEE802.11 OFDM channel to center frequency (MHz) + * Ref IEEE 802.11-2007 section 17.3.8.3.2 + */ +static inline int ieee80211_ofdm_chan_to_freq(int s_freq, int channel) +{ + if ((channel > 0) && (channel <= 200) && + (s_freq >= 4000)) + return s_freq + (channel * 5); + else + return -1; +} + +/** + * ieee80211_freq_to_ofdm_channel - get channel + * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz + * @freq: the frequency + * + * Convert frequency (MHz) to IEEE802.11 OFDM channel + * Ref IEEE 802.11-2007 section 17.3.8.3.2 + * + * This routine selects the channel with the closest center frequency. + */ +static inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq) +{ + if ((freq > (s_freq + 2)) && (freq <= (s_freq + 1202)) && + (s_freq >= 4000)) + return (freq + 2 - s_freq) / 5; + else + return -1; +} + #endif /* LINUX_IEEE80211_H */ -- cgit v1.2.3 From 6b1c7c67603efdf0b39f6056989b0f8194cdc1f3 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Thu, 25 Dec 2008 00:39:28 +0100 Subject: b43/ssb: Add SPROM8 extraction and LP-PHY detection This adds detection code for the LP-PHY and SPROM extraction code for version 8, which is needed by the LP-PHY and newer N-PHY. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 12 +++++++ drivers/ssb/b43_pci_bridge.c | 1 + drivers/ssb/pci.c | 74 ++++++++++++++++++++++++++++++++++------- include/linux/ssb/ssb_regs.h | 36 ++++++++++++++++++++ 4 files changed, 111 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 26e733a5a56e..c627bac87a40 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -97,6 +97,7 @@ static const struct ssb_device_id b43_ssb_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 10), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 11), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 13), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 15), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 16), SSB_DEVTABLE_END }; @@ -3755,6 +3756,12 @@ static int b43_phy_versioning(struct b43_wldev *dev) if (phy_rev > 4) unsupported = 1; break; +#endif +#ifdef CONFIG_B43_PHY_LP + case B43_PHYTYPE_LP: + if (phy_rev > 1) + unsupported = 1; + break; #endif default: unsupported = 1; @@ -3808,6 +3815,10 @@ static int b43_phy_versioning(struct b43_wldev *dev) if (radio_ver != 0x2055 && radio_ver != 0x2056) unsupported = 1; break; + case B43_PHYTYPE_LP: + if (radio_ver != 0x2062) + unsupported = 1; + break; default: B43_WARN_ON(1); } @@ -4402,6 +4413,7 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) break; case B43_PHYTYPE_G: case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: have_2ghz_phy = 1; break; default: diff --git a/drivers/ssb/b43_pci_bridge.c b/drivers/ssb/b43_pci_bridge.c index 6433a7ed39f8..27a677584a4c 100644 --- a/drivers/ssb/b43_pci_bridge.c +++ b/drivers/ssb/b43_pci_bridge.c @@ -21,6 +21,7 @@ static const struct pci_device_id b43_pci_bridge_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4307) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4311) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4312) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4315) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4318) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4319) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4320) }, diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index d5cde051806b..c958ac16423c 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -467,6 +467,51 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) /* TODO - get remaining rev 4 stuff needed */ } +static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in) +{ + int i; + u16 v; + + /* extract the MAC address */ + for (i = 0; i < 3; i++) { + v = in[SPOFF(SSB_SPROM1_IL0MAC) + i]; + *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); + } + SPEX(country_code, SSB_SPROM8_CCODE, 0xFFFF, 0); + SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0); + SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0); + SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A, + SSB_SPROM8_ANTAVAIL_A_SHIFT); + SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG, + SSB_SPROM8_ANTAVAIL_BG_SHIFT); + SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0); + SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG, + SSB_SPROM8_ITSSI_BG_SHIFT); + SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0); + SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A, + SSB_SPROM8_ITSSI_A_SHIFT); + SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0); + SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1, + SSB_SPROM8_GPIOA_P1_SHIFT); + SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0); + SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3, + SSB_SPROM8_GPIOB_P3_SHIFT); + + /* Extract the antenna gain values. */ + SPEX(antenna_gain.ghz24.a0, SSB_SPROM8_AGAIN01, + SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT); + SPEX(antenna_gain.ghz24.a1, SSB_SPROM8_AGAIN01, + SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT); + SPEX(antenna_gain.ghz24.a2, SSB_SPROM8_AGAIN23, + SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT); + SPEX(antenna_gain.ghz24.a3, SSB_SPROM8_AGAIN23, + SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT); + memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24, + sizeof(out->antenna_gain.ghz5)); + + /* TODO - get remaining rev 8 stuff needed */ +} + static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, const u16 *in, u16 size) { @@ -487,15 +532,25 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, out->revision = 4; sprom_extract_r45(out, in); } else { - if (out->revision == 0) - goto unsupported; - if (out->revision >= 1 && out->revision <= 3) { + switch (out->revision) { + case 1: + case 2: + case 3: sprom_extract_r123(out, in); - } - if (out->revision == 4 || out->revision == 5) + break; + case 4: + case 5: sprom_extract_r45(out, in); - if (out->revision > 5) - goto unsupported; + break; + case 8: + sprom_extract_r8(out, in); + break; + default: + ssb_printk(KERN_WARNING PFX "Unsupported SPROM" + " revision %d detected. Will extract" + " v1\n", out->revision); + sprom_extract_r123(out, in); + } } if (out->boardflags_lo == 0xFFFF) @@ -504,11 +559,6 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, out->boardflags_hi = 0; /* per specs */ return 0; -unsupported: - ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d " - "detected. Will extract v1\n", out->revision); - sprom_extract_r123(out, in); - return 0; } static int ssb_pci_sprom_get(struct ssb_bus *bus, diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index 99a0f991e850..a01b982b5783 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -326,6 +326,42 @@ #define SSB_SPROM5_GPIOB_P3 0xFF00 /* Pin 3 */ #define SSB_SPROM5_GPIOB_P3_SHIFT 8 +/* SPROM Revision 8 */ +#define SSB_SPROM8_BFLLO 0x1084 /* Boardflags (low 16 bits) */ +#define SSB_SPROM8_BFLHI 0x1086 /* Boardflags Hi */ +#define SSB_SPROM8_IL0MAC 0x108C /* 6 byte MAC address */ +#define SSB_SPROM8_CCODE 0x1092 /* 2 byte country code */ +#define SSB_SPROM8_ANTAVAIL 0x109C /* Antenna available bitfields*/ +#define SSB_SPROM8_ANTAVAIL_A 0xFF00 /* A-PHY bitfield */ +#define SSB_SPROM8_ANTAVAIL_A_SHIFT 8 +#define SSB_SPROM8_ANTAVAIL_BG 0x00FF /* B-PHY and G-PHY bitfield */ +#define SSB_SPROM8_ANTAVAIL_BG_SHIFT 0 +#define SSB_SPROM8_AGAIN01 0x109E /* Antenna Gain (in dBm Q5.2) */ +#define SSB_SPROM8_AGAIN0 0x00FF /* Antenna 0 */ +#define SSB_SPROM8_AGAIN0_SHIFT 0 +#define SSB_SPROM8_AGAIN1 0xFF00 /* Antenna 1 */ +#define SSB_SPROM8_AGAIN1_SHIFT 8 +#define SSB_SPROM8_AGAIN23 0x10A0 +#define SSB_SPROM8_AGAIN2 0x00FF /* Antenna 2 */ +#define SSB_SPROM8_AGAIN2_SHIFT 0 +#define SSB_SPROM8_AGAIN3 0xFF00 /* Antenna 3 */ +#define SSB_SPROM8_AGAIN3_SHIFT 8 +#define SSB_SPROM8_GPIOA 0x1096 /*Gen. Purpose IO # 0 and 1 */ +#define SSB_SPROM8_GPIOA_P0 0x00FF /* Pin 0 */ +#define SSB_SPROM8_GPIOA_P1 0xFF00 /* Pin 1 */ +#define SSB_SPROM8_GPIOA_P1_SHIFT 8 +#define SSB_SPROM8_GPIOB 0x1098 /* Gen. Purpose IO # 2 and 3 */ +#define SSB_SPROM8_GPIOB_P2 0x00FF /* Pin 2 */ +#define SSB_SPROM8_GPIOB_P3 0xFF00 /* Pin 3 */ +#define SSB_SPROM8_GPIOB_P3_SHIFT 8 +#define SSB_SPROM8_MAXP_BG 0x10C0 /* Max Power BG in path 1 */ +#define SSB_SPROM8_MAXP_BG_MASK 0x00FF /* Mask for Max Power BG */ +#define SSB_SPROM8_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ +#define SSB_SPROM8_ITSSI_BG_SHIFT 8 +#define SSB_SPROM8_MAXP_A 0x10C8 /* Max Power A in path 1 */ +#define SSB_SPROM8_MAXP_A_MASK 0x00FF /* Mask for Max Power A */ +#define SSB_SPROM8_ITSSI_A 0xFF00 /* Mask for path 1 itssi_a */ +#define SSB_SPROM8_ITSSI_A_SHIFT 8 /* Values for SSB_SPROM1_BINF_CCODE */ enum { -- cgit v1.2.3 From 63649b6cf0a964582af2b4d4734e28ca90ec8f5c Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 1 Jan 2009 15:01:44 -0500 Subject: ath5k: support LEDs on Acer Aspire One netbook Add vendor ID for Foxconn and use it to set the ath5k LED gpio and polarity for Acer branded laptops. base.c: Changes-licensed-under: 3-Clause-BSD Reported-by: Maxim Levitsky Tested-by: Maxim Levitsky Tested-by: Andreas Mohr Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 7 +++++++ include/linux/pci_ids.h | 2 ++ 2 files changed, 9 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 88618645a7e4..fdf7733e76e1 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2596,6 +2596,13 @@ ath5k_init_leds(struct ath5k_softc *sc) sc->led_pin = 1; sc->led_on = 1; /* active high */ } + /* Pin 3 on Foxconn chips used in Acer Aspire One (0x105b:e008) */ + if (pdev->subsystem_vendor == PCI_VENDOR_ID_FOXCONN) { + __set_bit(ATH_STAT_LEDSOFT, sc->status); + sc->led_pin = 3; + sc->led_on = 0; /* active low */ + } + if (!test_bit(ATH_STAT_LEDSOFT, sc->status)) goto out; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 302423afa136..5b7a48c1d616 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -834,6 +834,8 @@ #define PCI_DEVICE_ID_PROMISE_20276 0x5275 #define PCI_DEVICE_ID_PROMISE_20277 0x7275 +#define PCI_VENDOR_ID_FOXCONN 0x105b + #define PCI_VENDOR_ID_UMC 0x1060 #define PCI_DEVICE_ID_UMC_UM8673F 0x0101 #define PCI_DEVICE_ID_UMC_UM8886BF 0x673a -- cgit v1.2.3 From 5394af4d86ae51b369ff243c3f75b6f9a74e164b Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:31:59 +0200 Subject: mac80211: 802.11w - STA flag for MFP Add flags for setting STA entries and struct ieee80211_if_sta to indicate whether management frame protection (MFP) is used. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 2 ++ include/net/cfg80211.h | 2 ++ net/mac80211/cfg.c | 4 ++++ net/mac80211/debugfs_sta.c | 5 +++-- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 7 +++++-- net/mac80211/sta_info.h | 2 ++ 7 files changed, 19 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index e86ed59f9ad5..218f0e73a7ae 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -412,12 +412,14 @@ enum nl80211_iftype { * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames * with short barker preamble * @NL80211_STA_FLAG_WME: station is WME/QoS capable + * @NL80211_STA_FLAG_MFP: station uses management frame protection */ enum nl80211_sta_flags { __NL80211_STA_FLAG_INVALID, NL80211_STA_FLAG_AUTHORIZED, NL80211_STA_FLAG_SHORT_PREAMBLE, NL80211_STA_FLAG_WME, + NL80211_STA_FLAG_MFP, /* keep last */ __NL80211_STA_FLAG_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 23c0ab74ded6..6619ed106134 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -112,12 +112,14 @@ struct beacon_parameters { * @STATION_FLAG_SHORT_PREAMBLE: station is capable of receiving frames * with short preambles * @STATION_FLAG_WME: station is WME/QoS capable + * @STATION_FLAG_MFP: station uses management frame protection */ enum station_flags { STATION_FLAG_CHANGED = 1<<0, STATION_FLAG_AUTHORIZED = 1<flags &= ~WLAN_STA_WME; if (params->station_flags & STATION_FLAG_WME) sta->flags |= WLAN_STA_WME; + + sta->flags &= ~WLAN_STA_MFP; + if (params->station_flags & STATION_FLAG_MFP) + sta->flags |= WLAN_STA_MFP; spin_unlock_bh(&sta->lock); } diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index a2fbe0131312..90230c718b5b 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -67,14 +67,15 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf, char buf[100]; struct sta_info *sta = file->private_data; u32 staflags = get_sta_flags(sta); - int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s", + int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s", staflags & WLAN_STA_AUTH ? "AUTH\n" : "", staflags & WLAN_STA_ASSOC ? "ASSOC\n" : "", staflags & WLAN_STA_PS ? "PS\n" : "", staflags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "", staflags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "", staflags & WLAN_STA_WME ? "WME\n" : "", - staflags & WLAN_STA_WDS ? "WDS\n" : ""); + staflags & WLAN_STA_WDS ? "WDS\n" : "", + staflags & WLAN_STA_MFP ? "MFP\n" : ""); return simple_read_from_buffer(userbuf, count, ppos, buf, res); } STA_OPS(flags); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3db6bc3cdaf2..b5f86cb17630 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -260,6 +260,7 @@ struct mesh_preq_queue { #define IEEE80211_STA_PRIVACY_INVOKED BIT(13) #define IEEE80211_STA_TKIP_WEP_USED BIT(14) #define IEEE80211_STA_CSA_RECEIVED BIT(15) +#define IEEE80211_STA_MFP_ENABLED BIT(16) /* flags for MLME request */ #define IEEE80211_STA_REQ_SCAN 0 #define IEEE80211_STA_REQ_DIRECT_PROBE 1 diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a1e683e305f0..bc8a7f1a6a15 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1,6 +1,6 @@ /* * BSS client mode implementation - * Copyright 2003, Jouni Malinen + * Copyright 2003-2008, Jouni Malinen * Copyright 2004, Instant802 Networks, Inc. * Copyright 2005, Devicescape Software, Inc. * Copyright 2006-2007 Jiri Benc @@ -472,7 +472,7 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, /* u.deauth.reason_code == u.disassoc.reason_code */ mgmt->u.deauth.reason_code = cpu_to_le16(reason); - ieee80211_tx_skb(sdata, skb, 0); + ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED); } /* MLME */ @@ -1408,6 +1408,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, rate_control_rate_init(sta); + if (ifsta->flags & IEEE80211_STA_MFP_ENABLED) + set_sta_flags(sta, WLAN_STA_MFP); + if (elems.wmm_param) set_sta_flags(sta, WLAN_STA_WME); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b683d3f5ef8a..d13a44b935e2 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -34,6 +34,7 @@ * @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the * IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next * frame to this station is transmitted. + * @WLAN_STA_MFP: Management frame protection is used with this STA. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH = 1<<0, @@ -46,6 +47,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_WDS = 1<<7, WLAN_STA_PSPOLL = 1<<8, WLAN_STA_CLEAR_PS_FILT = 1<<9, + WLAN_STA_MFP = 1<<10, }; #define STA_TID_NUM 16 -- cgit v1.2.3 From fb7333367632c67d8b6b06fb8d906cdabb11b02a Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:00 +0200 Subject: mac80211: 802.11w - CCMP for management frames Extend CCMP to support encryption and decryption of unicast management frames. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 30 ++++++++++++++++++++++++++++++ net/mac80211/tx.c | 23 ++++++++++++++++++++++- net/mac80211/wpa.c | 18 ++++++++++++------ 3 files changed, 64 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index cade2556af0e..d5165895f316 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1030,6 +1030,7 @@ enum ieee80211_category { WLAN_CATEGORY_QOS = 1, WLAN_CATEGORY_DLS = 2, WLAN_CATEGORY_BACK = 3, + WLAN_CATEGORY_PUBLIC = 4, WLAN_CATEGORY_WMM = 17, }; @@ -1185,6 +1186,35 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) return hdr->addr1; } +/** + * ieee80211_is_robust_mgmt_frame - check if frame is a robust management frame + * @hdr: the frame (buffer must include at least the first octet of payload) + */ +static inline bool ieee80211_is_robust_mgmt_frame(struct ieee80211_hdr *hdr) +{ + if (ieee80211_is_disassoc(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control)) + return true; + + if (ieee80211_is_action(hdr->frame_control)) { + u8 *category; + + /* + * Action frames, excluding Public Action frames, are Robust + * Management Frames. However, if we are looking at a Protected + * frame, skip the check since the data may be encrypted and + * the frame has already been found to be a Robust Management + * Frame (by the other end). + */ + if (ieee80211_has_protected(hdr->frame_control)) + return true; + category = ((u8 *) hdr) + 24; + return *category != WLAN_CATEGORY_PUBLIC; + } + + return false; +} + /** * ieee80211_fhss_chan_to_freq - get channel frequency * @channel: the FHSS channel diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index cd6bc87eec73..50c6c4fabea5 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -330,6 +330,22 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) return TX_CONTINUE; } +static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta, + struct sk_buff *skb) +{ + if (!ieee80211_is_mgmt(fc)) + return 0; + + if (sta == NULL || !test_sta_flags(sta, WLAN_STA_MFP)) + return 0; + + if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) + skb->data)) + return 0; + + return 1; +} + static ieee80211_tx_result ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) { @@ -428,10 +444,15 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) if (ieee80211_is_auth(hdr->frame_control)) break; case ALG_TKIP: - case ALG_CCMP: if (!ieee80211_is_data_present(hdr->frame_control)) tx->key = NULL; break; + case ALG_CCMP: + if (!ieee80211_is_data_present(hdr->frame_control) && + !ieee80211_use_mfp(hdr->frame_control, tx->sta, + tx->skb)) + tx->key = NULL; + break; } } diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 7aa63caf8d50..aff46adde3f0 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -266,7 +266,7 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, int encrypted) { __le16 mask_fc; - int a4_included; + int a4_included, mgmt; u8 qos_tid; u8 *b_0, *aad; u16 data_len, len_a; @@ -277,12 +277,15 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, aad = scratch + 4 * AES_BLOCK_LEN; /* - * Mask FC: zero subtype b4 b5 b6 + * Mask FC: zero subtype b4 b5 b6 (if not mgmt) * Retry, PwrMgt, MoreData; set Protected */ + mgmt = ieee80211_is_mgmt(hdr->frame_control); mask_fc = hdr->frame_control; - mask_fc &= ~cpu_to_le16(0x0070 | IEEE80211_FCTL_RETRY | + mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY | IEEE80211_FCTL_PM | IEEE80211_FCTL_MOREDATA); + if (!mgmt) + mask_fc &= ~cpu_to_le16(0x0070); mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); hdrlen = ieee80211_hdrlen(hdr->frame_control); @@ -300,8 +303,10 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, /* First block, b_0 */ b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ - /* Nonce: QoS Priority | A2 | PN */ - b_0[1] = qos_tid; + /* Nonce: Nonce Flags | A2 | PN + * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) + */ + b_0[1] = qos_tid | (mgmt << 4); memcpy(&b_0[2], hdr->addr2, ETH_ALEN); memcpy(&b_0[8], pn, CCMP_PN_LEN); /* l(m) */ @@ -446,7 +451,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (!ieee80211_is_data(hdr->frame_control)) + if (!ieee80211_is_data(hdr->frame_control) && + !ieee80211_is_robust_mgmt_frame(hdr)) return RX_CONTINUE; data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN; -- cgit v1.2.3 From 765cb46a3fc856245ea68a7c961ac87c77e4ae2d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:01 +0200 Subject: mac80211: 802.11w - Add BIP (AES-128-CMAC) Implement Broadcast/Multicast Integrity Protocol for management frame protection. This patch adds the needed definitions for the new information element (MMIE) and implementation for the new "encryption" type (though, BIP is actually not encrypting data, it provides only integrity protection). These routines will be used by a follow-on patch that enables BIP for multicast/broadcast robust management frames. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 10 ++++ net/mac80211/Makefile | 1 + net/mac80211/aes_cmac.c | 135 +++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/aes_cmac.h | 19 +++++++ net/mac80211/ieee80211_i.h | 2 +- net/mac80211/key.h | 10 ++++ net/mac80211/wpa.c | 125 +++++++++++++++++++++++++++++++++++++++++ net/mac80211/wpa.h | 5 ++ 8 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 net/mac80211/aes_cmac.c create mode 100644 net/mac80211/aes_cmac.h (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index d5165895f316..cceb9e86c744 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -655,6 +655,15 @@ struct ieee80211_mgmt { #define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u) +/* Management MIC information element (IEEE 802.11w) */ +struct ieee80211_mmie { + u8 element_id; + u8 length; + __le16 key_id; + u8 sequence_number[6]; + u8 mic[8]; +} __attribute__ ((packed)); + /* Control frames */ struct ieee80211_rts { __le16 frame_control; @@ -1018,6 +1027,7 @@ enum ieee80211_eid { WLAN_EID_HT_INFORMATION = 61, /* 802.11i */ WLAN_EID_RSN = 48, + WLAN_EID_MMIE = 76 /* 802.11w */, WLAN_EID_WPA = 221, WLAN_EID_GENERIC = 221, WLAN_EID_VENDOR_SPECIFIC = 221, diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 7d4971aa443f..5c6fadfb6a00 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -15,6 +15,7 @@ mac80211-y := \ michael.o \ tkip.o \ aes_ccm.o \ + aes_cmac.o \ cfg.o \ rx.o \ spectmgmt.o \ diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c new file mode 100644 index 000000000000..3d097b3d7b62 --- /dev/null +++ b/net/mac80211/aes_cmac.c @@ -0,0 +1,135 @@ +/* + * AES-128-CMAC with TLen 16 for IEEE 802.11w BIP + * Copyright 2008, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include "key.h" +#include "aes_cmac.h" + +#define AES_BLOCK_SIZE 16 +#define AES_CMAC_KEY_LEN 16 +#define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */ +#define AAD_LEN 20 + + +static void gf_mulx(u8 *pad) +{ + int i, carry; + + carry = pad[0] & 0x80; + for (i = 0; i < AES_BLOCK_SIZE - 1; i++) + pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); + pad[AES_BLOCK_SIZE - 1] <<= 1; + if (carry) + pad[AES_BLOCK_SIZE - 1] ^= 0x87; +} + + +static void aes_128_cmac_vector(struct crypto_cipher *tfm, u8 *scratch, + size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + u8 *cbc, *pad; + const u8 *pos, *end; + size_t i, e, left, total_len; + + cbc = scratch; + pad = scratch + AES_BLOCK_SIZE; + + memset(cbc, 0, AES_BLOCK_SIZE); + + total_len = 0; + for (e = 0; e < num_elem; e++) + total_len += len[e]; + left = total_len; + + e = 0; + pos = addr[0]; + end = pos + len[0]; + + while (left >= AES_BLOCK_SIZE) { + for (i = 0; i < AES_BLOCK_SIZE; i++) { + cbc[i] ^= *pos++; + if (pos >= end) { + e++; + pos = addr[e]; + end = pos + len[e]; + } + } + if (left > AES_BLOCK_SIZE) + crypto_cipher_encrypt_one(tfm, cbc, cbc); + left -= AES_BLOCK_SIZE; + } + + memset(pad, 0, AES_BLOCK_SIZE); + crypto_cipher_encrypt_one(tfm, pad, pad); + gf_mulx(pad); + + if (left || total_len == 0) { + for (i = 0; i < left; i++) { + cbc[i] ^= *pos++; + if (pos >= end) { + e++; + pos = addr[e]; + end = pos + len[e]; + } + } + cbc[left] ^= 0x80; + gf_mulx(pad); + } + + for (i = 0; i < AES_BLOCK_SIZE; i++) + pad[i] ^= cbc[i]; + crypto_cipher_encrypt_one(tfm, pad, pad); + memcpy(mac, pad, CMAC_TLEN); +} + + +void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad, + const u8 *data, size_t data_len, u8 *mic) +{ + const u8 *addr[3]; + size_t len[3]; + u8 zero[CMAC_TLEN]; + + memset(zero, 0, CMAC_TLEN); + addr[0] = aad; + len[0] = AAD_LEN; + addr[1] = data; + len[1] = data_len - CMAC_TLEN; + addr[2] = zero; + len[2] = CMAC_TLEN; + + aes_128_cmac_vector(tfm, scratch, 3, addr, len, mic); +} + + +struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]) +{ + struct crypto_cipher *tfm; + + tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) + return NULL; + + crypto_cipher_setkey(tfm, key, AES_CMAC_KEY_LEN); + + return tfm; +} + + +void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm) +{ + if (tfm) + crypto_free_cipher(tfm); +} diff --git a/net/mac80211/aes_cmac.h b/net/mac80211/aes_cmac.h new file mode 100644 index 000000000000..0eb9a4831508 --- /dev/null +++ b/net/mac80211/aes_cmac.h @@ -0,0 +1,19 @@ +/* + * Copyright 2008, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef AES_CMAC_H +#define AES_CMAC_H + +#include + +struct crypto_cipher * ieee80211_aes_cmac_key_setup(const u8 key[]); +void ieee80211_aes_cmac(struct crypto_cipher *tfm, u8 *scratch, const u8 *aad, + const u8 *data, size_t data_len, u8 *mic); +void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm); + +#endif /* AES_CMAC_H */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b5f86cb17630..20af92abd61d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -43,7 +43,7 @@ struct ieee80211_local; /* Required encryption head and tailroom */ #define IEEE80211_ENCRYPT_HEADROOM 8 -#define IEEE80211_ENCRYPT_TAILROOM 12 +#define IEEE80211_ENCRYPT_TAILROOM 18 /* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent * reception of at least three fragmented frames. This limit can be increased diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 425816e0996c..73ac28ca2ede 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -96,6 +96,16 @@ struct ieee80211_key { u8 tx_crypto_buf[6 * AES_BLOCK_LEN]; u8 rx_crypto_buf[6 * AES_BLOCK_LEN]; } ccmp; + struct { + u8 tx_pn[6]; + u8 rx_pn[6]; + struct crypto_cipher *tfm; + u32 replays; /* dot11RSNAStatsCMACReplays */ + u32 icverrors; /* dot11RSNAStatsCMACICVErrors */ + /* scratch buffers for virt_to_page() (crypto API) */ + u8 tx_crypto_buf[2 * AES_BLOCK_LEN]; + u8 rx_crypto_buf[2 * AES_BLOCK_LEN]; + } aes_cmac; } u; /* number of times this key has been used */ diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index aff46adde3f0..53e11e6ff66e 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -1,5 +1,6 @@ /* * Copyright 2002-2004, Instant802 Networks, Inc. + * Copyright 2008, Jouni Malinen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,6 +20,7 @@ #include "michael.h" #include "tkip.h" #include "aes_ccm.h" +#include "aes_cmac.h" #include "wpa.h" ieee80211_tx_result @@ -491,3 +493,126 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) return RX_CONTINUE; } + + +static void bip_aad(struct sk_buff *skb, u8 *aad) +{ + /* BIP AAD: FC(masked) || A1 || A2 || A3 */ + + /* FC type/subtype */ + aad[0] = skb->data[0]; + /* Mask FC Retry, PwrMgt, MoreData flags to zero */ + aad[1] = skb->data[1] & ~(BIT(4) | BIT(5) | BIT(6)); + /* A1 || A2 || A3 */ + memcpy(aad + 2, skb->data + 4, 3 * ETH_ALEN); +} + + +static inline void bip_ipn_swap(u8 *d, const u8 *s) +{ + *d++ = s[5]; + *d++ = s[4]; + *d++ = s[3]; + *d++ = s[2]; + *d++ = s[1]; + *d = s[0]; +} + + +ieee80211_tx_result +ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx) +{ + struct sk_buff *skb = tx->skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_key *key = tx->key; + struct ieee80211_mmie *mmie; + u8 *pn, aad[20]; + int i; + + if (tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) { + /* hwaccel */ + info->control.hw_key = &tx->key->conf; + return 0; + } + + if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie))) + return TX_DROP; + + mmie = (struct ieee80211_mmie *) skb_put(skb, sizeof(*mmie)); + mmie->element_id = WLAN_EID_MMIE; + mmie->length = sizeof(*mmie) - 2; + mmie->key_id = cpu_to_le16(key->conf.keyidx); + + /* PN = PN + 1 */ + pn = key->u.aes_cmac.tx_pn; + + for (i = sizeof(key->u.aes_cmac.tx_pn) - 1; i >= 0; i--) { + pn[i]++; + if (pn[i]) + break; + } + bip_ipn_swap(mmie->sequence_number, pn); + + bip_aad(skb, aad); + + /* + * MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64) + */ + ieee80211_aes_cmac(key->u.aes_cmac.tfm, key->u.aes_cmac.tx_crypto_buf, + aad, skb->data + 24, skb->len - 24, mmie->mic); + + return TX_CONTINUE; +} + + +ieee80211_rx_result +ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx) +{ + struct sk_buff *skb = rx->skb; + struct ieee80211_key *key = rx->key; + struct ieee80211_mmie *mmie; + u8 aad[20], mic[8], ipn[6]; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + + if (!ieee80211_is_mgmt(hdr->frame_control)) + return RX_CONTINUE; + + if ((rx->status->flag & RX_FLAG_DECRYPTED) && + (rx->status->flag & RX_FLAG_IV_STRIPPED)) + return RX_CONTINUE; + + if (skb->len < 24 + sizeof(*mmie)) + return RX_DROP_UNUSABLE; + + mmie = (struct ieee80211_mmie *) + (skb->data + skb->len - sizeof(*mmie)); + if (mmie->element_id != WLAN_EID_MMIE || + mmie->length != sizeof(*mmie) - 2) + return RX_DROP_UNUSABLE; /* Invalid MMIE */ + + bip_ipn_swap(ipn, mmie->sequence_number); + + if (memcmp(ipn, key->u.aes_cmac.rx_pn, 6) <= 0) { + key->u.aes_cmac.replays++; + return RX_DROP_UNUSABLE; + } + + if (!(rx->status->flag & RX_FLAG_DECRYPTED)) { + /* hardware didn't decrypt/verify MIC */ + bip_aad(skb, aad); + ieee80211_aes_cmac(key->u.aes_cmac.tfm, + key->u.aes_cmac.rx_crypto_buf, aad, + skb->data + 24, skb->len - 24, mic); + if (memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) { + key->u.aes_cmac.icverrors++; + return RX_DROP_UNUSABLE; + } + } + + memcpy(key->u.aes_cmac.rx_pn, ipn, 6); + + /* Remove MMIE */ + skb_trim(skb, skb->len - sizeof(*mmie)); + + return RX_CONTINUE; +} diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h index d42d221d8a1d..baba0608313e 100644 --- a/net/mac80211/wpa.h +++ b/net/mac80211/wpa.h @@ -28,4 +28,9 @@ ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx); ieee80211_rx_result ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx); +ieee80211_tx_result +ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx); +ieee80211_rx_result +ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx); + #endif /* WPA_H */ -- cgit v1.2.3 From 3cfcf6ac6d69dc290e96416731eea5c88ac7d426 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:02 +0200 Subject: mac80211: 802.11w - Use BIP (AES-128-CMAC) Add mechanism for managing BIP keys (IGTK) and integrate BIP into the TX/RX paths. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/pcu.c | 3 ++ include/linux/ieee80211.h | 1 + include/linux/nl80211.h | 6 ++- include/net/cfg80211.h | 5 +++ include/net/mac80211.h | 2 + net/mac80211/cfg.c | 31 ++++++++++++++ net/mac80211/debugfs_key.c | 79 ++++++++++++++++++++++++++++++++++- net/mac80211/debugfs_key.h | 10 +++++ net/mac80211/ieee80211_i.h | 5 ++- net/mac80211/key.c | 62 ++++++++++++++++++++++++++- net/mac80211/key.h | 6 +++ net/mac80211/rx.c | 90 ++++++++++++++++++++++++++++++++++++---- net/mac80211/tx.c | 9 ++++ net/wireless/nl80211.c | 29 +++++++++---- 14 files changed, 317 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index 5b416ed65299..e758b70ab7eb 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c @@ -1026,6 +1026,9 @@ int ath5k_keycache_type(const struct ieee80211_key_conf *key) return AR5K_KEYTABLE_TYPE_40; else if (key->keylen == LEN_WEP104) return AR5K_KEYTABLE_TYPE_104; + return -EINVAL; + default: + return -EINVAL; } return -EINVAL; } diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index cceb9e86c744..df98a8a549a2 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1139,6 +1139,7 @@ enum ieee80211_back_parties { /* reserved: 0x000FAC03 */ #define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 #define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 +#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 #define WLAN_MAX_KEY_LEN 32 diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 218f0e73a7ae..ee742bc9761e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -72,8 +72,8 @@ * * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. - * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT or - * %NL80211_ATTR_KEY_THRESHOLD. + * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, + * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC and %NL80211_ATTR_KEY_CIPHER * attributes. @@ -346,6 +346,8 @@ enum nl80211_attrs { NL80211_ATTR_WIPHY_FREQ, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_ATTR_KEY_DEFAULT_MGMT, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 6619ed106134..df78abc496f1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -473,6 +473,8 @@ struct ieee80211_channel; * * @set_default_key: set the default key on an interface * + * @set_default_mgmt_key: set the default management frame key on an interface + * * @add_beacon: Add a beacon with given parameters, @head, @interval * and @dtim_period will be valid, @tail is optional. * @set_beacon: Change the beacon parameters for an access point mode @@ -520,6 +522,9 @@ struct cfg80211_ops { int (*set_default_key)(struct wiphy *wiphy, struct net_device *netdev, u8 key_index); + int (*set_default_mgmt_key)(struct wiphy *wiphy, + struct net_device *netdev, + u8 key_index); int (*add_beacon)(struct wiphy *wiphy, struct net_device *dev, struct beacon_parameters *info); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8a305bfdb87b..61f1f37a9e27 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -651,11 +651,13 @@ struct ieee80211_if_conf { * @ALG_WEP: WEP40 or WEP104 * @ALG_TKIP: TKIP * @ALG_CCMP: CCMP (AES) + * @ALG_AES_CMAC: AES-128-CMAC */ enum ieee80211_key_alg { ALG_WEP, ALG_TKIP, ALG_CCMP, + ALG_AES_CMAC, }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 309d9189aa49..72c106915433 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -133,6 +133,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, case WLAN_CIPHER_SUITE_CCMP: alg = ALG_CCMP; break; + case WLAN_CIPHER_SUITE_AES_CMAC: + alg = ALG_AES_CMAC; + break; default: return -EINVAL; } @@ -275,6 +278,17 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, else params.cipher = WLAN_CIPHER_SUITE_WEP104; break; + case ALG_AES_CMAC: + params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; + seq[0] = key->u.aes_cmac.tx_pn[5]; + seq[1] = key->u.aes_cmac.tx_pn[4]; + seq[2] = key->u.aes_cmac.tx_pn[3]; + seq[3] = key->u.aes_cmac.tx_pn[2]; + seq[4] = key->u.aes_cmac.tx_pn[1]; + seq[5] = key->u.aes_cmac.tx_pn[0]; + params.seq = seq; + params.seq_len = 6; + break; } params.key = key->conf.key; @@ -304,6 +318,22 @@ static int ieee80211_config_default_key(struct wiphy *wiphy, return 0; } +static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, + u8 key_idx) +{ + struct ieee80211_sub_if_data *sdata; + + rcu_read_lock(); + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + ieee80211_set_default_mgmt_key(sdata, key_idx); + + rcu_read_unlock(); + + return 0; +} + static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -1153,6 +1183,7 @@ struct cfg80211_ops mac80211_config_ops = { .del_key = ieee80211_del_key, .get_key = ieee80211_get_key, .set_default_key = ieee80211_config_default_key, + .set_default_mgmt_key = ieee80211_config_default_mgmt_key, .add_beacon = ieee80211_add_beacon, .set_beacon = ieee80211_set_beacon, .del_beacon = ieee80211_del_beacon, diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 6424ac565ae0..99c752588b30 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -76,6 +76,9 @@ static ssize_t key_algorithm_read(struct file *file, case ALG_CCMP: alg = "CCMP\n"; break; + case ALG_AES_CMAC: + alg = "AES-128-CMAC\n"; + break; default: return 0; } @@ -105,6 +108,12 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); break; + case ALG_AES_CMAC: + tpn = key->u.aes_cmac.tx_pn; + len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", + tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], + tpn[5]); + break; default: return 0; } @@ -142,6 +151,14 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, } len = p - buf; break; + case ALG_AES_CMAC: + rpn = key->u.aes_cmac.rx_pn; + p += scnprintf(p, sizeof(buf)+buf-p, + "%02x%02x%02x%02x%02x%02x\n", + rpn[0], rpn[1], rpn[2], + rpn[3], rpn[4], rpn[5]); + len = p - buf; + break; default: return 0; } @@ -156,13 +173,40 @@ static ssize_t key_replays_read(struct file *file, char __user *userbuf, char buf[20]; int len; - if (key->conf.alg != ALG_CCMP) + switch (key->conf.alg) { + case ALG_CCMP: + len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); + break; + case ALG_AES_CMAC: + len = scnprintf(buf, sizeof(buf), "%u\n", + key->u.aes_cmac.replays); + break; + default: return 0; - len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); + } return simple_read_from_buffer(userbuf, count, ppos, buf, len); } KEY_OPS(replays); +static ssize_t key_icverrors_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_key *key = file->private_data; + char buf[20]; + int len; + + switch (key->conf.alg) { + case ALG_AES_CMAC: + len = scnprintf(buf, sizeof(buf), "%u\n", + key->u.aes_cmac.icverrors); + break; + default: + return 0; + } + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} +KEY_OPS(icverrors); + static ssize_t key_key_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -222,6 +266,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key) DEBUGFS_ADD(tx_spec); DEBUGFS_ADD(rx_spec); DEBUGFS_ADD(replays); + DEBUGFS_ADD(icverrors); DEBUGFS_ADD(key); DEBUGFS_ADD(ifindex); }; @@ -243,6 +288,7 @@ void ieee80211_debugfs_key_remove(struct ieee80211_key *key) DEBUGFS_DEL(tx_spec); DEBUGFS_DEL(rx_spec); DEBUGFS_DEL(replays); + DEBUGFS_DEL(icverrors); DEBUGFS_DEL(key); DEBUGFS_DEL(ifindex); @@ -280,6 +326,35 @@ void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata) sdata->common_debugfs.default_key = NULL; } +void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata) +{ + char buf[50]; + struct ieee80211_key *key; + + if (!sdata->debugfsdir) + return; + + /* this is running under the key lock */ + + key = sdata->default_mgmt_key; + if (key) { + sprintf(buf, "../keys/%d", key->debugfs.cnt); + sdata->common_debugfs.default_mgmt_key = + debugfs_create_symlink("default_mgmt_key", + sdata->debugfsdir, buf); + } else + ieee80211_debugfs_key_remove_mgmt_default(sdata); +} + +void ieee80211_debugfs_key_remove_mgmt_default(struct ieee80211_sub_if_data *sdata) +{ + if (!sdata) + return; + + debugfs_remove(sdata->common_debugfs.default_mgmt_key); + sdata->common_debugfs.default_mgmt_key = NULL; +} + void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, struct sta_info *sta) { diff --git a/net/mac80211/debugfs_key.h b/net/mac80211/debugfs_key.h index b1a3754ee240..54717b4e1371 100644 --- a/net/mac80211/debugfs_key.h +++ b/net/mac80211/debugfs_key.h @@ -6,6 +6,10 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key); void ieee80211_debugfs_key_remove(struct ieee80211_key *key); void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata); void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata); +void ieee80211_debugfs_key_add_mgmt_default( + struct ieee80211_sub_if_data *sdata); +void ieee80211_debugfs_key_remove_mgmt_default( + struct ieee80211_sub_if_data *sdata); void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, struct sta_info *sta); #else @@ -19,6 +23,12 @@ static inline void ieee80211_debugfs_key_add_default( static inline void ieee80211_debugfs_key_remove_default( struct ieee80211_sub_if_data *sdata) {} +static inline void ieee80211_debugfs_key_add_mgmt_default( + struct ieee80211_sub_if_data *sdata) +{} +static inline void ieee80211_debugfs_key_remove_mgmt_default( + struct ieee80211_sub_if_data *sdata) +{} static inline void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, struct sta_info *sta) {} diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 20af92abd61d..8c3245717c55 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -409,8 +409,10 @@ struct ieee80211_sub_if_data { unsigned int fragment_next; #define NUM_DEFAULT_KEYS 4 - struct ieee80211_key *keys[NUM_DEFAULT_KEYS]; +#define NUM_DEFAULT_MGMT_KEYS 2 + struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; struct ieee80211_key *default_key; + struct ieee80211_key *default_mgmt_key; u16 sequence_number; @@ -482,6 +484,7 @@ struct ieee80211_sub_if_data { } debugfs; struct { struct dentry *default_key; + struct dentry *default_mgmt_key; } common_debugfs; #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/key.c b/net/mac80211/key.c index b0a025c9b615..19b480de4bbc 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -18,6 +18,7 @@ #include "ieee80211_i.h" #include "debugfs_key.h" #include "aes_ccm.h" +#include "aes_cmac.h" /** @@ -215,13 +216,38 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) spin_unlock_irqrestore(&sdata->local->key_lock, flags); } +static void +__ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx) +{ + struct ieee80211_key *key = NULL; + + if (idx >= NUM_DEFAULT_KEYS && + idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) + key = sdata->keys[idx]; + + rcu_assign_pointer(sdata->default_mgmt_key, key); + + if (key) + add_todo(key, KEY_FLAG_TODO_DEFMGMTKEY); +} + +void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, + int idx) +{ + unsigned long flags; + + spin_lock_irqsave(&sdata->local->key_lock, flags); + __ieee80211_set_default_mgmt_key(sdata, idx); + spin_unlock_irqrestore(&sdata->local->key_lock, flags); +} + static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_key *old, struct ieee80211_key *new) { - int idx, defkey; + int idx, defkey, defmgmtkey; if (new) list_add(&new->list, &sdata->key_list); @@ -237,13 +263,19 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, idx = new->conf.keyidx; defkey = old && sdata->default_key == old; + defmgmtkey = old && sdata->default_mgmt_key == old; if (defkey && !new) __ieee80211_set_default_key(sdata, -1); + if (defmgmtkey && !new) + __ieee80211_set_default_mgmt_key(sdata, -1); rcu_assign_pointer(sdata->keys[idx], new); if (defkey && new) __ieee80211_set_default_key(sdata, new->conf.keyidx); + if (defmgmtkey && new) + __ieee80211_set_default_mgmt_key(sdata, + new->conf.keyidx); } if (old) { @@ -262,7 +294,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, { struct ieee80211_key *key; - BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS); + BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS); key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); if (!key) @@ -291,6 +323,10 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, key->conf.iv_len = CCMP_HDR_LEN; key->conf.icv_len = CCMP_MIC_LEN; break; + case ALG_AES_CMAC: + key->conf.iv_len = 0; + key->conf.icv_len = sizeof(struct ieee80211_mmie); + break; } memcpy(key->conf.key, key_data, key_len); INIT_LIST_HEAD(&key->list); @@ -308,6 +344,19 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, } } + if (alg == ALG_AES_CMAC) { + /* + * Initialize AES key state here as an optimization so that + * it does not need to be initialized for every packet. + */ + key->u.aes_cmac.tfm = + ieee80211_aes_cmac_key_setup(key_data); + if (!key->u.aes_cmac.tfm) { + kfree(key); + return NULL; + } + } + return key; } @@ -461,6 +510,8 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key) if (key->conf.alg == ALG_CCMP) ieee80211_aes_key_free(key->u.ccmp.tfm); + if (key->conf.alg == ALG_AES_CMAC) + ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); ieee80211_debugfs_key_remove(key); kfree(key); @@ -483,6 +534,7 @@ static void __ieee80211_key_todo(void) list_del_init(&key->todo); todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | KEY_FLAG_TODO_DEFKEY | + KEY_FLAG_TODO_DEFMGMTKEY | KEY_FLAG_TODO_HWACCEL_ADD | KEY_FLAG_TODO_HWACCEL_REMOVE | KEY_FLAG_TODO_DELETE); @@ -500,6 +552,11 @@ static void __ieee80211_key_todo(void) ieee80211_debugfs_key_add_default(key->sdata); work_done = true; } + if (todoflags & KEY_FLAG_TODO_DEFMGMTKEY) { + ieee80211_debugfs_key_remove_mgmt_default(key->sdata); + ieee80211_debugfs_key_add_mgmt_default(key->sdata); + work_done = true; + } if (todoflags & KEY_FLAG_TODO_HWACCEL_ADD) { ieee80211_key_enable_hw_accel(key); work_done = true; @@ -535,6 +592,7 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) ieee80211_key_lock(); ieee80211_debugfs_key_remove_default(sdata); + ieee80211_debugfs_key_remove_mgmt_default(sdata); spin_lock_irqsave(&sdata->local->key_lock, flags); list_for_each_entry_safe(key, tmp, &sdata->key_list, list) diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 73ac28ca2ede..215d3ef42a4f 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -46,6 +46,8 @@ struct sta_info; * acceleration. * @KEY_FLAG_TODO_DEFKEY: Key is default key and debugfs needs to be updated. * @KEY_FLAG_TODO_ADD_DEBUGFS: Key needs to be added to debugfs. + * @KEY_FLAG_TODO_DEFMGMTKEY: Key is default management key and debugfs needs + * to be updated. */ enum ieee80211_internal_key_flags { KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0), @@ -54,6 +56,7 @@ enum ieee80211_internal_key_flags { KEY_FLAG_TODO_HWACCEL_REMOVE = BIT(3), KEY_FLAG_TODO_DEFKEY = BIT(4), KEY_FLAG_TODO_ADD_DEBUGFS = BIT(5), + KEY_FLAG_TODO_DEFMGMTKEY = BIT(6), }; struct tkip_ctx { @@ -124,6 +127,7 @@ struct ieee80211_key { struct dentry *tx_spec; struct dentry *rx_spec; struct dentry *replays; + struct dentry *icverrors; struct dentry *key; struct dentry *ifindex; int cnt; @@ -150,6 +154,8 @@ void ieee80211_key_link(struct ieee80211_key *key, struct sta_info *sta); void ieee80211_key_free(struct ieee80211_key *key); void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx); +void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, + int idx); void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata); void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata); void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b68e082e99ce..abc3aa583ca6 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -446,6 +446,52 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) return RX_CONTINUE; } + +static int ieee80211_is_unicast_robust_mgmt_frame(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + + if (skb->len < 24 || is_multicast_ether_addr(hdr->addr1)) + return 0; + + return ieee80211_is_robust_mgmt_frame(hdr); +} + + +static int ieee80211_is_multicast_robust_mgmt_frame(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + + if (skb->len < 24 || !is_multicast_ether_addr(hdr->addr1)) + return 0; + + return ieee80211_is_robust_mgmt_frame(hdr); +} + + +/* Get the BIP key index from MMIE; return -1 if this is not a BIP frame */ +static int ieee80211_get_mmie_keyidx(struct sk_buff *skb) +{ + struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data; + struct ieee80211_mmie *mmie; + + if (skb->len < 24 + sizeof(*mmie) || + !is_multicast_ether_addr(hdr->da)) + return -1; + + if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr)) + return -1; /* not a robust management frame */ + + mmie = (struct ieee80211_mmie *) + (skb->data + skb->len - sizeof(*mmie)); + if (mmie->element_id != WLAN_EID_MMIE || + mmie->length != sizeof(*mmie) - 2) + return -1; + + return le16_to_cpu(mmie->key_id); +} + + static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) { @@ -561,21 +607,23 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) int hdrlen; ieee80211_rx_result result = RX_DROP_UNUSABLE; struct ieee80211_key *stakey = NULL; + int mmie_keyidx = -1; /* * Key selection 101 * - * There are three types of keys: + * There are four types of keys: * - GTK (group keys) + * - IGTK (group keys for management frames) * - PTK (pairwise keys) * - STK (station-to-station pairwise keys) * * When selecting a key, we have to distinguish between multicast * (including broadcast) and unicast frames, the latter can only - * use PTKs and STKs while the former always use GTKs. Unless, of - * course, actual WEP keys ("pre-RSNA") are used, then unicast - * frames can also use key indizes like GTKs. Hence, if we don't - * have a PTK/STK we check the key index for a WEP key. + * use PTKs and STKs while the former always use GTKs and IGTKs. + * Unless, of course, actual WEP keys ("pre-RSNA") are used, then + * unicast frames can also use key indices like GTKs. Hence, if we + * don't have a PTK/STK we check the key index for a WEP key. * * Note that in a regular BSS, multicast frames are sent by the * AP only, associated stations unicast the frame to the AP first @@ -588,8 +636,14 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) * possible. */ - if (!ieee80211_has_protected(hdr->frame_control)) - return RX_CONTINUE; + if (!ieee80211_has_protected(hdr->frame_control)) { + if (!ieee80211_is_mgmt(hdr->frame_control) || + rx->sta == NULL || !test_sta_flags(rx->sta, WLAN_STA_MFP)) + return RX_CONTINUE; + mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb); + if (mmie_keyidx < 0) + return RX_CONTINUE; + } /* * No point in finding a key and decrypting if the frame is neither @@ -603,6 +657,16 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) if (!is_multicast_ether_addr(hdr->addr1) && stakey) { rx->key = stakey; + } else if (mmie_keyidx >= 0) { + /* Broadcast/multicast robust management frame / BIP */ + if ((rx->status->flag & RX_FLAG_DECRYPTED) && + (rx->status->flag & RX_FLAG_IV_STRIPPED)) + return RX_CONTINUE; + + if (mmie_keyidx < NUM_DEFAULT_KEYS || + mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) + return RX_DROP_MONITOR; /* unexpected BIP keyidx */ + rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]); } else { /* * The device doesn't give us the IV so we won't be @@ -665,6 +729,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) case ALG_CCMP: result = ieee80211_crypto_ccmp_decrypt(rx); break; + case ALG_AES_CMAC: + result = ieee80211_crypto_aes_cmac_decrypt(rx); + break; } /* either the frame has been decrypted or will be dropped */ @@ -1112,6 +1179,15 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) /* Drop unencrypted frames if key is set. */ if (unlikely(!ieee80211_has_protected(fc) && !ieee80211_is_nullfunc(fc) && + (!ieee80211_is_mgmt(fc) || + (ieee80211_is_unicast_robust_mgmt_frame(rx->skb) && + rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP))) && + (rx->key || rx->sdata->drop_unencrypted))) + return -EACCES; + /* BIP does not use Protected field, so need to check MMIE */ + if (unlikely(rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP) && + ieee80211_is_multicast_robust_mgmt_frame(rx->skb) && + ieee80211_get_mmie_keyidx(rx->skb) < 0 && (rx->key || rx->sdata->drop_unencrypted))) return -EACCES; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 50c6c4fabea5..ad53ea9e9c77 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -425,6 +425,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) tx->key = NULL; else if (tx->sta && (key = rcu_dereference(tx->sta->key))) tx->key = key; + else if (ieee80211_is_mgmt(hdr->frame_control) && + (key = rcu_dereference(tx->sdata->default_mgmt_key))) + tx->key = key; else if ((key = rcu_dereference(tx->sdata->default_key))) tx->key = key; else if (tx->sdata->drop_unencrypted && @@ -453,6 +456,10 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) tx->skb)) tx->key = NULL; break; + case ALG_AES_CMAC: + if (!ieee80211_is_mgmt(hdr->frame_control)) + tx->key = NULL; + break; } } @@ -808,6 +815,8 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) return ieee80211_crypto_tkip_encrypt(tx); case ALG_CCMP: return ieee80211_crypto_ccmp_encrypt(tx); + case ALG_AES_CMAC: + return ieee80211_crypto_aes_cmac_encrypt(tx); } /* not reached */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1e728fff474e..123d3b160fad 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -738,7 +738,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_KEY_IDX]) key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - if (key_idx > 3) + if (key_idx > 5) return -EINVAL; if (info->attrs[NL80211_ATTR_MAC]) @@ -804,30 +804,41 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) int err; struct net_device *dev; u8 key_idx; + int (*func)(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index); if (!info->attrs[NL80211_ATTR_KEY_IDX]) return -EINVAL; key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - if (key_idx > 3) + if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) { + if (key_idx < 4 || key_idx > 5) + return -EINVAL; + } else if (key_idx > 3) return -EINVAL; /* currently only support setting default key */ - if (!info->attrs[NL80211_ATTR_KEY_DEFAULT]) + if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] && + !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) return -EINVAL; err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); if (err) return err; - if (!drv->ops->set_default_key) { + if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) + func = drv->ops->set_default_key; + else + func = drv->ops->set_default_mgmt_key; + + if (!func) { err = -EOPNOTSUPP; goto out; } rtnl_lock(); - err = drv->ops->set_default_key(&drv->wiphy, dev, key_idx); + err = func(&drv->wiphy, dev, key_idx); rtnl_unlock(); out: @@ -863,7 +874,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - if (key_idx > 3) + if (key_idx > 5) return -EINVAL; /* @@ -894,6 +905,10 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) if (params.key_len != 13) return -EINVAL; break; + case WLAN_CIPHER_SUITE_AES_CMAC: + if (params.key_len != 16) + return -EINVAL; + break; default: return -EINVAL; } @@ -928,7 +943,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_KEY_IDX]) key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - if (key_idx > 3) + if (key_idx > 5) return -EINVAL; if (info->attrs[NL80211_ATTR_MAC]) -- cgit v1.2.3 From 54604d3a827b37525ef017adba313c7112e0f484 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:03 +0200 Subject: mac80211: 802.11w - WEXT parameter for setting mgmt cipher Add a new IW_AUTH parameter for setting cipher suite for multicast/broadcast management frames. This is for full-mac drivers that take care of RSN IE generation for (re)association request frames. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/wireless.h | 5 ++++- net/mac80211/wext.c | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/wireless.h b/include/linux/wireless.h index d7958f9b52cb..d426dce47e7c 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -577,18 +577,21 @@ #define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 #define IW_AUTH_ROAMING_CONTROL 9 #define IW_AUTH_PRIVACY_INVOKED 10 +#define IW_AUTH_CIPHER_GROUP_MGMT 11 /* IW_AUTH_WPA_VERSION values (bit field) */ #define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 #define IW_AUTH_WPA_VERSION_WPA 0x00000002 #define IW_AUTH_WPA_VERSION_WPA2 0x00000004 -/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */ +/* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT + * values (bit field) */ #define IW_AUTH_CIPHER_NONE 0x00000001 #define IW_AUTH_CIPHER_WEP40 0x00000002 #define IW_AUTH_CIPHER_TKIP 0x00000004 #define IW_AUTH_CIPHER_CCMP 0x00000008 #define IW_AUTH_CIPHER_WEP104 0x00000010 +#define IW_AUTH_CIPHER_AES_CMAC 0x00000020 /* IW_AUTH_KEY_MGMT values (bit field) */ #define IW_AUTH_KEY_MGMT_802_1X 1 diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 1e5b29bdb3a7..c3b2dd5706fb 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -927,6 +927,7 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev, case IW_AUTH_WPA_ENABLED: case IW_AUTH_RX_UNENCRYPTED_EAPOL: case IW_AUTH_KEY_MGMT: + case IW_AUTH_CIPHER_GROUP_MGMT: break; case IW_AUTH_CIPHER_PAIRWISE: if (sdata->vif.type == NL80211_IFTYPE_STATION) { -- cgit v1.2.3 From 22787dbaa3b952602542506e0426ea6d5f104042 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:04 +0200 Subject: mac80211: 802.11w - WEXT configuration for IGTK Added new SIOCSIWENCODEEXT algorithm for configuring BIP (AES-CMAC) keys (IGTK). Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/wireless.h | 1 + net/mac80211/wext.c | 62 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/include/linux/wireless.h b/include/linux/wireless.h index d426dce47e7c..5d1f3fbffd77 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -615,6 +615,7 @@ #define IW_ENCODE_ALG_TKIP 2 #define IW_ENCODE_ALG_CCMP 3 #define IW_ENCODE_ALG_PMK 4 +#define IW_ENCODE_ALG_AES_CMAC 5 /* struct iw_encode_ext ->ext_flags */ #define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 #define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index c3b2dd5706fb..7ba1d5ba3afa 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -37,7 +37,14 @@ static int ieee80211_set_encryption(struct ieee80211_sub_if_data *sdata, u8 *sta struct ieee80211_key *key; int err; - if (idx < 0 || idx >= NUM_DEFAULT_KEYS) { + if (alg == ALG_AES_CMAC) { + if (idx < NUM_DEFAULT_KEYS || + idx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) { + printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d " + "(BIP)\n", sdata->dev->name, idx); + return -EINVAL; + } + } else if (idx < 0 || idx >= NUM_DEFAULT_KEYS) { printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n", sdata->dev->name, idx); return -EINVAL; @@ -103,6 +110,9 @@ static int ieee80211_set_encryption(struct ieee80211_sub_if_data *sdata, u8 *sta if (set_tx_key || (!sta && !sdata->default_key && key)) ieee80211_set_default_key(sdata, idx); + if (alg == ALG_AES_CMAC && + (set_tx_key || (!sta && !sdata->default_mgmt_key && key))) + ieee80211_set_default_mgmt_key(sdata, idx); } out_unlock: @@ -1048,6 +1058,9 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev, case IW_ENCODE_ALG_CCMP: alg = ALG_CCMP; break; + case IW_ENCODE_ALG_AES_CMAC: + alg = ALG_AES_CMAC; + break; default: return -EOPNOTSUPP; } @@ -1056,20 +1069,41 @@ static int ieee80211_ioctl_siwencodeext(struct net_device *dev, remove = 1; idx = erq->flags & IW_ENCODE_INDEX; - if (idx < 1 || idx > 4) { - idx = -1; - if (!sdata->default_key) - idx = 0; - else for (i = 0; i < NUM_DEFAULT_KEYS; i++) { - if (sdata->default_key == sdata->keys[i]) { - idx = i; - break; + if (alg == ALG_AES_CMAC) { + if (idx < NUM_DEFAULT_KEYS + 1 || + idx > NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) { + idx = -1; + if (!sdata->default_mgmt_key) + idx = 0; + else for (i = NUM_DEFAULT_KEYS; + i < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS; + i++) { + if (sdata->default_mgmt_key == sdata->keys[i]) + { + idx = i; + break; + } } - } - if (idx < 0) - return -EINVAL; - } else - idx--; + if (idx < 0) + return -EINVAL; + } else + idx--; + } else { + if (idx < 1 || idx > 4) { + idx = -1; + if (!sdata->default_key) + idx = 0; + else for (i = 0; i < NUM_DEFAULT_KEYS; i++) { + if (sdata->default_key == sdata->keys[i]) { + idx = i; + break; + } + } + if (idx < 0) + return -EINVAL; + } else + idx--; + } return ieee80211_set_encryption(sdata, ext->addr.sa_data, idx, alg, remove, -- cgit v1.2.3 From fdfacf0ae2e8339098b1164d2317b792d7662c0a Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:05 +0200 Subject: mac80211: 802.11w - Configuration of MFP disabled/optional/required Add new WEXT IW_AUTH_* parameter for setting MFP disabled/optional/required. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/wireless.h | 6 ++++++ net/mac80211/ieee80211_i.h | 6 ++++++ net/mac80211/mlme.c | 4 ++++ net/mac80211/wext.c | 7 +++++++ 4 files changed, 23 insertions(+) (limited to 'include/linux') diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 5d1f3fbffd77..cb24204851f7 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -578,6 +578,7 @@ #define IW_AUTH_ROAMING_CONTROL 9 #define IW_AUTH_PRIVACY_INVOKED 10 #define IW_AUTH_CIPHER_GROUP_MGMT 11 +#define IW_AUTH_MFP 12 /* IW_AUTH_WPA_VERSION values (bit field) */ #define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 @@ -607,6 +608,11 @@ #define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming * control */ +/* IW_AUTH_MFP (management frame protection) values */ +#define IW_AUTH_MFP_DISABLED 0 /* MFP disabled */ +#define IW_AUTH_MFP_OPTIONAL 1 /* MFP optional */ +#define IW_AUTH_MFP_REQUIRED 2 /* MFP required */ + /* SIOCSIWENCODEEXT definitions */ #define IW_ENCODE_SEQ_MAX_SIZE 8 /* struct iw_encode_ext ->alg */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8c3245717c55..212c732fbba7 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -320,6 +320,12 @@ struct ieee80211_if_sta { int auth_alg; /* currently used IEEE 802.11 authentication algorithm */ int auth_transaction; + enum { + IEEE80211_MFP_DISABLED, + IEEE80211_MFP_OPTIONAL, + IEEE80211_MFP_REQUIRED + } mfp; /* management frame protection */ + unsigned long ibss_join_req; struct sk_buff *probe_resp; /* ProbeResp template for IBSS */ u32 supp_rates_bits[IEEE80211_NUM_BANDS]; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bc8a7f1a6a15..42c5f981c715 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2317,6 +2317,10 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata, selected->ssid_len); ieee80211_sta_set_bssid(sdata, selected->bssid); ieee80211_sta_def_wmm_params(sdata, selected); + if (sdata->u.sta.mfp == IEEE80211_MFP_REQUIRED) + sdata->u.sta.flags |= IEEE80211_STA_MFP_ENABLED; + else + sdata->u.sta.flags &= ~IEEE80211_STA_MFP_ENABLED; /* Send out direct probe if no probe resp was received or * the one we have is outdated diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 7ba1d5ba3afa..2dd387495dfe 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -975,6 +975,13 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev, else ret = -EOPNOTSUPP; break; + case IW_AUTH_MFP: + if (sdata->vif.type == NL80211_IFTYPE_STATION || + sdata->vif.type == NL80211_IFTYPE_ADHOC) + sdata->u.sta.mfp = data->value; + else + ret = -EOPNOTSUPP; + break; default: ret = -EOPNOTSUPP; break; -- cgit v1.2.3 From fea147328908b7e2bfcaf9dc4377909d5507ca35 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:06 +0200 Subject: mac80211: 802.11w - SA Query processing Process SA Query Requests for client mode in mac80211. AP side processing of SA Query Response frames is in user space (hostapd). Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 14 ++++++++++ net/mac80211/rx.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index df98a8a549a2..9fe1948d28d3 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -527,6 +527,8 @@ struct ieee80211_tim_ie { u8 virtual_map[0]; } __attribute__ ((packed)); +#define WLAN_SA_QUERY_TR_ID_LEN 16 + struct ieee80211_mgmt { __le16 frame_control; __le16 duration; @@ -646,6 +648,10 @@ struct ieee80211_mgmt { u8 action_code; u8 variable[0]; } __attribute__((packed)) mesh_action; + struct { + u8 action; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } __attribute__ ((packed)) sa_query; } u; } __attribute__ ((packed)) action; } u; @@ -1041,6 +1047,7 @@ enum ieee80211_category { WLAN_CATEGORY_DLS = 2, WLAN_CATEGORY_BACK = 3, WLAN_CATEGORY_PUBLIC = 4, + WLAN_CATEGORY_SA_QUERY = 8, WLAN_CATEGORY_WMM = 17, }; @@ -1129,6 +1136,13 @@ enum ieee80211_back_parties { WLAN_BACK_TIMER = 2, }; +/* SA Query action */ +enum ieee80211_sa_query_action { + WLAN_ACTION_SA_QUERY_REQUEST = 0, + WLAN_ACTION_SA_QUERY_RESPONSE = 1, +}; + + /* A-MSDU 802.11n */ #define IEEE80211_QOS_CONTROL_A_MSDU_PRESENT 0x0080 diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index abc3aa583ca6..63db89aef3e4 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1667,6 +1667,57 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx) return RX_CONTINUE; } +void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct ieee80211_local *local = sdata->local; + struct sk_buff *skb; + struct ieee80211_mgmt *resp; + + if (compare_ether_addr(mgmt->da, sdata->dev->dev_addr) != 0) { + /* Not to own unicast address */ + return; + } + + if (compare_ether_addr(mgmt->sa, sdata->u.sta.bssid) != 0 || + compare_ether_addr(mgmt->bssid, sdata->u.sta.bssid) != 0) { + /* Not from the current AP. */ + return; + } + + if (sdata->u.sta.state == IEEE80211_STA_MLME_ASSOCIATE) { + /* Association in progress; ignore SA Query */ + return; + } + + if (len < 24 + 1 + sizeof(resp->u.action.u.sa_query)) { + /* Too short SA Query request frame */ + return; + } + + skb = dev_alloc_skb(sizeof(*resp) + local->hw.extra_tx_headroom); + if (skb == NULL) + return; + + skb_reserve(skb, local->hw.extra_tx_headroom); + resp = (struct ieee80211_mgmt *) skb_put(skb, 24); + memset(resp, 0, 24); + memcpy(resp->da, mgmt->sa, ETH_ALEN); + memcpy(resp->sa, sdata->dev->dev_addr, ETH_ALEN); + memcpy(resp->bssid, sdata->u.sta.bssid, ETH_ALEN); + resp->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + skb_put(skb, 1 + sizeof(resp->u.action.u.sa_query)); + resp->u.action.category = WLAN_CATEGORY_SA_QUERY; + resp->u.action.u.sa_query.action = WLAN_ACTION_SA_QUERY_RESPONSE; + memcpy(resp->u.action.u.sa_query.trans_id, + mgmt->u.action.u.sa_query.trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + + ieee80211_tx_skb(sdata, skb, 1); +} + static ieee80211_rx_result debug_noinline ieee80211_rx_h_action(struct ieee80211_rx_data *rx) { @@ -1743,6 +1794,24 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) break; } break; + case WLAN_CATEGORY_SA_QUERY: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.sa_query))) + return RX_DROP_MONITOR; + switch (mgmt->u.action.u.sa_query.action) { + case WLAN_ACTION_SA_QUERY_REQUEST: + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return RX_DROP_MONITOR; + ieee80211_process_sa_query_req(sdata, mgmt, len); + break; + case WLAN_ACTION_SA_QUERY_RESPONSE: + /* + * SA Query response is currently only used in AP mode + * and it is processed in user space. + */ + return RX_CONTINUE; + } + break; default: return RX_CONTINUE; } -- cgit v1.2.3 From 63a5ab82255a4ff5d0783f16427210f1d45d7ec8 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 8 Jan 2009 13:32:09 +0200 Subject: mac80211: 802.11w - Implement Association Comeback processing When MFP is enabled, the AP does not allow a STA to associate if an existing security association exists without first going through SA Query process. When this happens, the association request is denied with a new status code ("temporarily rejected") ans Association Comeback IE is used to notify when the association may be tried again (i.e., when the SA Query procedure has timed out). Use the comeback time to update the mac80211 client MLME timer for next association attempt to minimize waiting time if association is temporarily rejected. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 4 ++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 20 +++++++++++++++++--- net/mac80211/util.c | 4 ++++ 4 files changed, 27 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 9fe1948d28d3..7800e20f197f 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -914,6 +914,9 @@ enum ieee80211_statuscode { /* 802.11g */ WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25, WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26, + /* 802.11w */ + WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY = 30, + WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION = 31, /* 802.11i */ WLAN_STATUS_INVALID_IE = 40, WLAN_STATUS_INVALID_GROUP_CIPHER = 41, @@ -1034,6 +1037,7 @@ enum ieee80211_eid { /* 802.11i */ WLAN_EID_RSN = 48, WLAN_EID_MMIE = 76 /* 802.11w */, + WLAN_EID_ASSOC_COMEBACK_TIME = 77, WLAN_EID_WPA = 221, WLAN_EID_GENERIC = 221, WLAN_EID_VENDOR_SPECIFIC = 221, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 212c732fbba7..9112c5247c35 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -820,6 +820,7 @@ struct ieee802_11_elems { u8 *country_elem; u8 *pwr_constr_elem; u8 *quiet_elem; /* first quite element */ + u8 *assoc_comeback; /* length of them, respectively */ u8 ssid_len; @@ -847,6 +848,7 @@ struct ieee802_11_elems { u8 pwr_constr_elem_len; u8 quiet_elem_len; u8 num_of_quiet_elem; /* can be more the one */ + u8 assoc_comeback_len; }; static inline struct ieee80211_local *hw_to_local( diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 42c5f981c715..82c598a83687 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1275,6 +1275,23 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, sdata->dev->name, reassoc ? "Rea" : "A", mgmt->sa, capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); + pos = mgmt->u.assoc_resp.variable; + ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); + + if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && + elems.assoc_comeback && elems.assoc_comeback_len == 4) { + u32 tu, ms; + tu = get_unaligned_le32(elems.assoc_comeback); + ms = tu * 1024 / 1000; + printk(KERN_DEBUG "%s: AP rejected association temporarily; " + "comeback duration %u TU (%u ms)\n", + sdata->dev->name, tu, ms); + if (ms > IEEE80211_ASSOC_TIMEOUT) + mod_timer(&ifsta->timer, + jiffies + msecs_to_jiffies(ms)); + return; + } + if (status_code != WLAN_STATUS_SUCCESS) { printk(KERN_DEBUG "%s: AP denied association (code=%d)\n", sdata->dev->name, status_code); @@ -1290,9 +1307,6 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, "set\n", sdata->dev->name, aid); aid &= ~(BIT(15) | BIT(14)); - pos = mgmt->u.assoc_resp.variable; - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); - if (!elems.supp_rates) { printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n", sdata->dev->name); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 5cd430333f08..963e0473205c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -653,6 +653,10 @@ void ieee802_11_parse_elems(u8 *start, size_t len, elems->pwr_constr_elem = pos; elems->pwr_constr_elem_len = elen; break; + case WLAN_EID_ASSOC_COMEBACK_TIME: + elems->assoc_comeback = pos; + elems->assoc_comeback_len = elen; + break; default: break; } -- cgit v1.2.3 From d2b21f191753abd12c4063776cb1a3d635397509 Mon Sep 17 00:00:00 2001 From: Colin McCabe Date: Fri, 9 Jan 2009 14:58:09 -0800 Subject: libertas: if_spi, driver for libertas GSPI devices Add initial support for libertas devices using a GSPI interface. This has been tested with the 8686. GSPI is intended to be used on embedded systems. Board-specific parameters are required (see libertas_spi.h). Thanks to everyone who took a look at the earlier versions of the patch. Signed-off-by: Colin McCabe Signed-off-by: Andrey Yurovsky Acked-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 6 + drivers/net/wireless/libertas/Makefile | 2 + drivers/net/wireless/libertas/defs.h | 2 + drivers/net/wireless/libertas/if_spi.c | 1203 ++++++++++++++++++++++++++++++++ drivers/net/wireless/libertas/if_spi.h | 208 ++++++ include/linux/spi/libertas_spi.h | 25 + 6 files changed, 1446 insertions(+) create mode 100644 drivers/net/wireless/libertas/if_spi.c create mode 100644 drivers/net/wireless/libertas/if_spi.h create mode 100644 include/linux/spi/libertas_spi.h (limited to 'include/linux') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index e4f9f747de88..2dddbd012a99 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -151,6 +151,12 @@ config LIBERTAS_SDIO ---help--- A driver for Marvell Libertas 8385 and 8686 SDIO devices. +config LIBERTAS_SPI + tristate "Marvell Libertas 8686 SPI 802.11b/g cards" + depends on LIBERTAS && SPI && GENERIC_GPIO + ---help--- + A driver for Marvell Libertas 8686 SPI devices. + config LIBERTAS_DEBUG bool "Enable full debugging output in the Libertas module." depends on LIBERTAS diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile index 02080a3682a9..0b6918584503 100644 --- a/drivers/net/wireless/libertas/Makefile +++ b/drivers/net/wireless/libertas/Makefile @@ -4,8 +4,10 @@ libertas-objs := main.o wext.o rx.o tx.o cmd.o cmdresp.o scan.o 11d.o \ usb8xxx-objs += if_usb.o libertas_cs-objs += if_cs.o libertas_sdio-objs += if_sdio.o +libertas_spi-objs += if_spi.o obj-$(CONFIG_LIBERTAS) += libertas.o obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o +obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index c364e4c01d1b..6388b05df4fc 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -41,6 +41,7 @@ #define LBS_DEB_HEX 0x00200000 #define LBS_DEB_SDIO 0x00400000 #define LBS_DEB_SYSFS 0x00800000 +#define LBS_DEB_SPI 0x01000000 extern unsigned int lbs_debug; @@ -84,6 +85,7 @@ do { if ((lbs_debug & (grp)) == (grp)) \ #define lbs_deb_thread(fmt, args...) LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args) #define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args) #define lbs_deb_sysfs(fmt, args...) LBS_DEB_LL(LBS_DEB_SYSFS, " sysfs", fmt, ##args) +#define lbs_deb_spi(fmt, args...) LBS_DEB_LL(LBS_DEB_SPI, " spi", fmt, ##args) #define lbs_pr_info(format, args...) \ printk(KERN_INFO DRV_NAME": " format, ## args) diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c new file mode 100644 index 000000000000..7c02ea314fd1 --- /dev/null +++ b/drivers/net/wireless/libertas/if_spi.c @@ -0,0 +1,1203 @@ +/* + * linux/drivers/net/wireless/libertas/if_spi.c + * + * Driver for Marvell SPI WLAN cards. + * + * Copyright 2008 Analog Devices Inc. + * + * Authors: + * Andrey Yurovsky + * Colin McCabe + * + * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "if_spi.h" + +struct if_spi_packet { + struct list_head list; + u16 blen; + u8 buffer[0] __attribute__((aligned(4))); +}; + +struct if_spi_card { + struct spi_device *spi; + struct lbs_private *priv; + + char helper_fw_name[FIRMWARE_NAME_MAX]; + char main_fw_name[FIRMWARE_NAME_MAX]; + + /* The card ID and card revision, as reported by the hardware. */ + u16 card_id; + u8 card_rev; + + /* Pin number for our GPIO chip-select. */ + /* TODO: Once the generic SPI layer has some additional features, we + * should take this out and use the normal chip select here. + * We need support for chip select delays, and not dropping chipselect + * after each word. */ + int gpio_cs; + + /* The last time that we initiated an SPU operation */ + unsigned long prev_xfer_time; + + int use_dummy_writes; + unsigned long spu_port_delay; + unsigned long spu_reg_delay; + + /* Handles all SPI communication (except for FW load) */ + struct task_struct *spi_thread; + int run_thread; + + /* Used to wake up the spi_thread */ + struct semaphore spi_ready; + struct semaphore spi_thread_terminated; + + u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE]; + + /* A buffer of incoming packets from libertas core. + * Since we can't sleep in hw_host_to_card, we have to buffer + * them. */ + struct list_head cmd_packet_list; + struct list_head data_packet_list; + + /* Protects cmd_packet_list and data_packet_list */ + spinlock_t buffer_lock; +}; + +static void free_if_spi_card(struct if_spi_card *card) +{ + struct list_head *cursor, *next; + struct if_spi_packet *packet; + + BUG_ON(card->run_thread); + list_for_each_safe(cursor, next, &card->cmd_packet_list) { + packet = container_of(cursor, struct if_spi_packet, list); + list_del(&packet->list); + kfree(packet); + } + list_for_each_safe(cursor, next, &card->data_packet_list) { + packet = container_of(cursor, struct if_spi_packet, list); + list_del(&packet->list); + kfree(packet); + } + spi_set_drvdata(card->spi, NULL); + kfree(card); +} + +static struct chip_ident chip_id_to_device_name[] = { + { .chip_id = 0x04, .name = 8385 }, + { .chip_id = 0x0b, .name = 8686 }, +}; + +/* + * SPI Interface Unit Routines + * + * The SPU sits between the host and the WLAN module. + * All communication with the firmware is through SPU transactions. + * + * First we have to put a SPU register name on the bus. Then we can + * either read from or write to that register. + * + * For 16-bit transactions, byte order on the bus is big-endian. + * We don't have to worry about that here, though. + * The translation takes place in the SPI routines. + */ + +static void spu_transaction_init(struct if_spi_card *card) +{ + if (!time_after(jiffies, card->prev_xfer_time + 1)) { + /* Unfortunately, the SPU requires a delay between successive + * transactions. If our last transaction was more than a jiffy + * ago, we have obviously already delayed enough. + * If not, we have to busy-wait to be on the safe side. */ + ndelay(400); + } + gpio_set_value(card->gpio_cs, 0); /* assert CS */ +} + +static void spu_transaction_finish(struct if_spi_card *card) +{ + gpio_set_value(card->gpio_cs, 1); /* drop CS */ + card->prev_xfer_time = jiffies; +} + +/* Write out a byte buffer to an SPI register, + * using a series of 16-bit transfers. */ +static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len) +{ + int err = 0; + u16 reg_out = reg | IF_SPI_WRITE_OPERATION_MASK; + + /* You must give an even number of bytes to the SPU, even if it + * doesn't care about the last one. */ + BUG_ON(len & 0x1); + + spu_transaction_init(card); + + /* write SPU register index */ + err = spi_write(card->spi, (u8 *)®_out, sizeof(u16)); + if (err) + goto out; + + err = spi_write(card->spi, buf, len); + +out: + spu_transaction_finish(card); + return err; +} + +static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val) +{ + return spu_write(card, reg, (u8 *)&val, sizeof(u16)); +} + +static inline int spu_write_u32(struct if_spi_card *card, u16 reg, u32 val) +{ + /* The lower 16 bits are written first. */ + u16 out[2]; + out[0] = val & 0xffff; + out[1] = (val & 0xffff0000) >> 16; + return spu_write(card, reg, (u8 *)&out, sizeof(u32)); +} + +static inline int spu_reg_is_port_reg(u16 reg) +{ + switch (reg) { + case IF_SPI_IO_RDWRPORT_REG: + case IF_SPI_CMD_RDWRPORT_REG: + case IF_SPI_DATA_RDWRPORT_REG: + return 1; + default: + return 0; + } +} + +static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len) +{ + unsigned int i, delay; + int err = 0; + u16 zero = 0; + u16 reg_out = reg | IF_SPI_READ_OPERATION_MASK; + + /* You must take an even number of bytes from the SPU, even if you + * don't care about the last one. */ + BUG_ON(len & 0x1); + + spu_transaction_init(card); + + /* write SPU register index */ + err = spi_write(card->spi, (u8 *)®_out, sizeof(u16)); + if (err) + goto out; + + delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay : + card->spu_reg_delay; + if (card->use_dummy_writes) { + /* Clock in dummy cycles while the SPU fills the FIFO */ + for (i = 0; i < delay / 16; ++i) { + err = spi_write(card->spi, (u8 *)&zero, sizeof(u16)); + if (err) + return err; + } + } else { + /* Busy-wait while the SPU fills the FIFO */ + ndelay(100 + (delay * 10)); + } + + /* read in data */ + err = spi_read(card->spi, buf, len); + +out: + spu_transaction_finish(card); + return err; +} + +/* Read 16 bits from an SPI register */ +static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val) +{ + return spu_read(card, reg, (u8 *)val, sizeof(u16)); +} + +/* Read 32 bits from an SPI register. + * The low 16 bits are read first. */ +static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val) +{ + u16 buf[2]; + int err; + err = spu_read(card, reg, (u8 *)buf, sizeof(u32)); + if (!err) + *val = buf[0] | (buf[1] << 16); + return err; +} + +/* Keep reading 16 bits from an SPI register until you get the correct result. + * + * If mask = 0, the correct result is any non-zero number. + * If mask != 0, the correct result is any number where + * number & target_mask == target + * + * Returns -ETIMEDOUT if a second passes without the correct result. */ +static int spu_wait_for_u16(struct if_spi_card *card, u16 reg, + u16 target_mask, u16 target) +{ + int err; + unsigned long timeout = jiffies + 5*HZ; + while (1) { + u16 val; + err = spu_read_u16(card, reg, &val); + if (err) + return err; + if (target_mask) { + if ((val & target_mask) == target) + return 0; + } else { + if (val) + return 0; + } + udelay(100); + if (time_after(jiffies, timeout)) { + lbs_pr_err("%s: timeout with val=%02x, " + "target_mask=%02x, target=%02x\n", + __func__, val, target_mask, target); + return -ETIMEDOUT; + } + } +} + +/* Read 16 bits from an SPI register until you receive a specific value. + * Returns -ETIMEDOUT if a 4 tries pass without success. */ +static int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target) +{ + int err, try; + for (try = 0; try < 4; ++try) { + u32 val = 0; + err = spu_read_u32(card, reg, &val); + if (err) + return err; + if (val == target) + return 0; + mdelay(100); + } + return -ETIMEDOUT; +} + +static int spu_set_interrupt_mode(struct if_spi_card *card, + int suppress_host_int, + int auto_int) +{ + int err = 0; + + /* We can suppress a host interrupt by clearing the appropriate + * bit in the "host interrupt status mask" register */ + if (suppress_host_int) { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); + if (err) + return err; + } else { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, + IF_SPI_HISM_TX_DOWNLOAD_RDY | + IF_SPI_HISM_RX_UPLOAD_RDY | + IF_SPI_HISM_CMD_DOWNLOAD_RDY | + IF_SPI_HISM_CARDEVENT | + IF_SPI_HISM_CMD_UPLOAD_RDY); + if (err) + return err; + } + + /* If auto-interrupts are on, the completion of certain transactions + * will trigger an interrupt automatically. If auto-interrupts + * are off, we need to set the "Card Interrupt Cause" register to + * trigger a card interrupt. */ + if (auto_int) { + err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG, + IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO | + IF_SPI_HICT_RX_UPLOAD_OVER_AUTO | + IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO | + IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO); + if (err) + return err; + } else { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); + if (err) + return err; + } + return err; +} + +static int spu_get_chip_revision(struct if_spi_card *card, + u16 *card_id, u8 *card_rev) +{ + int err = 0; + u32 dev_ctrl; + err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl); + if (err) + return err; + *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl); + *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl); + return err; +} + +static int spu_set_bus_mode(struct if_spi_card *card, u16 mode) +{ + int err = 0; + u16 rval; + /* set bus mode */ + err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode); + if (err) + return err; + /* Check that we were able to read back what we just wrote. */ + err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval); + if (err) + return err; + if (rval != mode) { + lbs_pr_err("Can't read bus mode register.\n"); + return -EIO; + } + return 0; +} + +static int spu_init(struct if_spi_card *card, int use_dummy_writes) +{ + int err = 0; + u32 delay; + + /* We have to start up in timed delay mode so that we can safely + * read the Delay Read Register. */ + card->use_dummy_writes = 0; + err = spu_set_bus_mode(card, + IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | + IF_SPI_BUS_MODE_DELAY_METHOD_TIMED | + IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); + if (err) + return err; + card->spu_port_delay = 1000; + card->spu_reg_delay = 1000; + err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay); + if (err) + return err; + card->spu_port_delay = delay & 0x0000ffff; + card->spu_reg_delay = (delay & 0xffff0000) >> 16; + + /* If dummy clock delay mode has been requested, switch to it now */ + if (use_dummy_writes) { + card->use_dummy_writes = 1; + err = spu_set_bus_mode(card, + IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | + IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK | + IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); + if (err) + return err; + } + + lbs_deb_spi("Initialized SPU unit. " + "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n", + card->spu_port_delay, card->spu_reg_delay); + return err; +} + +/* + * Firmware Loading + */ + +static int if_spi_prog_helper_firmware(struct if_spi_card *card) +{ + int err = 0; + const struct firmware *firmware = NULL; + int bytes_remaining; + const u8 *fw; + u8 temp[HELPER_FW_LOAD_CHUNK_SZ]; + struct spi_device *spi = card->spi; + + lbs_deb_enter(LBS_DEB_SPI); + + err = spu_set_interrupt_mode(card, 1, 0); + if (err) + goto out; + /* Get helper firmware image */ + err = request_firmware(&firmware, card->helper_fw_name, &spi->dev); + if (err) { + lbs_pr_err("request_firmware failed with err = %d\n", err); + goto out; + } + bytes_remaining = firmware->size; + fw = firmware->data; + + /* Load helper firmware image */ + while (bytes_remaining > 0) { + /* Scratch pad 1 should contain the number of bytes we + * want to download to the firmware */ + err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, + HELPER_FW_LOAD_CHUNK_SZ); + if (err) + goto release_firmware; + + err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, + IF_SPI_HIST_CMD_DOWNLOAD_RDY, + IF_SPI_HIST_CMD_DOWNLOAD_RDY); + if (err) + goto release_firmware; + + /* Feed the data into the command read/write port reg + * in chunks of 64 bytes */ + memset(temp, 0, sizeof(temp)); + memcpy(temp, fw, + min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ)); + mdelay(10); + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, + temp, HELPER_FW_LOAD_CHUNK_SZ); + if (err) + goto release_firmware; + + /* Interrupt the boot code */ + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto release_firmware; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, + IF_SPI_CIC_CMD_DOWNLOAD_OVER); + if (err) + goto release_firmware; + bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ; + fw += HELPER_FW_LOAD_CHUNK_SZ; + } + + /* Once the helper / single stage firmware download is complete, + * write 0 to scratch pad 1 and interrupt the + * bootloader. This completes the helper download. */ + err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK); + if (err) + goto release_firmware; + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto release_firmware; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, + IF_SPI_CIC_CMD_DOWNLOAD_OVER); + goto release_firmware; + + lbs_deb_spi("waiting for helper to boot...\n"); + +release_firmware: + release_firmware(firmware); +out: + if (err) + lbs_pr_err("failed to load helper firmware (err=%d)\n", err); + lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); + return err; +} + +/* Returns the length of the next packet the firmware expects us to send + * Sets crc_err if the previous transfer had a CRC error. */ +static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card, + int *crc_err) +{ + u16 len; + int err = 0; + + /* wait until the host interrupt status register indicates + * that we are ready to download */ + err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, + IF_SPI_HIST_CMD_DOWNLOAD_RDY, + IF_SPI_HIST_CMD_DOWNLOAD_RDY); + if (err) { + lbs_pr_err("timed out waiting for host_int_status\n"); + return err; + } + + /* Ask the device how many bytes of firmware it wants. */ + err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); + if (err) + return err; + + if (len > IF_SPI_CMD_BUF_SIZE) { + lbs_pr_err("firmware load device requested a larger " + "tranfer than we are prepared to " + "handle. (len = %d)\n", len); + return -EIO; + } + if (len & 0x1) { + lbs_deb_spi("%s: crc error\n", __func__); + len &= ~0x1; + *crc_err = 1; + } else + *crc_err = 0; + + return len; +} + +static int if_spi_prog_main_firmware(struct if_spi_card *card) +{ + int len, prev_len; + int bytes, crc_err = 0, err = 0; + const struct firmware *firmware = NULL; + const u8 *fw; + struct spi_device *spi = card->spi; + u16 num_crc_errs; + + lbs_deb_enter(LBS_DEB_SPI); + + err = spu_set_interrupt_mode(card, 1, 0); + if (err) + goto out; + + /* Get firmware image */ + err = request_firmware(&firmware, card->main_fw_name, &spi->dev); + if (err) { + lbs_pr_err("%s: can't get firmware '%s' from kernel. " + "err = %d\n", __func__, card->main_fw_name, err); + goto out; + } + + err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0); + if (err) { + lbs_pr_err("%s: timed out waiting for initial " + "scratch reg = 0\n", __func__); + goto release_firmware; + } + + num_crc_errs = 0; + prev_len = 0; + bytes = firmware->size; + fw = firmware->data; + while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) { + if (len < 0) { + err = len; + goto release_firmware; + } + if (bytes < 0) { + /* If there are no more bytes left, we would normally + * expect to have terminated with len = 0 */ + lbs_pr_err("Firmware load wants more bytes " + "than we have to offer.\n"); + break; + } + if (crc_err) { + /* Previous transfer failed. */ + if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) { + lbs_pr_err("Too many CRC errors encountered " + "in firmware load.\n"); + err = -EIO; + goto release_firmware; + } + } else { + /* Previous transfer succeeded. Advance counters. */ + bytes -= prev_len; + fw += prev_len; + } + if (bytes < len) { + memset(card->cmd_buffer, 0, len); + memcpy(card->cmd_buffer, fw, bytes); + } else + memcpy(card->cmd_buffer, fw, len); + + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto release_firmware; + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, + card->cmd_buffer, len); + if (err) + goto release_firmware; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG , + IF_SPI_CIC_CMD_DOWNLOAD_OVER); + if (err) + goto release_firmware; + prev_len = len; + } + if (bytes > prev_len) { + lbs_pr_err("firmware load wants fewer bytes than " + "we have to offer.\n"); + } + + /* Confirm firmware download */ + err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG, + SUCCESSFUL_FW_DOWNLOAD_MAGIC); + if (err) { + lbs_pr_err("failed to confirm the firmware download\n"); + goto release_firmware; + } + +release_firmware: + release_firmware(firmware); + +out: + if (err) + lbs_pr_err("failed to load firmware (err=%d)\n", err); + lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); + return err; +} + +/* + * SPI Transfer Thread + * + * The SPI thread handles all SPI transfers, so there is no need for a lock. + */ + +/* Move a command from the card to the host */ +static int if_spi_c2h_cmd(struct if_spi_card *card) +{ + struct lbs_private *priv = card->priv; + unsigned long flags; + int err = 0; + u16 len; + u8 i; + + /* We need a buffer big enough to handle whatever people send to + * hw_host_to_card */ + BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE); + BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE); + + /* It's just annoying if the buffer size isn't a multiple of 4, because + * then we might have len < IF_SPI_CMD_BUF_SIZE but + * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE */ + BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0); + + lbs_deb_enter(LBS_DEB_SPI); + + /* How many bytes are there to read? */ + err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len); + if (err) + goto out; + if (!len) { + lbs_pr_err("%s: error: card has no data for host\n", + __func__); + err = -EINVAL; + goto out; + } else if (len > IF_SPI_CMD_BUF_SIZE) { + lbs_pr_err("%s: error: response packet too large: " + "%d bytes, but maximum is %d\n", + __func__, len, IF_SPI_CMD_BUF_SIZE); + err = -EINVAL; + goto out; + } + + /* Read the data from the WLAN module into our command buffer */ + err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG, + card->cmd_buffer, ALIGN(len, 4)); + if (err) + goto out; + + spin_lock_irqsave(&priv->driver_lock, flags); + i = (priv->resp_idx == 0) ? 1 : 0; + BUG_ON(priv->resp_len[i]); + priv->resp_len[i] = len; + memcpy(priv->resp_buf[i], card->cmd_buffer, len); + lbs_notify_command_response(priv, i); + spin_unlock_irqrestore(&priv->driver_lock, flags); + +out: + if (err) + lbs_pr_err("%s: err=%d\n", __func__, err); + lbs_deb_leave(LBS_DEB_SPI); + return err; +} + +/* Move data from the card to the host */ +static int if_spi_c2h_data(struct if_spi_card *card) +{ + struct sk_buff *skb; + char *data; + u16 len; + int err = 0; + + lbs_deb_enter(LBS_DEB_SPI); + + /* How many bytes are there to read? */ + err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); + if (err) + goto out; + if (!len) { + lbs_pr_err("%s: error: card has no data for host\n", + __func__); + err = -EINVAL; + goto out; + } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { + lbs_pr_err("%s: error: card has %d bytes of data, but " + "our maximum skb size is %u\n", + __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); + err = -EINVAL; + goto out; + } + + /* TODO: should we allocate a smaller skb if we have less data? */ + skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); + if (!skb) { + err = -ENOBUFS; + goto out; + } + skb_reserve(skb, IPFIELD_ALIGN_OFFSET); + data = skb_put(skb, len); + + /* Read the data from the WLAN module into our skb... */ + err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 4)); + if (err) + goto free_skb; + + /* pass the SKB to libertas */ + err = lbs_process_rxed_packet(card->priv, skb); + if (err) + goto free_skb; + + /* success */ + goto out; + +free_skb: + dev_kfree_skb(skb); +out: + if (err) + lbs_pr_err("%s: err=%d\n", __func__, err); + lbs_deb_leave(LBS_DEB_SPI); + return err; +} + +/* Move data or a command from the host to the card. */ +static void if_spi_h2c(struct if_spi_card *card, + struct if_spi_packet *packet, int type) +{ + int err = 0; + u16 int_type, port_reg; + + switch (type) { + case MVMS_DAT: + int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER; + port_reg = IF_SPI_DATA_RDWRPORT_REG; + break; + case MVMS_CMD: + int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER; + port_reg = IF_SPI_CMD_RDWRPORT_REG; + break; + default: + lbs_pr_err("can't transfer buffer of type %d\n", type); + err = -EINVAL; + goto out; + } + + /* Write the data to the card */ + err = spu_write(card, port_reg, packet->buffer, packet->blen); + if (err) + goto out; + +out: + kfree(packet); + + if (err) + lbs_pr_err("%s: error %d\n", __func__, err); +} + +/* Inform the host about a card event */ +static void if_spi_e2h(struct if_spi_card *card) +{ + int err = 0; + unsigned long flags; + u32 cause; + struct lbs_private *priv = card->priv; + + err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause); + if (err) + goto out; + + spin_lock_irqsave(&priv->driver_lock, flags); + lbs_queue_event(priv, cause & 0xff); + spin_unlock_irqrestore(&priv->driver_lock, flags); + +out: + if (err) + lbs_pr_err("%s: error %d\n", __func__, err); +} + +static int lbs_spi_thread(void *data) +{ + int err; + struct if_spi_card *card = data; + u16 hiStatus; + unsigned long flags; + struct if_spi_packet *packet; + + while (1) { + /* Wait to be woken up by one of two things. First, our ISR + * could tell us that something happened on the WLAN. + * Secondly, libertas could call hw_host_to_card with more + * data, which we might be able to send. + */ + do { + err = down_interruptible(&card->spi_ready); + if (!card->run_thread) { + up(&card->spi_thread_terminated); + do_exit(0); + } + } while (err == EINTR); + + /* Read the host interrupt status register to see what we + * can do. */ + err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, + &hiStatus); + if (err) { + lbs_pr_err("I/O error\n"); + goto err; + } + + if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) + err = if_spi_c2h_cmd(card); + if (err) + goto err; + if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) + err = if_spi_c2h_data(card); + if (err) + goto err; + if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY) { + /* This means two things. First of all, + * if there was a previous command sent, the card has + * successfully received it. + * Secondly, it is now ready to download another + * command. + */ + lbs_host_to_card_done(card->priv); + + /* Do we have any command packets from the host to + * send? */ + packet = NULL; + spin_lock_irqsave(&card->buffer_lock, flags); + if (!list_empty(&card->cmd_packet_list)) { + packet = (struct if_spi_packet *)(card-> + cmd_packet_list.next); + list_del(&packet->list); + } + spin_unlock_irqrestore(&card->buffer_lock, flags); + + if (packet) + if_spi_h2c(card, packet, MVMS_CMD); + } + if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) { + /* Do we have any data packets from the host to + * send? */ + packet = NULL; + spin_lock_irqsave(&card->buffer_lock, flags); + if (!list_empty(&card->data_packet_list)) { + packet = (struct if_spi_packet *)(card-> + data_packet_list.next); + list_del(&packet->list); + } + spin_unlock_irqrestore(&card->buffer_lock, flags); + + if (packet) + if_spi_h2c(card, packet, MVMS_DAT); + } + if (hiStatus & IF_SPI_HIST_CARD_EVENT) + if_spi_e2h(card); + +err: + if (err) + lbs_pr_err("%s: got error %d\n", __func__, err); + } +} + +/* Block until lbs_spi_thread thread has terminated */ +static void if_spi_terminate_spi_thread(struct if_spi_card *card) +{ + /* It would be nice to use kthread_stop here, but that function + * can't wake threads waiting for a semaphore. */ + card->run_thread = 0; + up(&card->spi_ready); + down(&card->spi_thread_terminated); +} + +/* + * Host to Card + * + * Called from Libertas to transfer some data to the WLAN device + * We can't sleep here. */ +static int if_spi_host_to_card(struct lbs_private *priv, + u8 type, u8 *buf, u16 nb) +{ + int err = 0; + unsigned long flags; + struct if_spi_card *card = priv->card; + struct if_spi_packet *packet; + u16 blen; + + lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb); + + if (nb == 0) { + lbs_pr_err("%s: invalid size requested: %d\n", __func__, nb); + err = -EINVAL; + goto out; + } + blen = ALIGN(nb, 4); + packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC); + if (!packet) { + err = -ENOMEM; + goto out; + } + packet->blen = blen; + memcpy(packet->buffer, buf, nb); + memset(packet->buffer + nb, 0, blen - nb); + + switch (type) { + case MVMS_CMD: + priv->dnld_sent = DNLD_CMD_SENT; + spin_lock_irqsave(&card->buffer_lock, flags); + list_add_tail(&packet->list, &card->cmd_packet_list); + spin_unlock_irqrestore(&card->buffer_lock, flags); + break; + case MVMS_DAT: + priv->dnld_sent = DNLD_DATA_SENT; + spin_lock_irqsave(&card->buffer_lock, flags); + list_add_tail(&packet->list, &card->data_packet_list); + spin_unlock_irqrestore(&card->buffer_lock, flags); + break; + default: + lbs_pr_err("can't transfer buffer of type %d", type); + err = -EINVAL; + break; + } + + /* Wake up the spi thread */ + up(&card->spi_ready); +out: + lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err); + return err; +} + +/* + * Host Interrupts + * + * Service incoming interrupts from the WLAN device. We can't sleep here, so + * don't try to talk on the SPI bus, just wake up the SPI thread. + */ +static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id) +{ + struct if_spi_card *card = dev_id; + + up(&card->spi_ready); + return IRQ_HANDLED; +} + +/* + * SPI callbacks + */ + +static int if_spi_calculate_fw_names(u16 card_id, + char *helper_fw, char *main_fw) +{ + int i; + for (i = 0; i < ARRAY_SIZE(chip_id_to_device_name); ++i) { + if (card_id == chip_id_to_device_name[i].chip_id) + break; + } + if (i == ARRAY_SIZE(chip_id_to_device_name)) { + lbs_pr_err("Unsupported chip_id: 0x%02x\n", card_id); + return -EAFNOSUPPORT; + } + snprintf(helper_fw, FIRMWARE_NAME_MAX, "libertas/gspi%d_hlp.bin", + chip_id_to_device_name[i].name); + snprintf(main_fw, FIRMWARE_NAME_MAX, "libertas/gspi%d.bin", + chip_id_to_device_name[i].name); + return 0; +} + +static int __devinit if_spi_probe(struct spi_device *spi) +{ + struct if_spi_card *card; + struct lbs_private *priv = NULL; + struct libertas_spi_platform_data *pdata = spi->dev.platform_data; + int err = 0; + u32 scratch; + + lbs_deb_enter(LBS_DEB_SPI); + + /* Allocate card structure to represent this specific device */ + card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL); + if (!card) { + err = -ENOMEM; + goto out; + } + spi_set_drvdata(spi, card); + card->spi = spi; + card->gpio_cs = pdata->gpio_cs; + card->prev_xfer_time = jiffies; + + sema_init(&card->spi_ready, 0); + sema_init(&card->spi_thread_terminated, 0); + INIT_LIST_HEAD(&card->cmd_packet_list); + INIT_LIST_HEAD(&card->data_packet_list); + spin_lock_init(&card->buffer_lock); + + /* set up GPIO CS line. TODO: use regular CS line */ + err = gpio_request(card->gpio_cs, "if_spi_gpio_chip_select"); + if (err) + goto free_card; + err = gpio_direction_output(card->gpio_cs, 1); + if (err) + goto free_gpio; + + /* Initialize the SPI Interface Unit */ + err = spu_init(card, pdata->use_dummy_writes); + if (err) + goto free_gpio; + err = spu_get_chip_revision(card, &card->card_id, &card->card_rev); + if (err) + goto free_gpio; + + /* Firmware load */ + err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch); + if (err) + goto free_gpio; + if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC) + lbs_deb_spi("Firmware is already loaded for " + "Marvell WLAN 802.11 adapter\n"); + else { + err = if_spi_calculate_fw_names(card->card_id, + card->helper_fw_name, card->main_fw_name); + if (err) + goto free_gpio; + + lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter " + "(chip_id = 0x%04x, chip_rev = 0x%02x) " + "attached to SPI bus_num %d, chip_select %d. " + "spi->max_speed_hz=%d\n", + card->card_id, card->card_rev, + spi->master->bus_num, spi->chip_select, + spi->max_speed_hz); + err = if_spi_prog_helper_firmware(card); + if (err) + goto free_gpio; + err = if_spi_prog_main_firmware(card); + if (err) + goto free_gpio; + lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n"); + } + + err = spu_set_interrupt_mode(card, 0, 1); + if (err) + goto free_gpio; + + /* Register our card with libertas. + * This will call alloc_etherdev */ + priv = lbs_add_card(card, &spi->dev); + if (!priv) { + err = -ENOMEM; + goto free_gpio; + } + card->priv = priv; + priv->card = card; + priv->hw_host_to_card = if_spi_host_to_card; + priv->fw_ready = 1; + priv->ps_supported = 1; + + /* Initialize interrupt handling stuff. */ + card->run_thread = 1; + card->spi_thread = kthread_run(lbs_spi_thread, card, "lbs_spi_thread"); + if (IS_ERR(card->spi_thread)) { + card->run_thread = 0; + err = PTR_ERR(card->spi_thread); + lbs_pr_err("error creating SPI thread: err=%d\n", err); + goto remove_card; + } + err = request_irq(spi->irq, if_spi_host_interrupt, + IRQF_TRIGGER_FALLING, "libertas_spi", card); + if (err) { + lbs_pr_err("can't get host irq line-- request_irq failed\n"); + goto terminate_thread; + } + + /* Start the card. + * This will call register_netdev, and we'll start + * getting interrupts... */ + err = lbs_start_card(priv); + if (err) + goto release_irq; + + lbs_deb_spi("Finished initializing WLAN module.\n"); + + /* successful exit */ + goto out; + +release_irq: + free_irq(spi->irq, card); +terminate_thread: + if_spi_terminate_spi_thread(card); +remove_card: + lbs_remove_card(priv); /* will call free_netdev */ +free_gpio: + gpio_free(card->gpio_cs); +free_card: + free_if_spi_card(card); +out: + lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); + return err; +} + +static int __devexit libertas_spi_remove(struct spi_device *spi) +{ + struct if_spi_card *card = spi_get_drvdata(spi); + struct lbs_private *priv = card->priv; + + lbs_deb_spi("libertas_spi_remove\n"); + lbs_deb_enter(LBS_DEB_SPI); + priv->surpriseremoved = 1; + + lbs_stop_card(priv); + free_irq(spi->irq, card); + if_spi_terminate_spi_thread(card); + lbs_remove_card(priv); /* will call free_netdev */ + gpio_free(card->gpio_cs); + free_if_spi_card(card); + lbs_deb_leave(LBS_DEB_SPI); + return 0; +} + +static struct spi_driver libertas_spi_driver = { + .probe = if_spi_probe, + .remove = __devexit_p(libertas_spi_remove), + .driver = { + .name = "libertas_spi", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, +}; + +/* + * Module functions + */ + +static int __init if_spi_init_module(void) +{ + int ret = 0; + lbs_deb_enter(LBS_DEB_SPI); + printk(KERN_INFO "libertas_spi: Libertas SPI driver\n"); + ret = spi_register_driver(&libertas_spi_driver); + lbs_deb_leave(LBS_DEB_SPI); + return ret; +} + +static void __exit if_spi_exit_module(void) +{ + lbs_deb_enter(LBS_DEB_SPI); + spi_unregister_driver(&libertas_spi_driver); + lbs_deb_leave(LBS_DEB_SPI); +} + +module_init(if_spi_init_module); +module_exit(if_spi_exit_module); + +MODULE_DESCRIPTION("Libertas SPI WLAN Driver"); +MODULE_AUTHOR("Andrey Yurovsky , " + "Colin McCabe "); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/if_spi.h b/drivers/net/wireless/libertas/if_spi.h new file mode 100644 index 000000000000..2103869cc5b0 --- /dev/null +++ b/drivers/net/wireless/libertas/if_spi.h @@ -0,0 +1,208 @@ +/* + * linux/drivers/net/wireless/libertas/if_spi.c + * + * Driver for Marvell SPI WLAN cards. + * + * Copyright 2008 Analog Devices Inc. + * + * Authors: + * Andrey Yurovsky + * Colin McCabe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef _LBS_IF_SPI_H_ +#define _LBS_IF_SPI_H_ + +#define IPFIELD_ALIGN_OFFSET 2 +#define IF_SPI_CMD_BUF_SIZE 2400 + +/***************** Firmware *****************/ +struct chip_ident { + u16 chip_id; + u16 name; +}; + +#define MAX_MAIN_FW_LOAD_CRC_ERR 10 + +/* Chunk size when loading the helper firmware */ +#define HELPER_FW_LOAD_CHUNK_SZ 64 + +/* Value to write to indicate end of helper firmware dnld */ +#define FIRMWARE_DNLD_OK 0x0000 + +/* Value to check once the main firmware is downloaded */ +#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888 + +/***************** SPI Interface Unit *****************/ +/* Masks used in SPI register read/write operations */ +#define IF_SPI_READ_OPERATION_MASK 0x0 +#define IF_SPI_WRITE_OPERATION_MASK 0x8000 + +/* SPI register offsets. 4-byte aligned. */ +#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */ +#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */ +#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */ +#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */ + +#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */ +#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */ +#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */ + +#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */ +#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */ +#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */ + +#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */ +#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */ +#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */ +#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */ + +#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */ +#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */ + +#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */ + +#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */ +#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interupt status reg */ +#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */ +#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */ + +#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */ + +#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */ +#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */ +#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */ +#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */ +#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */ + +#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */ +#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */ + +/***************** IF_SPI_DEVICEID_CTRL_REG *****************/ +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16) +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff) + +/***************** IF_SPI_HOST_INT_CTRL_REG *****************/ +/** Host Interrupt Control bit : Wake up */ +#define IF_SPI_HICT_WAKE_UP (1<<0) +/** Host Interrupt Control bit : WLAN ready */ +#define IF_SPI_HICT_WLAN_READY (1<<1) +/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */ +/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */ +/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */ +/** Host Interrupt Control bit : Tx auto download */ +#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5) +/** Host Interrupt Control bit : Rx auto upload */ +#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6) +/** Host Interrupt Control bit : Command auto download */ +#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7) +/** Host Interrupt Control bit : Command auto upload */ +#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8) + +/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/ +/** Card Interrupt Case bit : Tx download over */ +#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0) +/** Card Interrupt Case bit : Rx upload over */ +#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1) +/** Card Interrupt Case bit : Command download over */ +#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2) +/** Card Interrupt Case bit : Host event */ +#define IF_SPI_CIC_HOST_EVENT (1<<3) +/** Card Interrupt Case bit : Command upload over */ +#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4) +/** Card Interrupt Case bit : Power down */ +#define IF_SPI_CIC_POWER_DOWN (1<<5) + +/***************** IF_SPI_CARD_INT_STATUS_REG *****************/ +#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0) +#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1) +#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2) +#define IF_SPI_CIS_HOST_EVENT (1<<3) +#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4) +#define IF_SPI_CIS_POWER_DOWN (1<<5) + +/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/ +#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0) +#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1) +#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2) +#define IF_SPI_HICU_CARD_EVENT (1<<3) +#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4) +#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5) +#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6) +#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7) +#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8) +#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9) +#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_HOST_INT_STATUS_REG *****************/ +/** Host Interrupt Status bit : Tx download ready */ +#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0) +/** Host Interrupt Status bit : Rx upload ready */ +#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1) +/** Host Interrupt Status bit : Command download ready */ +#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2) +/** Host Interrupt Status bit : Card event */ +#define IF_SPI_HIST_CARD_EVENT (1<<3) +/** Host Interrupt Status bit : Command upload ready */ +#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4) +/** Host Interrupt Status bit : I/O write FIFO overflow */ +#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5) +/** Host Interrupt Status bit : I/O read FIFO underflow */ +#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6) +/** Host Interrupt Status bit : Data write FIFO overflow */ +#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7) +/** Host Interrupt Status bit : Data read FIFO underflow */ +#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8) +/** Host Interrupt Status bit : Command write FIFO overflow */ +#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9) +/** Host Interrupt Status bit : Command read FIFO underflow */ +#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/ +/** Host Interrupt Status Mask bit : Tx download ready */ +#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0) +/** Host Interrupt Status Mask bit : Rx upload ready */ +#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1) +/** Host Interrupt Status Mask bit : Command download ready */ +#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2) +/** Host Interrupt Status Mask bit : Card event */ +#define IF_SPI_HISM_CARDEVENT (1<<3) +/** Host Interrupt Status Mask bit : Command upload ready */ +#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4) +/** Host Interrupt Status Mask bit : I/O write FIFO overflow */ +#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5) +/** Host Interrupt Status Mask bit : I/O read FIFO underflow */ +#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6) +/** Host Interrupt Status Mask bit : Data write FIFO overflow */ +#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7) +/** Host Interrupt Status Mask bit : Data write FIFO underflow */ +#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8) +/** Host Interrupt Status Mask bit : Command write FIFO overflow */ +#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9) +/** Host Interrupt Status Mask bit : Command write FIFO underflow */ +#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_SPU_BUS_MODE_REG *****************/ +/* SCK edge on which the WLAN module outputs data on MISO */ +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8 +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0 + +/* In a SPU read operation, there is a delay between writing the SPU + * register name and getting back data from the WLAN module. + * This can be specified in terms of nanoseconds or in terms of dummy + * clock cycles which the master must output before receiving a response. */ +#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4 +#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0 + +/* Some different modes of SPI operation */ +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00 +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01 +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02 +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03 + +#endif diff --git a/include/linux/spi/libertas_spi.h b/include/linux/spi/libertas_spi.h new file mode 100644 index 000000000000..ada71b4f3788 --- /dev/null +++ b/include/linux/spi/libertas_spi.h @@ -0,0 +1,25 @@ +/* + * board-specific data for the libertas_spi driver. + * + * Copyright 2008 Analog Devices Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#ifndef _LIBERTAS_SPI_H_ +#define _LIBERTAS_SPI_H_ +struct libertas_spi_platform_data { + /* There are two ways to read data from the WLAN module's SPI + * interface. Setting 0 or 1 here controls which one is used. + * + * Usually you want to set use_dummy_writes = 1. + * However, if that doesn't work or if you are using a slow SPI clock + * speed, you may want to use 0 here. */ + u16 use_dummy_writes; + + /* GPIO number to use as chip select */ + u16 gpio_cs; +}; +#endif -- cgit v1.2.3 From d03415e6771cd709b2b2ec64d3e6315cc3ebfa74 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 12 Jan 2009 14:24:40 +0200 Subject: nl80211: Fix documentation errors Couple of '_ATTR's were missing and SEC_CHAN_OFFSET to CHANNEL_TYPE rename was missed in couple of places. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index ee742bc9761e..4e7a7986a521 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -47,7 +47,7 @@ * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, and/or - * %NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET. + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request * or rename notification. Has attributes %NL80211_ATTR_WIPHY and * %NL80211_ATTR_WIPHY_NAME. @@ -84,7 +84,7 @@ * %NL80222_CMD_NEW_BEACON message) * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, - * %NL80211_BEACON_HEAD and %NL80211_BEACON_TAIL attributes. + * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, * parameters are like for %NL80211_CMD_SET_BEACON. * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it @@ -362,7 +362,7 @@ enum nl80211_attrs { #define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES #define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS #define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ -#define NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET +#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 -- cgit v1.2.3 From 9dbeb91a8b97e2892c04461e28d2bdd0198b719d Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 14 Jan 2009 20:17:08 +0100 Subject: ath9k: get EEPROM contents from platform data on AHB bus On the AR913x SOCs we have to provide EEPROM contents via platform_data, because accessing the flash via MMIO is not safe. Additionally different boards may store the radio calibration data at different locations. Changes-licensed-under: ISC Signed-off-by: Gabor Juhos Signed-off-by: Imre Kaloz Tested-by: Pavel Roskin Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/ahb.c | 27 ++++++++++++++++++++ drivers/net/wireless/ath9k/core.h | 1 + drivers/net/wireless/ath9k/eeprom.c | 51 +++---------------------------------- drivers/net/wireless/ath9k/pci.c | 18 +++++++++++++ include/linux/ath9k_platform.h | 28 ++++++++++++++++++++ 5 files changed, 77 insertions(+), 48 deletions(-) create mode 100644 include/linux/ath9k_platform.h (limited to 'include/linux') diff --git a/drivers/net/wireless/ath9k/ahb.c b/drivers/net/wireless/ath9k/ahb.c index 8cbd4c2a7fa0..7f2c3a09bcac 100644 --- a/drivers/net/wireless/ath9k/ahb.c +++ b/drivers/net/wireless/ath9k/ahb.c @@ -18,6 +18,7 @@ #include #include +#include #include "core.h" #include "reg.h" #include "hw.h" @@ -33,9 +34,29 @@ static void ath_ahb_cleanup(struct ath_softc *sc) iounmap(sc->mem); } +static bool ath_ahb_eeprom_read(struct ath_hal *ah, u32 off, u16 *data) +{ + struct ath_softc *sc = ah->ah_sc; + struct platform_device *pdev = to_platform_device(sc->dev); + struct ath9k_platform_data *pdata; + + pdata = (struct ath9k_platform_data *) pdev->dev.platform_data; + if (off >= (ARRAY_SIZE(pdata->eeprom_data))) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "%s: flash read failed, offset %08x is out of range\n", + __func__, off); + return false; + } + + *data = pdata->eeprom_data[off]; + return true; +} + static struct ath_bus_ops ath_ahb_bus_ops = { .read_cachesize = ath_ahb_read_cachesize, .cleanup = ath_ahb_cleanup, + + .eeprom_read = ath_ahb_eeprom_read, }; static int ath_ahb_probe(struct platform_device *pdev) @@ -48,6 +69,12 @@ static int ath_ahb_probe(struct platform_device *pdev) int ret = 0; struct ath_hal *ah; + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data specified\n"); + ret = -EINVAL; + goto err_out; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "no memory resource found\n"); diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h index c5dae11d6086..b687ae9c9e17 100644 --- a/drivers/net/wireless/ath9k/core.h +++ b/drivers/net/wireless/ath9k/core.h @@ -696,6 +696,7 @@ enum PROT_MODE { struct ath_bus_ops { void (*read_cachesize)(struct ath_softc *sc, int *csz); void (*cleanup)(struct ath_softc *sc); + bool (*eeprom_read)(struct ath_hal *ah, u32 off, u16 *data); }; struct ath_softc { diff --git a/drivers/net/wireless/ath9k/eeprom.c b/drivers/net/wireless/ath9k/eeprom.c index 1ef8b5a70e5b..50cb3883416a 100644 --- a/drivers/net/wireless/ath9k/eeprom.c +++ b/drivers/net/wireless/ath9k/eeprom.c @@ -91,53 +91,11 @@ static inline bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, return false; } -static bool ath9k_hw_eeprom_read(struct ath_hal *ah, u32 off, u16 *data) -{ - (void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); - - if (!ath9k_hw_wait(ah, - AR_EEPROM_STATUS_DATA, - AR_EEPROM_STATUS_DATA_BUSY | - AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0)) { - return false; - } - - *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA), - AR_EEPROM_STATUS_DATA_VAL); - - return true; -} - -static int ath9k_hw_flash_map(struct ath_hal *ah) -{ - struct ath_hal_5416 *ahp = AH5416(ah); - - ahp->ah_cal_mem = ioremap(AR5416_EEPROM_START_ADDR, AR5416_EEPROM_MAX); - - if (!ahp->ah_cal_mem) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "cannot remap eeprom region \n"); - return -EIO; - } - - return 0; -} - -static bool ath9k_hw_flash_read(struct ath_hal *ah, u32 off, u16 *data) -{ - struct ath_hal_5416 *ahp = AH5416(ah); - - *data = ioread16(ahp->ah_cal_mem + off); - - return true; -} - static inline bool ath9k_hw_nvram_read(struct ath_hal *ah, u32 off, u16 *data) { - if (ath9k_hw_use_flash(ah)) - return ath9k_hw_flash_read(ah, off, data); - else - return ath9k_hw_eeprom_read(ah, off, data); + struct ath_softc *sc = ah->ah_sc; + + return sc->bus_ops->eeprom_read(ah, off, data); } static bool ath9k_hw_fill_4k_eeprom(struct ath_hal *ah) @@ -2825,9 +2783,6 @@ int ath9k_hw_eeprom_attach(struct ath_hal *ah) int status; struct ath_hal_5416 *ahp = AH5416(ah); - if (ath9k_hw_use_flash(ah)) - ath9k_hw_flash_map(ah); - if (AR_SREV_9285(ah)) ahp->ah_eep_map = EEP_MAP_4KBITS; else diff --git a/drivers/net/wireless/ath9k/pci.c b/drivers/net/wireless/ath9k/pci.c index 4ff1caa9ba99..05612bf28360 100644 --- a/drivers/net/wireless/ath9k/pci.c +++ b/drivers/net/wireless/ath9k/pci.c @@ -58,9 +58,27 @@ static void ath_pci_cleanup(struct ath_softc *sc) pci_disable_device(pdev); } +static bool ath_pci_eeprom_read(struct ath_hal *ah, u32 off, u16 *data) +{ + (void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); + + if (!ath9k_hw_wait(ah, + AR_EEPROM_STATUS_DATA, + AR_EEPROM_STATUS_DATA_BUSY | + AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0)) { + return false; + } + + *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA), + AR_EEPROM_STATUS_DATA_VAL); + + return true; +} + static struct ath_bus_ops ath_pci_bus_ops = { .read_cachesize = ath_pci_read_cachesize, .cleanup = ath_pci_cleanup, + .eeprom_read = ath_pci_eeprom_read, }; static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/include/linux/ath9k_platform.h b/include/linux/ath9k_platform.h new file mode 100644 index 000000000000..b847fc7b93f9 --- /dev/null +++ b/include/linux/ath9k_platform.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * Copyright (c) 2009 Gabor Juhos + * Copyright (c) 2009 Imre Kaloz + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _LINUX_ATH9K_PLATFORM_H +#define _LINUX_ATH9K_PLATFORM_H + +#define ATH9K_PLAT_EEP_MAX_WORDS 2048 + +struct ath9k_platform_data { + u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS]; +}; + +#endif /* _LINUX_ATH9K_PLATFORM_H */ -- cgit v1.2.3 From 9aed3cc124343d92be6697e9af3928bdfe8eb03e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 13 Jan 2009 16:03:29 +0200 Subject: nl80211: New command for adding extra IE(s) into management frames A new nl80211 command, NL80211_CMD_SET_MGMT_EXTRA_IE, can be used to add arbitrary IE data into the end of management frames. The interface allows extra IEs to be configured for each management frame subtype, but only some of them (ProbeReq, ProbeResp, Auth, (Re)AssocReq, Deauth, Disassoc) are currently accepted in mac80211 implementation. This makes it easier to implement IEEE 802.11 extensions like WPS and FT that add IE(s) into some management frames. In addition, this can be useful for testing and experimentation purposes. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 22 +++++++++++++ include/net/cfg80211.h | 26 +++++++++++++++ net/mac80211/cfg.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/ieee80211_i.h | 16 +++++++++ net/mac80211/iface.c | 7 ++++ net/mac80211/mlme.c | 52 +++++++++++++++++++++++++---- net/wireless/nl80211.c | 47 ++++++++++++++++++++++++++ 7 files changed, 246 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 4e7a7986a521..76aae3d8e97e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -133,6 +133,14 @@ * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the * interface identified by %NL80211_ATTR_IFINDEX * + * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The + * interface is identified with %NL80211_ATTR_IFINDEX and the management + * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be + * added to the end of the specified management frame is specified with + * %NL80211_ATTR_IE. If the command succeeds, the requested data will be + * added to all specified management frames generated by + * kernel/firmware/driver. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -178,6 +186,8 @@ enum nl80211_commands { NL80211_CMD_GET_MESH_PARAMS, NL80211_CMD_SET_MESH_PARAMS, + NL80211_CMD_SET_MGMT_EXTRA_IE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -190,6 +200,7 @@ enum nl80211_commands { * here */ #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS +#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE /** * enum nl80211_attrs - nl80211 netlink attributes @@ -284,6 +295,12 @@ enum nl80211_commands { * supported interface types, each a flag attribute with the number * of the interface mode. * + * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for + * %NL80211_CMD_SET_MGMT_EXTRA_IE. + * + * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with + * %NL80211_CMD_SET_MGMT_EXTRA_IE). + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -348,6 +365,9 @@ enum nl80211_attrs { NL80211_ATTR_KEY_DEFAULT_MGMT, + NL80211_ATTR_MGMT_SUBTYPE, + NL80211_ATTR_IE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -363,6 +383,8 @@ enum nl80211_attrs { #define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS #define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ #define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE +#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE +#define NL80211_ATTR_IE NL80211_ATTR_IE #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index df78abc496f1..c7da88fb15b7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -433,6 +433,26 @@ struct ieee80211_txq_params { u8 aifs; }; +/** + * struct mgmt_extra_ie_params - Extra management frame IE parameters + * + * Used to add extra IE(s) into management frames. If the driver cannot add the + * requested data into all management frames of the specified subtype that are + * generated in kernel or firmware/hardware, it must reject the configuration + * call. The IE data buffer is added to the end of the specified management + * frame body after all other IEs. This addition is not applied to frames that + * are injected through a monitor interface. + * + * @subtype: Management frame subtype + * @ies: IE data buffer or %NULL to remove previous data + * @ies_len: Length of @ies in octets + */ +struct mgmt_extra_ie_params { + u8 subtype; + u8 *ies; + int ies_len; +}; + /* from net/wireless.h */ struct wiphy; @@ -501,6 +521,8 @@ struct ieee80211_channel; * @set_txq_params: Set TX queue parameters * * @set_channel: Set channel + * + * @set_mgmt_extra_ie: Set extra IE data for management frames */ struct cfg80211_ops { int (*add_virtual_intf)(struct wiphy *wiphy, char *name, @@ -571,6 +593,10 @@ struct cfg80211_ops { int (*set_channel)(struct wiphy *wiphy, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); + + int (*set_mgmt_extra_ie)(struct wiphy *wiphy, + struct net_device *dev, + struct mgmt_extra_ie_params *params); }; /* temporary wext handlers */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 72c106915433..d1ac3ab2c515 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1175,6 +1175,87 @@ static int ieee80211_set_channel(struct wiphy *wiphy, return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); } +static int set_mgmt_extra_ie_sta(struct ieee80211_if_sta *ifsta, u8 subtype, + u8 *ies, size_t ies_len) +{ + switch (subtype) { + case IEEE80211_STYPE_PROBE_REQ >> 4: + kfree(ifsta->ie_probereq); + ifsta->ie_probereq = ies; + ifsta->ie_probereq_len = ies_len; + return 0; + case IEEE80211_STYPE_PROBE_RESP >> 4: + kfree(ifsta->ie_proberesp); + ifsta->ie_proberesp = ies; + ifsta->ie_proberesp_len = ies_len; + return 0; + case IEEE80211_STYPE_AUTH >> 4: + kfree(ifsta->ie_auth); + ifsta->ie_auth = ies; + ifsta->ie_auth_len = ies_len; + return 0; + case IEEE80211_STYPE_ASSOC_REQ >> 4: + kfree(ifsta->ie_assocreq); + ifsta->ie_assocreq = ies; + ifsta->ie_assocreq_len = ies_len; + return 0; + case IEEE80211_STYPE_REASSOC_REQ >> 4: + kfree(ifsta->ie_reassocreq); + ifsta->ie_reassocreq = ies; + ifsta->ie_reassocreq_len = ies_len; + return 0; + case IEEE80211_STYPE_DEAUTH >> 4: + kfree(ifsta->ie_deauth); + ifsta->ie_deauth = ies; + ifsta->ie_deauth_len = ies_len; + return 0; + case IEEE80211_STYPE_DISASSOC >> 4: + kfree(ifsta->ie_disassoc); + ifsta->ie_disassoc = ies; + ifsta->ie_disassoc_len = ies_len; + return 0; + } + + return -EOPNOTSUPP; +} + +static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy, + struct net_device *dev, + struct mgmt_extra_ie_params *params) +{ + struct ieee80211_sub_if_data *sdata; + u8 *ies; + size_t ies_len; + int ret = -EOPNOTSUPP; + + if (params->ies) { + ies = kmemdup(params->ies, params->ies_len, GFP_KERNEL); + if (ies == NULL) + return -ENOMEM; + ies_len = params->ies_len; + } else { + ies = NULL; + ies_len = 0; + } + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + ret = set_mgmt_extra_ie_sta(&sdata->u.sta, params->subtype, + ies, ies_len); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + if (ret) + kfree(ies); + return ret; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1204,4 +1285,5 @@ struct cfg80211_ops mac80211_config_ops = { .change_bss = ieee80211_change_bss, .set_txq_params = ieee80211_set_txq_params, .set_channel = ieee80211_set_channel, + .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c9ffadb55d36..5eafd3affe27 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -331,6 +331,22 @@ struct ieee80211_if_sta { u32 supp_rates_bits[IEEE80211_NUM_BANDS]; int wmm_last_param_set; + + /* Extra IE data for management frames */ + u8 *ie_probereq; + size_t ie_probereq_len; + u8 *ie_proberesp; + size_t ie_proberesp_len; + u8 *ie_auth; + size_t ie_auth_len; + u8 *ie_assocreq; + size_t ie_assocreq_len; + u8 *ie_reassocreq; + size_t ie_reassocreq_len; + u8 *ie_deauth; + size_t ie_deauth_len; + u8 *ie_disassoc; + size_t ie_disassoc_len; }; struct ieee80211_if_mesh { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 5d5a029228be..8dc2c2188d92 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -632,6 +632,13 @@ static void ieee80211_teardown_sdata(struct net_device *dev) kfree(sdata->u.sta.assocreq_ies); kfree(sdata->u.sta.assocresp_ies); kfree_skb(sdata->u.sta.probe_resp); + kfree(sdata->u.sta.ie_probereq); + kfree(sdata->u.sta.ie_proberesp); + kfree(sdata->u.sta.ie_auth); + kfree(sdata->u.sta.ie_assocreq); + kfree(sdata->u.sta.ie_reassocreq); + kfree(sdata->u.sta.ie_deauth); + kfree(sdata->u.sta.ie_disassoc); break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_AP_VLAN: diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f0d42498c257..43da6227b37c 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -131,6 +131,12 @@ u64 ieee80211_sta_get_rates(struct ieee80211_local *local, /* frame sending functions */ +static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len) +{ + if (ies) + memcpy(skb_put(skb, ies_len), ies, ies_len); +} + /* also used by scanning code */ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u8 *ssid, size_t ssid_len) @@ -142,7 +148,8 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u8 *pos, *supp_rates, *esupp_rates = NULL; int i; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + + sdata->u.sta.ie_probereq_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for probe " "request\n", sdata->dev->name); @@ -189,6 +196,9 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, *pos = rate->bitrate / 5; } + add_extra_ies(skb, sdata->u.sta.ie_probereq, + sdata->u.sta.ie_probereq_len); + ieee80211_tx_skb(sdata, skb, 0); } @@ -202,7 +212,8 @@ static void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; skb = dev_alloc_skb(local->hw.extra_tx_headroom + - sizeof(*mgmt) + 6 + extra_len); + sizeof(*mgmt) + 6 + extra_len + + sdata->u.sta.ie_auth_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for auth " "frame\n", sdata->dev->name); @@ -225,6 +236,7 @@ static void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, mgmt->u.auth.status_code = cpu_to_le16(0); if (extra) memcpy(skb_put(skb, extra_len), extra, extra_len); + add_extra_ies(skb, sdata->u.sta.ie_auth, sdata->u.sta.ie_auth_len); ieee80211_tx_skb(sdata, skb, encrypt); } @@ -235,17 +247,26 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos, *ies, *ht_ie; + u8 *pos, *ies, *ht_ie, *e_ies; int i, len, count, rates_len, supp_rates_len; u16 capab; struct ieee80211_bss *bss; int wmm = 0; struct ieee80211_supported_band *sband; u64 rates = 0; + size_t e_ies_len; + + if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) { + e_ies = sdata->u.sta.ie_reassocreq; + e_ies_len = sdata->u.sta.ie_reassocreq_len; + } else { + e_ies = sdata->u.sta.ie_assocreq; + e_ies_len = sdata->u.sta.ie_assocreq_len; + } skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + ifsta->extra_ie_len + - ifsta->ssid_len); + ifsta->ssid_len + e_ies_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " "frame\n", sdata->dev->name); @@ -436,6 +457,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); } + add_extra_ies(skb, e_ies, e_ies_len); + kfree(ifsta->assocreq_ies); ifsta->assocreq_ies_len = (skb->data + skb->len) - ies; ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL); @@ -453,8 +476,19 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_sta *ifsta = &sdata->u.sta; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; + u8 *ies; + size_t ies_len; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt)); + if (stype == IEEE80211_STYPE_DEAUTH) { + ies = sdata->u.sta.ie_deauth; + ies_len = sdata->u.sta.ie_deauth_len; + } else { + ies = sdata->u.sta.ie_disassoc; + ies_len = sdata->u.sta.ie_disassoc_len; + } + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + + ies_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for " "deauth/disassoc frame\n", sdata->dev->name); @@ -472,6 +506,8 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, /* u.deauth.reason_code == u.disassoc.reason_code */ mgmt->u.deauth.reason_code = cpu_to_le16(reason); + add_extra_ies(skb, ies, ies_len); + ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED); } @@ -1473,7 +1509,8 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband; union iwreq_data wrqu; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + + sdata->u.sta.ie_proberesp_len); if (!skb) { printk(KERN_DEBUG "%s: failed to allocate buffer for probe " "response\n", sdata->dev->name); @@ -1556,6 +1593,9 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, memcpy(pos, &bss->supp_rates[8], rates); } + add_extra_ies(skb, sdata->u.sta.ie_proberesp, + sdata->u.sta.ie_proberesp_len); + ifsta->probe_resp = skb; ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 123d3b160fad..09a5d0f1d6dc 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -105,6 +105,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, .len = NL80211_HT_CAPABILITY_LEN }, + + [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, + [NL80211_ATTR_IE] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, }; /* message building helper */ @@ -2149,6 +2153,43 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } +static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int err; + struct net_device *dev; + struct mgmt_extra_ie_params params; + + memset(¶ms, 0, sizeof(params)); + + if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) + return -EINVAL; + params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); + if (params.subtype > 15) + return -EINVAL; /* FC Subtype field is 4 bits (0..15) */ + + if (info->attrs[NL80211_ATTR_IE]) { + params.ies = nla_data(info->attrs[NL80211_ATTR_IE]); + params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + return err; + + if (drv->ops->set_mgmt_extra_ie) { + rtnl_lock(); + err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms); + rtnl_unlock(); + } else + err = -EOPNOTSUPP; + + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -2310,6 +2351,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE, + .doit = nl80211_set_mgmt_extra_ie, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; /* multicast groups */ -- cgit v1.2.3 From f797eb7e2903571e9c0e7e5d64113f51209f8dc4 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 19 Jan 2009 18:48:46 +0200 Subject: mac80211: Fix MFP Association Comeback to use Timeout Interval IE The separate Association Comeback Time IE was removed from IEEE 802.11w and the Timeout Interval IE (from IEEE 802.11r) is used instead. The editing on this is still somewhat incomplete in IEEE 802.11w/D7.0, but still, the use of Timeout Interval IE is the expected mechanism. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 8 +++++++- net/mac80211/ieee80211_i.h | 4 ++-- net/mac80211/mlme.c | 5 +++-- net/mac80211/util.c | 6 +++--- 4 files changed, 15 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 7800e20f197f..b1bb817d1427 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1036,8 +1036,8 @@ enum ieee80211_eid { WLAN_EID_HT_INFORMATION = 61, /* 802.11i */ WLAN_EID_RSN = 48, + WLAN_EID_TIMEOUT_INTERVAL = 56, WLAN_EID_MMIE = 76 /* 802.11w */, - WLAN_EID_ASSOC_COMEBACK_TIME = 77, WLAN_EID_WPA = 221, WLAN_EID_GENERIC = 221, WLAN_EID_VENDOR_SPECIFIC = 221, @@ -1126,6 +1126,12 @@ struct ieee80211_country_ie_triplet { }; } __attribute__ ((packed)); +enum ieee80211_timeout_interval_type { + WLAN_TIMEOUT_REASSOC_DEADLINE = 1 /* 802.11r */, + WLAN_TIMEOUT_KEY_LIFETIME = 2 /* 802.11r */, + WLAN_TIMEOUT_ASSOC_COMEBACK = 3 /* 802.11w */, +}; + /* BACK action code */ enum ieee80211_back_actioncode { WLAN_ACTION_ADDBA_REQ = 0, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index faa2476a2451..a8c72742a8b1 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -837,7 +837,7 @@ struct ieee802_11_elems { u8 *country_elem; u8 *pwr_constr_elem; u8 *quiet_elem; /* first quite element */ - u8 *assoc_comeback; + u8 *timeout_int; /* length of them, respectively */ u8 ssid_len; @@ -865,7 +865,7 @@ struct ieee802_11_elems { u8 pwr_constr_elem_len; u8 quiet_elem_len; u8 num_of_quiet_elem; /* can be more the one */ - u8 assoc_comeback_len; + u8 timeout_int_len; }; static inline struct ieee80211_local *hw_to_local( diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 43da6227b37c..b9e4b93089c4 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1317,9 +1317,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && - elems.assoc_comeback && elems.assoc_comeback_len == 4) { + elems.timeout_int && elems.timeout_int_len == 5 && + elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { u32 tu, ms; - tu = get_unaligned_le32(elems.assoc_comeback); + tu = get_unaligned_le32(elems.timeout_int + 1); ms = tu * 1024 / 1000; printk(KERN_DEBUG "%s: AP rejected association temporarily; " "comeback duration %u TU (%u ms)\n", diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 963e0473205c..3f559e3d0a7c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -653,9 +653,9 @@ void ieee802_11_parse_elems(u8 *start, size_t len, elems->pwr_constr_elem = pos; elems->pwr_constr_elem_len = elen; break; - case WLAN_EID_ASSOC_COMEBACK_TIME: - elems->assoc_comeback = pos; - elems->assoc_comeback_len = elen; + case WLAN_EID_TIMEOUT_INTERVAL: + elems->timeout_int = pos; + elems->timeout_int_len = elen; break; default: break; -- cgit v1.2.3 From f677d7702d48b7b3dfcce3b2c0db601dbee0aa24 Mon Sep 17 00:00:00 2001 From: Tulio Magno Quites Machado Filho Date: Sun, 25 Jan 2009 23:54:25 +0100 Subject: ath5k: support LED's on emachines E510 notebook Add vendor ID for AMBIT and use it to set the ath5k LED gpio. base.c: Changes-licensed-under: 3-Clause-BSD Signed-off-by: Tulio Magno Quites Machado Filho Acked-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 8 ++++++-- include/linux/pci_ids.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index b3f41acb9065..368db944f11f 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2626,8 +2626,12 @@ ath5k_init_leds(struct ath5k_softc *sc) sc->led_pin = 1; sc->led_on = 1; /* active high */ } - /* Pin 3 on Foxconn chips used in Acer Aspire One (0x105b:e008) */ - if (pdev->subsystem_vendor == PCI_VENDOR_ID_FOXCONN) { + /* + * Pin 3 on Foxconn chips used in Acer Aspire One (0x105b:e008) and + * in emachines notebooks with AMBIT subsystem. + */ + if (pdev->subsystem_vendor == PCI_VENDOR_ID_FOXCONN || + pdev->subsystem_vendor == PCI_VENDOR_ID_AMBIT) { __set_bit(ATH_STAT_LEDSOFT, sc->status); sc->led_pin = 3; sc->led_on = 0; /* active low */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 5b7a48c1d616..b7697c934a49 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1967,6 +1967,8 @@ #define PCI_VENDOR_ID_SAMSUNG 0x144d +#define PCI_VENDOR_ID_AMBIT 0x1468 + #define PCI_VENDOR_ID_MYRICOM 0x14c1 #define PCI_VENDOR_ID_TITAN 0x14D2 -- cgit v1.2.3 From 5d0d9be8ef456afc6c3fb5f8aad06ef19b704b05 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 29 Jan 2009 14:19:48 +0000 Subject: gro: Move common completion code into helpers Currently VLAN still has a bit of common code handling the aftermath of GRO that's shared with the common path. This patch moves them into shared helpers to reduce code duplication. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 ++ net/8021q/vlan_core.c | 39 +++--------------------- net/core/dev.c | 76 ++++++++++++++++++++++++++++++++--------------- 3 files changed, 59 insertions(+), 59 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index dd8a35b3e8b2..20419508eec1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1375,12 +1375,15 @@ extern int netif_receive_skb(struct sk_buff *skb); extern void napi_gro_flush(struct napi_struct *napi); extern int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb); +extern int napi_skb_finish(int ret, struct sk_buff *skb); extern int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb); extern void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb); extern struct sk_buff * napi_fraginfo_skb(struct napi_struct *napi, struct napi_gro_fraginfo *info); +extern int napi_frags_finish(struct napi_struct *napi, + struct sk_buff *skb, int ret); extern int napi_gro_frags(struct napi_struct *napi, struct napi_gro_fraginfo *info); extern void netif_nit_deliver(struct sk_buff *skb); diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index e9db889d6222..2eb057a74654 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -98,22 +98,7 @@ drop: int vlan_gro_receive(struct napi_struct *napi, struct vlan_group *grp, unsigned int vlan_tci, struct sk_buff *skb) { - int err = NET_RX_SUCCESS; - - switch (vlan_gro_common(napi, grp, vlan_tci, skb)) { - case -1: - return netif_receive_skb(skb); - - case 2: - err = NET_RX_DROP; - /* fall through */ - - case 1: - kfree_skb(skb); - break; - } - - return err; + return napi_skb_finish(vlan_gro_common(napi, grp, vlan_tci, skb), skb); } EXPORT_SYMBOL(vlan_gro_receive); @@ -121,27 +106,11 @@ int vlan_gro_frags(struct napi_struct *napi, struct vlan_group *grp, unsigned int vlan_tci, struct napi_gro_fraginfo *info) { struct sk_buff *skb = napi_fraginfo_skb(napi, info); - int err = NET_RX_DROP; if (!skb) - goto out; - - err = NET_RX_SUCCESS; - - switch (vlan_gro_common(napi, grp, vlan_tci, skb)) { - case -1: - return netif_receive_skb(skb); - - case 2: - err = NET_RX_DROP; - /* fall through */ - - case 1: - napi_reuse_skb(napi, skb); - break; - } + return NET_RX_DROP; -out: - return err; + return napi_frags_finish(napi, skb, + vlan_gro_common(napi, grp, vlan_tci, skb)); } EXPORT_SYMBOL(vlan_gro_frags); diff --git a/net/core/dev.c b/net/core/dev.c index e61b95c11fc0..cd23ae15a1d5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -135,6 +135,14 @@ /* This should be increased if a protocol with a bigger head is added. */ #define GRO_MAX_HEAD (MAX_HEADER + 128) +enum { + GRO_MERGED, + GRO_MERGED_FREE, + GRO_HELD, + GRO_NORMAL, + GRO_DROP, +}; + /* * The list of packet types we will receive (as opposed to discard) * and the routines to invoke. @@ -2369,7 +2377,7 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) int count = 0; int same_flow; int mac_len; - int free; + int ret; if (!(skb->dev->features & NETIF_F_GRO)) goto normal; @@ -2412,7 +2420,7 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) goto normal; same_flow = NAPI_GRO_CB(skb)->same_flow; - free = NAPI_GRO_CB(skb)->free; + ret = NAPI_GRO_CB(skb)->free ? GRO_MERGED_FREE : GRO_MERGED; if (pp) { struct sk_buff *nskb = *pp; @@ -2435,12 +2443,13 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) skb_shinfo(skb)->gso_size = skb->len; skb->next = napi->gro_list; napi->gro_list = skb; + ret = GRO_HELD; ok: - return free; + return ret; normal: - return -1; + return GRO_NORMAL; } EXPORT_SYMBOL(dev_gro_receive); @@ -2456,18 +2465,30 @@ static int __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) return dev_gro_receive(napi, skb); } -int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) +int napi_skb_finish(int ret, struct sk_buff *skb) { - switch (__napi_gro_receive(napi, skb)) { - case -1: + int err = NET_RX_SUCCESS; + + switch (ret) { + case GRO_NORMAL: return netif_receive_skb(skb); - case 1: + case GRO_DROP: + err = NET_RX_DROP; + /* fall through */ + + case GRO_MERGED_FREE: kfree_skb(skb); break; } - return NET_RX_SUCCESS; + return err; +} +EXPORT_SYMBOL(napi_skb_finish); + +int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) +{ + return napi_skb_finish(__napi_gro_receive(napi, skb), skb); } EXPORT_SYMBOL(napi_gro_receive); @@ -2520,29 +2541,36 @@ out: } EXPORT_SYMBOL(napi_fraginfo_skb); -int napi_gro_frags(struct napi_struct *napi, struct napi_gro_fraginfo *info) +int napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb, int ret) { - struct sk_buff *skb = napi_fraginfo_skb(napi, info); - int err = NET_RX_DROP; - - if (!skb) - goto out; + int err = NET_RX_SUCCESS; - err = NET_RX_SUCCESS; - - switch (__napi_gro_receive(napi, skb)) { - case -1: + switch (ret) { + case GRO_NORMAL: return netif_receive_skb(skb); - case 0: - goto out; - } + case GRO_DROP: + err = NET_RX_DROP; + /* fall through */ - napi_reuse_skb(napi, skb); + case GRO_MERGED_FREE: + napi_reuse_skb(napi, skb); + break; + } -out: return err; } +EXPORT_SYMBOL(napi_frags_finish); + +int napi_gro_frags(struct napi_struct *napi, struct napi_gro_fraginfo *info) +{ + struct sk_buff *skb = napi_fraginfo_skb(napi, info); + + if (!skb) + return NET_RX_DROP; + + return napi_frags_finish(napi, skb, __napi_gro_receive(napi, skb)); +} EXPORT_SYMBOL(napi_gro_frags); static int process_backlog(struct napi_struct *napi, int quota) -- cgit v1.2.3 From 86911732d3996a9da07914b280621450111bb6da Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 29 Jan 2009 14:19:50 +0000 Subject: gro: Avoid copying headers of unmerged packets Unfortunately simplicity isn't always the best. The fraginfo interface turned out to be suboptimal. The problem was quite obvious. For every packet, we have to copy the headers from the frags structure into skb->head, even though for 99% of the packets this part is immediately thrown away after the merge. LRO didn't have this problem because it directly read the headers from the frags structure. This patch attempts to address this by creating an interface that allows GRO to access the headers in the first frag without having to copy it. Because all drivers that use frags place the headers in the first frag this optimisation should be enough. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/netdevice.h | 26 ++++++++++++++++++ include/linux/skbuff.h | 2 -- net/8021q/vlan_core.c | 2 ++ net/core/dev.c | 70 +++++++++++++++++++++++++++++++++++++++-------- net/core/skbuff.c | 23 ++++++++++------ net/ipv4/af_inet.c | 10 +++---- net/ipv4/tcp.c | 16 +++++------ net/ipv4/tcp_ipv4.c | 2 +- net/ipv6/af_inet6.c | 30 +++++++++++++------- net/ipv6/tcp_ipv6.c | 2 +- 10 files changed, 137 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 20419508eec1..7a5057fbb7cd 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -984,6 +984,9 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi, void netif_napi_del(struct napi_struct *napi); struct napi_gro_cb { + /* This indicates where we are processing relative to skb->data. */ + int data_offset; + /* This is non-zero if the packet may be of the same flow. */ int same_flow; @@ -1087,6 +1090,29 @@ extern int dev_restart(struct net_device *dev); #ifdef CONFIG_NETPOLL_TRAP extern int netpoll_trap(void); #endif +extern void *skb_gro_header(struct sk_buff *skb, unsigned int hlen); +extern int skb_gro_receive(struct sk_buff **head, + struct sk_buff *skb); + +static inline unsigned int skb_gro_offset(const struct sk_buff *skb) +{ + return NAPI_GRO_CB(skb)->data_offset; +} + +static inline unsigned int skb_gro_len(const struct sk_buff *skb) +{ + return skb->len - NAPI_GRO_CB(skb)->data_offset; +} + +static inline void skb_gro_pull(struct sk_buff *skb, unsigned int len) +{ + NAPI_GRO_CB(skb)->data_offset += len; +} + +static inline void skb_gro_reset_offset(struct sk_buff *skb) +{ + NAPI_GRO_CB(skb)->data_offset = 0; +} static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index a2c2378a9c58..08670d017479 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1687,8 +1687,6 @@ extern int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); extern struct sk_buff *skb_segment(struct sk_buff *skb, int features); -extern int skb_gro_receive(struct sk_buff **head, - struct sk_buff *skb); static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 2eb057a74654..378fa69d625a 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -98,6 +98,8 @@ drop: int vlan_gro_receive(struct napi_struct *napi, struct vlan_group *grp, unsigned int vlan_tci, struct sk_buff *skb) { + skb_gro_reset_offset(skb); + return napi_skb_finish(vlan_gro_common(napi, grp, vlan_tci, skb), skb); } EXPORT_SYMBOL(vlan_gro_receive); diff --git a/net/core/dev.c b/net/core/dev.c index cd23ae15a1d5..df406dcf7482 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -215,6 +215,13 @@ static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex) return &net->dev_index_head[ifindex & ((1 << NETDEV_HASHBITS) - 1)]; } +static inline void *skb_gro_mac_header(struct sk_buff *skb) +{ + return skb_headlen(skb) ? skb_mac_header(skb) : + page_address(skb_shinfo(skb)->frags[0].page) + + skb_shinfo(skb)->frags[0].page_offset; +} + /* Device list insertion */ static int list_netdevice(struct net_device *dev) { @@ -2350,7 +2357,6 @@ static int napi_gro_complete(struct sk_buff *skb) out: skb_shinfo(skb)->gso_size = 0; - __skb_push(skb, -skb_network_offset(skb)); return netif_receive_skb(skb); } @@ -2368,6 +2374,25 @@ void napi_gro_flush(struct napi_struct *napi) } EXPORT_SYMBOL(napi_gro_flush); +void *skb_gro_header(struct sk_buff *skb, unsigned int hlen) +{ + unsigned int offset = skb_gro_offset(skb); + + hlen += offset; + if (hlen <= skb_headlen(skb)) + return skb->data + offset; + + if (unlikely(!skb_shinfo(skb)->nr_frags || + skb_shinfo(skb)->frags[0].size <= + hlen - skb_headlen(skb) || + PageHighMem(skb_shinfo(skb)->frags[0].page))) + return pskb_may_pull(skb, hlen) ? skb->data + offset : NULL; + + return page_address(skb_shinfo(skb)->frags[0].page) + + skb_shinfo(skb)->frags[0].page_offset + offset; +} +EXPORT_SYMBOL(skb_gro_header); + int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) { struct sk_buff **pp = NULL; @@ -2388,11 +2413,13 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) rcu_read_lock(); list_for_each_entry_rcu(ptype, head, list) { struct sk_buff *p; + void *mac; if (ptype->type != type || ptype->dev || !ptype->gro_receive) continue; - skb_reset_network_header(skb); + skb_set_network_header(skb, skb_gro_offset(skb)); + mac = skb_gro_mac_header(skb); mac_len = skb->network_header - skb->mac_header; skb->mac_len = mac_len; NAPI_GRO_CB(skb)->same_flow = 0; @@ -2406,8 +2433,7 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) continue; if (p->mac_len != mac_len || - memcmp(skb_mac_header(p), skb_mac_header(skb), - mac_len)) + memcmp(skb_mac_header(p), mac, mac_len)) NAPI_GRO_CB(p)->same_flow = 0; } @@ -2434,13 +2460,11 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) if (same_flow) goto ok; - if (NAPI_GRO_CB(skb)->flush || count >= MAX_GRO_SKBS) { - __skb_push(skb, -skb_network_offset(skb)); + if (NAPI_GRO_CB(skb)->flush || count >= MAX_GRO_SKBS) goto normal; - } NAPI_GRO_CB(skb)->count = 1; - skb_shinfo(skb)->gso_size = skb->len; + skb_shinfo(skb)->gso_size = skb_gro_len(skb); skb->next = napi->gro_list; napi->gro_list = skb; ret = GRO_HELD; @@ -2488,6 +2512,8 @@ EXPORT_SYMBOL(napi_skb_finish); int napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) { + skb_gro_reset_offset(skb); + return napi_skb_finish(__napi_gro_receive(napi, skb), skb); } EXPORT_SYMBOL(napi_gro_receive); @@ -2506,6 +2532,7 @@ struct sk_buff *napi_fraginfo_skb(struct napi_struct *napi, { struct net_device *dev = napi->dev; struct sk_buff *skb = napi->skb; + struct ethhdr *eth; napi->skb = NULL; @@ -2525,13 +2552,23 @@ struct sk_buff *napi_fraginfo_skb(struct napi_struct *napi, skb->len += info->len; skb->truesize += info->len; - if (!pskb_may_pull(skb, ETH_HLEN)) { + skb_reset_mac_header(skb); + skb_gro_reset_offset(skb); + + eth = skb_gro_header(skb, sizeof(*eth)); + if (!eth) { napi_reuse_skb(napi, skb); skb = NULL; goto out; } - skb->protocol = eth_type_trans(skb, dev); + skb_gro_pull(skb, sizeof(*eth)); + + /* + * This works because the only protocols we care about don't require + * special handling. We'll fix it up properly at the end. + */ + skb->protocol = eth->h_proto; skb->ip_summed = info->ip_summed; skb->csum = info->csum; @@ -2544,10 +2581,21 @@ EXPORT_SYMBOL(napi_fraginfo_skb); int napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb, int ret) { int err = NET_RX_SUCCESS; + int may; switch (ret) { case GRO_NORMAL: - return netif_receive_skb(skb); + case GRO_HELD: + may = pskb_may_pull(skb, skb_gro_offset(skb)); + BUG_ON(!may); + + skb->protocol = eth_type_trans(skb, napi->dev); + + if (ret == GRO_NORMAL) + return netif_receive_skb(skb); + + skb_gro_pull(skb, -ETH_HLEN); + break; case GRO_DROP: err = NET_RX_DROP; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 2e5f2ca3bdcd..f9f4065a7e9b 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2584,17 +2584,21 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) struct sk_buff *p = *head; struct sk_buff *nskb; unsigned int headroom; - unsigned int hlen = p->data - skb_mac_header(p); - unsigned int len = skb->len; + unsigned int len = skb_gro_len(skb); - if (hlen + p->len + len >= 65536) + if (p->len + len >= 65536) return -E2BIG; if (skb_shinfo(p)->frag_list) goto merge; - else if (!skb_headlen(p) && !skb_headlen(skb) && - skb_shinfo(p)->nr_frags + skb_shinfo(skb)->nr_frags < + else if (skb_headlen(skb) <= skb_gro_offset(skb) && + skb_shinfo(p)->nr_frags + skb_shinfo(skb)->nr_frags <= MAX_SKB_FRAGS) { + skb_shinfo(skb)->frags[0].page_offset += + skb_gro_offset(skb) - skb_headlen(skb); + skb_shinfo(skb)->frags[0].size -= + skb_gro_offset(skb) - skb_headlen(skb); + memcpy(skb_shinfo(p)->frags + skb_shinfo(p)->nr_frags, skb_shinfo(skb)->frags, skb_shinfo(skb)->nr_frags * sizeof(skb_frag_t)); @@ -2611,7 +2615,7 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) } headroom = skb_headroom(p); - nskb = netdev_alloc_skb(p->dev, headroom); + nskb = netdev_alloc_skb(p->dev, headroom + skb_gro_offset(p)); if (unlikely(!nskb)) return -ENOMEM; @@ -2619,12 +2623,15 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) nskb->mac_len = p->mac_len; skb_reserve(nskb, headroom); + __skb_put(nskb, skb_gro_offset(p)); - skb_set_mac_header(nskb, -hlen); + skb_set_mac_header(nskb, skb_mac_header(p) - p->data); skb_set_network_header(nskb, skb_network_offset(p)); skb_set_transport_header(nskb, skb_transport_offset(p)); - memcpy(skb_mac_header(nskb), skb_mac_header(p), hlen); + __skb_pull(p, skb_gro_offset(p)); + memcpy(skb_mac_header(nskb), skb_mac_header(p), + p->data - skb_mac_header(p)); *NAPI_GRO_CB(nskb) = *NAPI_GRO_CB(p); skb_shinfo(nskb)->frag_list = p; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 743f5542d65a..d6770f295d5b 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1253,10 +1253,10 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head, int proto; int id; - if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) + iph = skb_gro_header(skb, sizeof(*iph)); + if (unlikely(!iph)) goto out; - iph = ip_hdr(skb); proto = iph->protocol & (MAX_INET_PROTOS - 1); rcu_read_lock(); @@ -1270,7 +1270,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head, if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl))) goto out_unlock; - flush = ntohs(iph->tot_len) != skb->len || + flush = ntohs(iph->tot_len) != skb_gro_len(skb) || iph->frag_off != htons(IP_DF); id = ntohs(iph->id); @@ -1298,8 +1298,8 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head, } NAPI_GRO_CB(skb)->flush |= flush; - __skb_pull(skb, sizeof(*iph)); - skb_reset_transport_header(skb); + skb_gro_pull(skb, sizeof(*iph)); + skb_set_transport_header(skb, skb_gro_offset(skb)); pp = ops->gro_receive(head, skb); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0cd71b84e483..1cd608253940 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2481,19 +2481,19 @@ struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb) unsigned int mss = 1; int flush = 1; - if (!pskb_may_pull(skb, sizeof(*th))) + th = skb_gro_header(skb, sizeof(*th)); + if (unlikely(!th)) goto out; - th = tcp_hdr(skb); thlen = th->doff * 4; if (thlen < sizeof(*th)) goto out; - if (!pskb_may_pull(skb, thlen)) + th = skb_gro_header(skb, thlen); + if (unlikely(!th)) goto out; - th = tcp_hdr(skb); - __skb_pull(skb, thlen); + skb_gro_pull(skb, thlen); flags = tcp_flag_word(th); @@ -2521,10 +2521,10 @@ found: flush |= th->ack_seq != th2->ack_seq || th->window != th2->window; flush |= memcmp(th + 1, th2 + 1, thlen - sizeof(*th)); - total = p->len; + total = skb_gro_len(p); mss = skb_shinfo(p)->gso_size; - flush |= skb->len > mss || skb->len <= 0; + flush |= skb_gro_len(skb) > mss || !skb_gro_len(skb); flush |= ntohl(th2->seq) + total != ntohl(th->seq); if (flush || skb_gro_receive(head, skb)) { @@ -2537,7 +2537,7 @@ found: tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH); out_check_final: - flush = skb->len < mss; + flush = skb_gro_len(skb) < mss; flush |= flags & (TCP_FLAG_URG | TCP_FLAG_PSH | TCP_FLAG_RST | TCP_FLAG_SYN | TCP_FLAG_FIN); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 19d7b429a262..f6b962f56ab4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2355,7 +2355,7 @@ struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb) switch (skb->ip_summed) { case CHECKSUM_COMPLETE: - if (!tcp_v4_check(skb->len, iph->saddr, iph->daddr, + if (!tcp_v4_check(skb_gro_len(skb), iph->saddr, iph->daddr, skb->csum)) { skb->ip_summed = CHECKSUM_UNNECESSARY; break; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index c802bc1658a8..bd91eadcbe3f 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -799,24 +799,34 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head, int proto; __wsum csum; - if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) + iph = skb_gro_header(skb, sizeof(*iph)); + if (unlikely(!iph)) goto out; - iph = ipv6_hdr(skb); - __skb_pull(skb, sizeof(*iph)); + skb_gro_pull(skb, sizeof(*iph)); + skb_set_transport_header(skb, skb_gro_offset(skb)); - flush += ntohs(iph->payload_len) != skb->len; + flush += ntohs(iph->payload_len) != skb_gro_len(skb); rcu_read_lock(); - proto = ipv6_gso_pull_exthdrs(skb, iph->nexthdr); - iph = ipv6_hdr(skb); - IPV6_GRO_CB(skb)->proto = proto; + proto = iph->nexthdr; ops = rcu_dereference(inet6_protos[proto]); - if (!ops || !ops->gro_receive) - goto out_unlock; + if (!ops || !ops->gro_receive) { + __pskb_pull(skb, skb_gro_offset(skb)); + proto = ipv6_gso_pull_exthdrs(skb, proto); + skb_gro_pull(skb, -skb_transport_offset(skb)); + skb_reset_transport_header(skb); + __skb_push(skb, skb_gro_offset(skb)); + + if (!ops || !ops->gro_receive) + goto out_unlock; + + iph = ipv6_hdr(skb); + } + + IPV6_GRO_CB(skb)->proto = proto; flush--; - skb_reset_transport_header(skb); nlen = skb_network_header_len(skb); for (p = *head; p; p = p->next) { diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index e5b85d45bee8..00f1269e11e9 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -948,7 +948,7 @@ struct sk_buff **tcp6_gro_receive(struct sk_buff **head, struct sk_buff *skb) switch (skb->ip_summed) { case CHECKSUM_COMPLETE: - if (!tcp_v6_check(skb->len, &iph->saddr, &iph->daddr, + if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr, skb->csum)) { skb->ip_summed = CHECKSUM_UNNECESSARY; break; -- cgit v1.2.3 From d23f028a4ddce8b783c212bfe911d1d307ff3617 Mon Sep 17 00:00:00 2001 From: Steve Glendinning Date: Tue, 27 Jan 2009 06:51:11 +0000 Subject: smsc911x: add external phy detection overrides On LAN9115/LAN9117/LAN9215/LAN9217, external phys are supported. These are usually indicated by a hardware strap which sets an "external PHY detected" bit in the HW_CFG register. In some cases it is desirable to override this hardware strap and force use of either the internal phy or an external PHY. This patch adds SMSC911X_FORCE_INTERNAL_PHY and SMSC911X_FORCE_EXTERNAL_PHY flags so a platform can indicate this preference via its platform_data. Signed-off-by: Steve Glendinning Acked-by: Sascha Hauer Tested-by: Sascha Hauer Signed-off-by: David S. Miller --- drivers/net/smsc911x.c | 75 ++++++++++++++++++++++++------------------------ include/linux/smsc911x.h | 2 ++ 2 files changed, 40 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index 6aa2b165de6a..27d2d7c45519 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -368,48 +368,53 @@ out: return reg; } -/* Autodetects and initialises external phy for SMSC9115 and SMSC9117 flavors. - * If something goes wrong, returns -ENODEV to revert back to internal phy. - * Performed at initialisation only, so interrupts are enabled */ -static int smsc911x_phy_initialise_external(struct smsc911x_data *pdata) +/* Switch to external phy. Assumes tx and rx are stopped. */ +static void smsc911x_phy_enable_external(struct smsc911x_data *pdata) { unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG); - /* External phy is requested, supported, and detected */ - if (hwcfg & HW_CFG_EXT_PHY_DET_) { + /* Disable phy clocks to the MAC */ + hwcfg &= (~HW_CFG_PHY_CLK_SEL_); + hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_; + smsc911x_reg_write(pdata, HW_CFG, hwcfg); + udelay(10); /* Enough time for clocks to stop */ - /* Switch to external phy. Assuming tx and rx are stopped - * because smsc911x_phy_initialise is called before - * smsc911x_rx_initialise and tx_initialise. */ + /* Switch to external phy */ + hwcfg |= HW_CFG_EXT_PHY_EN_; + smsc911x_reg_write(pdata, HW_CFG, hwcfg); - /* Disable phy clocks to the MAC */ - hwcfg &= (~HW_CFG_PHY_CLK_SEL_); - hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_; - smsc911x_reg_write(pdata, HW_CFG, hwcfg); - udelay(10); /* Enough time for clocks to stop */ + /* Enable phy clocks to the MAC */ + hwcfg &= (~HW_CFG_PHY_CLK_SEL_); + hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_; + smsc911x_reg_write(pdata, HW_CFG, hwcfg); + udelay(10); /* Enough time for clocks to restart */ - /* Switch to external phy */ - hwcfg |= HW_CFG_EXT_PHY_EN_; - smsc911x_reg_write(pdata, HW_CFG, hwcfg); - - /* Enable phy clocks to the MAC */ - hwcfg &= (~HW_CFG_PHY_CLK_SEL_); - hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_; - smsc911x_reg_write(pdata, HW_CFG, hwcfg); - udelay(10); /* Enough time for clocks to restart */ + hwcfg |= HW_CFG_SMI_SEL_; + smsc911x_reg_write(pdata, HW_CFG, hwcfg); +} - hwcfg |= HW_CFG_SMI_SEL_; - smsc911x_reg_write(pdata, HW_CFG, hwcfg); +/* Autodetects and enables external phy if present on supported chips. + * autodetection can be overridden by specifying SMSC911X_FORCE_INTERNAL_PHY + * or SMSC911X_FORCE_EXTERNAL_PHY in the platform_data flags. */ +static void smsc911x_phy_initialise_external(struct smsc911x_data *pdata) +{ + unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG); - SMSC_TRACE(HW, "Successfully switched to external PHY"); + if (pdata->config.flags & SMSC911X_FORCE_INTERNAL_PHY) { + SMSC_TRACE(HW, "Forcing internal PHY"); + pdata->using_extphy = 0; + } else if (pdata->config.flags & SMSC911X_FORCE_EXTERNAL_PHY) { + SMSC_TRACE(HW, "Forcing external PHY"); + smsc911x_phy_enable_external(pdata); + pdata->using_extphy = 1; + } else if (hwcfg & HW_CFG_EXT_PHY_DET_) { + SMSC_TRACE(HW, "HW_CFG EXT_PHY_DET set, using external PHY"); + smsc911x_phy_enable_external(pdata); pdata->using_extphy = 1; } else { - SMSC_WARNING(HW, "No external PHY detected, " - "Using internal PHY instead."); - /* Use internal phy */ - return -ENODEV; + SMSC_TRACE(HW, "HW_CFG EXT_PHY_DET clear, using internal PHY"); + pdata->using_extphy = 0; } - return 0; } /* Fetches a tx status out of the status fifo */ @@ -825,22 +830,18 @@ static int __devinit smsc911x_mii_init(struct platform_device *pdev, pdata->mii_bus->parent = &pdev->dev; - pdata->using_extphy = 0; - switch (pdata->idrev & 0xFFFF0000) { case 0x01170000: case 0x01150000: case 0x117A0000: case 0x115A0000: /* External PHY supported, try to autodetect */ - if (smsc911x_phy_initialise_external(pdata) < 0) { - SMSC_TRACE(HW, "No external PHY detected, " - "using internal PHY"); - } + smsc911x_phy_initialise_external(pdata); break; default: SMSC_TRACE(HW, "External PHY is not supported, " "using internal PHY"); + pdata->using_extphy = 0; break; } diff --git a/include/linux/smsc911x.h b/include/linux/smsc911x.h index 1cbf0313adde..170c76b8f7a6 100644 --- a/include/linux/smsc911x.h +++ b/include/linux/smsc911x.h @@ -43,5 +43,7 @@ struct smsc911x_platform_config { /* Constants for flags */ #define SMSC911X_USE_16BIT (BIT(0)) #define SMSC911X_USE_32BIT (BIT(1)) +#define SMSC911X_FORCE_INTERNAL_PHY (BIT(2)) +#define SMSC911X_FORCE_EXTERNAL_PHY (BIT(3)) #endif /* __LINUX_SMSC911X_H__ */ -- cgit v1.2.3 From 31f4574774e98aa275aeeee94f41ce042285ed8e Mon Sep 17 00:00:00 2001 From: Steve Glendinning Date: Tue, 27 Jan 2009 06:51:12 +0000 Subject: smsc911x: allow mac address to be saved before device reset Some platforms (for example pcm037) do not have an EEPROM fitted, instead storing their mac address somewhere else. The bootloader fetches this and configures the ethernet adapter before the kernel is started. This patch allows a platform to indicate to the driver via the SMSC911X_SAVE_MAC_ADDRESS flag that the mac address has already been configured via such a mechanism, and should be saved before resetting the chip. Signed-off-by: Steve Glendinning Acked-by: Sascha Hauer Tested-by: Sascha Hauer Signed-off-by: David S. Miller --- drivers/net/smsc911x.c | 30 ++++++++++++++++++++++-------- include/linux/smsc911x.h | 1 + 2 files changed, 23 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index 27d2d7c45519..6e175e5555a1 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -1742,6 +1742,21 @@ static const struct net_device_ops smsc911x_netdev_ops = { #endif }; +/* copies the current mac address from hardware to dev->dev_addr */ +static void __devinit smsc911x_read_mac_address(struct net_device *dev) +{ + struct smsc911x_data *pdata = netdev_priv(dev); + u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH); + u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL); + + dev->dev_addr[0] = (u8)(mac_low32); + dev->dev_addr[1] = (u8)(mac_low32 >> 8); + dev->dev_addr[2] = (u8)(mac_low32 >> 16); + dev->dev_addr[3] = (u8)(mac_low32 >> 24); + dev->dev_addr[4] = (u8)(mac_high16); + dev->dev_addr[5] = (u8)(mac_high16 >> 8); +} + /* Initializing private device structures, only called from probe */ static int __devinit smsc911x_init(struct net_device *dev) { @@ -1829,6 +1844,12 @@ static int __devinit smsc911x_init(struct net_device *dev) SMSC_WARNING(PROBE, "This driver is not intended for this chip revision"); + /* workaround for platforms without an eeprom, where the mac address + * is stored elsewhere and set by the bootloader. This saves the + * mac address before resetting the device */ + if (pdata->config.flags & SMSC911X_SAVE_MAC_ADDRESS) + smsc911x_read_mac_address(dev); + /* Reset the LAN911x */ if (smsc911x_soft_reset(pdata)) return -ENODEV; @@ -2009,14 +2030,7 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) } else { /* Try reading mac address from device. if EEPROM is present * it will already have been set */ - u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH); - u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL); - dev->dev_addr[0] = (u8)(mac_low32); - dev->dev_addr[1] = (u8)(mac_low32 >> 8); - dev->dev_addr[2] = (u8)(mac_low32 >> 16); - dev->dev_addr[3] = (u8)(mac_low32 >> 24); - dev->dev_addr[4] = (u8)(mac_high16); - dev->dev_addr[5] = (u8)(mac_high16 >> 8); + smsc911x_read_mac_address(dev); if (is_valid_ether_addr(dev->dev_addr)) { /* eeprom values are valid so use them */ diff --git a/include/linux/smsc911x.h b/include/linux/smsc911x.h index 170c76b8f7a6..b32725075d71 100644 --- a/include/linux/smsc911x.h +++ b/include/linux/smsc911x.h @@ -45,5 +45,6 @@ struct smsc911x_platform_config { #define SMSC911X_USE_32BIT (BIT(1)) #define SMSC911X_FORCE_INTERNAL_PHY (BIT(2)) #define SMSC911X_FORCE_EXTERNAL_PHY (BIT(3)) +#define SMSC911X_SAVE_MAC_ADDRESS (BIT(4)) #endif /* __LINUX_SMSC911X_H__ */ -- cgit v1.2.3 From eefef1cf7653cd4e0aaf743c00ae8345086cdc01 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sun, 1 Feb 2009 01:04:33 -0800 Subject: net: add ARP notify option for devices This adds another inet device option to enable gratuitous ARP when device is brought up or address change. This is handy for clusters or virtualization. Signed-off-by: Stephen Hemminger Signed-off-by: Jeremy Fitzhardinge Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 6 ++++++ include/linux/inetdevice.h | 1 + include/linux/sysctl.h | 1 + kernel/sysctl_check.c | 1 + net/ipv4/devinet.c | 9 +++++++++ 5 files changed, 18 insertions(+) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index c7712787933c..ff3f219ee4d7 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -782,6 +782,12 @@ arp_ignore - INTEGER The max value from conf/{all,interface}/arp_ignore is used when ARP request is received on the {interface} +arp_notify - BOOLEAN + Define mode for notification of address and device changes. + 0 - (default): do nothing + 1 - Generate gratuitous arp replies when device is brought up + or hardware address changes. + arp_accept - BOOLEAN Define behavior when gratuitous arp replies are received: 0 - drop gratuitous arp frames diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 06fcdb45106b..acef2a770b6b 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -108,6 +108,7 @@ static inline void ipv4_devconf_setall(struct in_device *in_dev) #define IN_DEV_ARPFILTER(in_dev) IN_DEV_ORCONF((in_dev), ARPFILTER) #define IN_DEV_ARP_ANNOUNCE(in_dev) IN_DEV_MAXCONF((in_dev), ARP_ANNOUNCE) #define IN_DEV_ARP_IGNORE(in_dev) IN_DEV_MAXCONF((in_dev), ARP_IGNORE) +#define IN_DEV_ARP_NOTIFY(in_dev) IN_DEV_MAXCONF((in_dev), ARP_NOTIFY) struct in_ifaddr { diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 39d471d1163b..e76d3b22a466 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -490,6 +490,7 @@ enum NET_IPV4_CONF_ARP_IGNORE=19, NET_IPV4_CONF_PROMOTE_SECONDARIES=20, NET_IPV4_CONF_ARP_ACCEPT=21, + NET_IPV4_CONF_ARP_NOTIFY=22, __NET_IPV4_CONF_MAX }; diff --git a/kernel/sysctl_check.c b/kernel/sysctl_check.c index fafeb48f27c0..b38423ca711a 100644 --- a/kernel/sysctl_check.c +++ b/kernel/sysctl_check.c @@ -219,6 +219,7 @@ static const struct trans_ctl_table trans_net_ipv4_conf_vars_table[] = { { NET_IPV4_CONF_ARP_IGNORE, "arp_ignore" }, { NET_IPV4_CONF_PROMOTE_SECONDARIES, "promote_secondaries" }, { NET_IPV4_CONF_ARP_ACCEPT, "arp_accept" }, + { NET_IPV4_CONF_ARP_NOTIFY, "arp_notify" }, {} }; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 309997edc8a5..d519a6a66726 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1075,6 +1075,14 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, } } ip_mc_up(in_dev); + /* fall through */ + case NETDEV_CHANGEADDR: + if (IN_DEV_ARP_NOTIFY(in_dev)) + arp_send(ARPOP_REQUEST, ETH_P_ARP, + in_dev->ifa_list->ifa_address, + dev, + in_dev->ifa_list->ifa_address, + NULL, dev->dev_addr, NULL); break; case NETDEV_DOWN: ip_mc_down(in_dev); @@ -1439,6 +1447,7 @@ static struct devinet_sysctl_table { DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), + DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), -- cgit v1.2.3 From 1a5645bc901aea6f3f446888061b2b084bbf1ba6 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 2 Feb 2009 23:22:04 -0800 Subject: connector: create connector workqueue only while needed once The netlink connector uses its own workqueue to relay the datas sent from userspace to the appropriate callback. If you launch the test from Documentation/connector and change it a bit to send a high flow of data, you will see thousands of events coming to the "cqueue" workqueue by looking at the workqueue tracer. This flow of events can be sent very quickly. So, to not encumber the kevent workqueue and delay other jobs, the "cqueue" workqueue should remain. But this workqueue is pointless most of the time, it will always be created (assuming you have built it of course) although only developpers with specific needs will use it. So avoid this "most of the time useless task", this patch proposes to create this workqueue only when needed once. The first jobs to be sent to connector callbacks will be sent to kevent while the "cqueue" thread creation will be scheduled to kevent too. The following jobs will continue to be scheduled to keventd until the cqueue workqueue is created, and then the rest of the jobs will continue to perform as usual, through this dedicated workqueue. Each time I tested this patch, only the first event was sent to keventd, the rest has been sent to cqueue which have been created quickly. Also, this patch fixes some trailing whitespaces on the connector files. Signed-off-by: Frederic Weisbecker Acked-by: Evgeniy Polyakov Signed-off-by: David S. Miller --- drivers/connector/cn_queue.c | 80 +++++++++++++++++++++++++++++++++++++------ drivers/connector/connector.c | 19 +++++----- include/linux/connector.h | 8 +++++ 3 files changed, 87 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/connector/cn_queue.c b/drivers/connector/cn_queue.c index b6fe7e7a2c2f..c769ef269fb5 100644 --- a/drivers/connector/cn_queue.c +++ b/drivers/connector/cn_queue.c @@ -1,9 +1,9 @@ /* * cn_queue.c - * + * * 2004-2005 Copyright (c) Evgeniy Polyakov * All rights reserved. - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -31,6 +31,48 @@ #include #include + +/* + * This job is sent to the kevent workqueue. + * While no event is once sent to any callback, the connector workqueue + * is not created to avoid a useless waiting kernel task. + * Once the first event is received, we create this dedicated workqueue which + * is necessary because the flow of data can be high and we don't want + * to encumber keventd with that. + */ +static void cn_queue_create(struct work_struct *work) +{ + struct cn_queue_dev *dev; + + dev = container_of(work, struct cn_queue_dev, wq_creation); + + dev->cn_queue = create_singlethread_workqueue(dev->name); + /* If we fail, we will use keventd for all following connector jobs */ + WARN_ON(!dev->cn_queue); +} + +/* + * Queue a data sent to a callback. + * If the connector workqueue is already created, we queue the job on it. + * Otherwise, we queue the job to kevent and queue the connector workqueue + * creation too. + */ +int queue_cn_work(struct cn_callback_entry *cbq, struct work_struct *work) +{ + struct cn_queue_dev *pdev = cbq->pdev; + + if (likely(pdev->cn_queue)) + return queue_work(pdev->cn_queue, work); + + /* Don't create the connector workqueue twice */ + if (atomic_inc_return(&pdev->wq_requested) == 1) + schedule_work(&pdev->wq_creation); + else + atomic_dec(&pdev->wq_requested); + + return schedule_work(work); +} + void cn_queue_wrapper(struct work_struct *work) { struct cn_callback_entry *cbq = @@ -58,14 +100,17 @@ static struct cn_callback_entry *cn_queue_alloc_callback_entry(char *name, struc snprintf(cbq->id.name, sizeof(cbq->id.name), "%s", name); memcpy(&cbq->id.id, id, sizeof(struct cb_id)); cbq->data.callback = callback; - + INIT_WORK(&cbq->work, &cn_queue_wrapper); return cbq; } static void cn_queue_free_callback(struct cn_callback_entry *cbq) { - flush_workqueue(cbq->pdev->cn_queue); + /* The first jobs have been sent to kevent, flush them too */ + flush_scheduled_work(); + if (cbq->pdev->cn_queue) + flush_workqueue(cbq->pdev->cn_queue); kfree(cbq); } @@ -143,14 +188,11 @@ struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *nls) atomic_set(&dev->refcnt, 0); INIT_LIST_HEAD(&dev->queue_list); spin_lock_init(&dev->queue_lock); + init_waitqueue_head(&dev->wq_created); dev->nls = nls; - dev->cn_queue = create_singlethread_workqueue(dev->name); - if (!dev->cn_queue) { - kfree(dev); - return NULL; - } + INIT_WORK(&dev->wq_creation, cn_queue_create); return dev; } @@ -158,9 +200,25 @@ struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *nls) void cn_queue_free_dev(struct cn_queue_dev *dev) { struct cn_callback_entry *cbq, *n; + long timeout; + DEFINE_WAIT(wait); + + /* Flush the first pending jobs queued on kevent */ + flush_scheduled_work(); + + /* If the connector workqueue creation is still pending, wait for it */ + prepare_to_wait(&dev->wq_created, &wait, TASK_UNINTERRUPTIBLE); + if (atomic_read(&dev->wq_requested) && !dev->cn_queue) { + timeout = schedule_timeout(HZ * 2); + if (!timeout && !dev->cn_queue) + WARN_ON(1); + } + finish_wait(&dev->wq_created, &wait); - flush_workqueue(dev->cn_queue); - destroy_workqueue(dev->cn_queue); + if (dev->cn_queue) { + flush_workqueue(dev->cn_queue); + destroy_workqueue(dev->cn_queue); + } spin_lock_bh(&dev->queue_lock); list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry) diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index bf4830082a13..fd336c5a9057 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -1,9 +1,9 @@ /* * connector.c - * + * * 2004-2005 Copyright (c) Evgeniy Polyakov * All rights reserved. - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -145,14 +145,13 @@ static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), v __cbq->data.ddata = data; __cbq->data.destruct_data = destruct_data; - if (queue_work(dev->cbdev->cn_queue, - &__cbq->work)) + if (queue_cn_work(__cbq, &__cbq->work)) err = 0; else err = -EINVAL; } else { struct cn_callback_data *d; - + err = -ENOMEM; __new_cbq = kzalloc(sizeof(struct cn_callback_entry), GFP_ATOMIC); if (__new_cbq) { @@ -163,10 +162,12 @@ static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), v d->destruct_data = destruct_data; d->free = __new_cbq; + __new_cbq->pdev = __cbq->pdev; + INIT_WORK(&__new_cbq->work, &cn_queue_wrapper); - if (queue_work(dev->cbdev->cn_queue, + if (queue_cn_work(__new_cbq, &__new_cbq->work)) err = 0; else { @@ -237,7 +238,7 @@ static void cn_notify(struct cb_id *id, u32 notify_event) req = (struct cn_notify_req *)ctl->data; for (i = 0; i < ctl->idx_notify_num; ++i, ++req) { - if (id->idx >= req->first && + if (id->idx >= req->first && id->idx < req->first + req->range) { idx_found = 1; break; @@ -245,7 +246,7 @@ static void cn_notify(struct cb_id *id, u32 notify_event) } for (i = 0; i < ctl->val_notify_num; ++i, ++req) { - if (id->val >= req->first && + if (id->val >= req->first && id->val < req->first + req->range) { val_found = 1; break; @@ -459,7 +460,7 @@ static int __devinit cn_init(void) netlink_kernel_release(dev->nls); return -EINVAL; } - + cn_already_initialized = 1; err = cn_add_callback(&dev->id, "connector", &cn_callback); diff --git a/include/linux/connector.h b/include/linux/connector.h index 34f2789d9b9b..fc65d219d88c 100644 --- a/include/linux/connector.h +++ b/include/linux/connector.h @@ -109,6 +109,12 @@ struct cn_queue_dev { unsigned char name[CN_CBQ_NAMELEN]; struct workqueue_struct *cn_queue; + /* Sent to kevent to create cn_queue only when needed */ + struct work_struct wq_creation; + /* Tell if the wq_creation job is pending/completed */ + atomic_t wq_requested; + /* Wait for cn_queue to be created */ + wait_queue_head_t wq_created; struct list_head queue_list; spinlock_t queue_lock; @@ -164,6 +170,8 @@ int cn_netlink_send(struct cn_msg *, u32, gfp_t); int cn_queue_add_callback(struct cn_queue_dev *dev, char *name, struct cb_id *id, void (*callback)(void *)); void cn_queue_del_callback(struct cn_queue_dev *dev, struct cb_id *id); +int queue_cn_work(struct cn_callback_entry *cbq, struct work_struct *work); + struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *); void cn_queue_free_dev(struct cn_queue_dev *dev); -- cgit v1.2.3 From 2a41f71d3bd97dde3305b4e1c43ab0eca46e7c71 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 4 Feb 2009 09:02:34 +0000 Subject: virtio_net: Add a virtqueue for outbound control commands This will be used for RX mode, MAC filter table, VLAN filtering, etc... The control transaction consists of one or more "out" sg entries and one or more "in" sg entries. The first out entry contains a header defining the class and command. Additional out entries may provide data for the command. The last in entry provides a status response back from the command. Virtqueues typically run asynchronous, running a callback function when there's data in the channel. We can't readily make use of this in the command paths where we need to use this. Instead, we kick the virtqueue and spin. The kick causes an I/O write, triggering an immediate trap into the hypervisor. Signed-off-by: Alex Williamson Acked-by: Rusty Russell Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 68 ++++++++++++++++++++++++++++++++++++++++++++-- include/linux/virtio_net.h | 18 ++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index fe576e75a538..67bb583b7fc9 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -37,10 +37,12 @@ module_param(gso, bool, 0444); #define MAX_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN) #define GOOD_COPY_LEN 128 +#define VIRTNET_SEND_COMMAND_SG_MAX 0 + struct virtnet_info { struct virtio_device *vdev; - struct virtqueue *rvq, *svq; + struct virtqueue *rvq, *svq, *cvq; struct net_device *dev; struct napi_struct napi; unsigned int status; @@ -589,6 +591,53 @@ static int virtnet_open(struct net_device *dev) return 0; } +/* + * Send command via the control virtqueue and check status. Commands + * supported by the hypervisor, as indicated by feature bits, should + * never fail unless improperly formated. + */ +static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, + struct scatterlist *data, int out, int in) +{ + struct scatterlist sg[VIRTNET_SEND_COMMAND_SG_MAX + 2]; + struct virtio_net_ctrl_hdr ctrl; + virtio_net_ctrl_ack status = ~0; + unsigned int tmp; + + if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) { + BUG(); /* Caller should know better */ + return false; + } + + BUG_ON(out + in > VIRTNET_SEND_COMMAND_SG_MAX); + + out++; /* Add header */ + in++; /* Add return status */ + + ctrl.class = class; + ctrl.cmd = cmd; + + sg_init_table(sg, out + in); + + sg_set_buf(&sg[0], &ctrl, sizeof(ctrl)); + memcpy(&sg[1], data, sizeof(struct scatterlist) * (out + in - 2)); + sg_set_buf(&sg[out + in - 1], &status, sizeof(status)); + + if (vi->cvq->vq_ops->add_buf(vi->cvq, sg, out, in, vi) != 0) + BUG(); + + vi->cvq->vq_ops->kick(vi->cvq); + + /* + * Spin for a response, the kick causes an ioport write, trapping + * into the hypervisor, so the request should be handled immediately. + */ + while (!vi->cvq->vq_ops->get_buf(vi->cvq, &tmp)) + cpu_relax(); + + return status == VIRTIO_NET_OK; +} + static int virtnet_close(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); @@ -752,6 +801,14 @@ static int virtnet_probe(struct virtio_device *vdev) goto free_recv; } + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) { + vi->cvq = vdev->config->find_vq(vdev, 2, NULL); + if (IS_ERR(vi->cvq)) { + err = PTR_ERR(vi->svq); + goto free_send; + } + } + /* Initialize our empty receive and send queues. */ skb_queue_head_init(&vi->recv); skb_queue_head_init(&vi->send); @@ -764,7 +821,7 @@ static int virtnet_probe(struct virtio_device *vdev) err = register_netdev(dev); if (err) { pr_debug("virtio_net: registering device failed\n"); - goto free_send; + goto free_ctrl; } /* Last of all, set up some receive buffers. */ @@ -784,6 +841,9 @@ static int virtnet_probe(struct virtio_device *vdev) unregister: unregister_netdev(dev); +free_ctrl: + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) + vdev->config->del_vq(vi->cvq); free_send: vdev->config->del_vq(vi->svq); free_recv: @@ -815,6 +875,8 @@ static void virtnet_remove(struct virtio_device *vdev) vdev->config->del_vq(vi->svq); vdev->config->del_vq(vi->rvq); + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) + vdev->config->del_vq(vi->cvq); unregister_netdev(vi->dev); while (vi->pages) @@ -834,7 +896,7 @@ static unsigned int features[] = { VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_ECN, /* We don't yet handle UFO input. */ - VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, + VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, VIRTIO_F_NOTIFY_ON_EMPTY, }; diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index d8e362d52fd8..245eda829aa8 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -23,6 +23,7 @@ #define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ #define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ #define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ +#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ @@ -59,4 +60,21 @@ struct virtio_net_hdr_mrg_rxbuf { __u16 num_buffers; /* Number of merged rx buffers */ }; +/* + * Control virtqueue data structures + * + * The control virtqueue expects a header in the first sg entry + * and an ack/status response in the last entry. Data for the + * command goes in between. + */ +struct virtio_net_ctrl_hdr { + __u8 class; + __u8 cmd; +} __attribute__((packed)); + +typedef __u8 virtio_net_ctrl_ack; + +#define VIRTIO_NET_OK 0 +#define VIRTIO_NET_ERR 1 + #endif /* _LINUX_VIRTIO_NET_H */ -- cgit v1.2.3 From 2af7698e2dd698d452ab9d63a9ca5956bbe8fc3b Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 4 Feb 2009 09:02:40 +0000 Subject: virtio_net: Add a set_rx_mode interface Make use of the RX_MODE control virtqueue class to enable the set_rx_mode netdev interface. This allows us to selectively enable/disable promiscuous and allmulti mode so we don't see packets we don't want. For now, we automatically enable these as needed if additional unicast or multicast addresses are requested. Signed-off-by: Alex Williamson Acked-by: Rusty Russell Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 34 +++++++++++++++++++++++++++++++++- include/linux/virtio_net.h | 11 +++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 67bb583b7fc9..1abea9dc6f0f 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -37,7 +37,7 @@ module_param(gso, bool, 0444); #define MAX_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN) #define GOOD_COPY_LEN 128 -#define VIRTNET_SEND_COMMAND_SG_MAX 0 +#define VIRTNET_SEND_COMMAND_SG_MAX 1 struct virtnet_info { @@ -658,6 +658,36 @@ static int virtnet_set_tx_csum(struct net_device *dev, u32 data) return ethtool_op_set_tx_hw_csum(dev, data); } +static void virtnet_set_rx_mode(struct net_device *dev) +{ + struct virtnet_info *vi = netdev_priv(dev); + struct scatterlist sg; + u8 promisc, allmulti; + + /* We can't dynamicaly set ndo_set_rx_mode, so return gracefully */ + if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_RX)) + return; + + promisc = ((dev->flags & IFF_PROMISC) != 0 || dev->uc_count > 0); + allmulti = ((dev->flags & IFF_ALLMULTI) != 0 || dev->mc_count > 0); + + sg_set_buf(&sg, &promisc, sizeof(promisc)); + + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX, + VIRTIO_NET_CTRL_RX_PROMISC, + &sg, 1, 0)) + dev_warn(&dev->dev, "Failed to %sable promisc mode.\n", + promisc ? "en" : "dis"); + + sg_set_buf(&sg, &allmulti, sizeof(allmulti)); + + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX, + VIRTIO_NET_CTRL_RX_ALLMULTI, + &sg, 1, 0)) + dev_warn(&dev->dev, "Failed to %sable allmulti mode.\n", + allmulti ? "en" : "dis"); +} + static struct ethtool_ops virtnet_ethtool_ops = { .set_tx_csum = virtnet_set_tx_csum, .set_sg = ethtool_op_set_sg, @@ -682,6 +712,7 @@ static const struct net_device_ops virtnet_netdev = { .ndo_start_xmit = start_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, + .ndo_set_rx_mode = virtnet_set_rx_mode, .ndo_change_mtu = virtnet_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = virtnet_netpoll, @@ -897,6 +928,7 @@ static unsigned int features[] = { VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_ECN, /* We don't yet handle UFO input. */ VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, + VIRTIO_NET_F_CTRL_RX, VIRTIO_F_NOTIFY_ON_EMPTY, }; diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 245eda829aa8..63b2461a40f1 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -24,6 +24,7 @@ #define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ #define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ #define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ +#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ @@ -77,4 +78,14 @@ typedef __u8 virtio_net_ctrl_ack; #define VIRTIO_NET_OK 0 #define VIRTIO_NET_ERR 1 +/* + * Control the RX mode, ie. promisucous and allmulti. PROMISC and + * ALLMULTI commands require an "out" sg entry containing a 1 byte + * state value, zero = disable, non-zero = enable. These commands + * are supported with the VIRTIO_NET_F_CTRL_RX feature. + */ +#define VIRTIO_NET_CTRL_RX 0 + #define VIRTIO_NET_CTRL_RX_PROMISC 0 + #define VIRTIO_NET_CTRL_RX_ALLMULTI 1 + #endif /* _LINUX_VIRTIO_NET_H */ -- cgit v1.2.3 From f565a7c259d71cc186753653d978c646d2354b36 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 4 Feb 2009 09:02:45 +0000 Subject: virtio_net: Add a MAC filter table Make use of the MAC control virtqueue class to support a MAC filter table. The filter table is managed by the hypervisor. We consider the table to be available if the CTRL_RX feature bit is set. We leave it to the hypervisor to manage the table and enable promiscuous or all-multi mode as necessary depending on the resources available to it. Signed-off-by: Alex Williamson Acked-by: Rusty Russell Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 55 +++++++++++++++++++++++++++++++++++++++------- include/linux/virtio_net.h | 23 +++++++++++++++++++ 2 files changed, 70 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 1abea9dc6f0f..daab9c9b0a40 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -37,7 +37,7 @@ module_param(gso, bool, 0444); #define MAX_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN) #define GOOD_COPY_LEN 128 -#define VIRTNET_SEND_COMMAND_SG_MAX 1 +#define VIRTNET_SEND_COMMAND_SG_MAX 2 struct virtnet_info { @@ -661,31 +661,70 @@ static int virtnet_set_tx_csum(struct net_device *dev, u32 data) static void virtnet_set_rx_mode(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); - struct scatterlist sg; + struct scatterlist sg[2]; u8 promisc, allmulti; + struct virtio_net_ctrl_mac *mac_data; + struct dev_addr_list *addr; + void *buf; + int i; /* We can't dynamicaly set ndo_set_rx_mode, so return gracefully */ if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_RX)) return; - promisc = ((dev->flags & IFF_PROMISC) != 0 || dev->uc_count > 0); - allmulti = ((dev->flags & IFF_ALLMULTI) != 0 || dev->mc_count > 0); + promisc = ((dev->flags & IFF_PROMISC) != 0); + allmulti = ((dev->flags & IFF_ALLMULTI) != 0); - sg_set_buf(&sg, &promisc, sizeof(promisc)); + sg_set_buf(sg, &promisc, sizeof(promisc)); if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_PROMISC, - &sg, 1, 0)) + sg, 1, 0)) dev_warn(&dev->dev, "Failed to %sable promisc mode.\n", promisc ? "en" : "dis"); - sg_set_buf(&sg, &allmulti, sizeof(allmulti)); + sg_set_buf(sg, &allmulti, sizeof(allmulti)); if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, - &sg, 1, 0)) + sg, 1, 0)) dev_warn(&dev->dev, "Failed to %sable allmulti mode.\n", allmulti ? "en" : "dis"); + + /* MAC filter - use one buffer for both lists */ + mac_data = buf = kzalloc(((dev->uc_count + dev->mc_count) * ETH_ALEN) + + (2 * sizeof(mac_data->entries)), GFP_ATOMIC); + if (!buf) { + dev_warn(&dev->dev, "No memory for MAC address buffer\n"); + return; + } + + /* Store the unicast list and count in the front of the buffer */ + mac_data->entries = dev->uc_count; + addr = dev->uc_list; + for (i = 0; i < dev->uc_count; i++, addr = addr->next) + memcpy(&mac_data->macs[i][0], addr->da_addr, ETH_ALEN); + + sg_set_buf(&sg[0], mac_data, + sizeof(mac_data->entries) + (dev->uc_count * ETH_ALEN)); + + /* multicast list and count fill the end */ + mac_data = (void *)&mac_data->macs[dev->uc_count][0]; + + mac_data->entries = dev->mc_count; + addr = dev->mc_list; + for (i = 0; i < dev->mc_count; i++, addr = addr->next) + memcpy(&mac_data->macs[i][0], addr->da_addr, ETH_ALEN); + + sg_set_buf(&sg[1], mac_data, + sizeof(mac_data->entries) + (dev->mc_count * ETH_ALEN)); + + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MAC, + VIRTIO_NET_CTRL_MAC_TABLE_SET, + sg, 2, 0)) + dev_warn(&dev->dev, "Failed to set MAC fitler table.\n"); + + kfree(buf); } static struct ethtool_ops virtnet_ethtool_ops = { diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 63b2461a40f1..ba82b653cace 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -88,4 +88,27 @@ typedef __u8 virtio_net_ctrl_ack; #define VIRTIO_NET_CTRL_RX_PROMISC 0 #define VIRTIO_NET_CTRL_RX_ALLMULTI 1 +/* + * Control the MAC filter table. + * + * The MAC filter table is managed by the hypervisor, the guest should + * assume the size is infinite. Filtering should be considered + * non-perfect, ie. based on hypervisor resources, the guest may + * received packets from sources not specified in the filter list. + * + * In addition to the class/cmd header, the TABLE_SET command requires + * two out scatterlists. Each contains a 4 byte count of entries followed + * by a concatenated byte stream of the ETH_ALEN MAC addresses. The + * first sg list contains unicast addresses, the second is for multicast. + * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature + * is available. + */ +struct virtio_net_ctrl_mac { + __u32 entries; + __u8 macs[][ETH_ALEN]; +} __attribute__((packed)); + +#define VIRTIO_NET_CTRL_MAC 1 + #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 + #endif /* _LINUX_VIRTIO_NET_H */ -- cgit v1.2.3 From 0bde95690d65653e420d04856c5d5783155c747c Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 4 Feb 2009 09:02:50 +0000 Subject: virtio_net: Add support for VLAN filtering in the hypervisor VLAN filtering allows the hypervisor to drop packets from VLANs that we're not a part of, further reducing the number of extraneous packets recieved. This makes use of the VLAN virtqueue command class. The CTRL_VLAN feature bit tells us whether the backend supports VLAN filtering. Signed-off-by: Alex Williamson Acked-by: Rusty Russell Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 31 ++++++++++++++++++++++++++++++- include/linux/virtio_net.h | 14 ++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index daab9c9b0a40..e68813a246db 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -727,6 +727,30 @@ static void virtnet_set_rx_mode(struct net_device *dev) kfree(buf); } +static void virnet_vlan_rx_add_vid(struct net_device *dev, u16 vid) +{ + struct virtnet_info *vi = netdev_priv(dev); + struct scatterlist sg; + + sg_set_buf(&sg, &vid, sizeof(vid)); + + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN, + VIRTIO_NET_CTRL_VLAN_ADD, &sg, 1, 0)) + dev_warn(&dev->dev, "Failed to add VLAN ID %d.\n", vid); +} + +static void virnet_vlan_rx_kill_vid(struct net_device *dev, u16 vid) +{ + struct virtnet_info *vi = netdev_priv(dev); + struct scatterlist sg; + + sg_set_buf(&sg, &vid, sizeof(vid)); + + if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN, + VIRTIO_NET_CTRL_VLAN_DEL, &sg, 1, 0)) + dev_warn(&dev->dev, "Failed to kill VLAN ID %d.\n", vid); +} + static struct ethtool_ops virtnet_ethtool_ops = { .set_tx_csum = virtnet_set_tx_csum, .set_sg = ethtool_op_set_sg, @@ -753,6 +777,8 @@ static const struct net_device_ops virtnet_netdev = { .ndo_set_mac_address = eth_mac_addr, .ndo_set_rx_mode = virtnet_set_rx_mode, .ndo_change_mtu = virtnet_change_mtu, + .ndo_vlan_rx_add_vid = virnet_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = virnet_vlan_rx_kill_vid, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = virtnet_netpoll, #endif @@ -877,6 +903,9 @@ static int virtnet_probe(struct virtio_device *vdev) err = PTR_ERR(vi->svq); goto free_send; } + + if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VLAN)) + dev->features |= NETIF_F_HW_VLAN_FILTER; } /* Initialize our empty receive and send queues. */ @@ -967,7 +996,7 @@ static unsigned int features[] = { VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_ECN, /* We don't yet handle UFO input. */ VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, - VIRTIO_NET_F_CTRL_RX, + VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, VIRTIO_F_NOTIFY_ON_EMPTY, }; diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index ba82b653cace..242348bb3766 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -25,6 +25,7 @@ #define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ #define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ #define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ +#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ #define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ @@ -111,4 +112,17 @@ struct virtio_net_ctrl_mac { #define VIRTIO_NET_CTRL_MAC 1 #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 +/* + * Control VLAN filtering + * + * The VLAN filter table is controlled via a simple ADD/DEL interface. + * VLAN IDs not added may be filterd by the hypervisor. Del is the + * opposite of add. Both commands expect an out entry containing a 2 + * byte VLAN ID. VLAN filterting is available with the + * VIRTIO_NET_F_CTRL_VLAN feature bit. + */ +#define VIRTIO_NET_CTRL_VLAN 2 + #define VIRTIO_NET_CTRL_VLAN_ADD 0 + #define VIRTIO_NET_CTRL_VLAN_DEL 1 + #endif /* _LINUX_VIRTIO_NET_H */ -- cgit v1.2.3 From 33dccbb050bbe35b88ca8cf1228dcf3e4d4b3554 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 5 Feb 2009 21:25:32 -0800 Subject: tun: Limit amount of queued packets per device Unlike a normal socket path, the tuntap device send path does not have any accounting. This means that the user-space sender may be able to pin down arbitrary amounts of kernel memory by continuing to send data to an end-point that is congested. Even when this isn't an issue because of limited queueing at most end points, this can also be a problem because its only response to congestion is packet loss. That is, when those local queues at the end-point fills up, the tuntap device will start wasting system time because it will continue to send data there which simply gets dropped straight away. Of course one could argue that everybody should do congestion control end-to-end, unfortunately there are people in this world still hooked on UDP, and they don't appear to be going away anywhere fast. In fact, we've always helped them by performing accounting in our UDP code, the sole purpose of which is to provide congestion feedback other than through packet loss. This patch attempts to apply the same bandaid to the tuntap device. It creates a pseudo-socket object which is used to account our packets just as a normal socket does for UDP. Of course things are a little complex because we're actually reinjecting traffic back into the stack rather than out of the stack. The stack complexities however should have been resolved by preceding patches. So this one can simply start using skb_set_owner_w. For now the accounting is essentially disabled by default for backwards compatibility. In particular, we set the cap to INT_MAX. This is so that existing applications don't get confused by the sudden arrival EAGAIN errors. In future we may wish (or be forced to) do this by default. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- drivers/net/tun.c | 167 +++++++++++++++++++++++++++++++++---------------- fs/compat_ioctl.c | 2 + include/linux/if_tun.h | 2 + 3 files changed, 118 insertions(+), 53 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 15d67635bb10..0476549841ac 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -95,6 +96,8 @@ struct tun_file { wait_queue_head_t read_wait; }; +struct tun_sock; + struct tun_struct { struct tun_file *tfile; unsigned int flags; @@ -107,12 +110,24 @@ struct tun_struct { struct fasync_struct *fasync; struct tap_filter txflt; + struct sock *sk; + struct socket socket; #ifdef TUN_DEBUG int debug; #endif }; +struct tun_sock { + struct sock sk; + struct tun_struct *tun; +}; + +static inline struct tun_sock *tun_sk(struct sock *sk) +{ + return container_of(sk, struct tun_sock, sk); +} + static int tun_attach(struct tun_struct *tun, struct file *file) { struct tun_file *tfile = file->private_data; @@ -461,7 +476,8 @@ static unsigned int tun_chr_poll(struct file *file, poll_table * wait) { struct tun_file *tfile = file->private_data; struct tun_struct *tun = __tun_get(tfile); - unsigned int mask = POLLOUT | POLLWRNORM; + struct sock *sk = tun->sk; + unsigned int mask = 0; if (!tun) return POLLERR; @@ -473,6 +489,11 @@ static unsigned int tun_chr_poll(struct file *file, poll_table * wait) if (!skb_queue_empty(&tun->readq)) mask |= POLLIN | POLLRDNORM; + if (sock_writeable(sk) || + (!test_and_set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags) && + sock_writeable(sk))) + mask |= POLLOUT | POLLWRNORM; + if (tun->dev->reg_state != NETREG_REGISTERED) mask = POLLERR; @@ -482,66 +503,35 @@ static unsigned int tun_chr_poll(struct file *file, poll_table * wait) /* prepad is the amount to reserve at front. len is length after that. * linear is a hint as to how much to copy (usually headers). */ -static struct sk_buff *tun_alloc_skb(size_t prepad, size_t len, size_t linear, - gfp_t gfp) +static inline struct sk_buff *tun_alloc_skb(struct tun_struct *tun, + size_t prepad, size_t len, + size_t linear, int noblock) { + struct sock *sk = tun->sk; struct sk_buff *skb; - unsigned int i; - - skb = alloc_skb(prepad + len, gfp|__GFP_NOWARN); - if (skb) { - skb_reserve(skb, prepad); - skb_put(skb, len); - return skb; - } + int err; /* Under a page? Don't bother with paged skb. */ if (prepad + len < PAGE_SIZE) - return NULL; + linear = len; - /* Start with a normal skb, and add pages. */ - skb = alloc_skb(prepad + linear, gfp); + skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock, + &err); if (!skb) - return NULL; + return ERR_PTR(err); skb_reserve(skb, prepad); skb_put(skb, linear); - - len -= linear; - - for (i = 0; i < MAX_SKB_FRAGS; i++) { - skb_frag_t *f = &skb_shinfo(skb)->frags[i]; - - f->page = alloc_page(gfp|__GFP_ZERO); - if (!f->page) - break; - - f->page_offset = 0; - f->size = PAGE_SIZE; - - skb->data_len += PAGE_SIZE; - skb->len += PAGE_SIZE; - skb->truesize += PAGE_SIZE; - skb_shinfo(skb)->nr_frags++; - - if (len < PAGE_SIZE) { - len = 0; - break; - } - len -= PAGE_SIZE; - } - - /* Too large, or alloc fail? */ - if (unlikely(len)) { - kfree_skb(skb); - skb = NULL; - } + skb->data_len = len - linear; + skb->len += len - linear; return skb; } /* Get packet from user space buffer */ -static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, size_t count) +static __inline__ ssize_t tun_get_user(struct tun_struct *tun, + struct iovec *iv, size_t count, + int noblock) { struct tun_pi pi = { 0, cpu_to_be16(ETH_P_IP) }; struct sk_buff *skb; @@ -573,9 +563,11 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, return -EINVAL; } - if (!(skb = tun_alloc_skb(align, len, gso.hdr_len, GFP_KERNEL))) { - tun->dev->stats.rx_dropped++; - return -ENOMEM; + skb = tun_alloc_skb(tun, align, len, gso.hdr_len, noblock); + if (IS_ERR(skb)) { + if (PTR_ERR(skb) != -EAGAIN) + tun->dev->stats.rx_dropped++; + return PTR_ERR(skb); } if (skb_copy_datagram_from_iovec(skb, 0, iv, len)) { @@ -661,7 +653,8 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, static ssize_t tun_chr_aio_write(struct kiocb *iocb, const struct iovec *iv, unsigned long count, loff_t pos) { - struct tun_struct *tun = tun_get(iocb->ki_filp); + struct file *file = iocb->ki_filp; + struct tun_struct *tun = file->private_data; ssize_t result; if (!tun) @@ -669,7 +662,8 @@ static ssize_t tun_chr_aio_write(struct kiocb *iocb, const struct iovec *iv, DBG(KERN_INFO "%s: tun_chr_write %ld\n", tun->dev->name, count); - result = tun_get_user(tun, (struct iovec *) iv, iov_length(iv, count)); + result = tun_get_user(tun, (struct iovec *)iv, iov_length(iv, count), + file->f_flags & O_NONBLOCK); tun_put(tun); return result; @@ -828,11 +822,40 @@ static struct rtnl_link_ops tun_link_ops __read_mostly = { .validate = tun_validate, }; +static void tun_sock_write_space(struct sock *sk) +{ + struct tun_struct *tun; + + if (!sock_writeable(sk)) + return; + + if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) + wake_up_interruptible_sync(sk->sk_sleep); + + if (!test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags)) + return; + + tun = container_of(sk, struct tun_sock, sk)->tun; + kill_fasync(&tun->fasync, SIGIO, POLL_OUT); +} + +static void tun_sock_destruct(struct sock *sk) +{ + dev_put(container_of(sk, struct tun_sock, sk)->tun->dev); +} + +static struct proto tun_proto = { + .name = "tun", + .owner = THIS_MODULE, + .obj_size = sizeof(struct tun_sock), +}; static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) { + struct sock *sk; struct tun_struct *tun; struct net_device *dev; + struct tun_file *tfile = file->private_data; int err; dev = __dev_get_by_name(net, ifr->ifr_name); @@ -885,14 +908,31 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) tun->flags = flags; tun->txflt.count = 0; + err = -ENOMEM; + sk = sk_alloc(net, AF_UNSPEC, GFP_KERNEL, &tun_proto); + if (!sk) + goto err_free_dev; + + /* This ref count is for tun->sk. */ + dev_hold(dev); + sock_init_data(&tun->socket, sk); + sk->sk_write_space = tun_sock_write_space; + sk->sk_destruct = tun_sock_destruct; + sk->sk_sndbuf = INT_MAX; + sk->sk_sleep = &tfile->read_wait; + + tun->sk = sk; + container_of(sk, struct tun_sock, sk)->tun = tun; + tun_net_init(dev); if (strchr(dev->name, '%')) { err = dev_alloc_name(dev, dev->name); if (err < 0) - goto err_free_dev; + goto err_free_sk; } + err = -EINVAL; err = register_netdevice(tun->dev); if (err < 0) goto err_free_dev; @@ -928,6 +968,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) strcpy(ifr->ifr_name, tun->dev->name); return 0; + err_free_sk: + sock_put(sk); err_free_dev: free_netdev(dev); failed: @@ -1012,6 +1054,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, struct tun_struct *tun; void __user* argp = (void __user*)arg; struct ifreq ifr; + int sndbuf; int ret; if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) @@ -1151,6 +1194,22 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr); rtnl_unlock(); break; + + case TUNGETSNDBUF: + sndbuf = tun->sk->sk_sndbuf; + if (copy_to_user(argp, &sndbuf, sizeof(sndbuf))) + ret = -EFAULT; + break; + + case TUNSETSNDBUF: + if (copy_from_user(&sndbuf, argp, sizeof(sndbuf))) { + ret = -EFAULT; + break; + } + + tun->sk->sk_sndbuf = sndbuf; + break; + default: ret = -EINVAL; break; @@ -1218,8 +1277,10 @@ static int tun_chr_close(struct inode *inode, struct file *file) __tun_detach(tun); /* If desireable, unregister the netdevice. */ - if (!(tun->flags & TUN_PERSIST)) + if (!(tun->flags & TUN_PERSIST)) { + sock_put(tun->sk); unregister_netdevice(tun->dev); + } rtnl_unlock(); } diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index c8f8d5904f5e..c03c10d7fb6b 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -1988,6 +1988,8 @@ COMPATIBLE_IOCTL(TUNSETGROUP) COMPATIBLE_IOCTL(TUNGETFEATURES) COMPATIBLE_IOCTL(TUNSETOFFLOAD) COMPATIBLE_IOCTL(TUNSETTXFILTER) +COMPATIBLE_IOCTL(TUNGETSNDBUF) +COMPATIBLE_IOCTL(TUNSETSNDBUF) /* Big V */ COMPATIBLE_IOCTL(VT_SETMODE) COMPATIBLE_IOCTL(VT_GETMODE) diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 8529f57ba263..049d6c9428db 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -46,6 +46,8 @@ #define TUNSETOFFLOAD _IOW('T', 208, unsigned int) #define TUNSETTXFILTER _IOW('T', 209, unsigned int) #define TUNGETIFF _IOR('T', 210, unsigned int) +#define TUNGETSNDBUF _IOR('T', 211, int) +#define TUNSETSNDBUF _IOW('T', 212, int) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 -- cgit v1.2.3 From fe2918b098cdbf55b69ba8762bd3de0ae64f33ff Mon Sep 17 00:00:00 2001 From: Graf Yang Date: Thu, 5 Feb 2009 21:26:19 -0800 Subject: net: fix some trailing whitespaces Signed-off-by: Graf Yang Signed-off-by: Bryan Wu Signed-off-by: David S. Miller --- include/linux/if_ether.h | 8 ++++---- include/linux/netdevice.h | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index 7f3c735f422b..0216e1bdbc56 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -17,7 +17,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ - + #ifndef _LINUX_IF_ETHER_H #define _LINUX_IF_ETHER_H @@ -25,7 +25,7 @@ /* * IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble - * and FCS/CRC (frame check sequence). + * and FCS/CRC (frame check sequence). */ #define ETH_ALEN 6 /* Octets in one ethernet addr */ @@ -83,7 +83,7 @@ /* * Non DIX types. Won't clash for 1500 types. */ - + #define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */ #define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */ #define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */ @@ -109,7 +109,7 @@ /* * This is an Ethernet frame header. */ - + struct ethhdr { unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7a5057fbb7cd..864519e585fc 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -96,7 +96,7 @@ struct wireless_dev; * Compute the worst case header length according to the protocols * used. */ - + #if defined(CONFIG_WLAN_80211) || defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) # if defined(CONFIG_MAC80211_MESH) # define LL_MAX_HEADER 128 @@ -124,7 +124,7 @@ struct wireless_dev; * Network device statistics. Akin to the 2.0 ether stats but * with byte counters. */ - + struct net_device_stats { unsigned long rx_packets; /* total packets received */ @@ -285,7 +285,7 @@ enum netdev_state_t /* * This structure holds at boot time configured netdevice settings. They - * are then used in the device probing. + * are then used in the device probing. */ struct netdev_boot_setup { char name[IFNAMSIZ]; @@ -740,7 +740,7 @@ struct net_device void *dsa_ptr; /* dsa specific data */ #endif void *atalk_ptr; /* AppleTalk link */ - void *ip_ptr; /* IPv4 specific data */ + void *ip_ptr; /* IPv4 specific data */ void *dn_ptr; /* DECnet specific data */ void *ip6_ptr; /* IPv6 specific data */ void *ec_ptr; /* Econet specific data */ @@ -753,7 +753,7 @@ struct net_device */ unsigned long last_rx; /* Time of last Rx */ /* Interface address info used in eth_type_trans() */ - unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address, (before bcast + unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address, (before bcast because most packets are unicast) */ unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ -- cgit v1.2.3 From d6301d3dd1c287b32132dda15272a50c11e92a14 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 8 Feb 2009 19:24:13 -0800 Subject: net: Increase default NET_SKB_PAD to 32. Several devices need to insert some "pre headers" in front of the main packet data when they transmit a packet. Currently we allocate only 16 bytes of pad room and this ends up not being enough for some types of hardware (NIU, usb-net, s390 qeth, etc.) So increase this to 32. Note that drivers still need to check in their transmit routine whether enough headroom exists, and if not use skb_realloc_headroom(). Tunneling, IPSEC, and other encapsulation methods can cause the padding area to be used up. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 08670d017479..5eba4007e07f 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1287,7 +1287,7 @@ static inline int skb_network_offset(const struct sk_buff *skb) * The networking layer reserves some headroom in skb data (via * dev_alloc_skb). This is used to avoid having to reallocate skb data when * the header has to grow. In the default case, if the header has to grow - * 16 bytes or less we avoid the reallocation. + * 32 bytes or less we avoid the reallocation. * * Unfortunately this headroom changes the DMA alignment of the resulting * network packet. As for NET_IP_ALIGN, this unaligned DMA is expensive @@ -1295,11 +1295,11 @@ static inline int skb_network_offset(const struct sk_buff *skb) * perhaps setting it to a cacheline in size (since that will maintain * cacheline alignment of the DMA). It must be a power of 2. * - * Various parts of the networking layer expect at least 16 bytes of + * Various parts of the networking layer expect at least 32 bytes of * headroom, you should not reduce this. */ #ifndef NET_SKB_PAD -#define NET_SKB_PAD 16 +#define NET_SKB_PAD 32 #endif extern int ___pskb_trim(struct sk_buff *skb, unsigned int len); -- cgit v1.2.3 From 4ae5544f9a33e4ae306e337f96951eb3ff2df6d9 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 8 Feb 2009 18:00:36 +0000 Subject: gro: Remember number of held packets instead of counting every time This patch prepares for the move of the same_flow checks out of dev_gro_receive. As such we need to remember the number of held packets since doing a loop just to count them every time is silly. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +++ net/core/dev.c | 12 +++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 864519e585fc..9ee344bc6c13 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -314,6 +314,9 @@ struct napi_struct { spinlock_t poll_lock; int poll_owner; #endif + + unsigned int gro_count; + struct net_device *dev; struct list_head dev_list; struct sk_buff *gro_list; diff --git a/net/core/dev.c b/net/core/dev.c index 709a9a922258..ae0b66936abe 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2372,6 +2372,7 @@ void napi_gro_flush(struct napi_struct *napi) napi_gro_complete(skb); } + napi->gro_count = 0; napi->gro_list = NULL; } EXPORT_SYMBOL(napi_gro_flush); @@ -2402,7 +2403,6 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) struct packet_type *ptype; __be16 type = skb->protocol; struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK]; - int count = 0; int same_flow; int mac_len; int ret; @@ -2430,8 +2430,6 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) NAPI_GRO_CB(skb)->free = 0; for (p = napi->gro_list; p; p = p->next) { - count++; - if (!NAPI_GRO_CB(p)->same_flow) continue; @@ -2457,15 +2455,16 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) *pp = nskb->next; nskb->next = NULL; napi_gro_complete(nskb); - count--; + napi->gro_count--; } if (same_flow) goto ok; - if (NAPI_GRO_CB(skb)->flush || count >= MAX_GRO_SKBS) + if (NAPI_GRO_CB(skb)->flush || napi->gro_count >= MAX_GRO_SKBS) goto normal; + napi->gro_count++; NAPI_GRO_CB(skb)->count = 1; skb_shinfo(skb)->gso_size = skb_gro_len(skb); skb->next = napi->gro_list; @@ -2713,6 +2712,7 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight) { INIT_LIST_HEAD(&napi->poll_list); + napi->gro_count = 0; napi->gro_list = NULL; napi->skb = NULL; napi->poll = poll; @@ -2741,6 +2741,7 @@ void netif_napi_del(struct napi_struct *napi) } napi->gro_list = NULL; + napi->gro_count = 0; } EXPORT_SYMBOL(netif_napi_del); @@ -5246,6 +5247,7 @@ static int __init net_dev_init(void) queue->backlog.poll = process_backlog; queue->backlog.weight = weight_p; queue->backlog.gro_list = NULL; + queue->backlog.gro_count = 0; } dev_boot_phase = 0; -- cgit v1.2.3 From aa4b9f533ed5a22952e038b9fac2447ccc682124 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 8 Feb 2009 18:00:37 +0000 Subject: gro: Optimise Ethernet header comparison This patch optimises the Ethernet header comparison to use 2-byte and 4-byte xors instead of memcmp. In order to facilitate this, the actual comparison is now carried out by the callers of the shared dev_gro_receive function. This has a significant impact when receiving 1500B packets through 10GbE. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/etherdevice.h | 21 +++++++++++++++++++++ include/linux/netdevice.h | 7 +++++++ net/8021q/vlan_core.c | 4 +++- net/core/dev.c | 23 ++--------------------- 4 files changed, 33 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 1cb0f0b90926..a1f17abba7dc 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -184,4 +184,25 @@ static inline unsigned compare_ether_addr_64bits(const u8 addr1[6+2], } #endif /* __KERNEL__ */ +/** + * compare_ether_header - Compare two Ethernet headers + * @a: Pointer to Ethernet header + * @b: Pointer to Ethernet header + * + * Compare two ethernet headers, returns 0 if equal. + * This assumes that the network header (i.e., IP header) is 4-byte + * aligned OR the platform can handle unaligned access. This is the + * case for all packets coming into netif_receive_skb or similar + * entry points. + */ + +static inline int compare_ether_header(const void *a, const void *b) +{ + u32 *a32 = (u32 *)((u8 *)a + 2); + u32 *b32 = (u32 *)((u8 *)b + 2); + + return (*(u16 *)a ^ *(u16 *)b) | (a32[0] ^ b32[0]) | + (a32[1] ^ b32[1]) | (a32[2] ^ b32[2]); +} + #endif /* _LINUX_ETHERDEVICE_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9ee344bc6c13..355662aac940 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1117,6 +1117,13 @@ static inline void skb_gro_reset_offset(struct sk_buff *skb) NAPI_GRO_CB(skb)->data_offset = 0; } +static inline void *skb_gro_mac_header(struct sk_buff *skb) +{ + return skb_mac_header(skb) < skb->data ? skb_mac_header(skb) : + page_address(skb_shinfo(skb)->frags[0].page) + + skb_shinfo(skb)->frags[0].page_offset; +} + static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, const void *daddr, const void *saddr, diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 378fa69d625a..70435af153f2 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -85,7 +85,9 @@ static int vlan_gro_common(struct napi_struct *napi, struct vlan_group *grp, goto drop; for (p = napi->gro_list; p; p = p->next) { - NAPI_GRO_CB(p)->same_flow = p->dev == skb->dev; + NAPI_GRO_CB(p)->same_flow = + p->dev == skb->dev && !compare_ether_header( + skb_mac_header(p), skb_gro_mac_header(skb)); NAPI_GRO_CB(p)->flush = 0; } diff --git a/net/core/dev.c b/net/core/dev.c index ae0b66936abe..1e27a67df242 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -215,13 +215,6 @@ static inline struct hlist_head *dev_index_hash(struct net *net, int ifindex) return &net->dev_index_head[ifindex & ((1 << NETDEV_HASHBITS) - 1)]; } -static inline void *skb_gro_mac_header(struct sk_buff *skb) -{ - return skb_mac_header(skb) < skb->data ? skb_mac_header(skb) : - page_address(skb_shinfo(skb)->frags[0].page) + - skb_shinfo(skb)->frags[0].page_offset; -} - /* Device list insertion */ static int list_netdevice(struct net_device *dev) { @@ -2415,29 +2408,16 @@ int dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) rcu_read_lock(); list_for_each_entry_rcu(ptype, head, list) { - struct sk_buff *p; - void *mac; - if (ptype->type != type || ptype->dev || !ptype->gro_receive) continue; skb_set_network_header(skb, skb_gro_offset(skb)); - mac = skb_gro_mac_header(skb); mac_len = skb->network_header - skb->mac_header; skb->mac_len = mac_len; NAPI_GRO_CB(skb)->same_flow = 0; NAPI_GRO_CB(skb)->flush = 0; NAPI_GRO_CB(skb)->free = 0; - for (p = napi->gro_list; p; p = p->next) { - if (!NAPI_GRO_CB(p)->same_flow) - continue; - - if (p->mac_len != mac_len || - memcmp(skb_mac_header(p), mac, mac_len)) - NAPI_GRO_CB(p)->same_flow = 0; - } - pp = ptype->gro_receive(&napi->gro_list, skb); break; } @@ -2492,7 +2472,8 @@ static int __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) struct sk_buff *p; for (p = napi->gro_list; p; p = p->next) { - NAPI_GRO_CB(p)->same_flow = 1; + NAPI_GRO_CB(p)->same_flow = !compare_ether_header( + skb_mac_header(p), skb_gro_mac_header(skb)); NAPI_GRO_CB(p)->flush = 0; } -- cgit v1.2.3 From f130347c2dd8e7ce0757cd3cf80bedbc6ed63c4c Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 30 Jan 2009 09:26:42 -0800 Subject: cfg80211: add get reg command This lets userspace request to get the currently set regulatory domain. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/linux/nl80211.h | 4 +++ net/wireless/nl80211.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/reg.c | 2 +- net/wireless/reg.h | 2 ++ 4 files changed, 88 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 76aae3d8e97e..4bc27049f4e5 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -113,6 +113,8 @@ * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by * %NL80211_ATTR_IFINDEX. * + * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set + * regulatory domain. * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command * after being queried by the kernel. CRDA replies by sending a regulatory * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our @@ -188,6 +190,8 @@ enum nl80211_commands { NL80211_CMD_SET_MGMT_EXTRA_IE, + NL80211_CMD_GET_REG, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e69da8d20474..d452396006ee 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2093,6 +2093,81 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) #undef FILL_IN_MESH_PARAM_IF_SET +static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + void *hdr = NULL; + struct nlattr *nl_reg_rules; + unsigned int i; + int err = -EINVAL; + + mutex_lock(&cfg80211_drv_mutex); + + if (!cfg80211_regdomain) + goto out; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) { + err = -ENOBUFS; + goto out; + } + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_GET_REG); + if (!hdr) + goto nla_put_failure; + + NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, + cfg80211_regdomain->alpha2); + + nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); + if (!nl_reg_rules) + goto nla_put_failure; + + for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { + struct nlattr *nl_reg_rule; + const struct ieee80211_reg_rule *reg_rule; + const struct ieee80211_freq_range *freq_range; + const struct ieee80211_power_rule *power_rule; + + reg_rule = &cfg80211_regdomain->reg_rules[i]; + freq_range = ®_rule->freq_range; + power_rule = ®_rule->power_rule; + + nl_reg_rule = nla_nest_start(msg, i); + if (!nl_reg_rule) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS, + reg_rule->flags); + NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START, + freq_range->start_freq_khz); + NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END, + freq_range->end_freq_khz); + NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW, + freq_range->max_bandwidth_khz); + NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, + power_rule->max_antenna_gain); + NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, + power_rule->max_eirp); + + nla_nest_end(msg, nl_reg_rule); + } + + nla_nest_end(msg, nl_reg_rules); + + genlmsg_end(msg, hdr); + err = genlmsg_unicast(msg, info->snd_pid); + goto out; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + err = -EMSGSIZE; +out: + mutex_unlock(&cfg80211_drv_mutex); + return err; +} + static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) { struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; @@ -2332,6 +2407,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_GET_REG, + .doit = nl80211_get_reg, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + }, { .cmd = NL80211_CMD_SET_REG, .doit = nl80211_set_reg, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index f643d3981102..2323644330cd 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -57,7 +57,7 @@ static u32 supported_bandwidths[] = { /* Central wireless core regulatory domains, we only need two, * the current one and a world regulatory domain in case we have no * information to give us an alpha2 */ -static const struct ieee80211_regdomain *cfg80211_regdomain; +const struct ieee80211_regdomain *cfg80211_regdomain; /* We use this as a place for the rd structure built from the * last parsed country IE to rest until CRDA gets back to us with diff --git a/net/wireless/reg.h b/net/wireless/reg.h index eb1dd5bc9b27..fe8c83f34fb7 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -1,6 +1,8 @@ #ifndef __NET_WIRELESS_REG_H #define __NET_WIRELESS_REG_H +extern const struct ieee80211_regdomain *cfg80211_regdomain; + bool is_world_regdom(const char *alpha2); bool reg_is_valid_request(const char *alpha2); -- cgit v1.2.3 From 0c2bec96945ccfc4a58a88d73531e392972ba6c5 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 3 Feb 2009 09:04:20 +0200 Subject: libertas: if_spi: add ability to call board specific setup/teardown methods In certain cases it is required to perform board specific actions before activating libertas G-SPI interface. These actions may include power up of the chip, GPIOs setup, proper pin-strapping and SPI controller config. This patch adds ability to call board specific setup/teardown methods Signed-off-by: Mike Rapoport Acked-by: Andrey Yurovsky Acked-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/if_spi.c | 15 +++++++++++++++ include/linux/spi/libertas_spi.h | 7 +++++++ 2 files changed, 22 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index 7c02ea314fd1..07311e71af92 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -42,6 +42,7 @@ struct if_spi_packet { struct if_spi_card { struct spi_device *spi; struct lbs_private *priv; + struct libertas_spi_platform_data *pdata; char helper_fw_name[FIRMWARE_NAME_MAX]; char main_fw_name[FIRMWARE_NAME_MAX]; @@ -1022,6 +1023,17 @@ static int __devinit if_spi_probe(struct spi_device *spi) lbs_deb_enter(LBS_DEB_SPI); + if (!pdata) { + err = -EINVAL; + goto out; + } + + if (pdata->setup) { + err = pdata->setup(spi); + if (err) + goto out; + } + /* Allocate card structure to represent this specific device */ card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL); if (!card) { @@ -1029,6 +1041,7 @@ static int __devinit if_spi_probe(struct spi_device *spi) goto out; } spi_set_drvdata(spi, card); + card->pdata = pdata; card->spi = spi; card->gpio_cs = pdata->gpio_cs; card->prev_xfer_time = jiffies; @@ -1158,6 +1171,8 @@ static int __devexit libertas_spi_remove(struct spi_device *spi) if_spi_terminate_spi_thread(card); lbs_remove_card(priv); /* will call free_netdev */ gpio_free(card->gpio_cs); + if (card->pdata->teardown) + card->pdata->teardown(spi); free_if_spi_card(card); lbs_deb_leave(LBS_DEB_SPI); return 0; diff --git a/include/linux/spi/libertas_spi.h b/include/linux/spi/libertas_spi.h index ada71b4f3788..79506f5f9e67 100644 --- a/include/linux/spi/libertas_spi.h +++ b/include/linux/spi/libertas_spi.h @@ -10,6 +10,9 @@ */ #ifndef _LIBERTAS_SPI_H_ #define _LIBERTAS_SPI_H_ + +struct spi_device; + struct libertas_spi_platform_data { /* There are two ways to read data from the WLAN module's SPI * interface. Setting 0 or 1 here controls which one is used. @@ -21,5 +24,9 @@ struct libertas_spi_platform_data { /* GPIO number to use as chip select */ u16 gpio_cs; + + /* Board specific setup/teardown */ + int (*setup)(struct spi_device *spi); + int (*teardown)(struct spi_device *spi); }; #endif -- cgit v1.2.3 From c9703146158c0415a60799570397e488bc982af5 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Tue, 3 Feb 2009 19:23:18 +0100 Subject: ssb: Add PMU support This adds support for the SSB PMU. A PMU is found on Low-Power devices. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/ssb/Makefile | 1 + drivers/ssb/driver_chipcommon.c | 14 +- drivers/ssb/driver_chipcommon_pmu.c | 508 ++++++++++++++++++++++++++++++ include/linux/ssb/ssb_driver_chipcommon.h | 224 +++++++++++++ 4 files changed, 734 insertions(+), 13 deletions(-) create mode 100644 drivers/ssb/driver_chipcommon_pmu.c (limited to 'include/linux') diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile index 6f255e9c5af9..cfbb74f2982e 100644 --- a/drivers/ssb/Makefile +++ b/drivers/ssb/Makefile @@ -9,6 +9,7 @@ ssb-$(CONFIG_SSB_PCMCIAHOST) += pcmcia.o # built-in drivers ssb-y += driver_chipcommon.o +ssb-y += driver_chipcommon_pmu.o ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c index 571f4fd55236..9681536163ca 100644 --- a/drivers/ssb/driver_chipcommon.c +++ b/drivers/ssb/driver_chipcommon.c @@ -26,19 +26,6 @@ enum ssb_clksrc { }; -static inline u32 chipco_read32(struct ssb_chipcommon *cc, - u16 offset) -{ - return ssb_read32(cc->dev, offset); -} - -static inline void chipco_write32(struct ssb_chipcommon *cc, - u16 offset, - u32 value) -{ - ssb_write32(cc->dev, offset, value); -} - static inline u32 chipco_write32_masked(struct ssb_chipcommon *cc, u16 offset, u32 mask, u32 value) { @@ -246,6 +233,7 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc) { if (!cc->dev) return; /* We don't have a ChipCommon */ + ssb_pmu_init(cc); chipco_powercontrol_init(cc); ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST); calc_fast_powerup_delay(cc); diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c new file mode 100644 index 000000000000..4aaddeec55a2 --- /dev/null +++ b/drivers/ssb/driver_chipcommon_pmu.c @@ -0,0 +1,508 @@ +/* + * Sonics Silicon Backplane + * Broadcom ChipCommon Power Management Unit driver + * + * Copyright 2009, Michael Buesch + * Copyright 2007, Broadcom Corporation + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include +#include +#include +#include + +#include "ssb_private.h" + +static u32 ssb_chipco_pll_read(struct ssb_chipcommon *cc, u32 offset) +{ + chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, offset); + return chipco_read32(cc, SSB_CHIPCO_PLLCTL_DATA); +} + +static void ssb_chipco_pll_write(struct ssb_chipcommon *cc, + u32 offset, u32 value) +{ + chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, offset); + chipco_write32(cc, SSB_CHIPCO_PLLCTL_DATA, value); +} + +struct pmu0_plltab_entry { + u16 freq; /* Crystal frequency in kHz.*/ + u8 xf; /* Crystal frequency value for PMU control */ + u8 wb_int; + u32 wb_frac; +}; + +static const struct pmu0_plltab_entry pmu0_plltab[] = { + { .freq = 12000, .xf = 1, .wb_int = 73, .wb_frac = 349525, }, + { .freq = 13000, .xf = 2, .wb_int = 67, .wb_frac = 725937, }, + { .freq = 14400, .xf = 3, .wb_int = 61, .wb_frac = 116508, }, + { .freq = 15360, .xf = 4, .wb_int = 57, .wb_frac = 305834, }, + { .freq = 16200, .xf = 5, .wb_int = 54, .wb_frac = 336579, }, + { .freq = 16800, .xf = 6, .wb_int = 52, .wb_frac = 399457, }, + { .freq = 19200, .xf = 7, .wb_int = 45, .wb_frac = 873813, }, + { .freq = 19800, .xf = 8, .wb_int = 44, .wb_frac = 466033, }, + { .freq = 20000, .xf = 9, .wb_int = 44, .wb_frac = 0, }, + { .freq = 25000, .xf = 10, .wb_int = 70, .wb_frac = 419430, }, + { .freq = 26000, .xf = 11, .wb_int = 67, .wb_frac = 725937, }, + { .freq = 30000, .xf = 12, .wb_int = 58, .wb_frac = 699050, }, + { .freq = 38400, .xf = 13, .wb_int = 45, .wb_frac = 873813, }, + { .freq = 40000, .xf = 14, .wb_int = 45, .wb_frac = 0, }, +}; +#define SSB_PMU0_DEFAULT_XTALFREQ 20000 + +static const struct pmu0_plltab_entry * pmu0_plltab_find_entry(u32 crystalfreq) +{ + const struct pmu0_plltab_entry *e; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pmu0_plltab); i++) { + e = &pmu0_plltab[i]; + if (e->freq == crystalfreq) + return e; + } + + return NULL; +} + +/* Tune the PLL to the crystal speed. crystalfreq is in kHz. */ +static void ssb_pmu0_pllinit_r0(struct ssb_chipcommon *cc, + u32 crystalfreq) +{ + struct ssb_bus *bus = cc->dev->bus; + const struct pmu0_plltab_entry *e = NULL; + u32 pmuctl, tmp, pllctl; + unsigned int i; + + if ((bus->chip_id == 0x5354) && !crystalfreq) { + /* The 5354 crystal freq is 25MHz */ + crystalfreq = 25000; + } + if (crystalfreq) + e = pmu0_plltab_find_entry(crystalfreq); + if (!e) + e = pmu0_plltab_find_entry(SSB_PMU0_DEFAULT_XTALFREQ); + BUG_ON(!e); + crystalfreq = e->freq; + cc->pmu.crystalfreq = e->freq; + + /* Check if the PLL already is programmed to this frequency. */ + pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); + if (((pmuctl & SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) == e->xf) { + /* We're already there... */ + return; + } + + ssb_printk(KERN_INFO PFX "Programming PLL to %u.%03u MHz\n", + (crystalfreq / 1000), (crystalfreq % 1000)); + + /* First turn the PLL off. */ + switch (bus->chip_id) { + case 0x4328: + chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, + ~(1 << SSB_PMURES_4328_BB_PLL_PU)); + chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, + ~(1 << SSB_PMURES_4328_BB_PLL_PU)); + break; + case 0x5354: + chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, + ~(1 << SSB_PMURES_5354_BB_PLL_PU)); + chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, + ~(1 << SSB_PMURES_5354_BB_PLL_PU)); + break; + default: + SSB_WARN_ON(1); + } + for (i = 1500; i; i--) { + tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); + if (!(tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)) + break; + udelay(10); + } + tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); + if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT) + ssb_printk(KERN_EMERG PFX "Failed to turn the PLL off!\n"); + + /* Set PDIV in PLL control 0. */ + pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL0); + if (crystalfreq >= SSB_PMU0_PLLCTL0_PDIV_FREQ) + pllctl |= SSB_PMU0_PLLCTL0_PDIV_MSK; + else + pllctl &= ~SSB_PMU0_PLLCTL0_PDIV_MSK; + ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL0, pllctl); + + /* Set WILD in PLL control 1. */ + pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL1); + pllctl &= ~SSB_PMU0_PLLCTL1_STOPMOD; + pllctl &= ~(SSB_PMU0_PLLCTL1_WILD_IMSK | SSB_PMU0_PLLCTL1_WILD_FMSK); + pllctl |= ((u32)e->wb_int << SSB_PMU0_PLLCTL1_WILD_IMSK_SHIFT) & SSB_PMU0_PLLCTL1_WILD_IMSK; + pllctl |= ((u32)e->wb_frac << SSB_PMU0_PLLCTL1_WILD_FMSK_SHIFT) & SSB_PMU0_PLLCTL1_WILD_FMSK; + if (e->wb_frac == 0) + pllctl |= SSB_PMU0_PLLCTL1_STOPMOD; + ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL1, pllctl); + + /* Set WILD in PLL control 2. */ + pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL2); + pllctl &= ~SSB_PMU0_PLLCTL2_WILD_IMSKHI; + pllctl |= (((u32)e->wb_int >> 4) << SSB_PMU0_PLLCTL2_WILD_IMSKHI_SHIFT) & SSB_PMU0_PLLCTL2_WILD_IMSKHI; + ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL2, pllctl); + + /* Set the crystalfrequency and the divisor. */ + pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); + pmuctl &= ~SSB_CHIPCO_PMU_CTL_ILP_DIV; + pmuctl |= (((crystalfreq + 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT) + & SSB_CHIPCO_PMU_CTL_ILP_DIV; + pmuctl &= ~SSB_CHIPCO_PMU_CTL_XTALFREQ; + pmuctl |= ((u32)e->xf << SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) & SSB_CHIPCO_PMU_CTL_XTALFREQ; + chipco_write32(cc, SSB_CHIPCO_PMU_CTL, pmuctl); +} + +struct pmu1_plltab_entry { + u16 freq; /* Crystal frequency in kHz.*/ + u8 xf; /* Crystal frequency value for PMU control */ + u8 ndiv_int; + u32 ndiv_frac; + u8 p1div; + u8 p2div; +}; + +static const struct pmu1_plltab_entry pmu1_plltab[] = { + { .freq = 12000, .xf = 1, .p1div = 3, .p2div = 22, .ndiv_int = 0x9, .ndiv_frac = 0xFFFFEF, }, + { .freq = 13000, .xf = 2, .p1div = 1, .p2div = 6, .ndiv_int = 0xb, .ndiv_frac = 0x483483, }, + { .freq = 14400, .xf = 3, .p1div = 1, .p2div = 10, .ndiv_int = 0xa, .ndiv_frac = 0x1C71C7, }, + { .freq = 15360, .xf = 4, .p1div = 1, .p2div = 5, .ndiv_int = 0xb, .ndiv_frac = 0x755555, }, + { .freq = 16200, .xf = 5, .p1div = 1, .p2div = 10, .ndiv_int = 0x5, .ndiv_frac = 0x6E9E06, }, + { .freq = 16800, .xf = 6, .p1div = 1, .p2div = 10, .ndiv_int = 0x5, .ndiv_frac = 0x3CF3CF, }, + { .freq = 19200, .xf = 7, .p1div = 1, .p2div = 9, .ndiv_int = 0x5, .ndiv_frac = 0x17B425, }, + { .freq = 19800, .xf = 8, .p1div = 1, .p2div = 11, .ndiv_int = 0x4, .ndiv_frac = 0xA57EB, }, + { .freq = 20000, .xf = 9, .p1div = 1, .p2div = 11, .ndiv_int = 0x4, .ndiv_frac = 0, }, + { .freq = 24000, .xf = 10, .p1div = 3, .p2div = 11, .ndiv_int = 0xa, .ndiv_frac = 0, }, + { .freq = 25000, .xf = 11, .p1div = 5, .p2div = 16, .ndiv_int = 0xb, .ndiv_frac = 0, }, + { .freq = 26000, .xf = 12, .p1div = 1, .p2div = 2, .ndiv_int = 0x10, .ndiv_frac = 0xEC4EC4, }, + { .freq = 30000, .xf = 13, .p1div = 3, .p2div = 8, .ndiv_int = 0xb, .ndiv_frac = 0, }, + { .freq = 38400, .xf = 14, .p1div = 1, .p2div = 5, .ndiv_int = 0x4, .ndiv_frac = 0x955555, }, + { .freq = 40000, .xf = 15, .p1div = 1, .p2div = 2, .ndiv_int = 0xb, .ndiv_frac = 0, }, +}; + +#define SSB_PMU1_DEFAULT_XTALFREQ 15360 + +static const struct pmu1_plltab_entry * pmu1_plltab_find_entry(u32 crystalfreq) +{ + const struct pmu1_plltab_entry *e; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pmu1_plltab); i++) { + e = &pmu1_plltab[i]; + if (e->freq == crystalfreq) + return e; + } + + return NULL; +} + +/* Tune the PLL to the crystal speed. crystalfreq is in kHz. */ +static void ssb_pmu1_pllinit_r0(struct ssb_chipcommon *cc, + u32 crystalfreq) +{ + struct ssb_bus *bus = cc->dev->bus; + const struct pmu1_plltab_entry *e = NULL; + u32 buffer_strength = 0; + u32 tmp, pllctl, pmuctl; + unsigned int i; + + if (bus->chip_id == 0x4312) { + /* We do not touch the BCM4312 PLL and assume + * the default crystal settings work out-of-the-box. */ + cc->pmu.crystalfreq = 20000; + return; + } + + if (crystalfreq) + e = pmu1_plltab_find_entry(crystalfreq); + if (!e) + e = pmu1_plltab_find_entry(SSB_PMU1_DEFAULT_XTALFREQ); + BUG_ON(!e); + crystalfreq = e->freq; + cc->pmu.crystalfreq = e->freq; + + /* Check if the PLL already is programmed to this frequency. */ + pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); + if (((pmuctl & SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) == e->xf) { + /* We're already there... */ + return; + } + + ssb_printk(KERN_INFO PFX "Programming PLL to %u.%03u MHz\n", + (crystalfreq / 1000), (crystalfreq % 1000)); + + /* First turn the PLL off. */ + switch (bus->chip_id) { + case 0x4325: + chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, + ~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU) | + (1 << SSB_PMURES_4325_HT_AVAIL))); + chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, + ~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU) | + (1 << SSB_PMURES_4325_HT_AVAIL))); + /* Adjust the BBPLL to 2 on all channels later. */ + buffer_strength = 0x222222; + break; + default: + SSB_WARN_ON(1); + } + for (i = 1500; i; i--) { + tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); + if (!(tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)) + break; + udelay(10); + } + tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST); + if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT) + ssb_printk(KERN_EMERG PFX "Failed to turn the PLL off!\n"); + + /* Set p1div and p2div. */ + pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL0); + pllctl &= ~(SSB_PMU1_PLLCTL0_P1DIV | SSB_PMU1_PLLCTL0_P2DIV); + pllctl |= ((u32)e->p1div << SSB_PMU1_PLLCTL0_P1DIV_SHIFT) & SSB_PMU1_PLLCTL0_P1DIV; + pllctl |= ((u32)e->p2div << SSB_PMU1_PLLCTL0_P2DIV_SHIFT) & SSB_PMU1_PLLCTL0_P2DIV; + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, pllctl); + + /* Set ndiv int and ndiv mode */ + pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL2); + pllctl &= ~(SSB_PMU1_PLLCTL2_NDIVINT | SSB_PMU1_PLLCTL2_NDIVMODE); + pllctl |= ((u32)e->ndiv_int << SSB_PMU1_PLLCTL2_NDIVINT_SHIFT) & SSB_PMU1_PLLCTL2_NDIVINT; + pllctl |= (1 << SSB_PMU1_PLLCTL2_NDIVMODE_SHIFT) & SSB_PMU1_PLLCTL2_NDIVMODE; + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, pllctl); + + /* Set ndiv frac */ + pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL3); + pllctl &= ~SSB_PMU1_PLLCTL3_NDIVFRAC; + pllctl |= ((u32)e->ndiv_frac << SSB_PMU1_PLLCTL3_NDIVFRAC_SHIFT) & SSB_PMU1_PLLCTL3_NDIVFRAC; + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL3, pllctl); + + /* Change the drive strength, if required. */ + if (buffer_strength) { + pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL5); + pllctl &= ~SSB_PMU1_PLLCTL5_CLKDRV; + pllctl |= (buffer_strength << SSB_PMU1_PLLCTL5_CLKDRV_SHIFT) & SSB_PMU1_PLLCTL5_CLKDRV; + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, pllctl); + } + + /* Tune the crystalfreq and the divisor. */ + pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL); + pmuctl &= ~(SSB_CHIPCO_PMU_CTL_ILP_DIV | SSB_CHIPCO_PMU_CTL_XTALFREQ); + pmuctl |= ((((u32)e->freq + 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT) + & SSB_CHIPCO_PMU_CTL_ILP_DIV; + pmuctl |= ((u32)e->xf << SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) & SSB_CHIPCO_PMU_CTL_XTALFREQ; + chipco_write32(cc, SSB_CHIPCO_PMU_CTL, pmuctl); +} + +static void ssb_pmu_pll_init(struct ssb_chipcommon *cc) +{ + struct ssb_bus *bus = cc->dev->bus; + u32 crystalfreq = 0; /* in kHz. 0 = keep default freq. */ + + if (bus->bustype == SSB_BUSTYPE_SSB) { + /* TODO: The user may override the crystal frequency. */ + } + + switch (bus->chip_id) { + case 0x4312: + case 0x4325: + ssb_pmu1_pllinit_r0(cc, crystalfreq); + break; + case 0x4328: + case 0x5354: + ssb_pmu0_pllinit_r0(cc, crystalfreq); + break; + default: + ssb_printk(KERN_ERR PFX + "ERROR: PLL init unknown for device %04X\n", + bus->chip_id); + } +} + +struct pmu_res_updown_tab_entry { + u8 resource; /* The resource number */ + u16 updown; /* The updown value */ +}; + +enum pmu_res_depend_tab_task { + PMU_RES_DEP_SET = 1, + PMU_RES_DEP_ADD, + PMU_RES_DEP_REMOVE, +}; + +struct pmu_res_depend_tab_entry { + u8 resource; /* The resource number */ + u8 task; /* SET | ADD | REMOVE */ + u32 depend; /* The depend mask */ +}; + +static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4328a0[] = { + { .resource = SSB_PMURES_4328_EXT_SWITCHER_PWM, .updown = 0x0101, }, + { .resource = SSB_PMURES_4328_BB_SWITCHER_PWM, .updown = 0x1F01, }, + { .resource = SSB_PMURES_4328_BB_SWITCHER_BURST, .updown = 0x010F, }, + { .resource = SSB_PMURES_4328_BB_EXT_SWITCHER_BURST, .updown = 0x0101, }, + { .resource = SSB_PMURES_4328_ILP_REQUEST, .updown = 0x0202, }, + { .resource = SSB_PMURES_4328_RADIO_SWITCHER_PWM, .updown = 0x0F01, }, + { .resource = SSB_PMURES_4328_RADIO_SWITCHER_BURST, .updown = 0x0F01, }, + { .resource = SSB_PMURES_4328_ROM_SWITCH, .updown = 0x0101, }, + { .resource = SSB_PMURES_4328_PA_REF_LDO, .updown = 0x0F01, }, + { .resource = SSB_PMURES_4328_RADIO_LDO, .updown = 0x0F01, }, + { .resource = SSB_PMURES_4328_AFE_LDO, .updown = 0x0F01, }, + { .resource = SSB_PMURES_4328_PLL_LDO, .updown = 0x0F01, }, + { .resource = SSB_PMURES_4328_BG_FILTBYP, .updown = 0x0101, }, + { .resource = SSB_PMURES_4328_TX_FILTBYP, .updown = 0x0101, }, + { .resource = SSB_PMURES_4328_RX_FILTBYP, .updown = 0x0101, }, + { .resource = SSB_PMURES_4328_XTAL_PU, .updown = 0x0101, }, + { .resource = SSB_PMURES_4328_XTAL_EN, .updown = 0xA001, }, + { .resource = SSB_PMURES_4328_BB_PLL_FILTBYP, .updown = 0x0101, }, + { .resource = SSB_PMURES_4328_RF_PLL_FILTBYP, .updown = 0x0101, }, + { .resource = SSB_PMURES_4328_BB_PLL_PU, .updown = 0x0701, }, +}; + +static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4328a0[] = { + { + /* Adjust ILP Request to avoid forcing EXT/BB into burst mode. */ + .resource = SSB_PMURES_4328_ILP_REQUEST, + .task = PMU_RES_DEP_SET, + .depend = ((1 << SSB_PMURES_4328_EXT_SWITCHER_PWM) | + (1 << SSB_PMURES_4328_BB_SWITCHER_PWM)), + }, +}; + +static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4325a0[] = { + { .resource = SSB_PMURES_4325_XTAL_PU, .updown = 0x1501, }, +}; + +static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4325a0[] = { + { + /* Adjust HT-Available dependencies. */ + .resource = SSB_PMURES_4325_HT_AVAIL, + .task = PMU_RES_DEP_ADD, + .depend = ((1 << SSB_PMURES_4325_RX_PWRSW_PU) | + (1 << SSB_PMURES_4325_TX_PWRSW_PU) | + (1 << SSB_PMURES_4325_LOGEN_PWRSW_PU) | + (1 << SSB_PMURES_4325_AFE_PWRSW_PU)), + }, +}; + +static void ssb_pmu_resources_init(struct ssb_chipcommon *cc) +{ + struct ssb_bus *bus = cc->dev->bus; + u32 min_msk = 0, max_msk = 0; + unsigned int i; + const struct pmu_res_updown_tab_entry *updown_tab = NULL; + unsigned int updown_tab_size; + const struct pmu_res_depend_tab_entry *depend_tab = NULL; + unsigned int depend_tab_size; + + switch (bus->chip_id) { + case 0x4312: + /* We keep the default settings: + * min_msk = 0xCBB + * max_msk = 0x7FFFF + */ + break; + case 0x4325: + /* Power OTP down later. */ + min_msk = (1 << SSB_PMURES_4325_CBUCK_BURST) | + (1 << SSB_PMURES_4325_LNLDO2_PU); + if (chipco_read32(cc, SSB_CHIPCO_CHIPSTAT) & + SSB_CHIPCO_CHST_4325_PMUTOP_2B) + min_msk |= (1 << SSB_PMURES_4325_CLDO_CBUCK_BURST); + /* The PLL may turn on, if it decides so. */ + max_msk = 0xFFFFF; + updown_tab = pmu_res_updown_tab_4325a0; + updown_tab_size = ARRAY_SIZE(pmu_res_updown_tab_4325a0); + depend_tab = pmu_res_depend_tab_4325a0; + depend_tab_size = ARRAY_SIZE(pmu_res_depend_tab_4325a0); + break; + case 0x4328: + min_msk = (1 << SSB_PMURES_4328_EXT_SWITCHER_PWM) | + (1 << SSB_PMURES_4328_BB_SWITCHER_PWM) | + (1 << SSB_PMURES_4328_XTAL_EN); + /* The PLL may turn on, if it decides so. */ + max_msk = 0xFFFFF; + updown_tab = pmu_res_updown_tab_4328a0; + updown_tab_size = ARRAY_SIZE(pmu_res_updown_tab_4328a0); + depend_tab = pmu_res_depend_tab_4328a0; + depend_tab_size = ARRAY_SIZE(pmu_res_depend_tab_4328a0); + break; + case 0x5354: + /* The PLL may turn on, if it decides so. */ + max_msk = 0xFFFFF; + break; + default: + ssb_printk(KERN_ERR PFX + "ERROR: PMU resource config unknown for device %04X\n", + bus->chip_id); + } + + if (updown_tab) { + for (i = 0; i < updown_tab_size; i++) { + chipco_write32(cc, SSB_CHIPCO_PMU_RES_TABSEL, + updown_tab[i].resource); + chipco_write32(cc, SSB_CHIPCO_PMU_RES_UPDNTM, + updown_tab[i].updown); + } + } + if (depend_tab) { + for (i = 0; i < depend_tab_size; i++) { + chipco_write32(cc, SSB_CHIPCO_PMU_RES_TABSEL, + depend_tab[i].resource); + switch (depend_tab[i].task) { + case PMU_RES_DEP_SET: + chipco_write32(cc, SSB_CHIPCO_PMU_RES_DEPMSK, + depend_tab[i].depend); + break; + case PMU_RES_DEP_ADD: + chipco_set32(cc, SSB_CHIPCO_PMU_RES_DEPMSK, + depend_tab[i].depend); + break; + case PMU_RES_DEP_REMOVE: + chipco_mask32(cc, SSB_CHIPCO_PMU_RES_DEPMSK, + ~(depend_tab[i].depend)); + break; + default: + SSB_WARN_ON(1); + } + } + } + + /* Set the resource masks. */ + if (min_msk) + chipco_write32(cc, SSB_CHIPCO_PMU_MINRES_MSK, min_msk); + if (max_msk) + chipco_write32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, max_msk); +} + +void ssb_pmu_init(struct ssb_chipcommon *cc) +{ + struct ssb_bus *bus = cc->dev->bus; + u32 pmucap; + + if (!(cc->capabilities & SSB_CHIPCO_CAP_PMU)) + return; + + pmucap = chipco_read32(cc, SSB_CHIPCO_PMU_CAP); + cc->pmu.rev = (pmucap & SSB_CHIPCO_PMU_CAP_REVISION); + + ssb_dprintk(KERN_DEBUG PFX "Found rev %u PMU (capabilities 0x%08X)\n", + cc->pmu.rev, pmucap); + + if (cc->pmu.rev >= 1) { + if ((bus->chip_id == 0x4325) && (bus->chip_rev < 2)) { + chipco_mask32(cc, SSB_CHIPCO_PMU_CTL, + ~SSB_CHIPCO_PMU_CTL_NOILPONW); + } else { + chipco_set32(cc, SSB_CHIPCO_PMU_CTL, + SSB_CHIPCO_PMU_CTL_NOILPONW); + } + } + ssb_pmu_pll_init(cc); + ssb_pmu_resources_init(cc); +} diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h index 7d7e03dcf77c..d3b1d18922f2 100644 --- a/include/linux/ssb/ssb_driver_chipcommon.h +++ b/include/linux/ssb/ssb_driver_chipcommon.h @@ -181,6 +181,16 @@ #define SSB_CHIPCO_PROG_WAITCNT 0x0124 #define SSB_CHIPCO_FLASH_CFG 0x0128 #define SSB_CHIPCO_FLASH_WAITCNT 0x012C +#define SSB_CHIPCO_CLKCTLST 0x01E0 /* Clock control and status (rev >= 20) */ +#define SSB_CHIPCO_CLKCTLST_FORCEALP 0x00000001 /* Force ALP request */ +#define SSB_CHIPCO_CLKCTLST_FORCEHT 0x00000002 /* Force HT request */ +#define SSB_CHIPCO_CLKCTLST_FORCEILP 0x00000004 /* Force ILP request */ +#define SSB_CHIPCO_CLKCTLST_HAVEALPREQ 0x00000008 /* ALP available request */ +#define SSB_CHIPCO_CLKCTLST_HAVEHTREQ 0x00000010 /* HT available request */ +#define SSB_CHIPCO_CLKCTLST_HWCROFF 0x00000020 /* Force HW clock request off */ +#define SSB_CHIPCO_CLKCTLST_HAVEHT 0x00010000 /* HT available */ +#define SSB_CHIPCO_CLKCTLST_HAVEALP 0x00020000 /* APL available */ +#define SSB_CHIPCO_HW_WORKAROUND 0x01E4 /* Hardware workaround (rev >= 20) */ #define SSB_CHIPCO_UART0_DATA 0x0300 #define SSB_CHIPCO_UART0_IMR 0x0304 #define SSB_CHIPCO_UART0_FCR 0x0308 @@ -197,6 +207,196 @@ #define SSB_CHIPCO_UART1_LSR 0x0414 #define SSB_CHIPCO_UART1_MSR 0x0418 #define SSB_CHIPCO_UART1_SCRATCH 0x041C +/* PMU registers (rev >= 20) */ +#define SSB_CHIPCO_PMU_CTL 0x0600 /* PMU control */ +#define SSB_CHIPCO_PMU_CTL_ILP_DIV 0xFFFF0000 /* ILP div mask */ +#define SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT 16 +#define SSB_CHIPCO_PMU_CTL_NOILPONW 0x00000200 /* No ILP on wait */ +#define SSB_CHIPCO_PMU_CTL_HTREQEN 0x00000100 /* HT req enable */ +#define SSB_CHIPCO_PMU_CTL_ALPREQEN 0x00000080 /* ALP req enable */ +#define SSB_CHIPCO_PMU_CTL_XTALFREQ 0x0000007C /* Crystal freq */ +#define SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT 2 +#define SSB_CHIPCO_PMU_CTL_ILPDIVEN 0x00000002 /* ILP div enable */ +#define SSB_CHIPCO_PMU_CTL_LPOSEL 0x00000001 /* LPO sel */ +#define SSB_CHIPCO_PMU_CAP 0x0604 /* PMU capabilities */ +#define SSB_CHIPCO_PMU_CAP_REVISION 0x000000FF /* Revision mask */ +#define SSB_CHIPCO_PMU_STAT 0x0608 /* PMU status */ +#define SSB_CHIPCO_PMU_STAT_INTPEND 0x00000040 /* Interrupt pending */ +#define SSB_CHIPCO_PMU_STAT_SBCLKST 0x00000030 /* Backplane clock status? */ +#define SSB_CHIPCO_PMU_STAT_HAVEALP 0x00000008 /* ALP available */ +#define SSB_CHIPCO_PMU_STAT_HAVEHT 0x00000004 /* HT available */ +#define SSB_CHIPCO_PMU_STAT_RESINIT 0x00000003 /* Res init */ +#define SSB_CHIPCO_PMU_RES_STAT 0x060C /* PMU res status */ +#define SSB_CHIPCO_PMU_RES_PEND 0x0610 /* PMU res pending */ +#define SSB_CHIPCO_PMU_TIMER 0x0614 /* PMU timer */ +#define SSB_CHIPCO_PMU_MINRES_MSK 0x0618 /* PMU min res mask */ +#define SSB_CHIPCO_PMU_MAXRES_MSK 0x061C /* PMU max res mask */ +#define SSB_CHIPCO_PMU_RES_TABSEL 0x0620 /* PMU res table sel */ +#define SSB_CHIPCO_PMU_RES_DEPMSK 0x0624 /* PMU res dep mask */ +#define SSB_CHIPCO_PMU_RES_UPDNTM 0x0628 /* PMU res updown timer */ +#define SSB_CHIPCO_PMU_RES_TIMER 0x062C /* PMU res timer */ +#define SSB_CHIPCO_PMU_CLKSTRETCH 0x0630 /* PMU clockstretch */ +#define SSB_CHIPCO_PMU_WATCHDOG 0x0634 /* PMU watchdog */ +#define SSB_CHIPCO_PMU_RES_REQTS 0x0640 /* PMU res req timer sel */ +#define SSB_CHIPCO_PMU_RES_REQT 0x0644 /* PMU res req timer */ +#define SSB_CHIPCO_PMU_RES_REQM 0x0648 /* PMU res req mask */ +#define SSB_CHIPCO_CHIPCTL_ADDR 0x0650 +#define SSB_CHIPCO_CHIPCTL_DATA 0x0654 +#define SSB_CHIPCO_REGCTL_ADDR 0x0658 +#define SSB_CHIPCO_REGCTL_DATA 0x065C +#define SSB_CHIPCO_PLLCTL_ADDR 0x0660 +#define SSB_CHIPCO_PLLCTL_DATA 0x0664 + + + +/** PMU PLL registers */ + +/* PMU rev 0 PLL registers */ +#define SSB_PMU0_PLLCTL0 0 +#define SSB_PMU0_PLLCTL0_PDIV_MSK 0x00000001 +#define SSB_PMU0_PLLCTL0_PDIV_FREQ 25000 /* kHz */ +#define SSB_PMU0_PLLCTL1 1 +#define SSB_PMU0_PLLCTL1_WILD_IMSK 0xF0000000 /* Wild int mask (low nibble) */ +#define SSB_PMU0_PLLCTL1_WILD_IMSK_SHIFT 28 +#define SSB_PMU0_PLLCTL1_WILD_FMSK 0x0FFFFF00 /* Wild frac mask */ +#define SSB_PMU0_PLLCTL1_WILD_FMSK_SHIFT 8 +#define SSB_PMU0_PLLCTL1_STOPMOD 0x00000040 /* Stop mod */ +#define SSB_PMU0_PLLCTL2 2 +#define SSB_PMU0_PLLCTL2_WILD_IMSKHI 0x0000000F /* Wild int mask (high nibble) */ +#define SSB_PMU0_PLLCTL2_WILD_IMSKHI_SHIFT 0 + +/* PMU rev 1 PLL registers */ +#define SSB_PMU1_PLLCTL0 0 +#define SSB_PMU1_PLLCTL0_P1DIV 0x00F00000 /* P1 div */ +#define SSB_PMU1_PLLCTL0_P1DIV_SHIFT 20 +#define SSB_PMU1_PLLCTL0_P2DIV 0x0F000000 /* P2 div */ +#define SSB_PMU1_PLLCTL0_P2DIV_SHIFT 24 +#define SSB_PMU1_PLLCTL1 1 +#define SSB_PMU1_PLLCTL1_M1DIV 0x000000FF /* M1 div */ +#define SSB_PMU1_PLLCTL1_M1DIV_SHIFT 0 +#define SSB_PMU1_PLLCTL1_M2DIV 0x0000FF00 /* M2 div */ +#define SSB_PMU1_PLLCTL1_M2DIV_SHIFT 8 +#define SSB_PMU1_PLLCTL1_M3DIV 0x00FF0000 /* M3 div */ +#define SSB_PMU1_PLLCTL1_M3DIV_SHIFT 16 +#define SSB_PMU1_PLLCTL1_M4DIV 0xFF000000 /* M4 div */ +#define SSB_PMU1_PLLCTL1_M4DIV_SHIFT 24 +#define SSB_PMU1_PLLCTL2 2 +#define SSB_PMU1_PLLCTL2_M5DIV 0x000000FF /* M5 div */ +#define SSB_PMU1_PLLCTL2_M5DIV_SHIFT 0 +#define SSB_PMU1_PLLCTL2_M6DIV 0x0000FF00 /* M6 div */ +#define SSB_PMU1_PLLCTL2_M6DIV_SHIFT 8 +#define SSB_PMU1_PLLCTL2_NDIVMODE 0x000E0000 /* NDIV mode */ +#define SSB_PMU1_PLLCTL2_NDIVMODE_SHIFT 17 +#define SSB_PMU1_PLLCTL2_NDIVINT 0x1FF00000 /* NDIV int */ +#define SSB_PMU1_PLLCTL2_NDIVINT_SHIFT 20 +#define SSB_PMU1_PLLCTL3 3 +#define SSB_PMU1_PLLCTL3_NDIVFRAC 0x00FFFFFF /* NDIV frac */ +#define SSB_PMU1_PLLCTL3_NDIVFRAC_SHIFT 0 +#define SSB_PMU1_PLLCTL4 4 +#define SSB_PMU1_PLLCTL5 5 +#define SSB_PMU1_PLLCTL5_CLKDRV 0xFFFFFF00 /* clk drv */ +#define SSB_PMU1_PLLCTL5_CLKDRV_SHIFT 8 + +/* BCM4312 PLL resource numbers. */ +#define SSB_PMURES_4312_SWITCHER_BURST 0 +#define SSB_PMURES_4312_SWITCHER_PWM 1 +#define SSB_PMURES_4312_PA_REF_LDO 2 +#define SSB_PMURES_4312_CORE_LDO_BURST 3 +#define SSB_PMURES_4312_CORE_LDO_PWM 4 +#define SSB_PMURES_4312_RADIO_LDO 5 +#define SSB_PMURES_4312_ILP_REQUEST 6 +#define SSB_PMURES_4312_BG_FILTBYP 7 +#define SSB_PMURES_4312_TX_FILTBYP 8 +#define SSB_PMURES_4312_RX_FILTBYP 9 +#define SSB_PMURES_4312_XTAL_PU 10 +#define SSB_PMURES_4312_ALP_AVAIL 11 +#define SSB_PMURES_4312_BB_PLL_FILTBYP 12 +#define SSB_PMURES_4312_RF_PLL_FILTBYP 13 +#define SSB_PMURES_4312_HT_AVAIL 14 + +/* BCM4325 PLL resource numbers. */ +#define SSB_PMURES_4325_BUCK_BOOST_BURST 0 +#define SSB_PMURES_4325_CBUCK_BURST 1 +#define SSB_PMURES_4325_CBUCK_PWM 2 +#define SSB_PMURES_4325_CLDO_CBUCK_BURST 3 +#define SSB_PMURES_4325_CLDO_CBUCK_PWM 4 +#define SSB_PMURES_4325_BUCK_BOOST_PWM 5 +#define SSB_PMURES_4325_ILP_REQUEST 6 +#define SSB_PMURES_4325_ABUCK_BURST 7 +#define SSB_PMURES_4325_ABUCK_PWM 8 +#define SSB_PMURES_4325_LNLDO1_PU 9 +#define SSB_PMURES_4325_LNLDO2_PU 10 +#define SSB_PMURES_4325_LNLDO3_PU 11 +#define SSB_PMURES_4325_LNLDO4_PU 12 +#define SSB_PMURES_4325_XTAL_PU 13 +#define SSB_PMURES_4325_ALP_AVAIL 14 +#define SSB_PMURES_4325_RX_PWRSW_PU 15 +#define SSB_PMURES_4325_TX_PWRSW_PU 16 +#define SSB_PMURES_4325_RFPLL_PWRSW_PU 17 +#define SSB_PMURES_4325_LOGEN_PWRSW_PU 18 +#define SSB_PMURES_4325_AFE_PWRSW_PU 19 +#define SSB_PMURES_4325_BBPLL_PWRSW_PU 20 +#define SSB_PMURES_4325_HT_AVAIL 21 + +/* BCM4328 PLL resource numbers. */ +#define SSB_PMURES_4328_EXT_SWITCHER_PWM 0 +#define SSB_PMURES_4328_BB_SWITCHER_PWM 1 +#define SSB_PMURES_4328_BB_SWITCHER_BURST 2 +#define SSB_PMURES_4328_BB_EXT_SWITCHER_BURST 3 +#define SSB_PMURES_4328_ILP_REQUEST 4 +#define SSB_PMURES_4328_RADIO_SWITCHER_PWM 5 +#define SSB_PMURES_4328_RADIO_SWITCHER_BURST 6 +#define SSB_PMURES_4328_ROM_SWITCH 7 +#define SSB_PMURES_4328_PA_REF_LDO 8 +#define SSB_PMURES_4328_RADIO_LDO 9 +#define SSB_PMURES_4328_AFE_LDO 10 +#define SSB_PMURES_4328_PLL_LDO 11 +#define SSB_PMURES_4328_BG_FILTBYP 12 +#define SSB_PMURES_4328_TX_FILTBYP 13 +#define SSB_PMURES_4328_RX_FILTBYP 14 +#define SSB_PMURES_4328_XTAL_PU 15 +#define SSB_PMURES_4328_XTAL_EN 16 +#define SSB_PMURES_4328_BB_PLL_FILTBYP 17 +#define SSB_PMURES_4328_RF_PLL_FILTBYP 18 +#define SSB_PMURES_4328_BB_PLL_PU 19 + +/* BCM5354 PLL resource numbers. */ +#define SSB_PMURES_5354_EXT_SWITCHER_PWM 0 +#define SSB_PMURES_5354_BB_SWITCHER_PWM 1 +#define SSB_PMURES_5354_BB_SWITCHER_BURST 2 +#define SSB_PMURES_5354_BB_EXT_SWITCHER_BURST 3 +#define SSB_PMURES_5354_ILP_REQUEST 4 +#define SSB_PMURES_5354_RADIO_SWITCHER_PWM 5 +#define SSB_PMURES_5354_RADIO_SWITCHER_BURST 6 +#define SSB_PMURES_5354_ROM_SWITCH 7 +#define SSB_PMURES_5354_PA_REF_LDO 8 +#define SSB_PMURES_5354_RADIO_LDO 9 +#define SSB_PMURES_5354_AFE_LDO 10 +#define SSB_PMURES_5354_PLL_LDO 11 +#define SSB_PMURES_5354_BG_FILTBYP 12 +#define SSB_PMURES_5354_TX_FILTBYP 13 +#define SSB_PMURES_5354_RX_FILTBYP 14 +#define SSB_PMURES_5354_XTAL_PU 15 +#define SSB_PMURES_5354_XTAL_EN 16 +#define SSB_PMURES_5354_BB_PLL_FILTBYP 17 +#define SSB_PMURES_5354_RF_PLL_FILTBYP 18 +#define SSB_PMURES_5354_BB_PLL_PU 19 + + + +/** Chip specific Chip-Status register contents. */ +#define SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL 0x00000003 +#define SSB_CHIPCO_CHST_4325_DEFCIS_SEL 0 /* OTP is powered up, use def. CIS, no SPROM */ +#define SSB_CHIPCO_CHST_4325_SPROM_SEL 1 /* OTP is powered up, SPROM is present */ +#define SSB_CHIPCO_CHST_4325_OTP_SEL 2 /* OTP is powered up, no SPROM */ +#define SSB_CHIPCO_CHST_4325_OTP_PWRDN 3 /* OTP is powered down, SPROM is present */ +#define SSB_CHIPCO_CHST_4325_SDIO_USB_MODE 0x00000004 +#define SSB_CHIPCO_CHST_4325_SDIO_USB_MODE_SHIFT 2 +#define SSB_CHIPCO_CHST_4325_RCAL_VALID 0x00000008 +#define SSB_CHIPCO_CHST_4325_RCAL_VALID_SHIFT 3 +#define SSB_CHIPCO_CHST_4325_RCAL_VALUE 0x000001F0 +#define SSB_CHIPCO_CHST_4325_RCAL_VALUE_SHIFT 4 +#define SSB_CHIPCO_CHST_4325_PMUTOP_2B 0x00000200 /* 1 for 2b, 0 for to 2a */ @@ -353,11 +553,20 @@ struct ssb_device; struct ssb_serial_port; +/* Data for the PMU, if available. + * Check availability with ((struct ssb_chipcommon)->capabilities & SSB_CHIPCO_CAP_PMU) + */ +struct ssb_chipcommon_pmu { + u8 rev; /* PMU revision */ + u32 crystalfreq; /* The active crystal frequency (in kHz) */ +}; + struct ssb_chipcommon { struct ssb_device *dev; u32 capabilities; /* Fast Powerup Delay constant */ u16 fast_pwrup_delay; + struct ssb_chipcommon_pmu pmu; }; static inline bool ssb_chipco_available(struct ssb_chipcommon *cc) @@ -365,6 +574,17 @@ static inline bool ssb_chipco_available(struct ssb_chipcommon *cc) return (cc->dev != NULL); } +/* Register access */ +#define chipco_read32(cc, offset) ssb_read32((cc)->dev, offset) +#define chipco_write32(cc, offset, val) ssb_write32((cc)->dev, offset, val) + +#define chipco_mask32(cc, offset, mask) \ + chipco_write32(cc, offset, chipco_read32(cc, offset) & (mask)) +#define chipco_set32(cc, offset, set) \ + chipco_write32(cc, offset, chipco_read32(cc, offset) | (set)) +#define chipco_maskset32(cc, offset, mask, set) \ + chipco_write32(cc, offset, (chipco_read32(cc, offset) & (mask)) | (set)) + extern void ssb_chipcommon_init(struct ssb_chipcommon *cc); extern void ssb_chipco_suspend(struct ssb_chipcommon *cc); @@ -406,4 +626,8 @@ extern int ssb_chipco_serial_init(struct ssb_chipcommon *cc, struct ssb_serial_port *ports); #endif /* CONFIG_SSB_SERIAL */ +/* PMU support */ +extern void ssb_pmu_init(struct ssb_chipcommon *cc); + + #endif /* LINUX_SSB_CHIPCO_H_ */ -- cgit v1.2.3 From d54e6d872767ae6512978f86a35d623a8ed948c5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 9 Feb 2009 23:45:29 -0800 Subject: net: Kill skbuff macros from the stone ages. This kills of HAVE_ALLOC_SKB and HAVE_ALIGNABLE_SKB. Nothing in-tree uses them and nothing in-tree has used them since 2.0.x times. Signed-off-by: David S. Miller --- include/linux/skbuff.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 5eba4007e07f..924700844580 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -29,9 +29,6 @@ #include #include -#define HAVE_ALLOC_SKB /* For the drivers to know */ -#define HAVE_ALIGNABLE_SKB /* Ditto 8) */ - /* Don't change this without changing skb_csum_unnecessary! */ #define CHECKSUM_NONE 0 #define CHECKSUM_UNNECESSARY 1 -- cgit v1.2.3 From 2a5193119269062608582418deba7af82844159a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 10 Feb 2009 21:25:55 +0100 Subject: cfg80211/nl80211: scanning (and mac80211 update to use it) This patch adds basic scan capability to cfg80211/nl80211 and changes mac80211 to use it. The BSS list that cfg80211 maintains is made driver-accessible with a private area in each BSS struct, but mac80211 doesn't yet use it. That's another large project. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 12 +- drivers/net/wireless/iwlwifi/iwl-core.c | 1 + drivers/net/wireless/iwlwifi/iwl-scan.c | 2 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 17 +- include/linux/nl80211.h | 65 +++ include/net/cfg80211.h | 131 +++++ include/net/mac80211.h | 6 +- include/net/wireless.h | 3 + net/mac80211/cfg.c | 20 + net/mac80211/ieee80211_i.h | 18 +- net/mac80211/iface.c | 2 +- net/mac80211/main.c | 32 +- net/mac80211/mlme.c | 37 +- net/mac80211/scan.c | 356 +++--------- net/mac80211/wext.c | 59 +- net/wireless/Makefile | 2 +- net/wireless/core.c | 8 + net/wireless/core.h | 20 + net/wireless/nl80211.c | 323 +++++++++++ net/wireless/nl80211.h | 8 + net/wireless/scan.c | 807 ++++++++++++++++++++++++++++ 21 files changed, 1546 insertions(+), 383 deletions(-) create mode 100644 net/wireless/scan.c (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index c196abc6db7a..539960da7e13 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2678,11 +2678,19 @@ static void iwl_bss_info_changed(struct ieee80211_hw *hw, } -static int iwl_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t ssid_len) +static int iwl_mac_hw_scan(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req) { unsigned long flags; struct iwl_priv *priv = hw->priv; int ret; + u8 *ssid = NULL; + size_t ssid_len = 0; + + if (req->n_ssids) { + ssid = req->ssids[0].ssid; + ssid_len = req->ssids[0].ssid_len; + } IWL_DEBUG_MAC80211(priv, "enter\n"); @@ -2718,7 +2726,7 @@ static int iwl_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t ssid_len) if (ssid_len) { priv->one_direct_scan = 1; - priv->direct_ssid_len = min_t(u8, ssid_len, IW_ESSID_MAX_SIZE); + priv->direct_ssid_len = ssid_len; memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len); } else { priv->one_direct_scan = 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index e18c3f326f71..260bf903cb71 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1271,6 +1271,7 @@ int iwl_setup_mac(struct iwl_priv *priv) BIT(NL80211_IFTYPE_ADHOC); hw->wiphy->custom_regulatory = true; + hw->wiphy->max_scan_ssids = 1; /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index 22bad3ce7d6a..1ec2b20eb37c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -860,7 +860,7 @@ void iwl_bg_scan_completed(struct work_struct *work) if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; - ieee80211_scan_completed(priv->hw); + ieee80211_scan_completed(priv->hw, false); /* Since setting the TXPOWER may have been deferred while * performing the scan, fire one off */ diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 42cc2884971c..0cd8cb96a5ef 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -4442,15 +4442,23 @@ static void iwl3945_bss_info_changed(struct ieee80211_hw *hw, } -static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) +static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req) { int rc = 0; unsigned long flags; struct iwl_priv *priv = hw->priv; + size_t len = 0; + u8 *ssid = NULL; DECLARE_SSID_BUF(ssid_buf); IWL_DEBUG_MAC80211(priv, "enter\n"); + if (req->n_ssids) { + ssid = req->ssids[0].ssid; + len = req->ssids[0].ssid_len; + } + mutex_lock(&priv->mutex); spin_lock_irqsave(&priv->lock, flags); @@ -4478,9 +4486,8 @@ static int iwl3945_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) print_ssid(ssid_buf, ssid, len), len); priv->one_direct_scan = 1; - priv->direct_ssid_len = (u8) - min((u8) len, (u8) IW_ESSID_MAX_SIZE); - memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len); + priv->direct_ssid_len = len; + memcpy(priv->direct_ssid, ssid, len); } else priv->one_direct_scan = 0; @@ -5412,6 +5419,8 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e hw->wiphy->custom_regulatory = true; + hw->wiphy->max_scan_ssids = 1; + /* 4 EDCA QOS priorities */ hw->queues = 4; diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 4bc27049f4e5..8802d1bda382 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -143,6 +143,13 @@ * added to all specified management frames generated by * kernel/firmware/driver. * + * @NL80211_CMD_GET_SCAN: get scan results + * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to + * NL80211_CMD_GET_SCAN and on the "scan" multicast group) + * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, + * partial scan results may be available + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -192,6 +199,11 @@ enum nl80211_commands { NL80211_CMD_GET_REG, + NL80211_CMD_GET_SCAN, + NL80211_CMD_TRIGGER_SCAN, + NL80211_CMD_NEW_SCAN_RESULTS, + NL80211_CMD_SCAN_ABORTED, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -305,6 +317,18 @@ enum nl80211_commands { * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with * %NL80211_CMD_SET_MGMT_EXTRA_IE). * + * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with + * a single scan request, a wiphy attribute. + * + * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) + * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive + * scanning and include a zero-length SSID (wildcard) for wildcard scan + * @NL80211_ATTR_SCAN_GENERATION: the scan generation increases whenever the + * scan result list changes (BSS expired or added) so that applications + * can verify that they got a single, consistent snapshot (when all dump + * messages carried the same generation number) + * @NL80211_ATTR_BSS: scan result BSS + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -372,6 +396,13 @@ enum nl80211_attrs { NL80211_ATTR_MGMT_SUBTYPE, NL80211_ATTR_IE, + NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + + NL80211_ATTR_SCAN_FREQUENCIES, + NL80211_ATTR_SCAN_SSIDS, + NL80211_ATTR_SCAN_GENERATION, + NL80211_ATTR_BSS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -841,4 +872,38 @@ enum nl80211_channel_type { NL80211_CHAN_HT40MINUS, NL80211_CHAN_HT40PLUS }; + +/** + * enum nl80211_bss - netlink attributes for a BSS + * + * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) + * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) + * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) + * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the + * raw information elements from the probe response/beacon (bin) + * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon + * in mBm (100 * dBm) (s32) + * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon + * in unspecified units, scaled to 0..100 (u8) + * @__NL80211_BSS_AFTER_LAST: internal + * @NL80211_BSS_MAX: highest BSS attribute + */ +enum nl80211_bss { + __NL80211_BSS_INVALID, + NL80211_BSS_BSSID, + NL80211_BSS_FREQUENCY, + NL80211_BSS_TSF, + NL80211_BSS_BEACON_INTERVAL, + NL80211_BSS_CAPABILITY, + NL80211_BSS_INFORMATION_ELEMENTS, + NL80211_BSS_SIGNAL_MBM, + NL80211_BSS_SIGNAL_UNSPEC, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index dd1fd51638fc..09a0b268e5cf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4,6 +4,10 @@ #include #include #include +#include +#include +#include +#include #include /* remove once we remove the wext stuff */ #include @@ -504,6 +508,83 @@ struct wiphy; /* from net/ieee80211.h */ struct ieee80211_channel; +/** + * struct cfg80211_ssid - SSID description + * @ssid: the SSID + * @ssid_len: length of the ssid + */ +struct cfg80211_ssid { + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 ssid_len; +}; + +/** + * struct cfg80211_scan_request - scan request description + * + * @ssids: SSIDs to scan for (active scan only) + * @n_ssids: number of SSIDs + * @channels: channels to scan on. + * @n_channels: number of channels for each band + * @wiphy: the wiphy this was for + * @ifidx: the interface index + */ +struct cfg80211_scan_request { + struct cfg80211_ssid *ssids; + int n_ssids; + struct ieee80211_channel **channels; + u32 n_channels; + + /* internal */ + struct wiphy *wiphy; + int ifidx; +}; + +/** + * enum cfg80211_signal_type - signal type + * + * @CFG80211_SIGNAL_TYPE_NONE: no signal strength information available + * @CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm) + * @CFG80211_SIGNAL_TYPE_UNSPEC: signal strength, increasing from 0 through 100 + */ +enum cfg80211_signal_type { + CFG80211_SIGNAL_TYPE_NONE, + CFG80211_SIGNAL_TYPE_MBM, + CFG80211_SIGNAL_TYPE_UNSPEC, +}; + +/** + * struct cfg80211_bss - BSS description + * + * This structure describes a BSS (which may also be a mesh network) + * for use in scan results and similar. + * + * @bssid: BSSID of the BSS + * @tsf: timestamp of last received update + * @beacon_interval: the beacon interval as from the frame + * @capability: the capability field in host byte order + * @information_elements: the information elements (Note that there + * is no guarantee that these are well-formed!) + * @len_information_elements: total length of the information elements + * @signal: signal strength value + * @signal_type: signal type + * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes + */ +struct cfg80211_bss { + struct ieee80211_channel *channel; + + u8 bssid[ETH_ALEN]; + u64 tsf; + u16 beacon_interval; + u16 capability; + u8 *information_elements; + size_t len_information_elements; + + s32 signal; + enum cfg80211_signal_type signal_type; + + u8 priv[0] __attribute__((__aligned__(sizeof(void *)))); +}; + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -571,6 +652,11 @@ struct ieee80211_channel; * @set_channel: Set channel * * @set_mgmt_extra_ie: Set extra IE data for management frames + * + * @scan: Request to do a scan. If returning zero, the scan request is given + * the driver, and will be valid until passed to cfg80211_scan_done(). + * For scan results, call cfg80211_inform_bss(); you can call this outside + * the scan/scan_done bracket too. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy); @@ -648,6 +734,9 @@ struct cfg80211_ops { int (*set_mgmt_extra_ie)(struct wiphy *wiphy, struct net_device *dev, struct mgmt_extra_ie_params *params); + + int (*scan)(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request); }; /* temporary wext handlers */ @@ -658,5 +747,47 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, u32 *mode, char *extra); int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, u32 *mode, char *extra); +int cfg80211_wext_siwscan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +int cfg80211_wext_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra); + +/** + * cfg80211_scan_done - notify that scan finished + * + * @request: the corresponding scan request + * @aborted: set to true if the scan was aborted for any reason, + * userspace will be notified of that + */ +void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted); + +/** + * cfg80211_inform_bss - inform cfg80211 of a new BSS + * + * @wiphy: the wiphy reporting the BSS + * @bss: the found BSS + * @gfp: context flags + * + * This informs cfg80211 that BSS information was found and + * the BSS should be updated/added. + */ +struct cfg80211_bss* +cfg80211_inform_bss_frame(struct wiphy *wiphy, + struct ieee80211_channel *channel, + struct ieee80211_mgmt *mgmt, size_t len, + s32 signal, enum cfg80211_signal_type sigtype, + gfp_t gfp); + +struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *bssid, + const u8 *ssid, size_t ssid_len); +struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *meshid, size_t meshidlen, + const u8 *meshcfg); +void cfg80211_put_bss(struct cfg80211_bss *bss); #endif /* __NET_CFG80211_H */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 341f3e595ebd..88fa3e03e3e9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1406,7 +1406,8 @@ struct ieee80211_ops { void (*update_tkip_key)(struct ieee80211_hw *hw, struct ieee80211_key_conf *conf, const u8 *address, u32 iv32, u16 *phase1key); - int (*hw_scan)(struct ieee80211_hw *hw, u8 *ssid, size_t len); + int (*hw_scan)(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req); int (*get_stats)(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx, @@ -1844,8 +1845,9 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw); * mac80211 that the scan finished. * * @hw: the hardware that finished the scan + * @aborted: set to true if scan was aborted */ -void ieee80211_scan_completed(struct ieee80211_hw *hw); +void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted); /** * ieee80211_iterate_active_interfaces - iterate active interfaces diff --git a/include/net/wireless.h b/include/net/wireless.h index a42c1562d52b..1c6285eb1666 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -213,6 +213,9 @@ struct wiphy { bool custom_regulatory; bool strict_regulatory; + int bss_priv_size; + u8 max_scan_ssids; + /* If multiple wiphys are registered and you're handed e.g. * a regular netdev with assigned ieee80211_ptr, you won't * know whether it points to a wiphy your driver has registered diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 42d692fd9bec..c8d969be440b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1277,6 +1277,25 @@ static int ieee80211_resume(struct wiphy *wiphy) #define ieee80211_resume NULL #endif +static int ieee80211_scan(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_scan_request *req) +{ + struct ieee80211_sub_if_data *sdata; + + if (!netif_running(dev)) + return -ENETDOWN; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + return ieee80211_request_scan(sdata, req); +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -1309,4 +1328,5 @@ struct cfg80211_ops mac80211_config_ops = { .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie, .suspend = ieee80211_suspend, .resume = ieee80211_resume, + .scan = ieee80211_scan, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9122416fd6af..cbc0b7d647f9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -294,8 +294,6 @@ struct ieee80211_if_sta { u8 ssid[IEEE80211_MAX_SSID_LEN]; enum ieee80211_sta_mlme_state state; size_t ssid_len; - u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; - size_t scan_ssid_len; u16 aid; u16 ap_capab, capab; u8 *extra_ie; /* to be added to the end of AssocReq */ @@ -658,17 +656,18 @@ struct ieee80211_local { /* Scanning and BSS list */ bool sw_scanning, hw_scanning; + struct cfg80211_ssid scan_ssid; + struct cfg80211_scan_request int_scan_req; + struct cfg80211_scan_request *scan_req; + struct ieee80211_channel *scan_channel; int scan_channel_idx; - enum ieee80211_band scan_band; enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; unsigned long last_scan_completed; struct delayed_work scan_work; struct ieee80211_sub_if_data *scan_sdata; - struct ieee80211_channel *oper_channel, *scan_channel, *csa_channel; enum nl80211_channel_type oper_channel_type; - u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; - size_t scan_ssid_len; + struct ieee80211_channel *oper_channel, *csa_channel; struct list_head bss_list; struct ieee80211_bss *bss_hash[STA_HASH_SIZE]; spinlock_t bss_lock; @@ -929,7 +928,7 @@ void ieee80211_send_pspoll(struct ieee80211_local *local, /* scan/BSS handling */ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, - u8 *ssid, size_t ssid_len); + struct cfg80211_scan_request *req); int ieee80211_scan_results(struct ieee80211_local *local, struct iw_request_info *info, char *buf, size_t len); @@ -944,14 +943,15 @@ int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local); int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, - u8 *ssid, size_t ssid_len); + struct cfg80211_scan_request *req); struct ieee80211_bss * ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status, struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, - int freq, bool beacon); + struct ieee80211_channel *channel, + bool beacon); struct ieee80211_bss * ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq, u8 *ssid, u8 ssid_len); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 1c17fb8e4058..df94b9365264 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -522,7 +522,7 @@ static int ieee80211_stop(struct net_device *dev) * scan event to userspace -- the scan is incomplete. */ if (local->sw_scanning) - ieee80211_scan_completed(&local->hw); + ieee80211_scan_completed(&local->hw, true); } conf.vif = &sdata->vif; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 956afea4214d..954edfbb6b6f 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -733,6 +733,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, return NULL; wiphy->privid = mac80211_wiphy_privid; + wiphy->max_scan_ssids = 4; local = wiphy_priv(wiphy); local->hw.wiphy = wiphy; @@ -817,25 +818,33 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) enum ieee80211_band band; struct net_device *mdev; struct ieee80211_master_priv *mpriv; + int channels, i, j; /* * generic code guarantees at least one band, * set this very early because much code assumes * that hw.conf.channel is assigned */ + channels = 0; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband; sband = local->hw.wiphy->bands[band]; - if (sband) { + if (sband && !local->oper_channel) { /* init channel we're on */ local->hw.conf.channel = local->oper_channel = local->scan_channel = &sband->channels[0]; - break; } + if (sband) + channels += sband->n_channels; } + local->int_scan_req.n_channels = channels; + local->int_scan_req.channels = kzalloc(sizeof(void *) * channels, GFP_KERNEL); + if (!local->int_scan_req.channels) + return -ENOMEM; + /* if low-level driver supports AP, we also support VLAN */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); @@ -845,7 +854,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) result = wiphy_register(local->hw.wiphy); if (result < 0) - return result; + goto fail_wiphy_register; /* * We use the number of queues for feature tests (QoS, HT) internally @@ -948,6 +957,20 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) ieee80211_led_init(local); + /* alloc internal scan request */ + i = 0; + local->int_scan_req.ssids = &local->scan_ssid; + local->int_scan_req.n_ssids = 1; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!hw->wiphy->bands[band]) + continue; + for (j = 0; j < hw->wiphy->bands[band]->n_channels; j++) { + local->int_scan_req.channels[i] = + &hw->wiphy->bands[band]->channels[j]; + i++; + } + } + return 0; fail_wep: @@ -966,6 +989,8 @@ fail_workqueue: free_netdev(local->mdev); fail_mdev_alloc: wiphy_unregister(local->hw.wiphy); +fail_wiphy_register: + kfree(local->int_scan_req.channels); return result; } EXPORT_SYMBOL(ieee80211_register_hw); @@ -1011,6 +1036,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) ieee80211_wep_free(local); ieee80211_led_exit(local); free_netdev(local->mdev); + kfree(local->int_scan_req.channels); } EXPORT_SYMBOL(ieee80211_unregister_hw); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bfc47b330687..46b4817cdea9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1743,7 +1743,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, - freq, beacon); + channel, beacon); if (!bss) return; @@ -2162,7 +2162,15 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other " "IBSS networks with same SSID (merge)\n", sdata->dev->name); - ieee80211_request_scan(sdata, ifsta->ssid, ifsta->ssid_len); + + /* XXX maybe racy? */ + if (sdata->local->scan_req) + return; + + memcpy(sdata->local->int_scan_req.ssids[0].ssid, + ifsta->ssid, IEEE80211_MAX_SSID_LEN); + sdata->local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len; + ieee80211_request_scan(sdata, &sdata->local->int_scan_req); } @@ -2378,8 +2386,15 @@ dont_join: IEEE80211_SCAN_INTERVAL)) { printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to " "join\n", sdata->dev->name); - return ieee80211_request_scan(sdata, ifsta->ssid, - ifsta->ssid_len); + + /* XXX maybe racy? */ + if (local->scan_req) + return -EBUSY; + + memcpy(local->int_scan_req.ssids[0].ssid, + ifsta->ssid, IEEE80211_MAX_SSID_LEN); + local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len; + return ieee80211_request_scan(sdata, &local->int_scan_req); } else if (ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED) { int interval = IEEE80211_SCAN_INTERVAL; @@ -2478,11 +2493,16 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata, } else { if (ifsta->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) { ifsta->assoc_scan_tries++; + /* XXX maybe racy? */ + if (local->scan_req) + return -1; + memcpy(local->int_scan_req.ssids[0].ssid, + ifsta->ssid, IEEE80211_MAX_SSID_LEN); if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) - ieee80211_start_scan(sdata, NULL, 0); + local->int_scan_req.ssids[0].ssid_len = 0; else - ieee80211_start_scan(sdata, ifsta->ssid, - ifsta->ssid_len); + local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len; + ieee80211_start_scan(sdata, &local->int_scan_req); ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE; set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request); } else { @@ -2520,8 +2540,7 @@ static void ieee80211_sta_work(struct work_struct *work) ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE && ifsta->state != IEEE80211_STA_MLME_ASSOCIATE && test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) { - ieee80211_start_scan(sdata, ifsta->scan_ssid, - ifsta->scan_ssid_len); + ieee80211_start_scan(sdata, local->scan_req); return; } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index eddca4e1e13c..c6b275b10cf9 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -13,6 +13,9 @@ */ /* TODO: + * figure out how to avoid that the "current BSS" expires + * clean up IBSS code (in MLME), see why it adds a BSS to the list + * use cfg80211's BSS handling (depends on IBSS TODO above) * order BSS list by RSSI(?) ("quality of AP") * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE, * SSID) @@ -225,10 +228,26 @@ ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_mgmt *mgmt, size_t len, struct ieee802_11_elems *elems, - int freq, bool beacon) + struct ieee80211_channel *channel, + bool beacon) { struct ieee80211_bss *bss; - int clen; + int clen, freq = channel->center_freq; + enum cfg80211_signal_type sigtype = CFG80211_SIGNAL_TYPE_NONE; + s32 signal = 0; + + if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { + sigtype = CFG80211_SIGNAL_TYPE_MBM; + signal = rx_status->signal * 100; + } else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) { + sigtype = CFG80211_SIGNAL_TYPE_UNSPEC; + signal = (rx_status->signal * 100) / local->hw.max_signal; + } + + cfg80211_put_bss( + cfg80211_inform_bss_frame(local->hw.wiphy, channel, + mgmt, len, signal, sigtype, + GFP_ATOMIC)); #ifdef CONFIG_MAC80211_MESH if (elems->mesh_config) @@ -401,7 +420,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bss = ieee80211_bss_info_update(sdata->local, rx_status, mgmt, skb->len, &elems, - freq, beacon); + channel, beacon); if (bss) ieee80211_rx_bss_put(sdata->local, bss); @@ -439,26 +458,22 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, ieee80211_tx_skb(sdata, skb, 0); } -void ieee80211_scan_completed(struct ieee80211_hw *hw) +void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; - union iwreq_data wrqu; if (WARN_ON(!local->hw_scanning && !local->sw_scanning)) return; - local->last_scan_completed = jiffies; - memset(&wrqu, 0, sizeof(wrqu)); + if (WARN_ON(!local->scan_req)) + return; - /* - * local->scan_sdata could have been NULLed by the interface - * down code in case we were scanning on an interface that is - * being taken down. - */ - sdata = local->scan_sdata; - if (sdata) - wireless_send_event(sdata->dev, SIOCGIWSCAN, &wrqu, NULL); + if (local->scan_req != &local->int_scan_req) + cfg80211_scan_done(local->scan_req, aborted); + local->scan_req = NULL; + + local->last_scan_completed = jiffies; if (local->hw_scanning) { local->hw_scanning = false; @@ -520,9 +535,8 @@ void ieee80211_scan_work(struct work_struct *work) struct ieee80211_local *local = container_of(work, struct ieee80211_local, scan_work.work); struct ieee80211_sub_if_data *sdata = local->scan_sdata; - struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; - int skip; + int skip, i; unsigned long next_delay = 0; /* @@ -533,33 +547,13 @@ void ieee80211_scan_work(struct work_struct *work) switch (local->scan_state) { case SCAN_SET_CHANNEL: - /* - * Get current scan band. scan_band may be IEEE80211_NUM_BANDS - * after we successfully scanned the last channel of the last - * band (and the last band is supported by the hw) - */ - if (local->scan_band < IEEE80211_NUM_BANDS) - sband = local->hw.wiphy->bands[local->scan_band]; - else - sband = NULL; - - /* - * If we are at an unsupported band and have more bands - * left to scan, advance to the next supported one. - */ - while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) { - local->scan_band++; - sband = local->hw.wiphy->bands[local->scan_band]; - local->scan_channel_idx = 0; - } - /* if no more bands/channels left, complete scan */ - if (!sband || local->scan_channel_idx >= sband->n_channels) { - ieee80211_scan_completed(local_to_hw(local)); + if (local->scan_channel_idx >= local->scan_req->n_channels) { + ieee80211_scan_completed(local_to_hw(local), false); return; } skip = 0; - chan = &sband->channels[local->scan_channel_idx]; + chan = local->scan_req->channels[local->scan_channel_idx]; if (chan->flags & IEEE80211_CHAN_DISABLED || (sdata->vif.type == NL80211_IFTYPE_ADHOC && @@ -575,15 +569,6 @@ void ieee80211_scan_work(struct work_struct *work) /* advance state machine to next channel/band */ local->scan_channel_idx++; - if (local->scan_channel_idx >= sband->n_channels) { - /* - * scan_band may end up == IEEE80211_NUM_BANDS, but - * we'll catch that case above and complete the scan - * if that is the case. - */ - local->scan_band++; - local->scan_channel_idx = 0; - } if (skip) break; @@ -596,10 +581,14 @@ void ieee80211_scan_work(struct work_struct *work) next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; local->scan_state = SCAN_SET_CHANNEL; - if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN) + if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN || + !local->scan_req->n_ssids) break; - ieee80211_send_probe_req(sdata, NULL, local->scan_ssid, - local->scan_ssid_len); + for (i = 0; i < local->scan_req->n_ssids; i++) + ieee80211_send_probe_req( + sdata, NULL, + local->scan_req->ssids[i].ssid, + local->scan_req->ssids[i].ssid_len); next_delay = IEEE80211_CHANNEL_TIME; break; } @@ -610,14 +599,19 @@ void ieee80211_scan_work(struct work_struct *work) int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, - u8 *ssid, size_t ssid_len) + struct cfg80211_scan_request *req) { struct ieee80211_local *local = scan_sdata->local; struct ieee80211_sub_if_data *sdata; - if (ssid_len > IEEE80211_MAX_SSID_LEN) + if (!req) return -EINVAL; + if (local->scan_req && local->scan_req != req) + return -EBUSY; + + local->scan_req = req; + /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS * BSSID: MACAddress @@ -645,7 +639,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, int rc; local->hw_scanning = true; - rc = local->ops->hw_scan(local_to_hw(local), ssid, ssid_len); + rc = local->ops->hw_scan(local_to_hw(local), req); if (rc) { local->hw_scanning = false; return rc; @@ -678,15 +672,10 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, } mutex_unlock(&local->iflist_mtx); - if (ssid) { - local->scan_ssid_len = ssid_len; - memcpy(local->scan_ssid, ssid, ssid_len); - } else - local->scan_ssid_len = 0; local->scan_state = SCAN_SET_CHANNEL; local->scan_channel_idx = 0; - local->scan_band = IEEE80211_BAND_2GHZ; local->scan_sdata = scan_sdata; + local->scan_req = req; netif_addr_lock_bh(local->mdev); local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; @@ -706,13 +695,21 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, - u8 *ssid, size_t ssid_len) + struct cfg80211_scan_request *req) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_sta *ifsta; + if (!req) + return -EINVAL; + + if (local->scan_req && local->scan_req != req) + return -EBUSY; + + local->scan_req = req; + if (sdata->vif.type != NL80211_IFTYPE_STATION) - return ieee80211_start_scan(sdata, ssid, ssid_len); + return ieee80211_start_scan(sdata, req); /* * STA has a state machine that might need to defer scanning @@ -727,241 +724,8 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, } ifsta = &sdata->u.sta; - - ifsta->scan_ssid_len = ssid_len; - if (ssid_len) - memcpy(ifsta->scan_ssid, ssid, ssid_len); set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request); queue_work(local->hw.workqueue, &ifsta->work); return 0; } - - -static void ieee80211_scan_add_ies(struct iw_request_info *info, - struct ieee80211_bss *bss, - char **current_ev, char *end_buf) -{ - u8 *pos, *end, *next; - struct iw_event iwe; - - if (bss == NULL || bss->ies == NULL) - return; - - /* - * If needed, fragment the IEs buffer (at IE boundaries) into short - * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. - */ - pos = bss->ies; - end = pos + bss->ies_len; - - while (end - pos > IW_GENERIC_IE_MAX) { - next = pos + 2 + pos[1]; - while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX) - next = next + 2 + next[1]; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = next - pos; - *current_ev = iwe_stream_add_point(info, *current_ev, - end_buf, &iwe, pos); - - pos = next; - } - - if (end > pos) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = end - pos; - *current_ev = iwe_stream_add_point(info, *current_ev, - end_buf, &iwe, pos); - } -} - - -static char * -ieee80211_scan_result(struct ieee80211_local *local, - struct iw_request_info *info, - struct ieee80211_bss *bss, - char *current_ev, char *end_buf) -{ - struct iw_event iwe; - char *buf; - - if (time_after(jiffies, - bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE)) - return current_ev; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_ADDR_LEN); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWESSID; - if (bss_mesh_cfg(bss)) { - iwe.u.data.length = bss_mesh_id_len(bss); - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss_mesh_id(bss)); - } else { - iwe.u.data.length = bss->ssid_len; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss->ssid); - } - - if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) - || bss_mesh_cfg(bss)) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWMODE; - if (bss_mesh_cfg(bss)) - iwe.u.mode = IW_MODE_MESH; - else if (bss->capability & WLAN_CAPABILITY_ESS) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_UINT_LEN); - } - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq); - iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = bss->freq; - iwe.u.freq.e = 6; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVQUAL; - iwe.u.qual.qual = bss->qual; - iwe.u.qual.level = bss->signal; - iwe.u.qual.noise = bss->noise; - iwe.u.qual.updated = local->wstats_flags; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_QUAL_LEN); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWENCODE; - if (bss->capability & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ""); - - ieee80211_scan_add_ies(info, bss, ¤t_ev, end_buf); - - if (bss->supp_rates_len > 0) { - /* display all supported rates in readable format */ - char *p = current_ev + iwe_stream_lcp_len(info); - int i; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWRATE; - /* Those two flags are ignored... */ - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - - for (i = 0; i < bss->supp_rates_len; i++) { - iwe.u.bitrate.value = ((bss->supp_rates[i] & - 0x7f) * 500000); - p = iwe_stream_add_value(info, current_ev, p, - end_buf, &iwe, IW_EV_PARAM_LEN); - } - current_ev = p; - } - - buf = kmalloc(30, GFP_ATOMIC); - if (buf) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, buf); - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, " Last beacon: %dms ago", - jiffies_to_msecs(jiffies - bss->last_update)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, &iwe, buf); - kfree(buf); - } - - if (bss_mesh_cfg(bss)) { - u8 *cfg = bss_mesh_cfg(bss); - buf = kmalloc(50, GFP_ATOMIC); - if (buf) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "Mesh network (version %d)", cfg[0]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Path Selection Protocol ID: " - "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], - cfg[4]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Path Selection Metric ID: " - "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], - cfg[8]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Congestion Control Mode ID: " - "0x%02X%02X%02X%02X", cfg[9], cfg[10], - cfg[11], cfg[12]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Channel Precedence: " - "0x%02X%02X%02X%02X", cfg[13], cfg[14], - cfg[15], cfg[16]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - kfree(buf); - } - } - - return current_ev; -} - - -int ieee80211_scan_results(struct ieee80211_local *local, - struct iw_request_info *info, - char *buf, size_t len) -{ - char *current_ev = buf; - char *end_buf = buf + len; - struct ieee80211_bss *bss; - - spin_lock_bh(&local->bss_lock); - list_for_each_entry(bss, &local->bss_list, list) { - if (buf + len - current_ev <= IW_EV_ADDR_LEN) { - spin_unlock_bh(&local->bss_lock); - return -E2BIG; - } - current_ev = ieee80211_scan_result(local, info, bss, - current_ev, end_buf); - } - spin_unlock_bh(&local->bss_lock); - return current_ev - buf; -} diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index acd5808b87f4..b337d7d5edb3 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -173,8 +173,9 @@ static int ieee80211_ioctl_giwrange(struct net_device *dev, range->num_encoding_sizes = 2; range->max_encoding_tokens = NUM_DEFAULT_KEYS; + /* cfg80211 requires this, and enforces 0..100 */ if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) - range->max_qual.level = local->hw.max_signal; + range->max_qual.level = 100; else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) range->max_qual.level = -110; else @@ -415,58 +416,6 @@ static int ieee80211_ioctl_giwap(struct net_device *dev, } -static int ieee80211_ioctl_siwscan(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct iw_scan_req *req = NULL; - u8 *ssid = NULL; - size_t ssid_len = 0; - - if (!netif_running(dev)) - return -ENETDOWN; - - if (sdata->vif.type != NL80211_IFTYPE_STATION && - sdata->vif.type != NL80211_IFTYPE_ADHOC && - sdata->vif.type != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - /* if SSID was specified explicitly then use that */ - if (wrqu->data.length == sizeof(struct iw_scan_req) && - wrqu->data.flags & IW_SCAN_THIS_ESSID) { - req = (struct iw_scan_req *)extra; - ssid = req->essid; - ssid_len = req->essid_len; - } - - return ieee80211_request_scan(sdata, ssid, ssid_len); -} - - -static int ieee80211_ioctl_giwscan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - int res; - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_sub_if_data *sdata; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - if (local->sw_scanning || local->hw_scanning) - return -EAGAIN; - - res = ieee80211_scan_results(local, info, extra, data->length); - if (res >= 0) { - data->length = res; - return 0; - } - data->length = 0; - return res; -} - - static int ieee80211_ioctl_siwrate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rate, char *extra) @@ -1165,8 +1114,8 @@ static const iw_handler ieee80211_handler[] = (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ (iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */ (iw_handler) NULL, /* SIOCGIWAPLIST */ - (iw_handler) ieee80211_ioctl_siwscan, /* SIOCSIWSCAN */ - (iw_handler) ieee80211_ioctl_giwscan, /* SIOCGIWSCAN */ + (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */ + (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */ (iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */ (iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */ (iw_handler) NULL, /* SIOCSIWNICKN */ diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 938a334c8dbc..dad43c24f695 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o -cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o +cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o cfg80211-$(CONFIG_NL80211) += nl80211.o diff --git a/net/wireless/core.c b/net/wireless/core.c index 125226476089..3cccd1390cea 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -240,6 +240,8 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv) mutex_init(&drv->mtx); mutex_init(&drv->devlist_mtx); INIT_LIST_HEAD(&drv->netdev_list); + spin_lock_init(&drv->bss_lock); + INIT_LIST_HEAD(&drv->bss_list); device_initialize(&drv->wiphy.dev); drv->wiphy.dev.class = &ieee80211_class; @@ -259,6 +261,9 @@ int wiphy_register(struct wiphy *wiphy) int i; u16 ifmodes = wiphy->interface_modes; + if (WARN_ON(wiphy->max_scan_ssids < 1)) + return -EINVAL; + /* sanity check ifmodes */ WARN_ON(!ifmodes); ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; @@ -367,8 +372,11 @@ EXPORT_SYMBOL(wiphy_unregister); void cfg80211_dev_free(struct cfg80211_registered_device *drv) { + struct cfg80211_internal_bss *scan, *tmp; mutex_destroy(&drv->mtx); mutex_destroy(&drv->devlist_mtx); + list_for_each_entry_safe(scan, tmp, &drv->bss_list, list) + kfree(scan); kfree(drv); } diff --git a/net/wireless/core.h b/net/wireless/core.h index f7fb9f413028..e29ad4cd464f 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -41,6 +43,13 @@ struct cfg80211_registered_device { struct mutex devlist_mtx; struct list_head netdev_list; + /* BSSes/scanning */ + spinlock_t bss_lock; + struct list_head bss_list; + struct rb_root bss_tree; + u32 bss_generation; + struct cfg80211_scan_request *scan_req; /* protected by RTNL */ + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); @@ -56,6 +65,15 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) extern struct mutex cfg80211_drv_mutex; extern struct list_head cfg80211_drv_list; +struct cfg80211_internal_bss { + struct list_head list; + struct rb_node rbn; + unsigned long ts; + struct kref ref; + /* must be last because of priv member */ + struct cfg80211_bss pub; +}; + /* * This function returns a pointer to the driver * that the genl_info item that is passed refers to. @@ -94,4 +112,6 @@ extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, void ieee80211_set_bitrate_flags(struct wiphy *wiphy); void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby); +void cfg80211_bss_expire(struct cfg80211_registered_device *dev); + #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d452396006ee..298a4de59948 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include "core.h" @@ -109,6 +110,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, [NL80211_ATTR_IE] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, + [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, }; /* message building helper */ @@ -141,6 +144,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx); NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); + NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + dev->wiphy.max_scan_ssids); nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); if (!nl_modes) @@ -2270,6 +2275,246 @@ static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, return err; } +static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct cfg80211_scan_request *request; + struct cfg80211_ssid *ssid; + struct ieee80211_channel *channel; + struct nlattr *attr; + struct wiphy *wiphy; + int err, tmp, n_ssids = 0, n_channels = 0, i; + enum ieee80211_band band; + + err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); + if (err) + return err; + + wiphy = &drv->wiphy; + + if (!drv->ops->scan) { + err = -EOPNOTSUPP; + goto out; + } + + rtnl_lock(); + + if (drv->scan_req) { + err = -EBUSY; + goto out_unlock; + } + + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) + n_channels++; + if (!n_channels) { + err = -EINVAL; + goto out_unlock; + } + } else { + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + } + + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) + n_ssids++; + + if (n_ssids > wiphy->max_scan_ssids) { + err = -EINVAL; + goto out_unlock; + } + + request = kzalloc(sizeof(*request) + + sizeof(*ssid) * n_ssids + + sizeof(channel) * n_channels, GFP_KERNEL); + if (!request) { + err = -ENOMEM; + goto out_unlock; + } + + request->channels = (void *)((char *)request + sizeof(*request)); + request->n_channels = n_channels; + if (n_ssids) + request->ssids = (void *)(request->channels + n_channels); + request->n_ssids = n_ssids; + + if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { + /* user specified, bail out if channel not found */ + request->n_channels = n_channels; + i = 0; + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { + request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr)); + if (!request->channels[i]) { + err = -EINVAL; + goto out_free; + } + i++; + } + } else { + /* all channels */ + i = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + if (!wiphy->bands[band]) + continue; + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + request->channels[i] = &wiphy->bands[band]->channels[j]; + i++; + } + } + } + + i = 0; + if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { + if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out_free; + } + memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr)); + request->ssids[i].ssid_len = nla_len(attr); + i++; + } + } + + request->ifidx = dev->ifindex; + request->wiphy = &drv->wiphy; + + drv->scan_req = request; + err = drv->ops->scan(&drv->wiphy, dev, request); + + out_free: + if (err) { + drv->scan_req = NULL; + kfree(request); + } + out_unlock: + rtnl_unlock(); + out: + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + +static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, + struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_bss *res) +{ + void *hdr; + struct nlattr *bss; + + hdr = nl80211hdr_put(msg, pid, seq, flags, + NL80211_CMD_NEW_SCAN_RESULTS); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION, + rdev->bss_generation); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + + bss = nla_nest_start(msg, NL80211_ATTR_BSS); + if (!bss) + goto nla_put_failure; + if (!is_zero_ether_addr(res->bssid)) + NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid); + if (res->information_elements && res->len_information_elements) + NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, + res->len_information_elements, + res->information_elements); + if (res->tsf) + NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); + if (res->beacon_interval) + NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); + NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); + NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); + + switch (res->signal_type) { + case CFG80211_SIGNAL_TYPE_MBM: + NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal); + break; + case CFG80211_SIGNAL_TYPE_UNSPEC: + NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal); + break; + default: + break; + } + + nla_nest_end(msg, bss); + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nl80211_dump_scan(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct cfg80211_registered_device *dev; + struct net_device *netdev; + struct cfg80211_internal_bss *scan; + int ifidx = cb->args[0]; + int start = cb->args[1], idx = 0; + int err; + + if (!ifidx) { + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, + nl80211_fam.attrbuf, nl80211_fam.maxattr, + nl80211_policy); + if (err) + return err; + + if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) + return -EINVAL; + + ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); + if (!ifidx) + return -EINVAL; + cb->args[0] = ifidx; + } + + netdev = dev_get_by_index(&init_net, ifidx); + if (!netdev) + return -ENODEV; + + dev = cfg80211_get_dev_from_ifindex(ifidx); + if (IS_ERR(dev)) { + err = PTR_ERR(dev); + goto out_put_netdev; + } + + spin_lock_bh(&dev->bss_lock); + cfg80211_bss_expire(dev); + + list_for_each_entry(scan, &dev->bss_list, list) { + if (++idx <= start) + continue; + if (nl80211_send_bss(skb, + NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + dev, netdev, &scan->pub) < 0) { + idx--; + goto out; + } + } + + out: + spin_unlock_bh(&dev->bss_lock); + + cb->args[1] = idx; + err = skb->len; + cfg80211_put_dev(dev); + out_put_netdev: + dev_put(netdev); + + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -2443,12 +2688,26 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_TRIGGER_SCAN, + .doit = nl80211_trigger_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_GET_SCAN, + .policy = nl80211_policy, + .dumpit = nl80211_dump_scan, + }, }; /* multicast groups */ static struct genl_multicast_group nl80211_config_mcgrp = { .name = "config", }; +static struct genl_multicast_group nl80211_scan_mcgrp = { + .name = "scan", +}; /* notification functions */ @@ -2468,6 +2727,66 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); } +static int nl80211_send_scan_donemsg(struct sk_buff *msg, + struct cfg80211_registered_device *rdev, + struct net_device *netdev, + u32 pid, u32 seq, int flags, + u32 cmd) +{ + void *hdr; + + hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); + if (!hdr) + return -1; + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + /* XXX: we should probably bounce back the request? */ + + return genlmsg_end(msg, hdr); + + nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_NEW_SCAN_RESULTS) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); +} + +void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{ + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0, + NL80211_CMD_SCAN_ABORTED) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); +} + /* initialisation/exit functions */ int nl80211_init(void) @@ -2488,6 +2807,10 @@ int nl80211_init(void) if (err) goto err_out; + err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp); + if (err) + goto err_out; + return 0; err_out: genl_unregister_family(&nl80211_fam); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index f3ea5c029aee..b565a5f84e97 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -7,6 +7,10 @@ extern int nl80211_init(void); extern void nl80211_exit(void); extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); +extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev); +extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, + struct net_device *netdev); #else static inline int nl80211_init(void) { @@ -19,6 +23,10 @@ static inline void nl80211_notify_dev_rename( struct cfg80211_registered_device *rdev) { } +static inline void +nl80211_send_scan_done(struct cfg80211_registered_device *rdev, + struct net_device *netdev) +{} #endif /* CONFIG_NL80211 */ #endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/scan.c b/net/wireless/scan.c new file mode 100644 index 000000000000..009d12810c55 --- /dev/null +++ b/net/wireless/scan.c @@ -0,0 +1,807 @@ +/* + * cfg80211 scan result handling + * + * Copyright 2008 Johannes Berg + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "nl80211.h" + +#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ) + +void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) +{ + struct net_device *dev; +#ifdef CONFIG_WIRELESS_EXT + union iwreq_data wrqu; +#endif + + dev = dev_get_by_index(&init_net, request->ifidx); + if (!dev) + goto out; + + WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); + wiphy_to_dev(request->wiphy)->scan_req = NULL; + + if (aborted) + nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); + else + nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev); + +#ifdef CONFIG_WIRELESS_EXT + if (!aborted) { + memset(&wrqu, 0, sizeof(wrqu)); + + wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); + } +#endif + + dev_put(dev); + + out: + kfree(request); +} +EXPORT_SYMBOL(cfg80211_scan_done); + +static void bss_release(struct kref *ref) +{ + struct cfg80211_internal_bss *bss; + + bss = container_of(ref, struct cfg80211_internal_bss, ref); + kfree(bss); +} + +/* must hold dev->bss_lock! */ +void cfg80211_bss_expire(struct cfg80211_registered_device *dev) +{ + struct cfg80211_internal_bss *bss, *tmp; + bool expired = false; + + list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { + if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) + continue; + list_del(&bss->list); + rb_erase(&bss->rbn, &dev->bss_tree); + kref_put(&bss->ref, bss_release); + expired = true; + } + + if (expired) + dev->bss_generation++; +} + +static u8 *find_ie(u8 num, u8 *ies, size_t len) +{ + while (len > 2 && ies[0] != num) { + len -= ies[1] + 2; + ies += ies[1] + 2; + } + if (len < 2) + return NULL; + if (len < 2 + ies[1]) + return NULL; + return ies; +} + +static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) +{ + const u8 *ie1 = find_ie(num, ies1, len1); + const u8 *ie2 = find_ie(num, ies2, len2); + int r; + + if (!ie1 && !ie2) + return 0; + if (!ie1) + return -1; + + r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1])); + if (r == 0 && ie1[1] != ie2[1]) + return ie2[1] - ie1[1]; + return r; +} + +static bool is_bss(struct cfg80211_bss *a, + const u8 *bssid, + const u8 *ssid, size_t ssid_len) +{ + const u8 *ssidie; + + if (compare_ether_addr(a->bssid, bssid)) + return false; + + ssidie = find_ie(WLAN_EID_SSID, + a->information_elements, + a->len_information_elements); + if (!ssidie) + return false; + if (ssidie[1] != ssid_len) + return false; + return memcmp(ssidie + 2, ssid, ssid_len) == 0; +} + +static bool is_mesh(struct cfg80211_bss *a, + const u8 *meshid, size_t meshidlen, + const u8 *meshcfg) +{ + const u8 *ie; + + if (!is_zero_ether_addr(a->bssid)) + return false; + + ie = find_ie(WLAN_EID_MESH_ID, + a->information_elements, + a->len_information_elements); + if (!ie) + return false; + if (ie[1] != meshidlen) + return false; + if (memcmp(ie + 2, meshid, meshidlen)) + return false; + + ie = find_ie(WLAN_EID_MESH_CONFIG, + a->information_elements, + a->len_information_elements); + if (ie[1] != IEEE80211_MESH_CONFIG_LEN) + return false; + + /* + * Ignore mesh capability (last two bytes of the IE) when + * comparing since that may differ between stations taking + * part in the same mesh. + */ + return memcmp(ie + 2, meshcfg, IEEE80211_MESH_CONFIG_LEN - 2) == 0; +} + +static int cmp_bss(struct cfg80211_bss *a, + struct cfg80211_bss *b) +{ + int r; + + if (a->channel != b->channel) + return b->channel->center_freq - a->channel->center_freq; + + r = memcmp(a->bssid, b->bssid, ETH_ALEN); + if (r) + return r; + + if (is_zero_ether_addr(a->bssid)) { + r = cmp_ies(WLAN_EID_MESH_ID, + a->information_elements, + a->len_information_elements, + b->information_elements, + b->len_information_elements); + if (r) + return r; + return cmp_ies(WLAN_EID_MESH_CONFIG, + a->information_elements, + a->len_information_elements, + b->information_elements, + b->len_information_elements); + } + + return cmp_ies(WLAN_EID_SSID, + a->information_elements, + a->len_information_elements, + b->information_elements, + b->len_information_elements); +} + +struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *bssid, + const u8 *ssid, size_t ssid_len) +{ + struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); + struct cfg80211_internal_bss *bss, *res = NULL; + + spin_lock_bh(&dev->bss_lock); + + list_for_each_entry(bss, &dev->bss_list, list) { + if (channel && bss->pub.channel != channel) + continue; + if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { + res = bss; + kref_get(&res->ref); + break; + } + } + + spin_unlock_bh(&dev->bss_lock); + if (!res) + return NULL; + return &res->pub; +} +EXPORT_SYMBOL(cfg80211_get_bss); + +struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, + struct ieee80211_channel *channel, + const u8 *meshid, size_t meshidlen, + const u8 *meshcfg) +{ + struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); + struct cfg80211_internal_bss *bss, *res = NULL; + + spin_lock_bh(&dev->bss_lock); + + list_for_each_entry(bss, &dev->bss_list, list) { + if (channel && bss->pub.channel != channel) + continue; + if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { + res = bss; + kref_get(&res->ref); + break; + } + } + + spin_unlock_bh(&dev->bss_lock); + if (!res) + return NULL; + return &res->pub; +} +EXPORT_SYMBOL(cfg80211_get_mesh); + + +static void rb_insert_bss(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *bss) +{ + struct rb_node **p = &dev->bss_tree.rb_node; + struct rb_node *parent = NULL; + struct cfg80211_internal_bss *tbss; + int cmp; + + while (*p) { + parent = *p; + tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); + + cmp = cmp_bss(&bss->pub, &tbss->pub); + + if (WARN_ON(!cmp)) { + /* will sort of leak this BSS */ + return; + } + + if (cmp < 0) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + rb_link_node(&bss->rbn, parent, p); + rb_insert_color(&bss->rbn, &dev->bss_tree); +} + +static struct cfg80211_internal_bss * +rb_find_bss(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *res) +{ + struct rb_node *n = dev->bss_tree.rb_node; + struct cfg80211_internal_bss *bss; + int r; + + while (n) { + bss = rb_entry(n, struct cfg80211_internal_bss, rbn); + r = cmp_bss(&res->pub, &bss->pub); + + if (r == 0) + return bss; + else if (r < 0) + n = n->rb_left; + else + n = n->rb_right; + } + + return NULL; +} + +static struct cfg80211_internal_bss * +cfg80211_bss_update(struct cfg80211_registered_device *dev, + struct cfg80211_internal_bss *res, + bool overwrite) +{ + struct cfg80211_internal_bss *found = NULL; + const u8 *meshid, *meshcfg; + + /* + * The reference to "res" is donated to this function. + */ + + if (WARN_ON(!res->pub.channel)) { + kref_put(&res->ref, bss_release); + return NULL; + } + + res->ts = jiffies; + + if (is_zero_ether_addr(res->pub.bssid)) { + /* must be mesh, verify */ + meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements, + res->pub.len_information_elements); + meshcfg = find_ie(WLAN_EID_MESH_CONFIG, + res->pub.information_elements, + res->pub.len_information_elements); + if (!meshid || !meshcfg || + meshcfg[1] != IEEE80211_MESH_CONFIG_LEN) { + /* bogus mesh */ + kref_put(&res->ref, bss_release); + return NULL; + } + } + + spin_lock_bh(&dev->bss_lock); + + found = rb_find_bss(dev, res); + + if (found && overwrite) { + list_replace(&found->list, &res->list); + rb_replace_node(&found->rbn, &res->rbn, + &dev->bss_tree); + kref_put(&found->ref, bss_release); + found = res; + } else if (found) { + kref_get(&found->ref); + found->pub.beacon_interval = res->pub.beacon_interval; + found->pub.tsf = res->pub.tsf; + found->pub.signal = res->pub.signal; + found->pub.signal_type = res->pub.signal_type; + found->pub.capability = res->pub.capability; + found->ts = res->ts; + kref_put(&res->ref, bss_release); + } else { + /* this "consumes" the reference */ + list_add_tail(&res->list, &dev->bss_list); + rb_insert_bss(dev, res); + found = res; + } + + dev->bss_generation++; + spin_unlock_bh(&dev->bss_lock); + + kref_get(&found->ref); + return found; +} + +struct cfg80211_bss * +cfg80211_inform_bss_frame(struct wiphy *wiphy, + struct ieee80211_channel *channel, + struct ieee80211_mgmt *mgmt, size_t len, + s32 signal, enum cfg80211_signal_type sigtype, + gfp_t gfp) +{ + struct cfg80211_internal_bss *res; + size_t ielen = len - offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + bool overwrite; + size_t privsz = wiphy->bss_priv_size; + + if (WARN_ON(sigtype == NL80211_BSS_SIGNAL_UNSPEC && + (signal < 0 || signal > 100))) + return NULL; + + if (WARN_ON(!mgmt || !wiphy || + len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) + return NULL; + + res = kzalloc(sizeof(*res) + privsz + ielen, gfp); + if (!res) + return NULL; + + memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); + res->pub.channel = channel; + res->pub.signal_type = sigtype; + res->pub.signal = signal; + res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); + res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); + res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); + /* point to after the private area */ + res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; + memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen); + res->pub.len_information_elements = ielen; + + kref_init(&res->ref); + + overwrite = ieee80211_is_probe_resp(mgmt->frame_control); + + res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite); + if (!res) + return NULL; + + /* cfg80211_bss_update gives us a referenced result */ + return &res->pub; +} +EXPORT_SYMBOL(cfg80211_inform_bss_frame); + +void cfg80211_put_bss(struct cfg80211_bss *pub) +{ + struct cfg80211_internal_bss *bss; + + if (!pub) + return; + + bss = container_of(pub, struct cfg80211_internal_bss, pub); + kref_put(&bss->ref, bss_release); +} +EXPORT_SYMBOL(cfg80211_put_bss); + +#ifdef CONFIG_WIRELESS_EXT +int cfg80211_wext_siwscan(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct cfg80211_registered_device *rdev; + struct wiphy *wiphy; + struct iw_scan_req *wreq = NULL; + struct cfg80211_scan_request *creq; + int i, err, n_channels = 0; + enum ieee80211_band band; + + if (!netif_running(dev)) + return -ENETDOWN; + + rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); + + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + if (rdev->scan_req) { + err = -EBUSY; + goto out; + } + + wiphy = &rdev->wiphy; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + if (wiphy->bands[band]) + n_channels += wiphy->bands[band]->n_channels; + + creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + + n_channels * sizeof(void *), + GFP_ATOMIC); + if (!creq) { + err = -ENOMEM; + goto out; + } + + creq->wiphy = wiphy; + creq->ifidx = dev->ifindex; + creq->ssids = (void *)(creq + 1); + creq->channels = (void *)(creq->ssids + 1); + creq->n_channels = n_channels; + creq->n_ssids = 1; + + /* all channels */ + i = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + if (!wiphy->bands[band]) + continue; + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + creq->channels[i] = &wiphy->bands[band]->channels[j]; + i++; + } + } + + /* translate scan request */ + if (wrqu->data.length == sizeof(struct iw_scan_req)) { + wreq = (struct iw_scan_req *)extra; + + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { + if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len); + creq->ssids[0].ssid_len = wreq->essid_len; + } + if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE) + creq->n_ssids = 0; + } + + rdev->scan_req = creq; + err = rdev->ops->scan(wiphy, dev, creq); + if (err) { + rdev->scan_req = NULL; + kfree(creq); + } + out: + cfg80211_put_dev(rdev); + return err; +} +EXPORT_SYMBOL(cfg80211_wext_siwscan); + +static void ieee80211_scan_add_ies(struct iw_request_info *info, + struct cfg80211_bss *bss, + char **current_ev, char *end_buf) +{ + u8 *pos, *end, *next; + struct iw_event iwe; + + if (!bss->information_elements || + !bss->len_information_elements) + return; + + /* + * If needed, fragment the IEs buffer (at IE boundaries) into short + * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. + */ + pos = bss->information_elements; + end = pos + bss->len_information_elements; + + while (end - pos > IW_GENERIC_IE_MAX) { + next = pos + 2 + pos[1]; + while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX) + next = next + 2 + next[1]; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = next - pos; + *current_ev = iwe_stream_add_point(info, *current_ev, + end_buf, &iwe, pos); + + pos = next; + } + + if (end > pos) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = end - pos; + *current_ev = iwe_stream_add_point(info, *current_ev, + end_buf, &iwe, pos); + } +} + + +static char * +ieee80211_bss(struct iw_request_info *info, + struct cfg80211_internal_bss *bss, + char *current_ev, char *end_buf) +{ + struct iw_event iwe; + u8 *buf, *cfg, *p; + u8 *ie = bss->pub.information_elements; + int rem = bss->pub.len_information_elements, i; + bool ismesh = false; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq); + iwe.u.freq.e = 0; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = bss->pub.channel->center_freq; + iwe.u.freq.e = 6; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + + if (bss->pub.signal_type != CFG80211_SIGNAL_TYPE_NONE) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED | + IW_QUAL_NOISE_INVALID | + IW_QUAL_QUAL_INVALID; + switch (bss->pub.signal_type) { + case CFG80211_SIGNAL_TYPE_MBM: + iwe.u.qual.level = bss->pub.signal / 100; + iwe.u.qual.updated |= IW_QUAL_DBM; + break; + case CFG80211_SIGNAL_TYPE_UNSPEC: + iwe.u.qual.level = bss->pub.signal; + break; + default: + /* not reached */ + break; + } + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ""); + + while (rem >= 2) { + /* invalid data */ + if (ie[1] > rem - 2) + break; + + switch (ie[0]) { + case WLAN_EID_SSID: + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = ie[1]; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ie + 2); + break; + case WLAN_EID_MESH_ID: + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = ie[1]; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ie + 2); + break; + case WLAN_EID_MESH_CONFIG: + ismesh = true; + if (ie[1] != IEEE80211_MESH_CONFIG_LEN) + break; + buf = kmalloc(50, GFP_ATOMIC); + if (!buf) + break; + cfg = ie + 2; + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "Mesh network (version %d)", cfg[0]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Path Selection Protocol ID: " + "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], + cfg[4]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Path Selection Metric ID: " + "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], + cfg[8]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Congestion Control Mode ID: " + "0x%02X%02X%02X%02X", cfg[9], cfg[10], + cfg[11], cfg[12]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + sprintf(buf, "Channel Precedence: " + "0x%02X%02X%02X%02X", cfg[13], cfg[14], + cfg[15], cfg[16]); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, + &iwe, buf); + kfree(buf); + break; + case WLAN_EID_SUPP_RATES: + case WLAN_EID_EXT_SUPP_RATES: + /* display all supported rates in readable format */ + p = current_ev + iwe_stream_lcp_len(info); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + + for (i = 0; i < ie[1]; i++) { + iwe.u.bitrate.value = + ((ie[i + 2] & 0x7f) * 500000); + p = iwe_stream_add_value(info, current_ev, p, + end_buf, &iwe, IW_EV_PARAM_LEN); + } + current_ev = p; + break; + } + rem -= ie[1] + 2; + ie += ie[1] + 2; + } + + if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) + || ismesh) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + if (ismesh) + iwe.u.mode = IW_MODE_MESH; + else if (bss->pub.capability & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); + } + + buf = kmalloc(30, GFP_ATOMIC); + if (buf) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, " Last beacon: %dms ago", + jiffies_to_msecs(jiffies - bss->ts)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); + kfree(buf); + } + + ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf); + + return current_ev; +} + + +static int ieee80211_scan_results(struct cfg80211_registered_device *dev, + struct iw_request_info *info, + char *buf, size_t len) +{ + char *current_ev = buf; + char *end_buf = buf + len; + struct cfg80211_internal_bss *bss; + + spin_lock_bh(&dev->bss_lock); + cfg80211_bss_expire(dev); + + list_for_each_entry(bss, &dev->bss_list, list) { + if (buf + len - current_ev <= IW_EV_ADDR_LEN) { + spin_unlock_bh(&dev->bss_lock); + return -E2BIG; + } + current_ev = ieee80211_bss(info, bss, + current_ev, end_buf); + } + spin_unlock_bh(&dev->bss_lock); + return current_ev - buf; +} + + +int cfg80211_wext_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct cfg80211_registered_device *rdev; + int res; + + if (!netif_running(dev)) + return -ENETDOWN; + + rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); + + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + if (rdev->scan_req) { + res = -EAGAIN; + goto out; + } + + res = ieee80211_scan_results(rdev, info, extra, data->length); + data->length = 0; + if (res >= 0) { + data->length = res; + res = 0; + } + + out: + cfg80211_put_dev(rdev); + return res; +} +EXPORT_SYMBOL(cfg80211_wext_giwscan); +#endif -- cgit v1.2.3 From 35c26c2cf6a6a2d1c48add732d8ba002bd90784c Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sat, 14 Feb 2009 22:56:56 -0800 Subject: rndis: remove private wrapper of __constant_cpu_to_le32 Use cpu_to_le32 directly as it handles constant folding now, replace direct uses of __constant_cpu_to_{endian} as well. Signed-off-by: Harvey Harrison Acked-by: David Brownell Signed-off-by: David S. Miller --- drivers/net/usb/rndis_host.c | 25 +++++------ drivers/net/wireless/rndis_wlan.c | 90 +++++++++++++++++++------------------- drivers/usb/gadget/rndis.c | 92 +++++++++++++++++++-------------------- include/linux/usb/rndis_host.h | 85 +++++++++++++++++------------------- 4 files changed, 143 insertions(+), 149 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index bcd858c567e0..b7f763e1298c 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -169,7 +169,7 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen) struct rndis_keepalive_c *msg = (void *)buf; msg->msg_type = RNDIS_MSG_KEEPALIVE_C; - msg->msg_len = ccpu2(sizeof *msg); + msg->msg_len = cpu_to_le32(sizeof *msg); msg->status = RNDIS_STATUS_SUCCESS; retval = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), @@ -237,7 +237,7 @@ static int rndis_query(struct usbnet *dev, struct usb_interface *intf, u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len); u.get->oid = oid; u.get->len = cpu_to_le32(in_len); - u.get->offset = ccpu2(20); + u.get->offset = cpu_to_le32(20); retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { @@ -297,9 +297,9 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) goto fail; u.init->msg_type = RNDIS_MSG_INIT; - u.init->msg_len = ccpu2(sizeof *u.init); - u.init->major_version = ccpu2(1); - u.init->minor_version = ccpu2(0); + u.init->msg_len = cpu_to_le32(sizeof *u.init); + u.init->major_version = cpu_to_le32(1); + u.init->minor_version = cpu_to_le32(0); /* max transfer (in spec) is 0x4000 at full speed, but for * TX we'll stick to one Ethernet packet plus RNDIS framing. @@ -403,10 +403,10 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) /* set a nonzero filter to enable data transfers */ memset(u.set, 0, sizeof *u.set); u.set->msg_type = RNDIS_MSG_SET; - u.set->msg_len = ccpu2(4 + sizeof *u.set); + u.set->msg_len = cpu_to_le32(4 + sizeof *u.set); u.set->oid = OID_GEN_CURRENT_PACKET_FILTER; - u.set->len = ccpu2(4); - u.set->offset = ccpu2((sizeof *u.set) - 8); + u.set->len = cpu_to_le32(4); + u.set->offset = cpu_to_le32((sizeof *u.set) - 8); *(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER; retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); @@ -423,7 +423,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) halt_fail_and_release: memset(u.halt, 0, sizeof *u.halt); u.halt->msg_type = RNDIS_MSG_HALT; - u.halt->msg_len = ccpu2(sizeof *u.halt); + u.halt->msg_len = cpu_to_le32(sizeof *u.halt); (void) rndis_command(dev, (void *)u.halt, CONTROL_BUFFER_SIZE); fail_and_release: usb_set_intfdata(info->data, NULL); @@ -448,7 +448,7 @@ void rndis_unbind(struct usbnet *dev, struct usb_interface *intf) halt = kzalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); if (halt) { halt->msg_type = RNDIS_MSG_HALT; - halt->msg_len = ccpu2(sizeof *halt); + halt->msg_len = cpu_to_le32(sizeof *halt); (void) rndis_command(dev, (void *)halt, CONTROL_BUFFER_SIZE); kfree(halt); } @@ -543,7 +543,7 @@ fill: memset(hdr, 0, sizeof *hdr); hdr->msg_type = RNDIS_MSG_PACKET; hdr->msg_len = cpu_to_le32(skb->len); - hdr->data_offset = ccpu2(sizeof(*hdr) - 8); + hdr->data_offset = cpu_to_le32(sizeof(*hdr) - 8); hdr->data_len = cpu_to_le32(len); /* FIXME make the last packet always be short ... */ @@ -562,9 +562,6 @@ static const struct driver_info rndis_info = { .tx_fixup = rndis_tx_fixup, }; -#undef ccpu2 - - /*-------------------------------------------------------------------------*/ static const struct usb_device_id products [] = { diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 105f214e21f4..82af21eeb592 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -90,44 +90,44 @@ MODULE_PARM_DESC(workaround_interval, /* various RNDIS OID defs */ -#define OID_GEN_LINK_SPEED ccpu2(0x00010107) -#define OID_GEN_RNDIS_CONFIG_PARAMETER ccpu2(0x0001021b) - -#define OID_GEN_XMIT_OK ccpu2(0x00020101) -#define OID_GEN_RCV_OK ccpu2(0x00020102) -#define OID_GEN_XMIT_ERROR ccpu2(0x00020103) -#define OID_GEN_RCV_ERROR ccpu2(0x00020104) -#define OID_GEN_RCV_NO_BUFFER ccpu2(0x00020105) - -#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101) -#define OID_802_3_CURRENT_ADDRESS ccpu2(0x01010102) -#define OID_802_3_MULTICAST_LIST ccpu2(0x01010103) -#define OID_802_3_MAXIMUM_LIST_SIZE ccpu2(0x01010104) - -#define OID_802_11_BSSID ccpu2(0x0d010101) -#define OID_802_11_SSID ccpu2(0x0d010102) -#define OID_802_11_INFRASTRUCTURE_MODE ccpu2(0x0d010108) -#define OID_802_11_ADD_WEP ccpu2(0x0d010113) -#define OID_802_11_REMOVE_WEP ccpu2(0x0d010114) -#define OID_802_11_DISASSOCIATE ccpu2(0x0d010115) -#define OID_802_11_AUTHENTICATION_MODE ccpu2(0x0d010118) -#define OID_802_11_PRIVACY_FILTER ccpu2(0x0d010119) -#define OID_802_11_BSSID_LIST_SCAN ccpu2(0x0d01011a) -#define OID_802_11_ENCRYPTION_STATUS ccpu2(0x0d01011b) -#define OID_802_11_ADD_KEY ccpu2(0x0d01011d) -#define OID_802_11_REMOVE_KEY ccpu2(0x0d01011e) -#define OID_802_11_ASSOCIATION_INFORMATION ccpu2(0x0d01011f) -#define OID_802_11_PMKID ccpu2(0x0d010123) -#define OID_802_11_NETWORK_TYPES_SUPPORTED ccpu2(0x0d010203) -#define OID_802_11_NETWORK_TYPE_IN_USE ccpu2(0x0d010204) -#define OID_802_11_TX_POWER_LEVEL ccpu2(0x0d010205) -#define OID_802_11_RSSI ccpu2(0x0d010206) -#define OID_802_11_RSSI_TRIGGER ccpu2(0x0d010207) -#define OID_802_11_FRAGMENTATION_THRESHOLD ccpu2(0x0d010209) -#define OID_802_11_RTS_THRESHOLD ccpu2(0x0d01020a) -#define OID_802_11_SUPPORTED_RATES ccpu2(0x0d01020e) -#define OID_802_11_CONFIGURATION ccpu2(0x0d010211) -#define OID_802_11_BSSID_LIST ccpu2(0x0d010217) +#define OID_GEN_LINK_SPEED cpu_to_le32(0x00010107) +#define OID_GEN_RNDIS_CONFIG_PARAMETER cpu_to_le32(0x0001021b) + +#define OID_GEN_XMIT_OK cpu_to_le32(0x00020101) +#define OID_GEN_RCV_OK cpu_to_le32(0x00020102) +#define OID_GEN_XMIT_ERROR cpu_to_le32(0x00020103) +#define OID_GEN_RCV_ERROR cpu_to_le32(0x00020104) +#define OID_GEN_RCV_NO_BUFFER cpu_to_le32(0x00020105) + +#define OID_802_3_PERMANENT_ADDRESS cpu_to_le32(0x01010101) +#define OID_802_3_CURRENT_ADDRESS cpu_to_le32(0x01010102) +#define OID_802_3_MULTICAST_LIST cpu_to_le32(0x01010103) +#define OID_802_3_MAXIMUM_LIST_SIZE cpu_to_le32(0x01010104) + +#define OID_802_11_BSSID cpu_to_le32(0x0d010101) +#define OID_802_11_SSID cpu_to_le32(0x0d010102) +#define OID_802_11_INFRASTRUCTURE_MODE cpu_to_le32(0x0d010108) +#define OID_802_11_ADD_WEP cpu_to_le32(0x0d010113) +#define OID_802_11_REMOVE_WEP cpu_to_le32(0x0d010114) +#define OID_802_11_DISASSOCIATE cpu_to_le32(0x0d010115) +#define OID_802_11_AUTHENTICATION_MODE cpu_to_le32(0x0d010118) +#define OID_802_11_PRIVACY_FILTER cpu_to_le32(0x0d010119) +#define OID_802_11_BSSID_LIST_SCAN cpu_to_le32(0x0d01011a) +#define OID_802_11_ENCRYPTION_STATUS cpu_to_le32(0x0d01011b) +#define OID_802_11_ADD_KEY cpu_to_le32(0x0d01011d) +#define OID_802_11_REMOVE_KEY cpu_to_le32(0x0d01011e) +#define OID_802_11_ASSOCIATION_INFORMATION cpu_to_le32(0x0d01011f) +#define OID_802_11_PMKID cpu_to_le32(0x0d010123) +#define OID_802_11_NETWORK_TYPES_SUPPORTED cpu_to_le32(0x0d010203) +#define OID_802_11_NETWORK_TYPE_IN_USE cpu_to_le32(0x0d010204) +#define OID_802_11_TX_POWER_LEVEL cpu_to_le32(0x0d010205) +#define OID_802_11_RSSI cpu_to_le32(0x0d010206) +#define OID_802_11_RSSI_TRIGGER cpu_to_le32(0x0d010207) +#define OID_802_11_FRAGMENTATION_THRESHOLD cpu_to_le32(0x0d010209) +#define OID_802_11_RTS_THRESHOLD cpu_to_le32(0x0d01020a) +#define OID_802_11_SUPPORTED_RATES cpu_to_le32(0x0d01020e) +#define OID_802_11_CONFIGURATION cpu_to_le32(0x0d010211) +#define OID_802_11_BSSID_LIST cpu_to_le32(0x0d010217) /* Typical noise/maximum signal level values taken from ndiswrapper iw_ndis.h */ @@ -144,8 +144,8 @@ MODULE_PARM_DESC(workaround_interval, /* codes for "status" field of completion messages */ -#define RNDIS_STATUS_ADAPTER_NOT_READY ccpu2(0xc0010011) -#define RNDIS_STATUS_ADAPTER_NOT_OPEN ccpu2(0xc0010012) +#define RNDIS_STATUS_ADAPTER_NOT_READY cpu_to_le32(0xc0010011) +#define RNDIS_STATUS_ADAPTER_NOT_OPEN cpu_to_le32(0xc0010012) /* NDIS data structures. Taken from wpa_supplicant driver_ndis.c @@ -442,7 +442,7 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len) memset(u.get, 0, sizeof *u.get); u.get->msg_type = RNDIS_MSG_QUERY; - u.get->msg_len = ccpu2(sizeof *u.get); + u.get->msg_len = cpu_to_le32(sizeof *u.get); u.get->oid = oid; ret = rndis_command(dev, u.header, buflen); @@ -491,8 +491,8 @@ static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len) u.set->msg_len = cpu_to_le32(sizeof(*u.set) + len); u.set->oid = oid; u.set->len = cpu_to_le32(len); - u.set->offset = ccpu2(sizeof(*u.set) - 8); - u.set->handle = ccpu2(0); + u.set->offset = cpu_to_le32(sizeof(*u.set) - 8); + u.set->handle = cpu_to_le32(0); memcpy(u.buf + sizeof(*u.set), data, len); ret = rndis_command(dev, u.header, buflen); @@ -1630,7 +1630,7 @@ static int rndis_iw_set_scan(struct net_device *dev, devdbg(usbdev, "SIOCSIWSCAN"); if (wrqu->data.flags == 0) { - tmp = ccpu2(1); + tmp = cpu_to_le32(1); ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp, sizeof(tmp)); evt.data.flags = 0; @@ -2428,7 +2428,7 @@ static void rndis_update_wireless_stats(struct work_struct *work) /* Send scan OID. Use of both OIDs is required to get device * working. */ - tmp = ccpu2(1); + tmp = cpu_to_le32(1); rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp, sizeof(tmp)); diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 8c26f5ea2b83..d2860a823680 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -63,7 +63,7 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; /* Driver Version */ -static const __le32 rndis_driver_version = __constant_cpu_to_le32 (1); +static const __le32 rndis_driver_version = cpu_to_le32 (1); /* Function Prototypes */ static rndis_resp_t *rndis_add_response (int configNr, u32 length); @@ -190,7 +190,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* response goes here, right after the header */ outbuf = (__le32 *) &resp[1]; - resp->InformationBufferOffset = __constant_cpu_to_le32 (16); + resp->InformationBufferOffset = cpu_to_le32 (16); net = rndis_per_dev_params[configNr].dev; if (net->get_stats) @@ -221,7 +221,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, * reddite ergo quae sunt Caesaris Caesari * et quae sunt Dei Deo! */ - *outbuf = __constant_cpu_to_le32 (0); + *outbuf = cpu_to_le32 (0); retval = 0; break; @@ -256,7 +256,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, pr_debug("%s: OID_GEN_LINK_SPEED\n", __func__); if (rndis_per_dev_params [configNr].media_state == NDIS_MEDIA_STATE_DISCONNECTED) - *outbuf = __constant_cpu_to_le32 (0); + *outbuf = cpu_to_le32 (0); else *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].speed); @@ -317,7 +317,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_MAXIMUM_TOTAL_SIZE: pr_debug("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__); - *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); + *outbuf = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); retval = 0; break; @@ -332,7 +332,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_GEN_PHYSICAL_MEDIUM: pr_debug("%s: OID_GEN_PHYSICAL_MEDIUM\n", __func__); - *outbuf = __constant_cpu_to_le32 (0); + *outbuf = cpu_to_le32 (0); retval = 0; break; @@ -342,7 +342,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, */ case OID_GEN_MAC_OPTIONS: /* from WinME */ pr_debug("%s: OID_GEN_MAC_OPTIONS\n", __func__); - *outbuf = __constant_cpu_to_le32( + *outbuf = cpu_to_le32( NDIS_MAC_OPTION_RECEIVE_SERIALIZED | NDIS_MAC_OPTION_FULL_DUPLEX); retval = 0; @@ -431,7 +431,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_802_3_MULTICAST_LIST: pr_debug("%s: OID_802_3_MULTICAST_LIST\n", __func__); /* Multicast base address only */ - *outbuf = __constant_cpu_to_le32 (0xE0000000); + *outbuf = cpu_to_le32 (0xE0000000); retval = 0; break; @@ -439,7 +439,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_802_3_MAXIMUM_LIST_SIZE: pr_debug("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __func__); /* Multicast base address only */ - *outbuf = __constant_cpu_to_le32 (1); + *outbuf = cpu_to_le32 (1); retval = 0; break; @@ -461,14 +461,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_XMIT_ONE_COLLISION: pr_debug("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__); - *outbuf = __constant_cpu_to_le32 (0); + *outbuf = cpu_to_le32 (0); retval = 0; break; /* mandatory */ case OID_802_3_XMIT_MORE_COLLISIONS: pr_debug("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __func__); - *outbuf = __constant_cpu_to_le32 (0); + *outbuf = cpu_to_le32 (0); retval = 0; break; @@ -572,24 +572,24 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf) return -ENOMEM; resp = (rndis_init_cmplt_type *) r->buf; - resp->MessageType = __constant_cpu_to_le32 ( + resp->MessageType = cpu_to_le32 ( REMOTE_NDIS_INITIALIZE_CMPLT); - resp->MessageLength = __constant_cpu_to_le32 (52); + resp->MessageLength = cpu_to_le32 (52); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); - resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION); - resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION); - resp->DeviceFlags = __constant_cpu_to_le32 (RNDIS_DF_CONNECTIONLESS); - resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3); - resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1); + resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->MajorVersion = cpu_to_le32 (RNDIS_MAJOR_VERSION); + resp->MinorVersion = cpu_to_le32 (RNDIS_MINOR_VERSION); + resp->DeviceFlags = cpu_to_le32 (RNDIS_DF_CONNECTIONLESS); + resp->Medium = cpu_to_le32 (RNDIS_MEDIUM_802_3); + resp->MaxPacketsPerTransfer = cpu_to_le32 (1); resp->MaxTransferSize = cpu_to_le32 ( params->dev->mtu + sizeof (struct ethhdr) + sizeof (struct rndis_packet_msg_type) + 22); - resp->PacketAlignmentFactor = __constant_cpu_to_le32 (0); - resp->AFListOffset = __constant_cpu_to_le32 (0); - resp->AFListSize = __constant_cpu_to_le32 (0); + resp->PacketAlignmentFactor = cpu_to_le32 (0); + resp->AFListOffset = cpu_to_le32 (0); + resp->AFListSize = cpu_to_le32 (0); params->resp_avail(params->v); return 0; @@ -617,7 +617,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) return -ENOMEM; resp = (rndis_query_cmplt_type *) r->buf; - resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT); + resp->MessageType = cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ if (gen_ndis_query_resp (configNr, le32_to_cpu (buf->OID), @@ -626,13 +626,13 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) le32_to_cpu(buf->InformationBufferLength), r)) { /* OID not supported */ - resp->Status = __constant_cpu_to_le32 ( + resp->Status = cpu_to_le32 ( RNDIS_STATUS_NOT_SUPPORTED); - resp->MessageLength = __constant_cpu_to_le32 (sizeof *resp); - resp->InformationBufferLength = __constant_cpu_to_le32 (0); - resp->InformationBufferOffset = __constant_cpu_to_le32 (0); + resp->MessageLength = cpu_to_le32 (sizeof *resp); + resp->InformationBufferLength = cpu_to_le32 (0); + resp->InformationBufferOffset = cpu_to_le32 (0); } else - resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS); params->resp_avail(params->v); return 0; @@ -665,14 +665,14 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) pr_debug("\n"); #endif - resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); - resp->MessageLength = __constant_cpu_to_le32 (16); + resp->MessageType = cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); + resp->MessageLength = cpu_to_le32 (16); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ if (gen_ndis_set_resp (configNr, le32_to_cpu (buf->OID), ((u8 *) buf) + 8 + BufOffset, BufLength, r)) - resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED); + resp->Status = cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED); else - resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS); params->resp_avail(params->v); return 0; @@ -689,11 +689,11 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) return -ENOMEM; resp = (rndis_reset_cmplt_type *) r->buf; - resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT); - resp->MessageLength = __constant_cpu_to_le32 (16); - resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->MessageType = cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT); + resp->MessageLength = cpu_to_le32 (16); + resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS); /* resent information */ - resp->AddressingReset = __constant_cpu_to_le32 (1); + resp->AddressingReset = cpu_to_le32 (1); params->resp_avail(params->v); return 0; @@ -713,11 +713,11 @@ static int rndis_keepalive_response (int configNr, return -ENOMEM; resp = (rndis_keepalive_cmplt_type *) r->buf; - resp->MessageType = __constant_cpu_to_le32 ( + resp->MessageType = cpu_to_le32 ( REMOTE_NDIS_KEEPALIVE_CMPLT); - resp->MessageLength = __constant_cpu_to_le32 (16); + resp->MessageLength = cpu_to_le32 (16); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->Status = cpu_to_le32 (RNDIS_STATUS_SUCCESS); params->resp_avail(params->v); return 0; @@ -742,12 +742,12 @@ static int rndis_indicate_status_msg (int configNr, u32 status) return -ENOMEM; resp = (rndis_indicate_status_msg_type *) r->buf; - resp->MessageType = __constant_cpu_to_le32 ( + resp->MessageType = cpu_to_le32 ( REMOTE_NDIS_INDICATE_STATUS_MSG); - resp->MessageLength = __constant_cpu_to_le32 (20); + resp->MessageLength = cpu_to_le32 (20); resp->Status = cpu_to_le32 (status); - resp->StatusBufferLength = __constant_cpu_to_le32 (0); - resp->StatusBufferOffset = __constant_cpu_to_le32 (0); + resp->StatusBufferLength = cpu_to_le32 (0); + resp->StatusBufferOffset = cpu_to_le32 (0); params->resp_avail(params->v); return 0; @@ -963,9 +963,9 @@ void rndis_add_hdr (struct sk_buff *skb) return; header = (void *) skb_push (skb, sizeof *header); memset (header, 0, sizeof *header); - header->MessageType = __constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG); + header->MessageType = cpu_to_le32(REMOTE_NDIS_PACKET_MSG); header->MessageLength = cpu_to_le32(skb->len); - header->DataOffset = __constant_cpu_to_le32 (36); + header->DataOffset = cpu_to_le32 (36); header->DataLength = cpu_to_le32(skb->len - sizeof *header); } @@ -1029,7 +1029,7 @@ int rndis_rm_hdr(struct sk_buff *skb) __le32 *tmp = (void *) skb->data; /* MessageType, MessageLength */ - if (__constant_cpu_to_le32(REMOTE_NDIS_PACKET_MSG) + if (cpu_to_le32(REMOTE_NDIS_PACKET_MSG) != get_unaligned(tmp++)) return -EINVAL; tmp++; diff --git a/include/linux/usb/rndis_host.h b/include/linux/usb/rndis_host.h index 0a6e6d4b929a..37836b937d97 100644 --- a/include/linux/usb/rndis_host.h +++ b/include/linux/usb/rndis_host.h @@ -49,48 +49,45 @@ struct rndis_msg_hdr { */ #define RNDIS_CONTROL_TIMEOUT_MS (5 * 1000) - -#define ccpu2 __constant_cpu_to_le32 - -#define RNDIS_MSG_COMPLETION ccpu2(0x80000000) +#define RNDIS_MSG_COMPLETION cpu_to_le32(0x80000000) /* codes for "msg_type" field of rndis messages; * only the data channel uses packet messages (maybe batched); * everything else goes on the control channel. */ -#define RNDIS_MSG_PACKET ccpu2(0x00000001) /* 1-N packets */ -#define RNDIS_MSG_INIT ccpu2(0x00000002) +#define RNDIS_MSG_PACKET cpu_to_le32(0x00000001) /* 1-N packets */ +#define RNDIS_MSG_INIT cpu_to_le32(0x00000002) #define RNDIS_MSG_INIT_C (RNDIS_MSG_INIT|RNDIS_MSG_COMPLETION) -#define RNDIS_MSG_HALT ccpu2(0x00000003) -#define RNDIS_MSG_QUERY ccpu2(0x00000004) +#define RNDIS_MSG_HALT cpu_to_le32(0x00000003) +#define RNDIS_MSG_QUERY cpu_to_le32(0x00000004) #define RNDIS_MSG_QUERY_C (RNDIS_MSG_QUERY|RNDIS_MSG_COMPLETION) -#define RNDIS_MSG_SET ccpu2(0x00000005) +#define RNDIS_MSG_SET cpu_to_le32(0x00000005) #define RNDIS_MSG_SET_C (RNDIS_MSG_SET|RNDIS_MSG_COMPLETION) -#define RNDIS_MSG_RESET ccpu2(0x00000006) +#define RNDIS_MSG_RESET cpu_to_le32(0x00000006) #define RNDIS_MSG_RESET_C (RNDIS_MSG_RESET|RNDIS_MSG_COMPLETION) -#define RNDIS_MSG_INDICATE ccpu2(0x00000007) -#define RNDIS_MSG_KEEPALIVE ccpu2(0x00000008) +#define RNDIS_MSG_INDICATE cpu_to_le32(0x00000007) +#define RNDIS_MSG_KEEPALIVE cpu_to_le32(0x00000008) #define RNDIS_MSG_KEEPALIVE_C (RNDIS_MSG_KEEPALIVE|RNDIS_MSG_COMPLETION) /* codes for "status" field of completion messages */ -#define RNDIS_STATUS_SUCCESS ccpu2(0x00000000) -#define RNDIS_STATUS_FAILURE ccpu2(0xc0000001) -#define RNDIS_STATUS_INVALID_DATA ccpu2(0xc0010015) -#define RNDIS_STATUS_NOT_SUPPORTED ccpu2(0xc00000bb) -#define RNDIS_STATUS_MEDIA_CONNECT ccpu2(0x4001000b) -#define RNDIS_STATUS_MEDIA_DISCONNECT ccpu2(0x4001000c) +#define RNDIS_STATUS_SUCCESS cpu_to_le32(0x00000000) +#define RNDIS_STATUS_FAILURE cpu_to_le32(0xc0000001) +#define RNDIS_STATUS_INVALID_DATA cpu_to_le32(0xc0010015) +#define RNDIS_STATUS_NOT_SUPPORTED cpu_to_le32(0xc00000bb) +#define RNDIS_STATUS_MEDIA_CONNECT cpu_to_le32(0x4001000b) +#define RNDIS_STATUS_MEDIA_DISCONNECT cpu_to_le32(0x4001000c) /* codes for OID_GEN_PHYSICAL_MEDIUM */ -#define RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED ccpu2(0x00000000) -#define RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN ccpu2(0x00000001) -#define RNDIS_PHYSICAL_MEDIUM_CABLE_MODEM ccpu2(0x00000002) -#define RNDIS_PHYSICAL_MEDIUM_PHONE_LINE ccpu2(0x00000003) -#define RNDIS_PHYSICAL_MEDIUM_POWER_LINE ccpu2(0x00000004) -#define RNDIS_PHYSICAL_MEDIUM_DSL ccpu2(0x00000005) -#define RNDIS_PHYSICAL_MEDIUM_FIBRE_CHANNEL ccpu2(0x00000006) -#define RNDIS_PHYSICAL_MEDIUM_1394 ccpu2(0x00000007) -#define RNDIS_PHYSICAL_MEDIUM_WIRELESS_WAN ccpu2(0x00000008) -#define RNDIS_PHYSICAL_MEDIUM_MAX ccpu2(0x00000009) +#define RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED cpu_to_le32(0x00000000) +#define RNDIS_PHYSICAL_MEDIUM_WIRELESS_LAN cpu_to_le32(0x00000001) +#define RNDIS_PHYSICAL_MEDIUM_CABLE_MODEM cpu_to_le32(0x00000002) +#define RNDIS_PHYSICAL_MEDIUM_PHONE_LINE cpu_to_le32(0x00000003) +#define RNDIS_PHYSICAL_MEDIUM_POWER_LINE cpu_to_le32(0x00000004) +#define RNDIS_PHYSICAL_MEDIUM_DSL cpu_to_le32(0x00000005) +#define RNDIS_PHYSICAL_MEDIUM_FIBRE_CHANNEL cpu_to_le32(0x00000006) +#define RNDIS_PHYSICAL_MEDIUM_1394 cpu_to_le32(0x00000007) +#define RNDIS_PHYSICAL_MEDIUM_WIRELESS_WAN cpu_to_le32(0x00000008) +#define RNDIS_PHYSICAL_MEDIUM_MAX cpu_to_le32(0x00000009) struct rndis_data_hdr { __le32 msg_type; /* RNDIS_MSG_PACKET */ @@ -228,24 +225,24 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */ * there are gobs more that may optionally be supported. We'll avoid as much * of that mess as possible. */ -#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101) -#define OID_GEN_MAXIMUM_FRAME_SIZE ccpu2(0x00010106) -#define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e) -#define OID_GEN_PHYSICAL_MEDIUM ccpu2(0x00010202) +#define OID_802_3_PERMANENT_ADDRESS cpu_to_le32(0x01010101) +#define OID_GEN_MAXIMUM_FRAME_SIZE cpu_to_le32(0x00010106) +#define OID_GEN_CURRENT_PACKET_FILTER cpu_to_le32(0x0001010e) +#define OID_GEN_PHYSICAL_MEDIUM cpu_to_le32(0x00010202) /* packet filter bits used by OID_GEN_CURRENT_PACKET_FILTER */ -#define RNDIS_PACKET_TYPE_DIRECTED ccpu2(0x00000001) -#define RNDIS_PACKET_TYPE_MULTICAST ccpu2(0x00000002) -#define RNDIS_PACKET_TYPE_ALL_MULTICAST ccpu2(0x00000004) -#define RNDIS_PACKET_TYPE_BROADCAST ccpu2(0x00000008) -#define RNDIS_PACKET_TYPE_SOURCE_ROUTING ccpu2(0x00000010) -#define RNDIS_PACKET_TYPE_PROMISCUOUS ccpu2(0x00000020) -#define RNDIS_PACKET_TYPE_SMT ccpu2(0x00000040) -#define RNDIS_PACKET_TYPE_ALL_LOCAL ccpu2(0x00000080) -#define RNDIS_PACKET_TYPE_GROUP ccpu2(0x00001000) -#define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL ccpu2(0x00002000) -#define RNDIS_PACKET_TYPE_FUNCTIONAL ccpu2(0x00004000) -#define RNDIS_PACKET_TYPE_MAC_FRAME ccpu2(0x00008000) +#define RNDIS_PACKET_TYPE_DIRECTED cpu_to_le32(0x00000001) +#define RNDIS_PACKET_TYPE_MULTICAST cpu_to_le32(0x00000002) +#define RNDIS_PACKET_TYPE_ALL_MULTICAST cpu_to_le32(0x00000004) +#define RNDIS_PACKET_TYPE_BROADCAST cpu_to_le32(0x00000008) +#define RNDIS_PACKET_TYPE_SOURCE_ROUTING cpu_to_le32(0x00000010) +#define RNDIS_PACKET_TYPE_PROMISCUOUS cpu_to_le32(0x00000020) +#define RNDIS_PACKET_TYPE_SMT cpu_to_le32(0x00000040) +#define RNDIS_PACKET_TYPE_ALL_LOCAL cpu_to_le32(0x00000080) +#define RNDIS_PACKET_TYPE_GROUP cpu_to_le32(0x00001000) +#define RNDIS_PACKET_TYPE_ALL_FUNCTIONAL cpu_to_le32(0x00002000) +#define RNDIS_PACKET_TYPE_FUNCTIONAL cpu_to_le32(0x00004000) +#define RNDIS_PACKET_TYPE_MAC_FRAME cpu_to_le32(0x00008000) /* default filter used with RNDIS devices */ #define RNDIS_DEFAULT_FILTER ( \ -- cgit v1.2.3 From f3a7c66b5ce0b75a9774a50b5dcce93e5ba28370 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sat, 14 Feb 2009 22:58:35 -0800 Subject: net: replace __constant_{endian} uses in net headers Base versions handle constant folding now. For headers exposed to userspace, we must only expose the __ prefixed versions. Signed-off-by: Harvey Harrison Signed-off-by: David S. Miller --- include/linux/if_pppox.h | 20 ++++----- include/linux/if_tunnel.h | 16 +++---- include/linux/ncp_no.h | 26 ++++++------ include/linux/netdevice.h | 4 +- include/linux/netfilter_bridge.h | 4 +- include/linux/pim.h | 4 +- include/linux/sctp.h | 90 ++++++++++++++++++++-------------------- include/linux/tcp.h | 20 ++++----- include/net/inet_ecn.h | 4 +- include/net/ip_vs.h | 4 +- include/net/ipv6.h | 4 +- include/net/ipx.h | 2 +- include/net/transp_v6.h | 2 +- include/rdma/ib_verbs.h | 2 +- 14 files changed, 101 insertions(+), 101 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index 30c88b2245ff..90b5fae5d714 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -95,16 +95,16 @@ struct pppoe_tag { } __attribute ((packed)); /* Tag identifiers */ -#define PTT_EOL __constant_htons(0x0000) -#define PTT_SRV_NAME __constant_htons(0x0101) -#define PTT_AC_NAME __constant_htons(0x0102) -#define PTT_HOST_UNIQ __constant_htons(0x0103) -#define PTT_AC_COOKIE __constant_htons(0x0104) -#define PTT_VENDOR __constant_htons(0x0105) -#define PTT_RELAY_SID __constant_htons(0x0110) -#define PTT_SRV_ERR __constant_htons(0x0201) -#define PTT_SYS_ERR __constant_htons(0x0202) -#define PTT_GEN_ERR __constant_htons(0x0203) +#define PTT_EOL __cpu_to_be16(0x0000) +#define PTT_SRV_NAME __cpu_to_be16(0x0101) +#define PTT_AC_NAME __cpu_to_be16(0x0102) +#define PTT_HOST_UNIQ __cpu_to_be16(0x0103) +#define PTT_AC_COOKIE __cpu_to_be16(0x0104) +#define PTT_VENDOR __cpu_to_be16(0x0105) +#define PTT_RELAY_SID __cpu_to_be16(0x0110) +#define PTT_SRV_ERR __cpu_to_be16(0x0201) +#define PTT_SYS_ERR __cpu_to_be16(0x0202) +#define PTT_GEN_ERR __cpu_to_be16(0x0203) struct pppoe_hdr { #if defined(__LITTLE_ENDIAN_BITFIELD) diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h index 82c43624c067..5a9aae4adb44 100644 --- a/include/linux/if_tunnel.h +++ b/include/linux/if_tunnel.h @@ -16,14 +16,14 @@ #define SIOCDELPRL (SIOCDEVPRIVATE + 6) #define SIOCCHGPRL (SIOCDEVPRIVATE + 7) -#define GRE_CSUM __constant_htons(0x8000) -#define GRE_ROUTING __constant_htons(0x4000) -#define GRE_KEY __constant_htons(0x2000) -#define GRE_SEQ __constant_htons(0x1000) -#define GRE_STRICT __constant_htons(0x0800) -#define GRE_REC __constant_htons(0x0700) -#define GRE_FLAGS __constant_htons(0x00F8) -#define GRE_VERSION __constant_htons(0x0007) +#define GRE_CSUM __cpu_to_be16(0x8000) +#define GRE_ROUTING __cpu_to_be16(0x4000) +#define GRE_KEY __cpu_to_be16(0x2000) +#define GRE_SEQ __cpu_to_be16(0x1000) +#define GRE_STRICT __cpu_to_be16(0x0800) +#define GRE_REC __cpu_to_be16(0x0700) +#define GRE_FLAGS __cpu_to_be16(0x00F8) +#define GRE_VERSION __cpu_to_be16(0x0007) struct ip_tunnel_parm { diff --git a/include/linux/ncp_no.h b/include/linux/ncp_no.h index f56a696a7cc6..cddaa48fb182 100644 --- a/include/linux/ncp_no.h +++ b/include/linux/ncp_no.h @@ -2,18 +2,18 @@ #define _NCP_NO /* these define the attribute byte as seen by NCP */ -#define aRONLY (__constant_cpu_to_le32(1)) -#define aHIDDEN (__constant_cpu_to_le32(2)) -#define aSYSTEM (__constant_cpu_to_le32(4)) -#define aEXECUTE (__constant_cpu_to_le32(8)) -#define aDIR (__constant_cpu_to_le32(0x10)) -#define aARCH (__constant_cpu_to_le32(0x20)) -#define aSHARED (__constant_cpu_to_le32(0x80)) -#define aDONTSUBALLOCATE (__constant_cpu_to_le32(1L<<11)) -#define aTRANSACTIONAL (__constant_cpu_to_le32(1L<<12)) -#define aPURGE (__constant_cpu_to_le32(1L<<16)) -#define aRENAMEINHIBIT (__constant_cpu_to_le32(1L<<17)) -#define aDELETEINHIBIT (__constant_cpu_to_le32(1L<<18)) -#define aDONTCOMPRESS (__constant_cpu_to_le32(1L<<27)) +#define aRONLY (__cpu_to_le32(1)) +#define aHIDDEN (__cpu_to_le32(2)) +#define aSYSTEM (__cpu_to_le32(4)) +#define aEXECUTE (__cpu_to_le32(8)) +#define aDIR (__cpu_to_le32(0x10)) +#define aARCH (__cpu_to_le32(0x20)) +#define aSHARED (__cpu_to_le32(0x80)) +#define aDONTSUBALLOCATE (__cpu_to_le32(1L<<11)) +#define aTRANSACTIONAL (__cpu_to_le32(1L<<12)) +#define aPURGE (__cpu_to_le32(1L<<16)) +#define aRENAMEINHIBIT (__cpu_to_le32(1L<<17)) +#define aDELETEINHIBIT (__cpu_to_le32(1L<<18)) +#define aDONTCOMPRESS (__cpu_to_le32(1L<<27)) #endif /* _NCP_NO */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 355662aac940..bd8b4ca85a2a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1863,7 +1863,7 @@ static inline int skb_bond_should_drop(struct sk_buff *skb) if (dev->priv_flags & IFF_SLAVE_INACTIVE) { if ((dev->priv_flags & IFF_SLAVE_NEEDARP) && - skb->protocol == __constant_htons(ETH_P_ARP)) + skb->protocol == __cpu_to_be16(ETH_P_ARP)) return 0; if (master->priv_flags & IFF_MASTER_ALB) { @@ -1872,7 +1872,7 @@ static inline int skb_bond_should_drop(struct sk_buff *skb) return 0; } if (master->priv_flags & IFF_MASTER_8023AD && - skb->protocol == __constant_htons(ETH_P_SLOW)) + skb->protocol == __cpu_to_be16(ETH_P_SLOW)) return 0; return 1; diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index 499aa9375901..f8105e54716a 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -59,9 +59,9 @@ static inline int nf_bridge_maybe_copy_header(struct sk_buff *skb) static inline unsigned int nf_bridge_encap_header_len(const struct sk_buff *skb) { switch (skb->protocol) { - case __constant_htons(ETH_P_8021Q): + case __cpu_to_be16(ETH_P_8021Q): return VLAN_HLEN; - case __constant_htons(ETH_P_PPP_SES): + case __cpu_to_be16(ETH_P_PPP_SES): return PPPOE_SES_HLEN; default: return 0; diff --git a/include/linux/pim.h b/include/linux/pim.h index 1ba0661561a4..252bf6644c51 100644 --- a/include/linux/pim.h +++ b/include/linux/pim.h @@ -4,14 +4,14 @@ #include /* Message types - V1 */ -#define PIM_V1_VERSION __constant_htonl(0x10000000) +#define PIM_V1_VERSION cpu_to_be32(0x10000000) #define PIM_V1_REGISTER 1 /* Message types - V2 */ #define PIM_VERSION 2 #define PIM_REGISTER 1 -#define PIM_NULL_REGISTER __constant_htonl(0x40000000) +#define PIM_NULL_REGISTER cpu_to_be32(0x40000000) /* PIMv2 register message header layout (ietf-draft-idmr-pimvsm-v2-00.ps */ struct pimreghdr diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 8ba1c320f975..bd50b371ffaa 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -172,35 +172,35 @@ typedef struct sctp_paramhdr { typedef enum { /* RFC 2960 Section 3.3.5 */ - SCTP_PARAM_HEARTBEAT_INFO = __constant_htons(1), + SCTP_PARAM_HEARTBEAT_INFO = cpu_to_be16(1), /* RFC 2960 Section 3.3.2.1 */ - SCTP_PARAM_IPV4_ADDRESS = __constant_htons(5), - SCTP_PARAM_IPV6_ADDRESS = __constant_htons(6), - SCTP_PARAM_STATE_COOKIE = __constant_htons(7), - SCTP_PARAM_UNRECOGNIZED_PARAMETERS = __constant_htons(8), - SCTP_PARAM_COOKIE_PRESERVATIVE = __constant_htons(9), - SCTP_PARAM_HOST_NAME_ADDRESS = __constant_htons(11), - SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = __constant_htons(12), - SCTP_PARAM_ECN_CAPABLE = __constant_htons(0x8000), + SCTP_PARAM_IPV4_ADDRESS = cpu_to_be16(5), + SCTP_PARAM_IPV6_ADDRESS = cpu_to_be16(6), + SCTP_PARAM_STATE_COOKIE = cpu_to_be16(7), + SCTP_PARAM_UNRECOGNIZED_PARAMETERS = cpu_to_be16(8), + SCTP_PARAM_COOKIE_PRESERVATIVE = cpu_to_be16(9), + SCTP_PARAM_HOST_NAME_ADDRESS = cpu_to_be16(11), + SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = cpu_to_be16(12), + SCTP_PARAM_ECN_CAPABLE = cpu_to_be16(0x8000), /* AUTH Extension Section 3 */ - SCTP_PARAM_RANDOM = __constant_htons(0x8002), - SCTP_PARAM_CHUNKS = __constant_htons(0x8003), - SCTP_PARAM_HMAC_ALGO = __constant_htons(0x8004), + SCTP_PARAM_RANDOM = cpu_to_be16(0x8002), + SCTP_PARAM_CHUNKS = cpu_to_be16(0x8003), + SCTP_PARAM_HMAC_ALGO = cpu_to_be16(0x8004), /* Add-IP: Supported Extensions, Section 4.2 */ - SCTP_PARAM_SUPPORTED_EXT = __constant_htons(0x8008), + SCTP_PARAM_SUPPORTED_EXT = cpu_to_be16(0x8008), /* PR-SCTP Sec 3.1 */ - SCTP_PARAM_FWD_TSN_SUPPORT = __constant_htons(0xc000), + SCTP_PARAM_FWD_TSN_SUPPORT = cpu_to_be16(0xc000), /* Add-IP Extension. Section 3.2 */ - SCTP_PARAM_ADD_IP = __constant_htons(0xc001), - SCTP_PARAM_DEL_IP = __constant_htons(0xc002), - SCTP_PARAM_ERR_CAUSE = __constant_htons(0xc003), - SCTP_PARAM_SET_PRIMARY = __constant_htons(0xc004), - SCTP_PARAM_SUCCESS_REPORT = __constant_htons(0xc005), - SCTP_PARAM_ADAPTATION_LAYER_IND = __constant_htons(0xc006), + SCTP_PARAM_ADD_IP = cpu_to_be16(0xc001), + SCTP_PARAM_DEL_IP = cpu_to_be16(0xc002), + SCTP_PARAM_ERR_CAUSE = cpu_to_be16(0xc003), + SCTP_PARAM_SET_PRIMARY = cpu_to_be16(0xc004), + SCTP_PARAM_SUCCESS_REPORT = cpu_to_be16(0xc005), + SCTP_PARAM_ADAPTATION_LAYER_IND = cpu_to_be16(0xc006), } sctp_param_t; /* enum */ @@ -212,13 +212,13 @@ typedef enum { * */ typedef enum { - SCTP_PARAM_ACTION_DISCARD = __constant_htons(0x0000), - SCTP_PARAM_ACTION_DISCARD_ERR = __constant_htons(0x4000), - SCTP_PARAM_ACTION_SKIP = __constant_htons(0x8000), - SCTP_PARAM_ACTION_SKIP_ERR = __constant_htons(0xc000), + SCTP_PARAM_ACTION_DISCARD = cpu_to_be16(0x0000), + SCTP_PARAM_ACTION_DISCARD_ERR = cpu_to_be16(0x4000), + SCTP_PARAM_ACTION_SKIP = cpu_to_be16(0x8000), + SCTP_PARAM_ACTION_SKIP_ERR = cpu_to_be16(0xc000), } sctp_param_action_t; -enum { SCTP_PARAM_ACTION_MASK = __constant_htons(0xc000), }; +enum { SCTP_PARAM_ACTION_MASK = cpu_to_be16(0xc000), }; /* RFC 2960 Section 3.3.1 Payload Data (DATA) (0) */ @@ -457,17 +457,17 @@ typedef struct sctp_operr_chunk { */ typedef enum { - SCTP_ERROR_NO_ERROR = __constant_htons(0x00), - SCTP_ERROR_INV_STRM = __constant_htons(0x01), - SCTP_ERROR_MISS_PARAM = __constant_htons(0x02), - SCTP_ERROR_STALE_COOKIE = __constant_htons(0x03), - SCTP_ERROR_NO_RESOURCE = __constant_htons(0x04), - SCTP_ERROR_DNS_FAILED = __constant_htons(0x05), - SCTP_ERROR_UNKNOWN_CHUNK = __constant_htons(0x06), - SCTP_ERROR_INV_PARAM = __constant_htons(0x07), - SCTP_ERROR_UNKNOWN_PARAM = __constant_htons(0x08), - SCTP_ERROR_NO_DATA = __constant_htons(0x09), - SCTP_ERROR_COOKIE_IN_SHUTDOWN = __constant_htons(0x0a), + SCTP_ERROR_NO_ERROR = cpu_to_be16(0x00), + SCTP_ERROR_INV_STRM = cpu_to_be16(0x01), + SCTP_ERROR_MISS_PARAM = cpu_to_be16(0x02), + SCTP_ERROR_STALE_COOKIE = cpu_to_be16(0x03), + SCTP_ERROR_NO_RESOURCE = cpu_to_be16(0x04), + SCTP_ERROR_DNS_FAILED = cpu_to_be16(0x05), + SCTP_ERROR_UNKNOWN_CHUNK = cpu_to_be16(0x06), + SCTP_ERROR_INV_PARAM = cpu_to_be16(0x07), + SCTP_ERROR_UNKNOWN_PARAM = cpu_to_be16(0x08), + SCTP_ERROR_NO_DATA = cpu_to_be16(0x09), + SCTP_ERROR_COOKIE_IN_SHUTDOWN = cpu_to_be16(0x0a), /* SCTP Implementation Guide: @@ -476,9 +476,9 @@ typedef enum { * 13 Protocol Violation */ - SCTP_ERROR_RESTART = __constant_htons(0x0b), - SCTP_ERROR_USER_ABORT = __constant_htons(0x0c), - SCTP_ERROR_PROTO_VIOLATION = __constant_htons(0x0d), + SCTP_ERROR_RESTART = cpu_to_be16(0x0b), + SCTP_ERROR_USER_ABORT = cpu_to_be16(0x0c), + SCTP_ERROR_PROTO_VIOLATION = cpu_to_be16(0x0d), /* ADDIP Section 3.3 New Error Causes * @@ -493,11 +493,11 @@ typedef enum { * 0x0103 Association Aborted due to illegal ASCONF-ACK * 0x0104 Request refused - no authorization. */ - SCTP_ERROR_DEL_LAST_IP = __constant_htons(0x0100), - SCTP_ERROR_RSRC_LOW = __constant_htons(0x0101), - SCTP_ERROR_DEL_SRC_IP = __constant_htons(0x0102), - SCTP_ERROR_ASCONF_ACK = __constant_htons(0x0103), - SCTP_ERROR_REQ_REFUSED = __constant_htons(0x0104), + SCTP_ERROR_DEL_LAST_IP = cpu_to_be16(0x0100), + SCTP_ERROR_RSRC_LOW = cpu_to_be16(0x0101), + SCTP_ERROR_DEL_SRC_IP = cpu_to_be16(0x0102), + SCTP_ERROR_ASCONF_ACK = cpu_to_be16(0x0103), + SCTP_ERROR_REQ_REFUSED = cpu_to_be16(0x0104), /* AUTH Section 4. New Error Cause * @@ -509,7 +509,7 @@ typedef enum { * -------------------------------------------------------------- * 0x0105 Unsupported HMAC Identifier */ - SCTP_ERROR_UNSUP_HMAC = __constant_htons(0x0105) + SCTP_ERROR_UNSUP_HMAC = cpu_to_be16(0x0105) } sctp_error_t; diff --git a/include/linux/tcp.h b/include/linux/tcp.h index fe77e1499ab7..0cd99e6baca5 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -69,16 +69,16 @@ union tcp_word_hdr { #define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3]) enum { - TCP_FLAG_CWR = __constant_htonl(0x00800000), - TCP_FLAG_ECE = __constant_htonl(0x00400000), - TCP_FLAG_URG = __constant_htonl(0x00200000), - TCP_FLAG_ACK = __constant_htonl(0x00100000), - TCP_FLAG_PSH = __constant_htonl(0x00080000), - TCP_FLAG_RST = __constant_htonl(0x00040000), - TCP_FLAG_SYN = __constant_htonl(0x00020000), - TCP_FLAG_FIN = __constant_htonl(0x00010000), - TCP_RESERVED_BITS = __constant_htonl(0x0F000000), - TCP_DATA_OFFSET = __constant_htonl(0xF0000000) + TCP_FLAG_CWR = __cpu_to_be32(0x00800000), + TCP_FLAG_ECE = __cpu_to_be32(0x00400000), + TCP_FLAG_URG = __cpu_to_be32(0x00200000), + TCP_FLAG_ACK = __cpu_to_be32(0x00100000), + TCP_FLAG_PSH = __cpu_to_be32(0x00080000), + TCP_FLAG_RST = __cpu_to_be32(0x00040000), + TCP_FLAG_SYN = __cpu_to_be32(0x00020000), + TCP_FLAG_FIN = __cpu_to_be32(0x00010000), + TCP_RESERVED_BITS = __cpu_to_be32(0x0F000000), + TCP_DATA_OFFSET = __cpu_to_be32(0xF0000000) }; /* TCP socket options */ diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h index 7040a782c656..9b5d08f4f6e8 100644 --- a/include/net/inet_ecn.h +++ b/include/net/inet_ecn.h @@ -113,12 +113,12 @@ static inline void ipv6_copy_dscp(unsigned int dscp, struct ipv6hdr *inner) static inline int INET_ECN_set_ce(struct sk_buff *skb) { switch (skb->protocol) { - case __constant_htons(ETH_P_IP): + case cpu_to_be16(ETH_P_IP): if (skb->network_header + sizeof(struct iphdr) <= skb->tail) return IP_ECN_set_ce(ip_hdr(skb)); break; - case __constant_htons(ETH_P_IPV6): + case cpu_to_be16(ETH_P_IPV6): if (skb->network_header + sizeof(struct ipv6hdr) <= skb->tail) return IP6_ECN_set_ce(ipv6_hdr(skb)); break; diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index ab9b003ab671..bbae1e87efcd 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -184,8 +184,8 @@ static inline const char *ip_vs_dbg_addr(int af, char *buf, size_t buf_len, /* * The port number of FTP service (in network order). */ -#define FTPPORT __constant_htons(21) -#define FTPDATA __constant_htons(20) +#define FTPPORT cpu_to_be16(21) +#define FTPDATA cpu_to_be16(20) /* * TCP State Values diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 6d5b58a1c743..c1f16fc49ade 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -196,8 +196,8 @@ struct ip6_flowlabel struct net *fl_net; }; -#define IPV6_FLOWINFO_MASK __constant_htonl(0x0FFFFFFF) -#define IPV6_FLOWLABEL_MASK __constant_htonl(0x000FFFFF) +#define IPV6_FLOWINFO_MASK cpu_to_be32(0x0FFFFFFF) +#define IPV6_FLOWLABEL_MASK cpu_to_be32(0x000FFFFF) struct ipv6_fl_socklist { diff --git a/include/net/ipx.h b/include/net/ipx.h index 4cc0b4eca948..a14121dd1932 100644 --- a/include/net/ipx.h +++ b/include/net/ipx.h @@ -27,7 +27,7 @@ struct ipx_address { struct ipxhdr { __be16 ipx_checksum __attribute__ ((packed)); -#define IPX_NO_CHECKSUM __constant_htons(0xFFFF) +#define IPX_NO_CHECKSUM cpu_to_be16(0xFFFF) __be16 ipx_pktsize __attribute__ ((packed)); __u8 ipx_tctrl; __u8 ipx_type; diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h index 876b6f2bb4fd..bfb240c6cf79 100644 --- a/include/net/transp_v6.h +++ b/include/net/transp_v6.h @@ -46,7 +46,7 @@ extern int datagram_send_ctl(struct net *net, struct ipv6_txoptions *opt, int *hlimit, int *tclass); -#define LOOPBACK4_IPV6 __constant_htonl(0x7f000006) +#define LOOPBACK4_IPV6 cpu_to_be32(0x7f000006) /* * address family specific functions diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 936e333e7ce5..c179318edd92 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -388,7 +388,7 @@ enum { IB_MULTICAST_QPN = 0xffffff }; -#define IB_LID_PERMISSIVE __constant_htons(0xFFFF) +#define IB_LID_PERMISSIVE cpu_to_be16(0xFFFF) enum ib_ah_flags { IB_AH_GRH = 1 -- cgit v1.2.3 From a038a353c3de4040d8445ec568acebdac144436f Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 12 Feb 2009 05:03:34 +0000 Subject: clocksource: allow usage independent of timekeeping.c So far struct clocksource acted as the interface between time/timekeeping.c and hardware. This patch generalizes the concept so that a similar interface can also be used in other contexts. For that it introduces new structures and related functions *without* touching the existing struct clocksource. The reasons for adding these new structures to clocksource.[ch] are * the APIs are clearly related * struct clocksource could be cleaned up to use the new structs * avoids proliferation of files with similar names (timesource.h? timecounter.h?) As outlined in the discussion with John Stultz, this patch adds * struct cyclecounter: stateless API to hardware which counts clock cycles * struct timecounter: stateful utility code built on a cyclecounter which provides a nanosecond counter * only the function to read the nanosecond counter; deltas are used internally and not exposed to users of timecounter The code does no locking of the shared state. It must be called at least as often as the cycle counter wraps around to detect these wrap arounds. Both is the responsibility of the timecounter user. Acked-by: John Stultz Signed-off-by: Patrick Ohly Signed-off-by: David S. Miller --- include/linux/clocksource.h | 101 ++++++++++++++++++++++++++++++++++++++++++++ kernel/time/clocksource.c | 76 +++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) (limited to 'include/linux') diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index f88d32f8ff7c..573819ef4cc0 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -21,9 +21,110 @@ typedef u64 cycle_t; struct clocksource; +/** + * struct cyclecounter - hardware abstraction for a free running counter + * Provides completely state-free accessors to the underlying hardware. + * Depending on which hardware it reads, the cycle counter may wrap + * around quickly. Locking rules (if necessary) have to be defined + * by the implementor and user of specific instances of this API. + * + * @read: returns the current cycle value + * @mask: bitmask for two's complement + * subtraction of non 64 bit counters, + * see CLOCKSOURCE_MASK() helper macro + * @mult: cycle to nanosecond multiplier + * @shift: cycle to nanosecond divisor (power of two) + */ +struct cyclecounter { + cycle_t (*read)(const struct cyclecounter *cc); + cycle_t mask; + u32 mult; + u32 shift; +}; + +/** + * struct timecounter - layer above a %struct cyclecounter which counts nanoseconds + * Contains the state needed by timecounter_read() to detect + * cycle counter wrap around. Initialize with + * timecounter_init(). Also used to convert cycle counts into the + * corresponding nanosecond counts with timecounter_cyc2time(). Users + * of this code are responsible for initializing the underlying + * cycle counter hardware, locking issues and reading the time + * more often than the cycle counter wraps around. The nanosecond + * counter will only wrap around after ~585 years. + * + * @cc: the cycle counter used by this instance + * @cycle_last: most recent cycle counter value seen by + * timecounter_read() + * @nsec: continuously increasing count + */ +struct timecounter { + const struct cyclecounter *cc; + cycle_t cycle_last; + u64 nsec; +}; + +/** + * cyclecounter_cyc2ns - converts cycle counter cycles to nanoseconds + * @tc: Pointer to cycle counter. + * @cycles: Cycles + * + * XXX - This could use some mult_lxl_ll() asm optimization. Same code + * as in cyc2ns, but with unsigned result. + */ +static inline u64 cyclecounter_cyc2ns(const struct cyclecounter *cc, + cycle_t cycles) +{ + u64 ret = (u64)cycles; + ret = (ret * cc->mult) >> cc->shift; + return ret; +} + +/** + * timecounter_init - initialize a time counter + * @tc: Pointer to time counter which is to be initialized/reset + * @cc: A cycle counter, ready to be used. + * @start_tstamp: Arbitrary initial time stamp. + * + * After this call the current cycle register (roughly) corresponds to + * the initial time stamp. Every call to timecounter_read() increments + * the time stamp counter by the number of elapsed nanoseconds. + */ +extern void timecounter_init(struct timecounter *tc, + const struct cyclecounter *cc, + u64 start_tstamp); + +/** + * timecounter_read - return nanoseconds elapsed since timecounter_init() + * plus the initial time stamp + * @tc: Pointer to time counter. + * + * In other words, keeps track of time since the same epoch as + * the function which generated the initial time stamp. + */ +extern u64 timecounter_read(struct timecounter *tc); + +/** + * timecounter_cyc2time - convert a cycle counter to same + * time base as values returned by + * timecounter_read() + * @tc: Pointer to time counter. + * @cycle: a value returned by tc->cc->read() + * + * Cycle counts that are converted correctly as long as they + * fall into the interval [-1/2 max cycle count, +1/2 max cycle count], + * with "max cycle count" == cs->mask+1. + * + * This allows conversion of cycle counter values which were generated + * in the past. + */ +extern u64 timecounter_cyc2time(struct timecounter *tc, + cycle_t cycle_tstamp); + /** * struct clocksource - hardware abstraction for a free running counter * Provides mostly state-free accessors to the underlying hardware. + * This is the structure used for system time. * * @name: ptr to clocksource name * @list: list head for registration diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index ca89e1593f08..c46c931a7fe7 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -31,6 +31,82 @@ #include /* for spin_unlock_irq() using preempt_count() m68k */ #include +void timecounter_init(struct timecounter *tc, + const struct cyclecounter *cc, + u64 start_tstamp) +{ + tc->cc = cc; + tc->cycle_last = cc->read(cc); + tc->nsec = start_tstamp; +} +EXPORT_SYMBOL(timecounter_init); + +/** + * timecounter_read_delta - get nanoseconds since last call of this function + * @tc: Pointer to time counter + * + * When the underlying cycle counter runs over, this will be handled + * correctly as long as it does not run over more than once between + * calls. + * + * The first call to this function for a new time counter initializes + * the time tracking and returns an undefined result. + */ +static u64 timecounter_read_delta(struct timecounter *tc) +{ + cycle_t cycle_now, cycle_delta; + u64 ns_offset; + + /* read cycle counter: */ + cycle_now = tc->cc->read(tc->cc); + + /* calculate the delta since the last timecounter_read_delta(): */ + cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask; + + /* convert to nanoseconds: */ + ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta); + + /* update time stamp of timecounter_read_delta() call: */ + tc->cycle_last = cycle_now; + + return ns_offset; +} + +u64 timecounter_read(struct timecounter *tc) +{ + u64 nsec; + + /* increment time by nanoseconds since last call */ + nsec = timecounter_read_delta(tc); + nsec += tc->nsec; + tc->nsec = nsec; + + return nsec; +} +EXPORT_SYMBOL(timecounter_read); + +u64 timecounter_cyc2time(struct timecounter *tc, + cycle_t cycle_tstamp) +{ + u64 cycle_delta = (cycle_tstamp - tc->cycle_last) & tc->cc->mask; + u64 nsec; + + /* + * Instead of always treating cycle_tstamp as more recent + * than tc->cycle_last, detect when it is too far in the + * future and treat it as old time stamp instead. + */ + if (cycle_delta > tc->cc->mask / 2) { + cycle_delta = (tc->cycle_last - cycle_tstamp) & tc->cc->mask; + nsec = tc->nsec - cyclecounter_cyc2ns(tc->cc, cycle_delta); + } else { + nsec = cyclecounter_cyc2ns(tc->cc, cycle_delta) + tc->nsec; + } + + return nsec; +} +EXPORT_SYMBOL(timecounter_cyc2time); + /* XXX - Would like a better way for initializing curr_clocksource */ extern struct clocksource clocksource_jiffies; -- cgit v1.2.3 From a75244c3d519fcb490ca2bf3f123c98017f1e8d0 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 12 Feb 2009 05:03:35 +0000 Subject: timecompare: generic infrastructure to map between two time bases Mapping from a struct timecounter to a time returned by functions like ktime_get_real() is implemented. This is sufficient to use this code in a network device driver which wants to support hardware time stamping and transformation of hardware time stamps to system time. The interface could have been made more versatile by not depending on a time counter, but this wasn't done to avoid writing glue code elsewhere. The method implemented here is the one used and analyzed under the name "assisted PTP" in the LCI PTP paper: http://www.linuxclustersinstitute.org/conferences/archive/2008/PDF/Ohly_92221.pdf Acked-by: John Stultz Signed-off-by: Patrick Ohly Signed-off-by: David S. Miller --- include/linux/timecompare.h | 125 +++++++++++++++++++++++++++++ kernel/time/Makefile | 2 +- kernel/time/timecompare.c | 191 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 include/linux/timecompare.h create mode 100644 kernel/time/timecompare.c (limited to 'include/linux') diff --git a/include/linux/timecompare.h b/include/linux/timecompare.h new file mode 100644 index 000000000000..546e2234e4b3 --- /dev/null +++ b/include/linux/timecompare.h @@ -0,0 +1,125 @@ +/* + * Utility code which helps transforming between two different time + * bases, called "source" and "target" time in this code. + * + * Source time has to be provided via the timecounter API while target + * time is accessed via a function callback whose prototype + * intentionally matches ktime_get() and ktime_get_real(). These + * interfaces where chosen like this so that the code serves its + * initial purpose without additional glue code. + * + * This purpose is synchronizing a hardware clock in a NIC with system + * time, in order to implement the Precision Time Protocol (PTP, + * IEEE1588) with more accurate hardware assisted time stamping. In + * that context only synchronization against system time (= + * ktime_get_real()) is currently needed. But this utility code might + * become useful in other situations, which is why it was written as + * general purpose utility code. + * + * The source timecounter is assumed to return monotonically + * increasing time (but this code does its best to compensate if that + * is not the case) whereas target time may jump. + * + * The target time corresponding to a source time is determined by + * reading target time, reading source time, reading target time + * again, then assuming that average target time corresponds to source + * time. In other words, the assumption is that reading the source + * time is slow and involves equal time for sending the request and + * receiving the reply, whereas reading target time is assumed to be + * fast. + * + * Copyright (C) 2009 Intel Corporation. + * Author: Patrick Ohly + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef _LINUX_TIMECOMPARE_H +#define _LINUX_TIMECOMPARE_H + +#include +#include + +/** + * struct timecompare - stores state and configuration for the two clocks + * + * Initialize to zero, then set source/target/num_samples. + * + * Transformation between source time and target time is done with: + * target_time = source_time + offset + + * (source_time - last_update) * skew / + * TIMECOMPARE_SKEW_RESOLUTION + * + * @source: used to get source time stamps via timecounter_read() + * @target: function returning target time (for example, ktime_get + * for monotonic time, or ktime_get_real for wall clock) + * @num_samples: number of times that source time and target time are to + * be compared when determining their offset + * @offset: (target time - source time) at the time of the last update + * @skew: average (target time - source time) / delta source time * + * TIMECOMPARE_SKEW_RESOLUTION + * @last_update: last source time stamp when time offset was measured + */ +struct timecompare { + struct timecounter *source; + ktime_t (*target)(void); + int num_samples; + + s64 offset; + s64 skew; + u64 last_update; +}; + +/** + * timecompare_transform - transform source time stamp into target time base + * @sync: context for time sync + * @source_tstamp: the result of timecounter_read() or + * timecounter_cyc2time() + */ +extern ktime_t timecompare_transform(struct timecompare *sync, + u64 source_tstamp); + +/** + * timecompare_offset - measure current (target time - source time) offset + * @sync: context for time sync + * @offset: average offset during sample period returned here + * @source_tstamp: average source time during sample period returned here + * + * Returns number of samples used. Might be zero (= no result) in the + * unlikely case that target time was monotonically decreasing for all + * samples (= broken). + */ +extern int timecompare_offset(struct timecompare *sync, + s64 *offset, + u64 *source_tstamp); + +extern void __timecompare_update(struct timecompare *sync, + u64 source_tstamp); + +/** + * timecompare_update - update offset and skew by measuring current offset + * @sync: context for time sync + * @source_tstamp: the result of timecounter_read() or + * timecounter_cyc2time(), pass zero to force update + * + * Updates are only done at most once per second. + */ +static inline void timecompare_update(struct timecompare *sync, + u64 source_tstamp) +{ + if (!source_tstamp || + (s64)(source_tstamp - sync->last_update) >= NSEC_PER_SEC) + __timecompare_update(sync, source_tstamp); +} + +#endif /* _LINUX_TIMECOMPARE_H */ diff --git a/kernel/time/Makefile b/kernel/time/Makefile index 905b0b50792d..0b0a6366c9d4 100644 --- a/kernel/time/Makefile +++ b/kernel/time/Makefile @@ -1,4 +1,4 @@ -obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o +obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o timecompare.o obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o diff --git a/kernel/time/timecompare.c b/kernel/time/timecompare.c new file mode 100644 index 000000000000..71e7f1a19156 --- /dev/null +++ b/kernel/time/timecompare.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2009 Intel Corporation. + * Author: Patrick Ohly + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +/* + * fixed point arithmetic scale factor for skew + * + * Usually one would measure skew in ppb (parts per billion, 1e9), but + * using a factor of 2 simplifies the math. + */ +#define TIMECOMPARE_SKEW_RESOLUTION (((s64)1)<<30) + +ktime_t timecompare_transform(struct timecompare *sync, + u64 source_tstamp) +{ + u64 nsec; + + nsec = source_tstamp + sync->offset; + nsec += (s64)(source_tstamp - sync->last_update) * sync->skew / + TIMECOMPARE_SKEW_RESOLUTION; + + return ns_to_ktime(nsec); +} +EXPORT_SYMBOL(timecompare_transform); + +int timecompare_offset(struct timecompare *sync, + s64 *offset, + u64 *source_tstamp) +{ + u64 start_source = 0, end_source = 0; + struct { + s64 offset; + s64 duration_target; + } buffer[10], sample, *samples; + int counter = 0, i; + int used; + int index; + int num_samples = sync->num_samples; + + if (num_samples > sizeof(buffer)/sizeof(buffer[0])) { + samples = kmalloc(sizeof(*samples) * num_samples, GFP_ATOMIC); + if (!samples) { + samples = buffer; + num_samples = sizeof(buffer)/sizeof(buffer[0]); + } + } else { + samples = buffer; + } + + /* run until we have enough valid samples, but do not try forever */ + i = 0; + counter = 0; + while (1) { + u64 ts; + ktime_t start, end; + + start = sync->target(); + ts = timecounter_read(sync->source); + end = sync->target(); + + if (!i) + start_source = ts; + + /* ignore negative durations */ + sample.duration_target = ktime_to_ns(ktime_sub(end, start)); + if (sample.duration_target >= 0) { + /* + * assume symetric delay to and from source: + * average target time corresponds to measured + * source time + */ + sample.offset = + ktime_to_ns(ktime_add(end, start)) / 2 - + ts; + + /* simple insertion sort based on duration */ + index = counter - 1; + while (index >= 0) { + if (samples[index].duration_target < + sample.duration_target) + break; + samples[index + 1] = samples[index]; + index--; + } + samples[index + 1] = sample; + counter++; + } + + i++; + if (counter >= num_samples || i >= 100000) { + end_source = ts; + break; + } + } + + *source_tstamp = (end_source + start_source) / 2; + + /* remove outliers by only using 75% of the samples */ + used = counter * 3 / 4; + if (!used) + used = counter; + if (used) { + /* calculate average */ + s64 off = 0; + for (index = 0; index < used; index++) + off += samples[index].offset; + *offset = div_s64(off, used); + } + + if (samples && samples != buffer) + kfree(samples); + + return used; +} +EXPORT_SYMBOL(timecompare_offset); + +void __timecompare_update(struct timecompare *sync, + u64 source_tstamp) +{ + s64 offset; + u64 average_time; + + if (!timecompare_offset(sync, &offset, &average_time)) + return; + + if (!sync->last_update) { + sync->last_update = average_time; + sync->offset = offset; + sync->skew = 0; + } else { + s64 delta_nsec = average_time - sync->last_update; + + /* avoid division by negative or small deltas */ + if (delta_nsec >= 10000) { + s64 delta_offset_nsec = offset - sync->offset; + s64 skew; /* delta_offset_nsec * + TIMECOMPARE_SKEW_RESOLUTION / + delta_nsec */ + u64 divisor; + + /* div_s64() is limited to 32 bit divisor */ + skew = delta_offset_nsec * TIMECOMPARE_SKEW_RESOLUTION; + divisor = delta_nsec; + while (unlikely(divisor >= ((s64)1) << 32)) { + /* divide both by 2; beware, right shift + of negative value has undefined + behavior and can only be used for + the positive divisor */ + skew = div_s64(skew, 2); + divisor >>= 1; + } + skew = div_s64(skew, divisor); + + /* + * Calculate new overall skew as 4/16 the + * old value and 12/16 the new one. This is + * a rather arbitrary tradeoff between + * only using the latest measurement (0/16 and + * 16/16) and even more weight on past measurements. + */ +#define TIMECOMPARE_NEW_SKEW_PER_16 12 + sync->skew = + div_s64((16 - TIMECOMPARE_NEW_SKEW_PER_16) * + sync->skew + + TIMECOMPARE_NEW_SKEW_PER_16 * skew, + 16); + sync->last_update = average_time; + sync->offset = offset; + } + } +} +EXPORT_SYMBOL(__timecompare_update); -- cgit v1.2.3 From cb9eff097831007afb30d64373f29d99825d0068 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 12 Feb 2009 05:03:36 +0000 Subject: net: new user space API for time stamping of incoming and outgoing packets User space can request hardware and/or software time stamping. Reporting of the result(s) via a new control message is enabled separately for each field in the message because some of the fields may require additional computation and thus cause overhead. User space can tell the different kinds of time stamps apart and choose what suits its needs. When a TX timestamp operation is requested, the TX skb will be cloned and the clone will be time stamped (in hardware or software) and added to the socket error queue of the skb, if the skb has a socket associated with it. The actual TX timestamp will reach userspace as a RX timestamp on the cloned packet. If timestamping is requested and no timestamping is done in the device driver (potentially this may use hardware timestamping), it will be done in software after the device's start_hard_xmit routine. Signed-off-by: Patrick Ohly Signed-off-by: David S. Miller --- Documentation/networking/timestamping.txt | 178 +++++++ Documentation/networking/timestamping/.gitignore | 1 + Documentation/networking/timestamping/Makefile | 6 + .../networking/timestamping/timestamping.c | 533 +++++++++++++++++++++ arch/alpha/include/asm/socket.h | 3 + arch/arm/include/asm/socket.h | 3 + arch/avr32/include/asm/socket.h | 3 + arch/blackfin/include/asm/socket.h | 3 + arch/cris/include/asm/socket.h | 3 + arch/h8300/include/asm/socket.h | 3 + arch/ia64/include/asm/socket.h | 3 + arch/m68k/include/asm/socket.h | 3 + arch/mips/include/asm/socket.h | 3 + arch/parisc/include/asm/socket.h | 3 + arch/powerpc/include/asm/socket.h | 3 + arch/s390/include/asm/socket.h | 3 + arch/sh/include/asm/socket.h | 3 + arch/sparc/include/asm/socket.h | 3 + arch/x86/include/asm/socket.h | 3 + arch/xtensa/include/asm/socket.h | 3 + include/asm-frv/socket.h | 3 + include/asm-m32r/socket.h | 3 + include/asm-mn10300/socket.h | 3 + include/linux/errqueue.h | 1 + include/linux/net_tstamp.h | 104 ++++ include/linux/sockios.h | 3 + 26 files changed, 883 insertions(+) create mode 100644 Documentation/networking/timestamping.txt create mode 100644 Documentation/networking/timestamping/.gitignore create mode 100644 Documentation/networking/timestamping/Makefile create mode 100644 Documentation/networking/timestamping/timestamping.c create mode 100644 include/linux/net_tstamp.h (limited to 'include/linux') diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt new file mode 100644 index 000000000000..a681a65b5bc7 --- /dev/null +++ b/Documentation/networking/timestamping.txt @@ -0,0 +1,178 @@ +The existing interfaces for getting network packages time stamped are: + +* SO_TIMESTAMP + Generate time stamp for each incoming packet using the (not necessarily + monotonous!) system time. Result is returned via recv_msg() in a + control message as timeval (usec resolution). + +* SO_TIMESTAMPNS + Same time stamping mechanism as SO_TIMESTAMP, but returns result as + timespec (nsec resolution). + +* IP_MULTICAST_LOOP + SO_TIMESTAMP[NS] + Only for multicasts: approximate send time stamp by receiving the looped + packet and using its receive time stamp. + +The following interface complements the existing ones: receive time +stamps can be generated and returned for arbitrary packets and much +closer to the point where the packet is really sent. Time stamps can +be generated in software (as before) or in hardware (if the hardware +has such a feature). + +SO_TIMESTAMPING: + +Instructs the socket layer which kind of information is wanted. The +parameter is an integer with some of the following bits set. Setting +other bits is an error and doesn't change the current state. + +SOF_TIMESTAMPING_TX_HARDWARE: try to obtain send time stamp in hardware +SOF_TIMESTAMPING_TX_SOFTWARE: if SOF_TIMESTAMPING_TX_HARDWARE is off or + fails, then do it in software +SOF_TIMESTAMPING_RX_HARDWARE: return the original, unmodified time stamp + as generated by the hardware +SOF_TIMESTAMPING_RX_SOFTWARE: if SOF_TIMESTAMPING_RX_HARDWARE is off or + fails, then do it in software +SOF_TIMESTAMPING_RAW_HARDWARE: return original raw hardware time stamp +SOF_TIMESTAMPING_SYS_HARDWARE: return hardware time stamp transformed to + the system time base +SOF_TIMESTAMPING_SOFTWARE: return system time stamp generated in + software + +SOF_TIMESTAMPING_TX/RX determine how time stamps are generated. +SOF_TIMESTAMPING_RAW/SYS determine how they are reported in the +following control message: + struct scm_timestamping { + struct timespec systime; + struct timespec hwtimetrans; + struct timespec hwtimeraw; + }; + +recvmsg() can be used to get this control message for regular incoming +packets. For send time stamps the outgoing packet is looped back to +the socket's error queue with the send time stamp(s) attached. It can +be received with recvmsg(flags=MSG_ERRQUEUE). The call returns the +original outgoing packet data including all headers preprended down to +and including the link layer, the scm_timestamping control message and +a sock_extended_err control message with ee_errno==ENOMSG and +ee_origin==SO_EE_ORIGIN_TIMESTAMPING. A socket with such a pending +bounced packet is ready for reading as far as select() is concerned. + +All three values correspond to the same event in time, but were +generated in different ways. Each of these values may be empty (= all +zero), in which case no such value was available. If the application +is not interested in some of these values, they can be left blank to +avoid the potential overhead of calculating them. + +systime is the value of the system time at that moment. This +corresponds to the value also returned via SO_TIMESTAMP[NS]. If the +time stamp was generated by hardware, then this field is +empty. Otherwise it is filled in if SOF_TIMESTAMPING_SOFTWARE is +set. + +hwtimeraw is the original hardware time stamp. Filled in if +SOF_TIMESTAMPING_RAW_HARDWARE is set. No assumptions about its +relation to system time should be made. + +hwtimetrans is the hardware time stamp transformed so that it +corresponds as good as possible to system time. This correlation is +not perfect; as a consequence, sorting packets received via different +NICs by their hwtimetrans may differ from the order in which they were +received. hwtimetrans may be non-monotonic even for the same NIC. +Filled in if SOF_TIMESTAMPING_SYS_HARDWARE is set. Requires support +by the network device and will be empty without that support. + + +SIOCSHWTSTAMP: + +Hardware time stamping must also be initialized for each device driver +that is expected to do hardware time stamping. The parameter is: + +struct hwtstamp_config { + int flags; /* no flags defined right now, must be zero */ + int tx_type; /* HWTSTAMP_TX_* */ + int rx_filter; /* HWTSTAMP_FILTER_* */ +}; + +Desired behavior is passed into the kernel and to a specific device by +calling ioctl(SIOCSHWTSTAMP) with a pointer to a struct ifreq whose +ifr_data points to a struct hwtstamp_config. The tx_type and +rx_filter are hints to the driver what it is expected to do. If +the requested fine-grained filtering for incoming packets is not +supported, the driver may time stamp more than just the requested types +of packets. + +A driver which supports hardware time stamping shall update the struct +with the actual, possibly more permissive configuration. If the +requested packets cannot be time stamped, then nothing should be +changed and ERANGE shall be returned (in contrast to EINVAL, which +indicates that SIOCSHWTSTAMP is not supported at all). + +Only a processes with admin rights may change the configuration. User +space is responsible to ensure that multiple processes don't interfere +with each other and that the settings are reset. + +/* possible values for hwtstamp_config->tx_type */ +enum { + /* + * no outgoing packet will need hardware time stamping; + * should a packet arrive which asks for it, no hardware + * time stamping will be done + */ + HWTSTAMP_TX_OFF, + + /* + * enables hardware time stamping for outgoing packets; + * the sender of the packet decides which are to be + * time stamped by setting SOF_TIMESTAMPING_TX_SOFTWARE + * before sending the packet + */ + HWTSTAMP_TX_ON, +}; + +/* possible values for hwtstamp_config->rx_filter */ +enum { + /* time stamp no incoming packet at all */ + HWTSTAMP_FILTER_NONE, + + /* time stamp any incoming packet */ + HWTSTAMP_FILTER_ALL, + + /* return value: time stamp all packets requested plus some others */ + HWTSTAMP_FILTER_SOME, + + /* PTP v1, UDP, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V1_L4_EVENT, + + ... +}; + + +DEVICE IMPLEMENTATION + +A driver which supports hardware time stamping must support the +SIOCSHWTSTAMP ioctl. Time stamps for received packets must be stored +in the skb with skb_hwtstamp_set(). + +Time stamps for outgoing packets are to be generated as follows: +- In hard_start_xmit(), check if skb_hwtstamp_check_tx_hardware() + returns non-zero. If yes, then the driver is expected + to do hardware time stamping. +- If this is possible for the skb and requested, then declare + that the driver is doing the time stamping by calling + skb_hwtstamp_tx_in_progress(). A driver not supporting + hardware time stamping doesn't do that. A driver must never + touch sk_buff::tstamp! It is used to store how time stamping + for an outgoing packets is to be done. +- As soon as the driver has sent the packet and/or obtained a + hardware time stamp for it, it passes the time stamp back by + calling skb_hwtstamp_tx() with the original skb, the raw + hardware time stamp and a handle to the device (necessary + to convert the hardware time stamp to system time). If obtaining + the hardware time stamp somehow fails, then the driver should + not fall back to software time stamping. The rationale is that + this would occur at a later time in the processing pipeline + than other software time stamping and therefore could lead + to unexpected deltas between time stamps. +- If the driver did not call skb_hwtstamp_tx_in_progress(), then + dev_hard_start_xmit() checks whether software time stamping + is wanted as fallback and potentially generates the time stamp. diff --git a/Documentation/networking/timestamping/.gitignore b/Documentation/networking/timestamping/.gitignore new file mode 100644 index 000000000000..71e81eb2e22f --- /dev/null +++ b/Documentation/networking/timestamping/.gitignore @@ -0,0 +1 @@ +timestamping diff --git a/Documentation/networking/timestamping/Makefile b/Documentation/networking/timestamping/Makefile new file mode 100644 index 000000000000..2a1489fdc036 --- /dev/null +++ b/Documentation/networking/timestamping/Makefile @@ -0,0 +1,6 @@ +CPPFLAGS = -I../../../include + +timestamping: timestamping.c + +clean: + rm -f timestamping diff --git a/Documentation/networking/timestamping/timestamping.c b/Documentation/networking/timestamping/timestamping.c new file mode 100644 index 000000000000..43d143104210 --- /dev/null +++ b/Documentation/networking/timestamping/timestamping.c @@ -0,0 +1,533 @@ +/* + * This program demonstrates how the various time stamping features in + * the Linux kernel work. It emulates the behavior of a PTP + * implementation in stand-alone master mode by sending PTPv1 Sync + * multicasts once every second. It looks for similar packets, but + * beyond that doesn't actually implement PTP. + * + * Outgoing packets are time stamped with SO_TIMESTAMPING with or + * without hardware support. + * + * Incoming packets are time stamped with SO_TIMESTAMPING with or + * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and + * SO_TIMESTAMP[NS]. + * + * Copyright (C) 2009 Intel Corporation. + * Author: Patrick Ohly + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "asm/types.h" +#include "linux/net_tstamp.h" +#include "linux/errqueue.h" + +#ifndef SO_TIMESTAMPING +# define SO_TIMESTAMPING 37 +# define SCM_TIMESTAMPING SO_TIMESTAMPING +#endif + +#ifndef SO_TIMESTAMPNS +# define SO_TIMESTAMPNS 35 +#endif + +#ifndef SIOCGSTAMPNS +# define SIOCGSTAMPNS 0x8907 +#endif + +#ifndef SIOCSHWTSTAMP +# define SIOCSHWTSTAMP 0x89b0 +#endif + +static void usage(const char *error) +{ + if (error) + printf("invalid option: %s\n", error); + printf("timestamping interface option*\n\n" + "Options:\n" + " IP_MULTICAST_LOOP - looping outgoing multicasts\n" + " SO_TIMESTAMP - normal software time stamping, ms resolution\n" + " SO_TIMESTAMPNS - more accurate software time stamping\n" + " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n" + " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n" + " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n" + " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n" + " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n" + " SOF_TIMESTAMPING_SYS_HARDWARE - request reporting of transformed HW time stamps\n" + " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n" + " SIOCGSTAMP - check last socket time stamp\n" + " SIOCGSTAMPNS - more accurate socket time stamp\n"); + exit(1); +} + +static void bail(const char *error) +{ + printf("%s: %s\n", error, strerror(errno)); + exit(1); +} + +static const unsigned char sync[] = { + 0x00, 0x01, 0x00, 0x01, + 0x5f, 0x44, 0x46, 0x4c, + 0x54, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, + + /* fake uuid */ + 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, + + 0x00, 0x01, 0x00, 0x37, + 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, + 0x49, 0x05, 0xcd, 0x01, + 0x29, 0xb1, 0x8d, 0xb0, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + + /* fake uuid */ + 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, + + 0x00, 0x00, 0x00, 0x37, + 0x00, 0x00, 0x00, 0x04, + 0x44, 0x46, 0x4c, 0x54, + 0x00, 0x00, 0xf0, 0x60, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0xf0, 0x60, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, + 0x44, 0x46, 0x4c, 0x54, + 0x00, 0x01, + + /* fake uuid */ + 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, + + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len) +{ + struct timeval now; + int res; + + res = sendto(sock, sync, sizeof(sync), 0, + addr, addr_len); + gettimeofday(&now, 0); + if (res < 0) + printf("%s: %s\n", "send", strerror(errno)); + else + printf("%ld.%06ld: sent %d bytes\n", + (long)now.tv_sec, (long)now.tv_usec, + res); +} + +static void printpacket(struct msghdr *msg, int res, + char *data, + int sock, int recvmsg_flags, + int siocgstamp, int siocgstampns) +{ + struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name; + struct cmsghdr *cmsg; + struct timeval tv; + struct timespec ts; + struct timeval now; + + gettimeofday(&now, 0); + + printf("%ld.%06ld: received %s data, %d bytes from %s, %d bytes control messages\n", + (long)now.tv_sec, (long)now.tv_usec, + (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", + res, + inet_ntoa(from_addr->sin_addr), + msg->msg_controllen); + for (cmsg = CMSG_FIRSTHDR(msg); + cmsg; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + printf(" cmsg len %d: ", cmsg->cmsg_len); + switch (cmsg->cmsg_level) { + case SOL_SOCKET: + printf("SOL_SOCKET "); + switch (cmsg->cmsg_type) { + case SO_TIMESTAMP: { + struct timeval *stamp = + (struct timeval *)CMSG_DATA(cmsg); + printf("SO_TIMESTAMP %ld.%06ld", + (long)stamp->tv_sec, + (long)stamp->tv_usec); + break; + } + case SO_TIMESTAMPNS: { + struct timespec *stamp = + (struct timespec *)CMSG_DATA(cmsg); + printf("SO_TIMESTAMPNS %ld.%09ld", + (long)stamp->tv_sec, + (long)stamp->tv_nsec); + break; + } + case SO_TIMESTAMPING: { + struct timespec *stamp = + (struct timespec *)CMSG_DATA(cmsg); + printf("SO_TIMESTAMPING "); + printf("SW %ld.%09ld ", + (long)stamp->tv_sec, + (long)stamp->tv_nsec); + stamp++; + printf("HW transformed %ld.%09ld ", + (long)stamp->tv_sec, + (long)stamp->tv_nsec); + stamp++; + printf("HW raw %ld.%09ld", + (long)stamp->tv_sec, + (long)stamp->tv_nsec); + break; + } + default: + printf("type %d", cmsg->cmsg_type); + break; + } + break; + case IPPROTO_IP: + printf("IPPROTO_IP "); + switch (cmsg->cmsg_type) { + case IP_RECVERR: { + struct sock_extended_err *err = + (struct sock_extended_err *)CMSG_DATA(cmsg); + printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s", + strerror(err->ee_errno), + err->ee_origin, +#ifdef SO_EE_ORIGIN_TIMESTAMPING + err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ? + "bounced packet" : "unexpected origin" +#else + "probably SO_EE_ORIGIN_TIMESTAMPING" +#endif + ); + if (res < sizeof(sync)) + printf(" => truncated data?!"); + else if (!memcmp(sync, data + res - sizeof(sync), + sizeof(sync))) + printf(" => GOT OUR DATA BACK (HURRAY!)"); + break; + } + case IP_PKTINFO: { + struct in_pktinfo *pktinfo = + (struct in_pktinfo *)CMSG_DATA(cmsg); + printf("IP_PKTINFO interface index %u", + pktinfo->ipi_ifindex); + break; + } + default: + printf("type %d", cmsg->cmsg_type); + break; + } + break; + default: + printf("level %d type %d", + cmsg->cmsg_level, + cmsg->cmsg_type); + break; + } + printf("\n"); + } + + if (siocgstamp) { + if (ioctl(sock, SIOCGSTAMP, &tv)) + printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno)); + else + printf("SIOCGSTAMP %ld.%06ld\n", + (long)tv.tv_sec, + (long)tv.tv_usec); + } + if (siocgstampns) { + if (ioctl(sock, SIOCGSTAMPNS, &ts)) + printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno)); + else + printf("SIOCGSTAMPNS %ld.%09ld\n", + (long)ts.tv_sec, + (long)ts.tv_nsec); + } +} + +static void recvpacket(int sock, int recvmsg_flags, + int siocgstamp, int siocgstampns) +{ + char data[256]; + struct msghdr msg; + struct iovec entry; + struct sockaddr_in from_addr; + struct { + struct cmsghdr cm; + char control[512]; + } control; + int res; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &entry; + msg.msg_iovlen = 1; + entry.iov_base = data; + entry.iov_len = sizeof(data); + msg.msg_name = (caddr_t)&from_addr; + msg.msg_namelen = sizeof(from_addr); + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT); + if (res < 0) { + printf("%s %s: %s\n", + "recvmsg", + (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", + strerror(errno)); + } else { + printpacket(&msg, res, data, + sock, recvmsg_flags, + siocgstamp, siocgstampns); + } +} + +int main(int argc, char **argv) +{ + int so_timestamping_flags = 0; + int so_timestamp = 0; + int so_timestampns = 0; + int siocgstamp = 0; + int siocgstampns = 0; + int ip_multicast_loop = 0; + char *interface; + int i; + int enabled = 1; + int sock; + struct ifreq device; + struct ifreq hwtstamp; + struct hwtstamp_config hwconfig, hwconfig_requested; + struct sockaddr_in addr; + struct ip_mreq imr; + struct in_addr iaddr; + int val; + socklen_t len; + struct timeval next; + + if (argc < 2) + usage(0); + interface = argv[1]; + + for (i = 2; i < argc; i++) { + if (!strcasecmp(argv[i], "SO_TIMESTAMP")) + so_timestamp = 1; + else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS")) + so_timestampns = 1; + else if (!strcasecmp(argv[i], "SIOCGSTAMP")) + siocgstamp = 1; + else if (!strcasecmp(argv[i], "SIOCGSTAMPNS")) + siocgstampns = 1; + else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP")) + ip_multicast_loop = 1; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SYS_HARDWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_SYS_HARDWARE; + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE")) + so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE; + else + usage(argv[i]); + } + + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (socket < 0) + bail("socket"); + + memset(&device, 0, sizeof(device)); + strncpy(device.ifr_name, interface, sizeof(device.ifr_name)); + if (ioctl(sock, SIOCGIFADDR, &device) < 0) + bail("getting interface IP address"); + + memset(&hwtstamp, 0, sizeof(hwtstamp)); + strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name)); + hwtstamp.ifr_data = (void *)&hwconfig; + memset(&hwconfig, 0, sizeof(&hwconfig)); + hwconfig.tx_type = + (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ? + HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; + hwconfig.rx_filter = + (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ? + HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE; + hwconfig_requested = hwconfig; + if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) { + if ((errno == EINVAL || errno == ENOTSUP) && + hwconfig_requested.tx_type == HWTSTAMP_TX_OFF && + hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE) + printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n"); + else + bail("SIOCSHWTSTAMP"); + } + printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n", + hwconfig_requested.tx_type, hwconfig.tx_type, + hwconfig_requested.rx_filter, hwconfig.rx_filter); + + /* bind to PTP port */ + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(319 /* PTP event port */); + if (bind(sock, + (struct sockaddr *)&addr, + sizeof(struct sockaddr_in)) < 0) + bail("bind"); + + /* set multicast group for outgoing packets */ + inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */ + addr.sin_addr = iaddr; + imr.imr_multiaddr.s_addr = iaddr.s_addr; + imr.imr_interface.s_addr = + ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr; + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, + &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0) + bail("set multicast"); + + /* join multicast group, loop our own packet */ + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &imr, sizeof(struct ip_mreq)) < 0) + bail("join multicast group"); + + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, + &ip_multicast_loop, sizeof(enabled)) < 0) { + bail("loop multicast"); + } + + /* set socket options for time stamping */ + if (so_timestamp && + setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, + &enabled, sizeof(enabled)) < 0) + bail("setsockopt SO_TIMESTAMP"); + + if (so_timestampns && + setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, + &enabled, sizeof(enabled)) < 0) + bail("setsockopt SO_TIMESTAMPNS"); + + if (so_timestamping_flags && + setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, + &so_timestamping_flags, + sizeof(so_timestamping_flags)) < 0) + bail("setsockopt SO_TIMESTAMPING"); + + /* request IP_PKTINFO for debugging purposes */ + if (setsockopt(sock, SOL_IP, IP_PKTINFO, + &enabled, sizeof(enabled)) < 0) + printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno)); + + /* verify socket options */ + len = sizeof(val); + if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0) + printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno)); + else + printf("SO_TIMESTAMP %d\n", val); + + if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0) + printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS", + strerror(errno)); + else + printf("SO_TIMESTAMPNS %d\n", val); + + if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) { + printf("%s: %s\n", "getsockopt SO_TIMESTAMPING", + strerror(errno)); + } else { + printf("SO_TIMESTAMPING %d\n", val); + if (val != so_timestamping_flags) + printf(" not the expected value %d\n", + so_timestamping_flags); + } + + /* send packets forever every five seconds */ + gettimeofday(&next, 0); + next.tv_sec = (next.tv_sec + 1) / 5 * 5; + next.tv_usec = 0; + while (1) { + struct timeval now; + struct timeval delta; + long delta_us; + int res; + fd_set readfs, errorfs; + + gettimeofday(&now, 0); + delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 + + (long)(next.tv_usec - now.tv_usec); + if (delta_us > 0) { + /* continue waiting for timeout or data */ + delta.tv_sec = delta_us / 1000000; + delta.tv_usec = delta_us % 1000000; + + FD_ZERO(&readfs); + FD_ZERO(&errorfs); + FD_SET(sock, &readfs); + FD_SET(sock, &errorfs); + printf("%ld.%06ld: select %ldus\n", + (long)now.tv_sec, (long)now.tv_usec, + delta_us); + res = select(sock + 1, &readfs, 0, &errorfs, &delta); + gettimeofday(&now, 0); + printf("%ld.%06ld: select returned: %d, %s\n", + (long)now.tv_sec, (long)now.tv_usec, + res, + res < 0 ? strerror(errno) : "success"); + if (res > 0) { + if (FD_ISSET(sock, &readfs)) + printf("ready for reading\n"); + if (FD_ISSET(sock, &errorfs)) + printf("has error\n"); + recvpacket(sock, 0, + siocgstamp, + siocgstampns); + recvpacket(sock, MSG_ERRQUEUE, + siocgstamp, + siocgstampns); + } + } else { + /* write one packet */ + sendpacket(sock, + (struct sockaddr *)&addr, + sizeof(addr)); + next.tv_sec += 5; + continue; + } + } + + return 0; +} diff --git a/arch/alpha/include/asm/socket.h b/arch/alpha/include/asm/socket.h index a1057c2d95e7..3641ec1452f4 100644 --- a/arch/alpha/include/asm/socket.h +++ b/arch/alpha/include/asm/socket.h @@ -62,6 +62,9 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff --git a/arch/arm/include/asm/socket.h b/arch/arm/include/asm/socket.h index 6817be9573a6..537de4e0ef50 100644 --- a/arch/arm/include/asm/socket.h +++ b/arch/arm/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/avr32/include/asm/socket.h b/arch/avr32/include/asm/socket.h index 35863f260929..04c860619700 100644 --- a/arch/avr32/include/asm/socket.h +++ b/arch/avr32/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* __ASM_AVR32_SOCKET_H */ diff --git a/arch/blackfin/include/asm/socket.h b/arch/blackfin/include/asm/socket.h index 2ca702e44d47..fac7fe9e1f8a 100644 --- a/arch/blackfin/include/asm/socket.h +++ b/arch/blackfin/include/asm/socket.h @@ -53,4 +53,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/cris/include/asm/socket.h b/arch/cris/include/asm/socket.h index 9df0ca82f5de..d5cf74005408 100644 --- a/arch/cris/include/asm/socket.h +++ b/arch/cris/include/asm/socket.h @@ -56,6 +56,9 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/h8300/include/asm/socket.h b/arch/h8300/include/asm/socket.h index da2520dbf254..602518a70a1a 100644 --- a/arch/h8300/include/asm/socket.h +++ b/arch/h8300/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/asm/socket.h b/arch/ia64/include/asm/socket.h index d5ef0aa3e312..745421225ec6 100644 --- a/arch/ia64/include/asm/socket.h +++ b/arch/ia64/include/asm/socket.h @@ -63,4 +63,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m68k/include/asm/socket.h b/arch/m68k/include/asm/socket.h index dbc64e92c41a..ca87f938b03f 100644 --- a/arch/m68k/include/asm/socket.h +++ b/arch/m68k/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/mips/include/asm/socket.h b/arch/mips/include/asm/socket.h index facc2d7a87ca..2abca1780169 100644 --- a/arch/mips/include/asm/socket.h +++ b/arch/mips/include/asm/socket.h @@ -75,6 +75,9 @@ To add: #define SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #ifdef __KERNEL__ /** sock_type - Socket types diff --git a/arch/parisc/include/asm/socket.h b/arch/parisc/include/asm/socket.h index fba402c95ac2..885472bf7b78 100644 --- a/arch/parisc/include/asm/socket.h +++ b/arch/parisc/include/asm/socket.h @@ -54,6 +54,9 @@ #define SO_MARK 0x401f +#define SO_TIMESTAMPING 0x4020 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff --git a/arch/powerpc/include/asm/socket.h b/arch/powerpc/include/asm/socket.h index f5a4e168e498..1e5cfad0e3f7 100644 --- a/arch/powerpc/include/asm/socket.h +++ b/arch/powerpc/include/asm/socket.h @@ -61,4 +61,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/asm/socket.h b/arch/s390/include/asm/socket.h index c786ab623b2d..02330c50241b 100644 --- a/arch/s390/include/asm/socket.h +++ b/arch/s390/include/asm/socket.h @@ -62,4 +62,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sh/include/asm/socket.h b/arch/sh/include/asm/socket.h index 6d4bf6512959..345653b96826 100644 --- a/arch/sh/include/asm/socket.h +++ b/arch/sh/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* __ASM_SH_SOCKET_H */ diff --git a/arch/sparc/include/asm/socket.h b/arch/sparc/include/asm/socket.h index bf50d0c2d583..982a12f959f4 100644 --- a/arch/sparc/include/asm/socket.h +++ b/arch/sparc/include/asm/socket.h @@ -50,6 +50,9 @@ #define SO_MARK 0x0022 +#define SO_TIMESTAMPING 0x0023 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 diff --git a/arch/x86/include/asm/socket.h b/arch/x86/include/asm/socket.h index 8ab9cc8b2ecc..ca8bf2cd0ba9 100644 --- a/arch/x86/include/asm/socket.h +++ b/arch/x86/include/asm/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_X86_SOCKET_H */ diff --git a/arch/xtensa/include/asm/socket.h b/arch/xtensa/include/asm/socket.h index 6100682b1da2..dd1a7a4a1cea 100644 --- a/arch/xtensa/include/asm/socket.h +++ b/arch/xtensa/include/asm/socket.h @@ -65,4 +65,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _XTENSA_SOCKET_H */ diff --git a/include/asm-frv/socket.h b/include/asm-frv/socket.h index e51ca67b9356..57c3d4054e8b 100644 --- a/include/asm-frv/socket.h +++ b/include/asm-frv/socket.h @@ -54,5 +54,8 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-m32r/socket.h b/include/asm-m32r/socket.h index 9a0e20012224..be7ed589af5c 100644 --- a/include/asm-m32r/socket.h +++ b/include/asm-m32r/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/include/asm-mn10300/socket.h b/include/asm-mn10300/socket.h index 80af9c4ccad7..fb5daf438ec9 100644 --- a/include/asm-mn10300/socket.h +++ b/include/asm-mn10300/socket.h @@ -54,4 +54,7 @@ #define SO_MARK 36 +#define SO_TIMESTAMPING 37 +#define SCM_TIMESTAMPING SO_TIMESTAMPING + #endif /* _ASM_SOCKET_H */ diff --git a/include/linux/errqueue.h b/include/linux/errqueue.h index ceb1454b6977..ec12cc74366f 100644 --- a/include/linux/errqueue.h +++ b/include/linux/errqueue.h @@ -18,6 +18,7 @@ struct sock_extended_err #define SO_EE_ORIGIN_LOCAL 1 #define SO_EE_ORIGIN_ICMP 2 #define SO_EE_ORIGIN_ICMP6 3 +#define SO_EE_ORIGIN_TIMESTAMPING 4 #define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1)) diff --git a/include/linux/net_tstamp.h b/include/linux/net_tstamp.h new file mode 100644 index 000000000000..a3b8546354ac --- /dev/null +++ b/include/linux/net_tstamp.h @@ -0,0 +1,104 @@ +/* + * Userspace API for hardware time stamping of network packets + * + * Copyright (C) 2008,2009 Intel Corporation + * Author: Patrick Ohly + * + */ + +#ifndef _NET_TIMESTAMPING_H +#define _NET_TIMESTAMPING_H + +#include /* for SO_TIMESTAMPING */ + +/* SO_TIMESTAMPING gets an integer bit field comprised of these values */ +enum { + SOF_TIMESTAMPING_TX_HARDWARE = (1<<0), + SOF_TIMESTAMPING_TX_SOFTWARE = (1<<1), + SOF_TIMESTAMPING_RX_HARDWARE = (1<<2), + SOF_TIMESTAMPING_RX_SOFTWARE = (1<<3), + SOF_TIMESTAMPING_SOFTWARE = (1<<4), + SOF_TIMESTAMPING_SYS_HARDWARE = (1<<5), + SOF_TIMESTAMPING_RAW_HARDWARE = (1<<6), + SOF_TIMESTAMPING_MASK = + (SOF_TIMESTAMPING_RAW_HARDWARE - 1) | + SOF_TIMESTAMPING_RAW_HARDWARE +}; + +/** + * struct hwtstamp_config - %SIOCSHWTSTAMP parameter + * + * @flags: no flags defined right now, must be zero + * @tx_type: one of HWTSTAMP_TX_* + * @rx_type: one of one of HWTSTAMP_FILTER_* + * + * %SIOCSHWTSTAMP expects a &struct ifreq with a ifr_data pointer to + * this structure. dev_ifsioc() in the kernel takes care of the + * translation between 32 bit userspace and 64 bit kernel. The + * structure is intentionally chosen so that it has the same layout on + * 32 and 64 bit systems, don't break this! + */ +struct hwtstamp_config { + int flags; + int tx_type; + int rx_filter; +}; + +/* possible values for hwtstamp_config->tx_type */ +enum { + /* + * No outgoing packet will need hardware time stamping; + * should a packet arrive which asks for it, no hardware + * time stamping will be done. + */ + HWTSTAMP_TX_OFF, + + /* + * Enables hardware time stamping for outgoing packets; + * the sender of the packet decides which are to be + * time stamped by setting %SOF_TIMESTAMPING_TX_SOFTWARE + * before sending the packet. + */ + HWTSTAMP_TX_ON, +}; + +/* possible values for hwtstamp_config->rx_filter */ +enum { + /* time stamp no incoming packet at all */ + HWTSTAMP_FILTER_NONE, + + /* time stamp any incoming packet */ + HWTSTAMP_FILTER_ALL, + + /* return value: time stamp all packets requested plus some others */ + HWTSTAMP_FILTER_SOME, + + /* PTP v1, UDP, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V1_L4_EVENT, + /* PTP v1, UDP, Sync packet */ + HWTSTAMP_FILTER_PTP_V1_L4_SYNC, + /* PTP v1, UDP, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ, + /* PTP v2, UDP, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V2_L4_EVENT, + /* PTP v2, UDP, Sync packet */ + HWTSTAMP_FILTER_PTP_V2_L4_SYNC, + /* PTP v2, UDP, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ, + + /* 802.AS1, Ethernet, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V2_L2_EVENT, + /* 802.AS1, Ethernet, Sync packet */ + HWTSTAMP_FILTER_PTP_V2_L2_SYNC, + /* 802.AS1, Ethernet, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ, + + /* PTP v2/802.AS1, any layer, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V2_EVENT, + /* PTP v2/802.AS1, any layer, Sync packet */ + HWTSTAMP_FILTER_PTP_V2_SYNC, + /* PTP v2/802.AS1, any layer, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V2_DELAY_REQ, +}; + +#endif /* _NET_TIMESTAMPING_H */ diff --git a/include/linux/sockios.h b/include/linux/sockios.h index abef7596655a..241f179347d9 100644 --- a/include/linux/sockios.h +++ b/include/linux/sockios.h @@ -122,6 +122,9 @@ #define SIOCBRADDIF 0x89a2 /* add interface to bridge */ #define SIOCBRDELIF 0x89a3 /* remove interface from bridge */ +/* hardware time stamping: parameters in linux/net_tstamp.h */ +#define SIOCSHWTSTAMP 0x89b0 + /* Device private ioctl calls */ /* -- cgit v1.2.3 From ac45f602ee3d1b6f326f68bc0c2591ceebf05ba4 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 12 Feb 2009 05:03:37 +0000 Subject: net: infrastructure for hardware time stamping The additional per-packet information (16 bytes for time stamps, 1 byte for flags) is stored for all packets in the skb_shared_info struct. This implementation detail is hidden from users of that information via skb_* accessor functions. A separate struct resp. union is used for the additional information so that it can be stored/copied easily outside of skb_shared_info. Compared to previous implementations (reusing the tstamp field depending on the context, optional additional structures) this is the simplest solution. It does not extend sk_buff itself. TX time stamping is implemented in software if the device driver doesn't support hardware time stamping. The new semantic for hardware/software time stamping around ndo_start_xmit() is based on two assumptions about existing network device drivers which don't support hardware time stamping and know nothing about it: - they leave the new skb_shared_tx unmodified - the keep the connection to the originating socket in skb->sk alive, i.e., don't call skb_orphan() Given that skb_shared_tx is new, the first assumption is safe. The second is only true for some drivers. As a result, software TX time stamping currently works with the bnx2 driver, but not with the unmodified igb driver (the two drivers this patch series was tested with). Signed-off-by: Patrick Ohly Signed-off-by: David S. Miller --- include/linux/skbuff.h | 91 +++++++++++++++++++++++++++++++++++++++++++++++++- net/core/dev.c | 32 ++++++++++++++++-- net/core/skbuff.c | 41 +++++++++++++++++++++++ 3 files changed, 161 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 924700844580..f96bc91bf0a3 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -132,6 +132,57 @@ struct skb_frag_struct { __u32 size; }; +#define HAVE_HW_TIME_STAMP + +/** + * skb_shared_hwtstamps - hardware time stamps + * + * @hwtstamp: hardware time stamp transformed into duration + * since arbitrary point in time + * @syststamp: hwtstamp transformed to system time base + * + * Software time stamps generated by ktime_get_real() are stored in + * skb->tstamp. The relation between the different kinds of time + * stamps is as follows: + * + * syststamp and tstamp can be compared against each other in + * arbitrary combinations. The accuracy of a + * syststamp/tstamp/"syststamp from other device" comparison is + * limited by the accuracy of the transformation into system time + * base. This depends on the device driver and its underlying + * hardware. + * + * hwtstamps can only be compared against other hwtstamps from + * the same device. + * + * This structure is attached to packets as part of the + * &skb_shared_info. Use skb_hwtstamps() to get a pointer. + */ +struct skb_shared_hwtstamps { + ktime_t hwtstamp; + ktime_t syststamp; +}; + +/** + * skb_shared_tx - instructions for time stamping of outgoing packets + * + * @hardware: generate hardware time stamp + * @software: generate software time stamp + * @in_progress: device driver is going to provide + * hardware time stamp + * + * These flags are attached to packets as part of the + * &skb_shared_info. Use skb_tx() to get a pointer. + */ +union skb_shared_tx { + struct { + __u8 hardware:1, + software:1, + in_progress:1; + }; + __u8 flags; +}; + /* This data is invariant across clones and lives at * the end of the header data, ie. at skb->end. */ @@ -143,10 +194,12 @@ struct skb_shared_info { unsigned short gso_segs; unsigned short gso_type; __be32 ip6_frag_id; + union skb_shared_tx tx_flags; #ifdef CONFIG_HAS_DMA unsigned int num_dma_maps; #endif struct sk_buff *frag_list; + struct skb_shared_hwtstamps hwtstamps; skb_frag_t frags[MAX_SKB_FRAGS]; #ifdef CONFIG_HAS_DMA dma_addr_t dma_maps[MAX_SKB_FRAGS + 1]; @@ -465,6 +518,16 @@ static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) /* Internal */ #define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB))) +static inline struct skb_shared_hwtstamps *skb_hwtstamps(struct sk_buff *skb) +{ + return &skb_shinfo(skb)->hwtstamps; +} + +static inline union skb_shared_tx *skb_tx(struct sk_buff *skb) +{ + return &skb_shinfo(skb)->tx_flags; +} + /** * skb_queue_empty - check if a queue is empty * @list: queue head @@ -1730,6 +1793,11 @@ static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb, extern void skb_init(void); +static inline ktime_t skb_get_ktime(const struct sk_buff *skb) +{ + return skb->tstamp; +} + /** * skb_get_timestamp - get timestamp from a skb * @skb: skb to get stamp from @@ -1739,11 +1807,18 @@ extern void skb_init(void); * This function converts the offset back to a struct timeval and stores * it in stamp. */ -static inline void skb_get_timestamp(const struct sk_buff *skb, struct timeval *stamp) +static inline void skb_get_timestamp(const struct sk_buff *skb, + struct timeval *stamp) { *stamp = ktime_to_timeval(skb->tstamp); } +static inline void skb_get_timestampns(const struct sk_buff *skb, + struct timespec *stamp) +{ + *stamp = ktime_to_timespec(skb->tstamp); +} + static inline void __net_timestamp(struct sk_buff *skb) { skb->tstamp = ktime_get_real(); @@ -1759,6 +1834,20 @@ static inline ktime_t net_invalid_timestamp(void) return ktime_set(0, 0); } +/** + * skb_tstamp_tx - queue clone of skb with send time stamps + * @orig_skb: the original outgoing packet + * @hwtstamps: hardware time stamps, may be NULL if not available + * + * If the skb has a socket associated, then this function clones the + * skb (thus sharing the actual data and optional structures), stores + * the optional hardware time stamping information (if non NULL) or + * generates a software time stamp (otherwise), then queues the clone + * to the error queue of the socket. Errors are silently ignored. + */ +extern void skb_tstamp_tx(struct sk_buff *orig_skb, + struct skb_shared_hwtstamps *hwtstamps); + extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len); extern __sum16 __skb_checksum_complete(struct sk_buff *skb); diff --git a/net/core/dev.c b/net/core/dev.c index 1e27a67df242..d20c28e839d3 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1672,10 +1672,21 @@ static int dev_gso_segment(struct sk_buff *skb) return 0; } +static void tstamp_tx(struct sk_buff *skb) +{ + union skb_shared_tx *shtx = + skb_tx(skb); + if (unlikely(shtx->software && + !shtx->in_progress)) { + skb_tstamp_tx(skb, NULL); + } +} + int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq) { const struct net_device_ops *ops = dev->netdev_ops; + int rc; prefetch(&dev->netdev_ops->ndo_start_xmit); if (likely(!skb->next)) { @@ -1689,13 +1700,29 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, goto gso; } - return ops->ndo_start_xmit(skb, dev); + rc = ops->ndo_start_xmit(skb, dev); + /* + * TODO: if skb_orphan() was called by + * dev->hard_start_xmit() (for example, the unmodified + * igb driver does that; bnx2 doesn't), then + * skb_tx_software_timestamp() will be unable to send + * back the time stamp. + * + * How can this be prevented? Always create another + * reference to the socket before calling + * dev->hard_start_xmit()? Prevent that skb_orphan() + * does anything in dev->hard_start_xmit() by clearing + * the skb destructor before the call and restoring it + * afterwards, then doing the skb_orphan() ourselves? + */ + if (likely(!rc)) + tstamp_tx(skb); + return rc; } gso: do { struct sk_buff *nskb = skb->next; - int rc; skb->next = nskb->next; nskb->next = NULL; @@ -1705,6 +1732,7 @@ gso: skb->next = nskb; return rc; } + tstamp_tx(skb); if (unlikely(netif_tx_queue_stopped(txq) && skb->next)) return NETDEV_TX_BUSY; } while (skb->next); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ab7d2e9f02fa..e5a8351ff12d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -215,7 +216,9 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, shinfo->gso_segs = 0; shinfo->gso_type = 0; shinfo->ip6_frag_id = 0; + shinfo->tx_flags.flags = 0; shinfo->frag_list = NULL; + memset(&shinfo->hwtstamps, 0, sizeof(shinfo->hwtstamps)); if (fclone) { struct sk_buff *child = skb + 1; @@ -2945,6 +2948,44 @@ int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer) } EXPORT_SYMBOL_GPL(skb_cow_data); +void skb_tstamp_tx(struct sk_buff *orig_skb, + struct skb_shared_hwtstamps *hwtstamps) +{ + struct sock *sk = orig_skb->sk; + struct sock_exterr_skb *serr; + struct sk_buff *skb; + int err; + + if (!sk) + return; + + skb = skb_clone(orig_skb, GFP_ATOMIC); + if (!skb) + return; + + if (hwtstamps) { + *skb_hwtstamps(skb) = + *hwtstamps; + } else { + /* + * no hardware time stamps available, + * so keep the skb_shared_tx and only + * store software time stamp + */ + skb->tstamp = ktime_get_real(); + } + + serr = SKB_EXT_ERR(skb); + memset(serr, 0, sizeof(*serr)); + serr->ee.ee_errno = ENOMSG; + serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING; + err = sock_queue_err_skb(sk, skb); + if (err) + kfree_skb(skb); +} +EXPORT_SYMBOL_GPL(skb_tstamp_tx); + + /** * skb_partial_csum_set - set up and verify partial csum values for packet * @skb: the skb to set -- cgit v1.2.3 From 4458f04c02a46c679a90ef71f866a415c192deb4 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 13 Feb 2009 08:33:42 +0000 Subject: sctp: Clean up sctp checksumming code The sctp crc32c checksum is always generated in little endian. So, we clean up the code to treat it as little endian and remove all the __force casts. Suggested by Herbert Xu. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/linux/sctp.h | 2 +- include/net/sctp/checksum.h | 14 +++++++------- net/sctp/input.c | 11 ++++++----- net/sctp/output.c | 14 ++++++-------- 4 files changed, 20 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index bd50b371ffaa..c2731bfe04d8 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -60,7 +60,7 @@ typedef struct sctphdr { __be16 source; __be16 dest; __be32 vtag; - __be32 checksum; + __le32 checksum; } __attribute__((packed)) sctp_sctphdr_t; #ifdef __KERNEL__ diff --git a/include/net/sctp/checksum.h b/include/net/sctp/checksum.h index 2fec3c366e81..befc8d2a1b9f 100644 --- a/include/net/sctp/checksum.h +++ b/include/net/sctp/checksum.h @@ -46,14 +46,14 @@ #include #include -static inline __be32 sctp_crc32c(__be32 crc, u8 *buffer, u16 length) +static inline __u32 sctp_crc32c(__u32 crc, u8 *buffer, u16 length) { - return (__force __be32)crc32c((__force u32)crc, buffer, length); + return crc32c(crc, buffer, length); } -static inline __be32 sctp_start_cksum(__u8 *buffer, __u16 length) +static inline __u32 sctp_start_cksum(__u8 *buffer, __u16 length) { - __be32 crc = ~cpu_to_be32(0); + __u32 crc = ~(__u32)0; __u8 zero[sizeof(__u32)] = {0}; /* Optimize this routine to be SCTP specific, knowing how @@ -72,12 +72,12 @@ static inline __be32 sctp_start_cksum(__u8 *buffer, __u16 length) return crc; } -static inline __be32 sctp_update_cksum(__u8 *buffer, __u16 length, __be32 crc32) +static inline __u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32) { return sctp_crc32c(crc32, buffer, length); } -static inline __be32 sctp_end_cksum(__be32 crc32) +static inline __le32 sctp_end_cksum(__be32 crc32) { - return (__force __be32)~cpu_to_le32((__force u32)crc32); + return cpu_to_le32(~crc32); } diff --git a/net/sctp/input.c b/net/sctp/input.c index 693fd0804810..d2e98803ffe3 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -83,14 +83,15 @@ static inline int sctp_rcv_checksum(struct sk_buff *skb) { struct sk_buff *list = skb_shinfo(skb)->frag_list; struct sctphdr *sh = sctp_hdr(skb); - __be32 cmp = sh->checksum; - __be32 val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb)); + __le32 cmp = sh->checksum; + __le32 val; + __u32 tmp = sctp_start_cksum((__u8 *)sh, skb_headlen(skb)); for (; list; list = list->next) - val = sctp_update_cksum((__u8 *)list->data, skb_headlen(list), - val); + tmp = sctp_update_cksum((__u8 *)list->data, skb_headlen(list), + tmp); - val = sctp_end_cksum(val); + val = sctp_end_cksum(tmp); if (val != cmp) { /* CRC failure, dump it. */ diff --git a/net/sctp/output.c b/net/sctp/output.c index 2d65b7a7330b..07d58903a746 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -367,7 +367,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) struct sctp_transport *tp = packet->transport; struct sctp_association *asoc = tp->asoc; struct sctphdr *sh; - __be32 crc32 = cpu_to_be32(0); struct sk_buff *nskb; struct sctp_chunk *chunk, *tmp; struct sock *sk; @@ -532,16 +531,15 @@ int sctp_packet_transmit(struct sctp_packet *packet) * by CRC32-C as described in . */ if (!sctp_checksum_disable && !(dst->dev->features & NETIF_F_NO_CSUM)) { - crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); - crc32 = sctp_end_cksum(crc32); + __u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); + + /* 3) Put the resultant value into the checksum field in the + * common header, and leave the rest of the bits unchanged. + */ + sh->checksum = sctp_end_cksum(crc32); } else nskb->ip_summed = CHECKSUM_UNNECESSARY; - /* 3) Put the resultant value into the checksum field in the - * common header, and leave the rest of the bits unchanged. - */ - sh->checksum = crc32; - /* IP layer ECN support * From RFC 2481 * "The ECN-Capable Transport (ECT) bit would be set by the -- cgit v1.2.3 From 886a63e865eb346ab20572e802fc3118cb9aee14 Mon Sep 17 00:00:00 2001 From: Hannes Eder Date: Sat, 14 Feb 2009 11:36:20 +0000 Subject: drivers/net/hamradio: fix sparse warnings: fix signedness Fix this sparse warnings: drivers/net/hamradio/hdlcdrv.c:274:34: warning: incorrect type in argument 2 (different signedness) drivers/net/hamradio/hdlcdrv.c:279:47: warning: incorrect type in argument 2 (different signedness) drivers/net/hamradio/hdlcdrv.c:288:39: warning: incorrect type in argument 2 (different signedness) drivers/net/hamradio/hdlcdrv.c:300:47: warning: incorrect type in argument 2 (different signedness) Signed-off-by: Hannes Eder Signed-off-by: David S. Miller --- include/linux/hdlcdrv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/hdlcdrv.h b/include/linux/hdlcdrv.h index 0821bac62b83..c010b4a785b8 100644 --- a/include/linux/hdlcdrv.h +++ b/include/linux/hdlcdrv.h @@ -215,7 +215,7 @@ struct hdlcdrv_state { struct hdlcdrv_hdlctx { struct hdlcdrv_hdlcbuffer hbuf; - long in_hdlc_tx; + unsigned long in_hdlc_tx; /* * 0 = send flags * 1 = send txtail (flags) -- cgit v1.2.3 From 4a2f965ca5a4e2593744bf75425d85e0e8ff814a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 18 Feb 2009 16:29:44 +0100 Subject: netfilter: x_tables: change elements in x_tables Change to proper type on private pointer rather than anonymous void. Keep active elements on same cache line. Signed-off-by: Stephen Hemminger Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index c7ee8744d26b..9fac88fc0e72 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -349,9 +349,6 @@ struct xt_table { struct list_head list; - /* A unique name... */ - const char name[XT_TABLE_MAXNAMELEN]; - /* What hooks you will enter on */ unsigned int valid_hooks; @@ -359,13 +356,15 @@ struct xt_table rwlock_t lock; /* Man behind the curtain... */ - //struct ip6t_table_info *private; - void *private; + struct xt_table_info *private; /* Set this to THIS_MODULE if you are a module, otherwise NULL */ struct module *me; u_int8_t af; /* address/protocol family */ + + /* A unique name... */ + const char name[XT_TABLE_MAXNAMELEN]; }; #include -- cgit v1.2.3 From 59089d8d162ddcb5c434672e915331964d38a754 Mon Sep 17 00:00:00 2001 From: Santwona Behera Date: Fri, 20 Feb 2009 00:58:13 -0800 Subject: ethtool: Add RX pkt classification interface Signed-off-by: Santwona Behera Signed-off-by: David S. Miller --- include/linux/ethtool.h | 89 +++++++++++++++++++++++++++++++++++++++++++++---- net/core/ethtool.c | 58 ++++++++++++++++++++++++++------ 2 files changed, 131 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 27c67a542235..131b127b70f8 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -7,6 +7,7 @@ * Portions Copyright 2002 Intel (eli.kupermann@intel.com, * christopher.leech@intel.com, * scott.feldman@intel.com) + * Portions Copyright (C) Sun Microsystems 2008 */ #ifndef _LINUX_ETHTOOL_H @@ -287,10 +288,75 @@ enum ethtool_flags { ETH_FLAG_LRO = (1 << 15), /* LRO is enabled */ }; -struct ethtool_rxnfc { - __u32 cmd; +/* The following structures are for supporting RX network flow + * classification configuration. Note, all multibyte fields, e.g., + * ip4src, ip4dst, psrc, pdst, spi, etc. are expected to be in network + * byte order. + */ +struct ethtool_tcpip4_spec { + __be32 ip4src; + __be32 ip4dst; + __be16 psrc; + __be16 pdst; + __u8 tos; +}; + +struct ethtool_ah_espip4_spec { + __be32 ip4src; + __be32 ip4dst; + __be32 spi; + __u8 tos; +}; + +struct ethtool_rawip4_spec { + __be32 ip4src; + __be32 ip4dst; + __u8 hdata[64]; +}; + +struct ethtool_ether_spec { + __be16 ether_type; + __u8 frame_size; + __u8 eframe[16]; +}; + +#define ETH_RX_NFC_IP4 1 +#define ETH_RX_NFC_IP6 2 + +struct ethtool_usrip4_spec { + __be32 ip4src; + __be32 ip4dst; + __be32 l4_4_bytes; + __u8 tos; + __u8 ip_ver; + __u8 proto; +}; + +struct ethtool_rx_flow_spec { __u32 flow_type; - __u64 data; + union { + struct ethtool_tcpip4_spec tcp_ip4_spec; + struct ethtool_tcpip4_spec udp_ip4_spec; + struct ethtool_tcpip4_spec sctp_ip4_spec; + struct ethtool_ah_espip4_spec ah_ip4_spec; + struct ethtool_ah_espip4_spec esp_ip4_spec; + struct ethtool_rawip4_spec raw_ip4_spec; + struct ethtool_ether_spec ether_spec; + struct ethtool_usrip4_spec usr_ip4_spec; + __u8 hdata[64]; + } h_u, m_u; /* entry, mask */ + __u64 ring_cookie; + __u32 location; +}; + +struct ethtool_rxnfc { + __u32 cmd; + __u32 flow_type; + /* The rx flow hash value or the rule DB size */ + __u64 data; + struct ethtool_rx_flow_spec fs; + __u32 rule_cnt; + __u32 rule_locs[0]; }; #ifdef __KERNEL__ @@ -417,8 +483,8 @@ struct ethtool_ops { /* the following hooks are obsolete */ int (*self_test_count)(struct net_device *);/* use get_sset_count */ int (*get_stats_count)(struct net_device *);/* use get_sset_count */ - int (*get_rxhash)(struct net_device *, struct ethtool_rxnfc *); - int (*set_rxhash)(struct net_device *, struct ethtool_rxnfc *); + int (*get_rxnfc)(struct net_device *, struct ethtool_rxnfc *, void *); + int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *); }; #endif /* __KERNEL__ */ @@ -469,6 +535,12 @@ struct ethtool_ops { #define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */ #define ETHTOOL_GGRO 0x0000002b /* Get GRO enable (ethtool_value) */ #define ETHTOOL_SGRO 0x0000002c /* Set GRO enable (ethtool_value) */ +#define ETHTOOL_GRXRINGS 0x0000002d /* Get RX rings available for LB */ +#define ETHTOOL_GRXCLSRLCNT 0x0000002e /* Get RX class rule count */ +#define ETHTOOL_GRXCLSRULE 0x0000002f /* Get RX classification rule */ +#define ETHTOOL_GRXCLSRLALL 0x00000030 /* Get all RX classification rule */ +#define ETHTOOL_SRXCLSRLDEL 0x00000031 /* Delete RX classification rule */ +#define ETHTOOL_SRXCLSRLINS 0x00000032 /* Insert RX classification rule */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET @@ -565,9 +637,13 @@ struct ethtool_ops { #define UDP_V6_FLOW 0x06 #define SCTP_V6_FLOW 0x07 #define AH_ESP_V6_FLOW 0x08 +#define AH_V4_FLOW 0x09 +#define ESP_V4_FLOW 0x0a +#define AH_V6_FLOW 0x0b +#define ESP_V6_FLOW 0x0c +#define IP_USER_FLOW 0x0d /* L3-L4 network traffic flow hash options */ -#define RXH_DEV_PORT (1 << 0) #define RXH_L2DA (1 << 1) #define RXH_VLAN (1 << 2) #define RXH_L3_PROTO (1 << 3) @@ -577,5 +653,6 @@ struct ethtool_ops { #define RXH_L4_B_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */ #define RXH_DISCARD (1 << 31) +#define RX_CLS_FLOW_DISC 0xffffffffffffffffULL #endif /* _LINUX_ETHTOOL_H */ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 947710a36ced..244ca56dffac 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -209,34 +209,62 @@ static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr) return 0; } -static int ethtool_set_rxhash(struct net_device *dev, void __user *useraddr) +static int ethtool_set_rxnfc(struct net_device *dev, void __user *useraddr) { struct ethtool_rxnfc cmd; - if (!dev->ethtool_ops->set_rxhash) + if (!dev->ethtool_ops->set_rxnfc) return -EOPNOTSUPP; if (copy_from_user(&cmd, useraddr, sizeof(cmd))) return -EFAULT; - return dev->ethtool_ops->set_rxhash(dev, &cmd); + return dev->ethtool_ops->set_rxnfc(dev, &cmd); } -static int ethtool_get_rxhash(struct net_device *dev, void __user *useraddr) +static int ethtool_get_rxnfc(struct net_device *dev, void __user *useraddr) { struct ethtool_rxnfc info; + const struct ethtool_ops *ops = dev->ethtool_ops; + int ret; + void *rule_buf = NULL; - if (!dev->ethtool_ops->get_rxhash) + if (!ops->get_rxnfc) return -EOPNOTSUPP; if (copy_from_user(&info, useraddr, sizeof(info))) return -EFAULT; - dev->ethtool_ops->get_rxhash(dev, &info); + if (info.cmd == ETHTOOL_GRXCLSRLALL) { + if (info.rule_cnt > 0) { + rule_buf = kmalloc(info.rule_cnt * sizeof(u32), + GFP_USER); + if (!rule_buf) + return -ENOMEM; + } + } + ret = ops->get_rxnfc(dev, &info, rule_buf); + if (ret < 0) + goto err_out; + + ret = -EFAULT; if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; + goto err_out; + + if (rule_buf) { + useraddr += offsetof(struct ethtool_rxnfc, rule_locs); + if (copy_to_user(useraddr, rule_buf, + info.rule_cnt * sizeof(u32))) + goto err_out; + } + ret = 0; + +err_out: + if (rule_buf) + kfree(rule_buf); + + return ret; } static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) @@ -901,6 +929,10 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GFLAGS: case ETHTOOL_GPFLAGS: case ETHTOOL_GRXFH: + case ETHTOOL_GRXRINGS: + case ETHTOOL_GRXCLSRLCNT: + case ETHTOOL_GRXCLSRULE: + case ETHTOOL_GRXCLSRLALL: break; default: if (!capable(CAP_NET_ADMIN)) @@ -1052,10 +1084,16 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) dev->ethtool_ops->set_priv_flags); break; case ETHTOOL_GRXFH: - rc = ethtool_get_rxhash(dev, useraddr); + case ETHTOOL_GRXRINGS: + case ETHTOOL_GRXCLSRLCNT: + case ETHTOOL_GRXCLSRULE: + case ETHTOOL_GRXCLSRLALL: + rc = ethtool_get_rxnfc(dev, useraddr); break; case ETHTOOL_SRXFH: - rc = ethtool_set_rxhash(dev, useraddr); + case ETHTOOL_SRXCLSRLDEL: + case ETHTOOL_SRXCLSRLINS: + rc = ethtool_set_rxnfc(dev, useraddr); break; case ETHTOOL_GGRO: rc = ethtool_get_gro(dev, useraddr); -- cgit v1.2.3 From be0c22a46cfb79ab2342bb28fde99afa94ef868e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 18 Feb 2009 01:40:43 +0000 Subject: netlink: add NETLINK_BROADCAST_ERROR socket option This patch adds NETLINK_BROADCAST_ERROR which is a netlink socket option that the listener can set to make netlink_broadcast() return errors in the delivery to the caller. This option is useful if the caller of netlink_broadcast() do something with the result of the message delivery, like in ctnetlink where it drops a network packet if the event delivery failed, this is used to enable reliable logging and state-synchronization. If this socket option is not set, netlink_broadcast() only reports ESRCH errors and silently ignore ENOBUFS errors, which is what most netlink_broadcast() callers should do. This socket option is based on a suggestion from Patrick McHardy. Patrick McHardy can exchange this patch for a beer from me ;). Signed-off-by: Pablo Neira Ayuso Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netlink.h | 1 + net/netlink/af_netlink.c | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 51b09a1f46c3..1e6bf995435c 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -103,6 +103,7 @@ struct nlmsgerr #define NETLINK_ADD_MEMBERSHIP 1 #define NETLINK_DROP_MEMBERSHIP 2 #define NETLINK_PKTINFO 3 +#define NETLINK_BROADCAST_ERROR 4 struct nl_pktinfo { diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6ee69c27f806..ed587be1e1c2 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -85,6 +85,7 @@ struct netlink_sock { #define NETLINK_KERNEL_SOCKET 0x1 #define NETLINK_RECV_PKTINFO 0x2 +#define NETLINK_BROADCAST_SEND_ERROR 0x4 static inline struct netlink_sock *nlk_sk(struct sock *sk) { @@ -995,12 +996,15 @@ static inline int do_one_broadcast(struct sock *sk, netlink_overrun(sk); /* Clone failed. Notify ALL listeners. */ p->failure = 1; + if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR) + p->delivery_failure = 1; } else if (sk_filter(sk, p->skb2)) { kfree_skb(p->skb2); p->skb2 = NULL; } else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) { netlink_overrun(sk); - p->delivery_failure = 1; + if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR) + p->delivery_failure = 1; } else { p->congested |= val; p->delivered = 1; @@ -1048,7 +1052,7 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, if (info.skb2) kfree_skb(info.skb2); - if (info.delivery_failure || info.failure) + if (info.delivery_failure) return -ENOBUFS; if (info.delivered) { @@ -1163,6 +1167,13 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, err = 0; break; } + case NETLINK_BROADCAST_ERROR: + if (val) + nlk->flags |= NETLINK_BROADCAST_SEND_ERROR; + else + nlk->flags &= ~NETLINK_BROADCAST_SEND_ERROR; + err = 0; + break; default: err = -ENOPROTOOPT; } @@ -1195,6 +1206,16 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname, return -EFAULT; err = 0; break; + case NETLINK_BROADCAST_ERROR: + if (len < sizeof(int)) + return -EINVAL; + len = sizeof(int); + val = nlk->flags & NETLINK_BROADCAST_SEND_ERROR ? 1 : 0; + if (put_user(len, optlen) || + put_user(val, optval)) + return -EFAULT; + err = 0; + break; default: err = -ENOPROTOOPT; } -- cgit v1.2.3 From 784544739a25c30637397ace5489eeb6e15d7d49 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 20 Feb 2009 10:35:32 +0100 Subject: netfilter: iptables: lock free counters The reader/writer lock in ip_tables is acquired in the critical path of processing packets and is one of the reasons just loading iptables can cause a 20% performance loss. The rwlock serves two functions: 1) it prevents changes to table state (xt_replace) while table is in use. This is now handled by doing rcu on the xt_table. When table is replaced, the new table(s) are put in and the old one table(s) are freed after RCU period. 2) it provides synchronization when accesing the counter values. This is now handled by swapping in new table_info entries for each cpu then summing the old values, and putting the result back onto one cpu. On a busy system it may cause sampling to occur at different times on each cpu, but no packet/byte counts are lost in the process. Signed-off-by: Stephen Hemminger Sucessfully tested on my dual quad core machine too, but iptables only (no ipv6 here) BTW, my new "tbench 8" result is 2450 MB/s, (it was 2150 MB/s not so long ago) Acked-by: Eric Dumazet Signed-off-by: Patrick McHardy --- include/linux/netfilter/x_tables.h | 6 +- net/ipv4/netfilter/arp_tables.c | 115 ++++++++++++++++++++++++++--------- net/ipv4/netfilter/ip_tables.c | 120 +++++++++++++++++++++++++++---------- net/ipv6/netfilter/ip6_tables.c | 119 +++++++++++++++++++++++++----------- net/netfilter/x_tables.c | 26 ++++++-- 5 files changed, 284 insertions(+), 102 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 9fac88fc0e72..e8e08d036752 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -353,7 +353,7 @@ struct xt_table unsigned int valid_hooks; /* Lock for the curtain */ - rwlock_t lock; + struct mutex lock; /* Man behind the curtain... */ struct xt_table_info *private; @@ -385,7 +385,7 @@ struct xt_table_info /* ipt_entry tables: one per CPU */ /* Note : this field MUST be the last one, see XT_TABLE_INFO_SZ */ - char *entries[1]; + void *entries[1]; }; #define XT_TABLE_INFO_SZ (offsetof(struct xt_table_info, entries) \ @@ -432,6 +432,8 @@ extern void xt_proto_fini(struct net *net, u_int8_t af); extern struct xt_table_info *xt_alloc_table_info(unsigned int size); extern void xt_free_table_info(struct xt_table_info *info); +extern void xt_table_entry_swap_rcu(struct xt_table_info *old, + struct xt_table_info *new); #ifdef CONFIG_COMPAT #include diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index b5db46342614..64a7c6ce0b98 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -261,9 +261,10 @@ unsigned int arpt_do_table(struct sk_buff *skb, indev = in ? in->name : nulldevname; outdev = out ? out->name : nulldevname; - read_lock_bh(&table->lock); - private = table->private; - table_base = (void *)private->entries[smp_processor_id()]; + rcu_read_lock(); + private = rcu_dereference(table->private); + table_base = rcu_dereference(private->entries[smp_processor_id()]); + e = get_entry(table_base, private->hook_entry[hook]); back = get_entry(table_base, private->underflow[hook]); @@ -335,7 +336,8 @@ unsigned int arpt_do_table(struct sk_buff *skb, e = (void *)e + e->next_offset; } } while (!hotdrop); - read_unlock_bh(&table->lock); + + rcu_read_unlock(); if (hotdrop) return NF_DROP; @@ -738,11 +740,65 @@ static void get_counters(const struct xt_table_info *t, } } -static inline struct xt_counters *alloc_counters(struct xt_table *table) + +/* We're lazy, and add to the first CPU; overflow works its fey magic + * and everything is OK. */ +static int +add_counter_to_entry(struct arpt_entry *e, + const struct xt_counters addme[], + unsigned int *i) +{ + ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt); + + (*i)++; + return 0; +} + +/* Take values from counters and add them back onto the current cpu */ +static void put_counters(struct xt_table_info *t, + const struct xt_counters counters[]) +{ + unsigned int i, cpu; + + local_bh_disable(); + cpu = smp_processor_id(); + i = 0; + ARPT_ENTRY_ITERATE(t->entries[cpu], + t->size, + add_counter_to_entry, + counters, + &i); + local_bh_enable(); +} + +static inline int +zero_entry_counter(struct arpt_entry *e, void *arg) +{ + e->counters.bcnt = 0; + e->counters.pcnt = 0; + return 0; +} + +static void +clone_counters(struct xt_table_info *newinfo, const struct xt_table_info *info) +{ + unsigned int cpu; + const void *loc_cpu_entry = info->entries[raw_smp_processor_id()]; + + memcpy(newinfo, info, offsetof(struct xt_table_info, entries)); + for_each_possible_cpu(cpu) { + memcpy(newinfo->entries[cpu], loc_cpu_entry, info->size); + ARPT_ENTRY_ITERATE(newinfo->entries[cpu], newinfo->size, + zero_entry_counter, NULL); + } +} + +static struct xt_counters *alloc_counters(struct xt_table *table) { unsigned int countersize; struct xt_counters *counters; - const struct xt_table_info *private = table->private; + struct xt_table_info *private = table->private; + struct xt_table_info *info; /* We need atomic snapshot of counters: rest doesn't change * (other than comefrom, which userspace doesn't care @@ -752,14 +808,30 @@ static inline struct xt_counters *alloc_counters(struct xt_table *table) counters = vmalloc_node(countersize, numa_node_id()); if (counters == NULL) - return ERR_PTR(-ENOMEM); + goto nomem; + + info = xt_alloc_table_info(private->size); + if (!info) + goto free_counters; - /* First, sum counters... */ - write_lock_bh(&table->lock); - get_counters(private, counters); - write_unlock_bh(&table->lock); + clone_counters(info, private); + + mutex_lock(&table->lock); + xt_table_entry_swap_rcu(private, info); + synchronize_net(); /* Wait until smoke has cleared */ + + get_counters(info, counters); + put_counters(private, counters); + mutex_unlock(&table->lock); + + xt_free_table_info(info); return counters; + + free_counters: + vfree(counters); + nomem: + return ERR_PTR(-ENOMEM); } static int copy_entries_to_user(unsigned int total_size, @@ -1099,20 +1171,6 @@ static int do_replace(struct net *net, void __user *user, unsigned int len) return ret; } -/* We're lazy, and add to the first CPU; overflow works its fey magic - * and everything is OK. - */ -static inline int add_counter_to_entry(struct arpt_entry *e, - const struct xt_counters addme[], - unsigned int *i) -{ - - ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt); - - (*i)++; - return 0; -} - static int do_add_counters(struct net *net, void __user *user, unsigned int len, int compat) { @@ -1172,13 +1230,14 @@ static int do_add_counters(struct net *net, void __user *user, unsigned int len, goto free; } - write_lock_bh(&t->lock); + mutex_lock(&t->lock); private = t->private; if (private->number != num_counters) { ret = -EINVAL; goto unlock_up_free; } + preempt_disable(); i = 0; /* Choose the copy that is on our node */ loc_cpu_entry = private->entries[smp_processor_id()]; @@ -1187,8 +1246,10 @@ static int do_add_counters(struct net *net, void __user *user, unsigned int len, add_counter_to_entry, paddc, &i); + preempt_enable(); unlock_up_free: - write_unlock_bh(&t->lock); + mutex_unlock(&t->lock); + xt_table_unlock(t); module_put(t->me); free: diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index ef8b6ca068b2..08cde5bd70a5 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -347,10 +347,12 @@ ipt_do_table(struct sk_buff *skb, mtpar.family = tgpar.family = NFPROTO_IPV4; tgpar.hooknum = hook; - read_lock_bh(&table->lock); IP_NF_ASSERT(table->valid_hooks & (1 << hook)); - private = table->private; - table_base = (void *)private->entries[smp_processor_id()]; + + rcu_read_lock(); + private = rcu_dereference(table->private); + table_base = rcu_dereference(private->entries[smp_processor_id()]); + e = get_entry(table_base, private->hook_entry[hook]); /* For return from builtin chain */ @@ -445,7 +447,7 @@ ipt_do_table(struct sk_buff *skb, } } while (!hotdrop); - read_unlock_bh(&table->lock); + rcu_read_unlock(); #ifdef DEBUG_ALLOW_ALL return NF_ACCEPT; @@ -924,13 +926,68 @@ get_counters(const struct xt_table_info *t, counters, &i); } + +} + +/* We're lazy, and add to the first CPU; overflow works its fey magic + * and everything is OK. */ +static int +add_counter_to_entry(struct ipt_entry *e, + const struct xt_counters addme[], + unsigned int *i) +{ + ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt); + + (*i)++; + return 0; +} + +/* Take values from counters and add them back onto the current cpu */ +static void put_counters(struct xt_table_info *t, + const struct xt_counters counters[]) +{ + unsigned int i, cpu; + + local_bh_disable(); + cpu = smp_processor_id(); + i = 0; + IPT_ENTRY_ITERATE(t->entries[cpu], + t->size, + add_counter_to_entry, + counters, + &i); + local_bh_enable(); +} + + +static inline int +zero_entry_counter(struct ipt_entry *e, void *arg) +{ + e->counters.bcnt = 0; + e->counters.pcnt = 0; + return 0; +} + +static void +clone_counters(struct xt_table_info *newinfo, const struct xt_table_info *info) +{ + unsigned int cpu; + const void *loc_cpu_entry = info->entries[raw_smp_processor_id()]; + + memcpy(newinfo, info, offsetof(struct xt_table_info, entries)); + for_each_possible_cpu(cpu) { + memcpy(newinfo->entries[cpu], loc_cpu_entry, info->size); + IPT_ENTRY_ITERATE(newinfo->entries[cpu], newinfo->size, + zero_entry_counter, NULL); + } } static struct xt_counters * alloc_counters(struct xt_table *table) { unsigned int countersize; struct xt_counters *counters; - const struct xt_table_info *private = table->private; + struct xt_table_info *private = table->private; + struct xt_table_info *info; /* We need atomic snapshot of counters: rest doesn't change (other than comefrom, which userspace doesn't care @@ -939,14 +996,30 @@ static struct xt_counters * alloc_counters(struct xt_table *table) counters = vmalloc_node(countersize, numa_node_id()); if (counters == NULL) - return ERR_PTR(-ENOMEM); + goto nomem; - /* First, sum counters... */ - write_lock_bh(&table->lock); - get_counters(private, counters); - write_unlock_bh(&table->lock); + info = xt_alloc_table_info(private->size); + if (!info) + goto free_counters; + + clone_counters(info, private); + + mutex_lock(&table->lock); + xt_table_entry_swap_rcu(private, info); + synchronize_net(); /* Wait until smoke has cleared */ + + get_counters(info, counters); + put_counters(private, counters); + mutex_unlock(&table->lock); + + xt_free_table_info(info); return counters; + + free_counters: + vfree(counters); + nomem: + return ERR_PTR(-ENOMEM); } static int @@ -1312,27 +1385,6 @@ do_replace(struct net *net, void __user *user, unsigned int len) return ret; } -/* We're lazy, and add to the first CPU; overflow works its fey magic - * and everything is OK. */ -static int -add_counter_to_entry(struct ipt_entry *e, - const struct xt_counters addme[], - unsigned int *i) -{ -#if 0 - duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n", - *i, - (long unsigned int)e->counters.pcnt, - (long unsigned int)e->counters.bcnt, - (long unsigned int)addme[*i].pcnt, - (long unsigned int)addme[*i].bcnt); -#endif - - ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt); - - (*i)++; - return 0; -} static int do_add_counters(struct net *net, void __user *user, unsigned int len, int compat) @@ -1393,13 +1445,14 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, int compat goto free; } - write_lock_bh(&t->lock); + mutex_lock(&t->lock); private = t->private; if (private->number != num_counters) { ret = -EINVAL; goto unlock_up_free; } + preempt_disable(); i = 0; /* Choose the copy that is on our node */ loc_cpu_entry = private->entries[raw_smp_processor_id()]; @@ -1408,8 +1461,9 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, int compat add_counter_to_entry, paddc, &i); + preempt_enable(); unlock_up_free: - write_unlock_bh(&t->lock); + mutex_unlock(&t->lock); xt_table_unlock(t); module_put(t->me); free: diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index d64594b6c061..34af7bb8df5f 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -382,10 +382,12 @@ ip6t_do_table(struct sk_buff *skb, mtpar.family = tgpar.family = NFPROTO_IPV6; tgpar.hooknum = hook; - read_lock_bh(&table->lock); IP_NF_ASSERT(table->valid_hooks & (1 << hook)); - private = table->private; - table_base = (void *)private->entries[smp_processor_id()]; + + rcu_read_lock(); + private = rcu_dereference(table->private); + table_base = rcu_dereference(private->entries[smp_processor_id()]); + e = get_entry(table_base, private->hook_entry[hook]); /* For return from builtin chain */ @@ -483,7 +485,7 @@ ip6t_do_table(struct sk_buff *skb, #ifdef CONFIG_NETFILTER_DEBUG ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON; #endif - read_unlock_bh(&table->lock); + rcu_read_unlock(); #ifdef DEBUG_ALLOW_ALL return NF_ACCEPT; @@ -964,11 +966,64 @@ get_counters(const struct xt_table_info *t, } } +/* We're lazy, and add to the first CPU; overflow works its fey magic + * and everything is OK. */ +static int +add_counter_to_entry(struct ip6t_entry *e, + const struct xt_counters addme[], + unsigned int *i) +{ + ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt); + + (*i)++; + return 0; +} + +/* Take values from counters and add them back onto the current cpu */ +static void put_counters(struct xt_table_info *t, + const struct xt_counters counters[]) +{ + unsigned int i, cpu; + + local_bh_disable(); + cpu = smp_processor_id(); + i = 0; + IP6T_ENTRY_ITERATE(t->entries[cpu], + t->size, + add_counter_to_entry, + counters, + &i); + local_bh_enable(); +} + +static inline int +zero_entry_counter(struct ip6t_entry *e, void *arg) +{ + e->counters.bcnt = 0; + e->counters.pcnt = 0; + return 0; +} + +static void +clone_counters(struct xt_table_info *newinfo, const struct xt_table_info *info) +{ + unsigned int cpu; + const void *loc_cpu_entry = info->entries[raw_smp_processor_id()]; + + memcpy(newinfo, info, offsetof(struct xt_table_info, entries)); + for_each_possible_cpu(cpu) { + memcpy(newinfo->entries[cpu], loc_cpu_entry, info->size); + IP6T_ENTRY_ITERATE(newinfo->entries[cpu], newinfo->size, + zero_entry_counter, NULL); + } +} + static struct xt_counters *alloc_counters(struct xt_table *table) { unsigned int countersize; struct xt_counters *counters; - const struct xt_table_info *private = table->private; + struct xt_table_info *private = table->private; + struct xt_table_info *info; /* We need atomic snapshot of counters: rest doesn't change (other than comefrom, which userspace doesn't care @@ -977,14 +1032,28 @@ static struct xt_counters *alloc_counters(struct xt_table *table) counters = vmalloc_node(countersize, numa_node_id()); if (counters == NULL) - return ERR_PTR(-ENOMEM); + goto nomem; + + info = xt_alloc_table_info(private->size); + if (!info) + goto free_counters; + + clone_counters(info, private); + + mutex_lock(&table->lock); + xt_table_entry_swap_rcu(private, info); + synchronize_net(); /* Wait until smoke has cleared */ + + get_counters(info, counters); + put_counters(private, counters); + mutex_unlock(&table->lock); - /* First, sum counters... */ - write_lock_bh(&table->lock); - get_counters(private, counters); - write_unlock_bh(&table->lock); + xt_free_table_info(info); - return counters; + free_counters: + vfree(counters); + nomem: + return ERR_PTR(-ENOMEM); } static int @@ -1351,28 +1420,6 @@ do_replace(struct net *net, void __user *user, unsigned int len) return ret; } -/* We're lazy, and add to the first CPU; overflow works its fey magic - * and everything is OK. */ -static inline int -add_counter_to_entry(struct ip6t_entry *e, - const struct xt_counters addme[], - unsigned int *i) -{ -#if 0 - duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n", - *i, - (long unsigned int)e->counters.pcnt, - (long unsigned int)e->counters.bcnt, - (long unsigned int)addme[*i].pcnt, - (long unsigned int)addme[*i].bcnt); -#endif - - ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt); - - (*i)++; - return 0; -} - static int do_add_counters(struct net *net, void __user *user, unsigned int len, int compat) @@ -1433,13 +1480,14 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, goto free; } - write_lock_bh(&t->lock); + mutex_lock(&t->lock); private = t->private; if (private->number != num_counters) { ret = -EINVAL; goto unlock_up_free; } + preempt_disable(); i = 0; /* Choose the copy that is on our node */ loc_cpu_entry = private->entries[raw_smp_processor_id()]; @@ -1448,8 +1496,9 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, add_counter_to_entry, paddc, &i); + preempt_enable(); unlock_up_free: - write_unlock_bh(&t->lock); + mutex_unlock(&t->lock); xt_table_unlock(t); module_put(t->me); free: diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index bfbf521f6ea5..bfcac92d5563 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -625,6 +625,20 @@ void xt_free_table_info(struct xt_table_info *info) } EXPORT_SYMBOL(xt_free_table_info); +void xt_table_entry_swap_rcu(struct xt_table_info *oldinfo, + struct xt_table_info *newinfo) +{ + unsigned int cpu; + + for_each_possible_cpu(cpu) { + void *p = oldinfo->entries[cpu]; + rcu_assign_pointer(oldinfo->entries[cpu], newinfo->entries[cpu]); + newinfo->entries[cpu] = p; + } + +} +EXPORT_SYMBOL_GPL(xt_table_entry_swap_rcu); + /* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */ struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af, const char *name) @@ -671,21 +685,22 @@ xt_replace_table(struct xt_table *table, struct xt_table_info *oldinfo, *private; /* Do the substitution. */ - write_lock_bh(&table->lock); + mutex_lock(&table->lock); private = table->private; /* Check inside lock: is the old number correct? */ if (num_counters != private->number) { duprintf("num_counters != table->private->number (%u/%u)\n", num_counters, private->number); - write_unlock_bh(&table->lock); + mutex_unlock(&table->lock); *error = -EAGAIN; return NULL; } oldinfo = private; - table->private = newinfo; + rcu_assign_pointer(table->private, newinfo); newinfo->initial_entries = oldinfo->initial_entries; - write_unlock_bh(&table->lock); + mutex_unlock(&table->lock); + synchronize_net(); return oldinfo; } EXPORT_SYMBOL_GPL(xt_replace_table); @@ -719,7 +734,8 @@ struct xt_table *xt_register_table(struct net *net, struct xt_table *table, /* Simplifies replace_table code. */ table->private = bootstrap; - rwlock_init(&table->lock); + mutex_init(&table->lock); + if (!xt_replace_table(table, 0, newinfo, &ret)) goto unlock; -- cgit v1.2.3 From 268cb38e1802db560c73167e643f14a3dcb4b07c Mon Sep 17 00:00:00 2001 From: Adam Nielsen Date: Fri, 20 Feb 2009 10:55:14 +0100 Subject: netfilter: x_tables: add LED trigger target Kernel module providing implementation of LED netfilter target. Each instance of the target appears as a led-trigger device, which can be associated with one or more LEDs in /sys/class/leds/ Signed-off-by: Adam Nielsen Acked-by: Richard Purdie Signed-off-by: Patrick McHardy --- drivers/leds/Kconfig | 3 + include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/xt_LED.h | 13 ++++ net/netfilter/Kconfig | 24 ++++++ net/netfilter/Makefile | 1 + net/netfilter/xt_LED.c | 161 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 203 insertions(+) create mode 100644 include/linux/netfilter/xt_LED.h create mode 100644 net/netfilter/xt_LED.c (limited to 'include/linux') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 742713611bc5..556aeca0d860 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -223,4 +223,7 @@ config LEDS_TRIGGER_DEFAULT_ON This allows LEDs to be initialised in the ON state. If unsure, say Y. +comment "iptables trigger is under Netfilter config (LED target)" + depends on LEDS_TRIGGERS + endif # NEW_LEDS diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 5a8af875bce2..deeaee5c83f2 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -7,6 +7,7 @@ header-y += xt_CLASSIFY.h header-y += xt_CONNMARK.h header-y += xt_CONNSECMARK.h header-y += xt_DSCP.h +header-y += xt_LED.h header-y += xt_MARK.h header-y += xt_NFLOG.h header-y += xt_NFQUEUE.h diff --git a/include/linux/netfilter/xt_LED.h b/include/linux/netfilter/xt_LED.h new file mode 100644 index 000000000000..4c91a0d770d0 --- /dev/null +++ b/include/linux/netfilter/xt_LED.h @@ -0,0 +1,13 @@ +#ifndef _XT_LED_H +#define _XT_LED_H + +struct xt_led_info { + char id[27]; /* Unique ID for this trigger in the LED class */ + __u8 always_blink; /* Blink even if the LED is already on */ + __u32 delay; /* Delay until LED is switched off after trigger */ + + /* Kernel data used in the module */ + void *internal_data __attribute__((aligned(8))); +}; + +#endif /* _XT_LED_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 0eb98b4fbf44..cdbaaff6d0d6 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -372,6 +372,30 @@ config NETFILTER_XT_TARGET_HL since you can easily create immortal packets that loop forever on the network. +config NETFILTER_XT_TARGET_LED + tristate '"LED" target support' + depends on LEDS_CLASS + depends on NETFILTER_ADVANCED + help + This option adds a `LED' target, which allows you to blink LEDs in + response to particular packets passing through your machine. + + This can be used to turn a spare LED into a network activity LED, + which only flashes in response to FTP transfers, for example. Or + you could have an LED which lights up for a minute or two every time + somebody connects to your machine via SSH. + + You will need support for the "led" class to make this work. + + To create an LED trigger for incoming SSH traffic: + iptables -A INPUT -p tcp --dport 22 -j LED --led-trigger-id ssh --led-delay 1000 + + Then attach the new trigger to an LED on your system: + echo netfilter-ssh > /sys/class/leds//trigger + + For more information on the LEDs available on your system, see + Documentation/leds-class.txt + config NETFILTER_XT_TARGET_MARK tristate '"MARK" target support' default m if NETFILTER_ADVANCED=n diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index da73ed25701c..7a9b8397573a 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_CONNMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o +obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_MARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o diff --git a/net/netfilter/xt_LED.c b/net/netfilter/xt_LED.c new file mode 100644 index 000000000000..8ff7843bb921 --- /dev/null +++ b/net/netfilter/xt_LED.c @@ -0,0 +1,161 @@ +/* + * xt_LED.c - netfilter target to make LEDs blink upon packet matches + * + * Copyright (C) 2008 Adam Nielsen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include + +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Adam Nielsen "); +MODULE_DESCRIPTION("Xtables: trigger LED devices on packet match"); + +/* + * This is declared in here (the kernel module) only, to avoid having these + * dependencies in userspace code. This is what xt_led_info.internal_data + * points to. + */ +struct xt_led_info_internal { + struct led_trigger netfilter_led_trigger; + struct timer_list timer; +}; + +static unsigned int +led_tg(struct sk_buff *skb, const struct xt_target_param *par) +{ + const struct xt_led_info *ledinfo = par->targinfo; + struct xt_led_info_internal *ledinternal = ledinfo->internal_data; + + /* + * If "always blink" is enabled, and there's still some time until the + * LED will switch off, briefly switch it off now. + */ + if ((ledinfo->delay > 0) && ledinfo->always_blink && + timer_pending(&ledinternal->timer)) + led_trigger_event(&ledinternal->netfilter_led_trigger,LED_OFF); + + led_trigger_event(&ledinternal->netfilter_led_trigger, LED_FULL); + + /* If there's a positive delay, start/update the timer */ + if (ledinfo->delay > 0) { + mod_timer(&ledinternal->timer, + jiffies + msecs_to_jiffies(ledinfo->delay)); + + /* Otherwise if there was no delay given, blink as fast as possible */ + } else if (ledinfo->delay == 0) { + led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF); + } + + /* else the delay is negative, which means switch on and stay on */ + + return XT_CONTINUE; +} + +static void led_timeout_callback(unsigned long data) +{ + struct xt_led_info *ledinfo = (struct xt_led_info *)data; + struct xt_led_info_internal *ledinternal = ledinfo->internal_data; + + led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF); +} + +static bool led_tg_check(const struct xt_tgchk_param *par) +{ + struct xt_led_info *ledinfo = par->targinfo; + struct xt_led_info_internal *ledinternal; + int err; + + if (ledinfo->id[0] == '\0') { + printk(KERN_ERR KBUILD_MODNAME ": No 'id' parameter given.\n"); + return false; + } + + ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL); + if (!ledinternal) { + printk(KERN_CRIT KBUILD_MODNAME ": out of memory\n"); + return false; + } + + ledinternal->netfilter_led_trigger.name = ledinfo->id; + + err = led_trigger_register(&ledinternal->netfilter_led_trigger); + if (err) { + printk(KERN_CRIT KBUILD_MODNAME + ": led_trigger_register() failed\n"); + if (err == -EEXIST) + printk(KERN_ERR KBUILD_MODNAME + ": Trigger name is already in use.\n"); + goto exit_alloc; + } + + /* See if we need to set up a timer */ + if (ledinfo->delay > 0) + setup_timer(&ledinternal->timer, led_timeout_callback, + (unsigned long)ledinfo); + + ledinfo->internal_data = ledinternal; + + return true; + +exit_alloc: + kfree(ledinternal); + + return false; +} + +static void led_tg_destroy(const struct xt_tgdtor_param *par) +{ + const struct xt_led_info *ledinfo = par->targinfo; + struct xt_led_info_internal *ledinternal = ledinfo->internal_data; + + if (ledinfo->delay > 0) + del_timer_sync(&ledinternal->timer); + + led_trigger_unregister(&ledinternal->netfilter_led_trigger); + kfree(ledinternal); +} + +static struct xt_target led_tg_reg __read_mostly = { + .name = "LED", + .revision = 0, + .family = NFPROTO_UNSPEC, + .target = led_tg, + .targetsize = XT_ALIGN(sizeof(struct xt_led_info)), + .checkentry = led_tg_check, + .destroy = led_tg_destroy, + .me = THIS_MODULE, +}; + +static int __init led_tg_init(void) +{ + return xt_register_target(&led_tg_reg); +} + +static void __exit led_tg_exit(void) +{ + xt_unregister_target(&led_tg_reg); +} + +module_init(led_tg_init); +module_exit(led_tg_exit); -- cgit v1.2.3 From d060ffc1840e37100628f520e66600c5ae483b44 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 24 Feb 2009 15:23:58 +0100 Subject: netfilter: install missing headers iptables imports headers from (the unifdefed headers of a) kernel tree, but some headers happened to not be installed. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/Kbuild | 6 ++++++ include/linux/netfilter_ipv6/Kbuild | 1 + 2 files changed, 7 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index deeaee5c83f2..947b47d7f6c0 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -14,8 +14,11 @@ header-y += xt_NFQUEUE.h header-y += xt_RATEEST.h header-y += xt_SECMARK.h header-y += xt_TCPMSS.h +header-y += xt_TCPOPTSTRIP.h +header-y += xt_TPROXY.h header-y += xt_comment.h header-y += xt_connbytes.h +header-y += xt_connlimit.h header-y += xt_connmark.h header-y += xt_conntrack.h header-y += xt_dccp.h @@ -31,6 +34,7 @@ header-y += xt_mark.h header-y += xt_multiport.h header-y += xt_owner.h header-y += xt_pkttype.h +header-y += xt_quota.h header-y += xt_rateest.h header-y += xt_realm.h header-y += xt_recent.h @@ -40,6 +44,8 @@ header-y += xt_statistic.h header-y += xt_string.h header-y += xt_tcpmss.h header-y += xt_tcpudp.h +header-y += xt_time.h +header-y += xt_u32.h unifdef-y += nf_conntrack_common.h unifdef-y += nf_conntrack_ftp.h diff --git a/include/linux/netfilter_ipv6/Kbuild b/include/linux/netfilter_ipv6/Kbuild index 8887a5fcd1d0..aca4bd1f6d7c 100644 --- a/include/linux/netfilter_ipv6/Kbuild +++ b/include/linux/netfilter_ipv6/Kbuild @@ -11,6 +11,7 @@ header-y += ip6t_length.h header-y += ip6t_limit.h header-y += ip6t_mac.h header-y += ip6t_mark.h +header-y += ip6t_mh.h header-y += ip6t_multiport.h header-y += ip6t_opts.h header-y += ip6t_owner.h -- cgit v1.2.3 From 1ce85fe402137824246bad03ff85f3913d565c17 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 24 Feb 2009 23:18:28 -0800 Subject: netlink: change nlmsg_notify() return value logic This patch changes the return value of nlmsg_notify() as follows: If NETLINK_BROADCAST_ERROR is set by any of the listeners and an error in the delivery happened, return the broadcast error; else if there are no listeners apart from the socket that requested a change with the echo flag, return the result of the unicast notification. Thus, with this patch, the unicast notification is handled in the same way of a broadcast listener that has set the NETLINK_BROADCAST_ERROR socket flag. This patch is useful in case that the caller of nlmsg_notify() wants to know the result of the delivery of a netlink notification (including the broadcast delivery) and take any action in case that the delivery failed. For example, ctnetlink can drop packets if the event delivery failed to provide reliable logging and state-synchronization at the cost of dropping packets. This patch also modifies the rtnetlink code to ignore the return value of rtnl_notify() in all callers. The function rtnl_notify() (before this patch) returned the error of the unicast notification which makes rtnl_set_sk_err() reports errors to all listeners. This is not of any help since the origin of the change (the socket that requested the echoing) notices the ENOBUFS error if the notification fails and should resync itself. Signed-off-by: Pablo Neira Ayuso Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 4 ++-- net/bridge/br_netlink.c | 3 ++- net/core/fib_rules.c | 3 ++- net/core/neighbour.c | 3 ++- net/core/rtnetlink.c | 9 +++++---- net/decnet/dn_dev.c | 3 ++- net/decnet/dn_table.c | 3 ++- net/ipv4/devinet.c | 3 ++- net/ipv4/fib_semantics.c | 5 +++-- net/ipv6/addrconf.c | 9 ++++++--- net/ipv6/ndisc.c | 6 +----- net/ipv6/route.c | 5 +++-- net/netlink/af_netlink.c | 14 ++++++++++---- net/phonet/pn_netlink.c | 5 +++-- 14 files changed, 45 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 1e5f6730ff31..35a07c830f79 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -622,8 +622,8 @@ static __inline__ int rtattr_strcmp(const struct rtattr *rta, const char *str) extern int rtnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, u32 group, int echo); extern int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid); -extern int rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group, - struct nlmsghdr *nlh, gfp_t flags); +extern void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, + u32 group, struct nlmsghdr *nlh, gfp_t flags); extern void rtnl_set_sk_err(struct net *net, u32 group, int error); extern int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics); extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index ba7be195803c..fcffb3fb1177 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -98,7 +98,8 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port) kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); + return; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_LINK, err); diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 32b3a0152d7a..98691e1466b8 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -588,7 +588,8 @@ static void notify_rule_change(int event, struct fib_rule *rule, goto errout; } - err = rtnl_notify(skb, net, pid, ops->nlgroup, nlh, GFP_KERNEL); + rtnl_notify(skb, net, pid, ops->nlgroup, nlh, GFP_KERNEL); + return; errout: if (err < 0) rtnl_set_sk_err(net, ops->nlgroup, err); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 278a142d1047..e1144cb94b99 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2534,7 +2534,8 @@ static void __neigh_notify(struct neighbour *n, int type, int flags) kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); + rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); + return; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 790dd205bb5d..d78030f88bd0 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -455,8 +455,8 @@ int rtnl_unicast(struct sk_buff *skb, struct net *net, u32 pid) return nlmsg_unicast(rtnl, skb, pid); } -int rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group, - struct nlmsghdr *nlh, gfp_t flags) +void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group, + struct nlmsghdr *nlh, gfp_t flags) { struct sock *rtnl = net->rtnl; int report = 0; @@ -464,7 +464,7 @@ int rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group, if (nlh) report = nlmsg_report(nlh); - return nlmsg_notify(rtnl, skb, pid, group, report, flags); + nlmsg_notify(rtnl, skb, pid, group, report, flags); } void rtnl_set_sk_err(struct net *net, u32 group, int error) @@ -1246,7 +1246,8 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change) kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL); + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL); + return; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_LINK, err); diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index daf2b98b15fe..e457769bf7a7 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -769,7 +769,8 @@ static void dn_ifaddr_notify(int event, struct dn_ifaddr *ifa) kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, &init_net, 0, RTNLGRP_DECnet_IFADDR, NULL, GFP_KERNEL); + rtnl_notify(skb, &init_net, 0, RTNLGRP_DECnet_IFADDR, NULL, GFP_KERNEL); + return; errout: if (err < 0) rtnl_set_sk_err(&init_net, RTNLGRP_DECnet_IFADDR, err); diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c index 69ad9280c693..67054b0d550f 100644 --- a/net/decnet/dn_table.c +++ b/net/decnet/dn_table.c @@ -375,7 +375,8 @@ static void dn_rtmsg_fib(int event, struct dn_fib_node *f, int z, u32 tb_id, kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, &init_net, pid, RTNLGRP_DECnet_ROUTE, nlh, GFP_KERNEL); + rtnl_notify(skb, &init_net, pid, RTNLGRP_DECnet_ROUTE, nlh, GFP_KERNEL); + return; errout: if (err < 0) rtnl_set_sk_err(&init_net, RTNLGRP_DECnet_ROUTE, err); diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index d519a6a66726..126bb911880f 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1216,7 +1216,8 @@ static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); + rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); + return; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 4817dea3bc73..f831df500907 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -322,8 +322,9 @@ void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, info->nl_net, info->pid, RTNLGRP_IPV4_ROUTE, - info->nlh, GFP_KERNEL); + rtnl_notify(skb, info->nl_net, info->pid, RTNLGRP_IPV4_ROUTE, + info->nlh, GFP_KERNEL); + return; errout: if (err < 0) rtnl_set_sk_err(info->nl_net, RTNLGRP_IPV4_ROUTE, err); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 03e2a1ad71e9..f8f76d6e21cb 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3638,7 +3638,8 @@ static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa) kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC); + rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC); + return; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err); @@ -3849,7 +3850,8 @@ void inet6_ifinfo_notify(int event, struct inet6_dev *idev) kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC); + rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC); + return; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err); @@ -3919,7 +3921,8 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev, kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, net, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC); + rtnl_notify(skb, net, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC); + return; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 3cd83b85e9ef..9f061d1adbc2 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1095,11 +1095,7 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt) &ipv6_hdr(ra)->saddr); nlmsg_end(skb, nlh); - err = rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, - GFP_ATOMIC); - if (err < 0) - goto errout; - + rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC); return; nla_put_failure: diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c3d486a3edad..1394ddb6e35c 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2400,8 +2400,9 @@ void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info) kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE, - info->nlh, gfp_any()); + rtnl_notify(skb, net, info->pid, RTNLGRP_IPV6_ROUTE, + info->nlh, gfp_any()); + return; errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_IPV6_ROUTE, err); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index ed587be1e1c2..2760b62dc2c1 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1760,12 +1760,18 @@ int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 pid, exclude_pid = pid; } - /* errors reported via destination sk->sk_err */ - nlmsg_multicast(sk, skb, exclude_pid, group, flags); + /* errors reported via destination sk->sk_err, but propagate + * delivery errors if NETLINK_BROADCAST_ERROR flag is set */ + err = nlmsg_multicast(sk, skb, exclude_pid, group, flags); } - if (report) - err = nlmsg_unicast(sk, skb, pid); + if (report) { + int err2; + + err2 = nlmsg_unicast(sk, skb, pid); + if (!err || err == -ESRCH) + err = err2; + } return err; } diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index 1ceea1f92413..cec4e5951681 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -47,8 +47,9 @@ static void rtmsg_notify(int event, struct net_device *dev, u8 addr) kfree_skb(skb); goto errout; } - err = rtnl_notify(skb, dev_net(dev), 0, - RTNLGRP_PHONET_IFADDR, NULL, GFP_KERNEL); + rtnl_notify(skb, dev_net(dev), 0, + RTNLGRP_PHONET_IFADDR, NULL, GFP_KERNEL); + return; errout: if (err < 0) rtnl_set_sk_err(dev_net(dev), RTNLGRP_PHONET_IFADDR, err); -- cgit v1.2.3 From 8a7c4c77267b1c77296cd03e6704813cb70706d1 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Thu, 26 Feb 2009 23:41:38 -0800 Subject: RDS: Add AF and PF #defines for RDS sockets RDS is a reliable datagram protocol used for IPC on Oracle database clusters. This adds address and protocol family numbers for it. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- include/linux/socket.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux') diff --git a/include/linux/socket.h b/include/linux/socket.h index 20fc4bbfca42..9c90dc403efc 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -179,6 +179,7 @@ struct ucred { #define AF_ASH 18 /* Ash */ #define AF_ECONET 19 /* Acorn Econet */ #define AF_ATMSVC 20 /* ATM SVCs */ +#define AF_RDS 21 /* RDS sockets */ #define AF_SNA 22 /* Linux SNA Project (nutters!) */ #define AF_IRDA 23 /* IRDA sockets */ #define AF_PPPOX 24 /* PPPoX sockets */ @@ -217,6 +218,7 @@ struct ucred { #define PF_ASH AF_ASH #define PF_ECONET AF_ECONET #define PF_ATMSVC AF_ATMSVC +#define PF_RDS AF_RDS #define PF_SNA AF_SNA #define PF_IRDA AF_IRDA #define PF_PPPOX AF_PPPOX @@ -298,6 +300,7 @@ struct ucred { #define SOL_PPPOL2TP 273 #define SOL_BLUETOOTH 274 #define SOL_PNPIPE 275 +#define SOL_RDS 276 /* IPX options */ #define IPX_TYPE 1 -- cgit v1.2.3 From db49b9d26c1966c683efced9e1c37f391d8f8182 Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Tue, 24 Feb 2009 15:30:42 +0000 Subject: RDS: Add userspace header Applications include this header in order to use RDS sockets. Signed-off-by: Andy Grover Signed-off-by: David S. Miller --- include/linux/rds.h | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 include/linux/rds.h (limited to 'include/linux') diff --git a/include/linux/rds.h b/include/linux/rds.h new file mode 100644 index 000000000000..d91dc91f5443 --- /dev/null +++ b/include/linux/rds.h @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2008 Oracle. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _LINUX_RDS_H +#define _LINUX_RDS_H + +#include + +/* These sparse annotated types shouldn't be in any user + * visible header file. We should clean this up rather + * than kludging around them. */ +#ifndef __KERNEL__ +#define __be16 u_int16_t +#define __be32 u_int32_t +#define __be64 u_int64_t +#endif + +#define RDS_IB_ABI_VERSION 0x301 + +/* + * setsockopt/getsockopt for SOL_RDS + */ +#define RDS_CANCEL_SENT_TO 1 +#define RDS_GET_MR 2 +#define RDS_FREE_MR 3 +/* deprecated: RDS_BARRIER 4 */ +#define RDS_RECVERR 5 +#define RDS_CONG_MONITOR 6 + +/* + * Control message types for SOL_RDS. + * + * CMSG_RDMA_ARGS (sendmsg) + * Request a RDMA transfer to/from the specified + * memory ranges. + * The cmsg_data is a struct rds_rdma_args. + * RDS_CMSG_RDMA_DEST (recvmsg, sendmsg) + * Kernel informs application about intended + * source/destination of a RDMA transfer + * RDS_CMSG_RDMA_MAP (sendmsg) + * Application asks kernel to map the given + * memory range into a IB MR, and send the + * R_Key along in an RDS extension header. + * The cmsg_data is a struct rds_get_mr_args, + * the same as for the GET_MR setsockopt. + * RDS_CMSG_RDMA_STATUS (recvmsg) + * Returns the status of a completed RDMA operation. + */ +#define RDS_CMSG_RDMA_ARGS 1 +#define RDS_CMSG_RDMA_DEST 2 +#define RDS_CMSG_RDMA_MAP 3 +#define RDS_CMSG_RDMA_STATUS 4 +#define RDS_CMSG_CONG_UPDATE 5 + +#define RDS_INFO_FIRST 10000 +#define RDS_INFO_COUNTERS 10000 +#define RDS_INFO_CONNECTIONS 10001 +/* 10002 aka RDS_INFO_FLOWS is deprecated */ +#define RDS_INFO_SEND_MESSAGES 10003 +#define RDS_INFO_RETRANS_MESSAGES 10004 +#define RDS_INFO_RECV_MESSAGES 10005 +#define RDS_INFO_SOCKETS 10006 +#define RDS_INFO_TCP_SOCKETS 10007 +#define RDS_INFO_IB_CONNECTIONS 10008 +#define RDS_INFO_CONNECTION_STATS 10009 +#define RDS_INFO_IWARP_CONNECTIONS 10010 +#define RDS_INFO_LAST 10010 + +struct rds_info_counter { + u_int8_t name[32]; + u_int64_t value; +} __attribute__((packed)); + +#define RDS_INFO_CONNECTION_FLAG_SENDING 0x01 +#define RDS_INFO_CONNECTION_FLAG_CONNECTING 0x02 +#define RDS_INFO_CONNECTION_FLAG_CONNECTED 0x04 + +#define TRANSNAMSIZ 16 + +struct rds_info_connection { + u_int64_t next_tx_seq; + u_int64_t next_rx_seq; + __be32 laddr; + __be32 faddr; + u_int8_t transport[TRANSNAMSIZ]; /* null term ascii */ + u_int8_t flags; +} __attribute__((packed)); + +struct rds_info_flow { + __be32 laddr; + __be32 faddr; + u_int32_t bytes; + __be16 lport; + __be16 fport; +} __attribute__((packed)); + +#define RDS_INFO_MESSAGE_FLAG_ACK 0x01 +#define RDS_INFO_MESSAGE_FLAG_FAST_ACK 0x02 + +struct rds_info_message { + u_int64_t seq; + u_int32_t len; + __be32 laddr; + __be32 faddr; + __be16 lport; + __be16 fport; + u_int8_t flags; +} __attribute__((packed)); + +struct rds_info_socket { + u_int32_t sndbuf; + __be32 bound_addr; + __be32 connected_addr; + __be16 bound_port; + __be16 connected_port; + u_int32_t rcvbuf; + u_int64_t inum; +} __attribute__((packed)); + +#define RDS_IB_GID_LEN 16 +struct rds_info_rdma_connection { + __be32 src_addr; + __be32 dst_addr; + uint8_t src_gid[RDS_IB_GID_LEN]; + uint8_t dst_gid[RDS_IB_GID_LEN]; + + uint32_t max_send_wr; + uint32_t max_recv_wr; + uint32_t max_send_sge; + uint32_t rdma_mr_max; + uint32_t rdma_mr_size; +}; + +/* + * Congestion monitoring. + * Congestion control in RDS happens at the host connection + * level by exchanging a bitmap marking congested ports. + * By default, a process sleeping in poll() is always woken + * up when the congestion map is updated. + * With explicit monitoring, an application can have more + * fine-grained control. + * The application installs a 64bit mask value in the socket, + * where each bit corresponds to a group of ports. + * When a congestion update arrives, RDS checks the set of + * ports that are now uncongested against the list bit mask + * installed in the socket, and if they overlap, we queue a + * cong_notification on the socket. + * + * To install the congestion monitor bitmask, use RDS_CONG_MONITOR + * with the 64bit mask. + * Congestion updates are received via RDS_CMSG_CONG_UPDATE + * control messages. + * + * The correspondence between bits and ports is + * 1 << (portnum % 64) + */ +#define RDS_CONG_MONITOR_SIZE 64 +#define RDS_CONG_MONITOR_BIT(port) (((unsigned int) port) % RDS_CONG_MONITOR_SIZE) +#define RDS_CONG_MONITOR_MASK(port) (1ULL << RDS_CONG_MONITOR_BIT(port)) + +/* + * RDMA related types + */ + +/* + * This encapsulates a remote memory location. + * In the current implementation, it contains the R_Key + * of the remote memory region, and the offset into it + * (so that the application does not have to worry about + * alignment). + */ +typedef u_int64_t rds_rdma_cookie_t; + +struct rds_iovec { + u_int64_t addr; + u_int64_t bytes; +}; + +struct rds_get_mr_args { + struct rds_iovec vec; + u_int64_t cookie_addr; + uint64_t flags; +}; + +struct rds_free_mr_args { + rds_rdma_cookie_t cookie; + u_int64_t flags; +}; + +struct rds_rdma_args { + rds_rdma_cookie_t cookie; + struct rds_iovec remote_vec; + u_int64_t local_vec_addr; + u_int64_t nr_local; + u_int64_t flags; + u_int64_t user_token; +}; + +struct rds_rdma_notify { + u_int64_t user_token; + int32_t status; +}; + +#define RDS_RDMA_SUCCESS 0 +#define RDS_RDMA_REMOTE_ERROR 1 +#define RDS_RDMA_CANCELED 2 +#define RDS_RDMA_DROPPED 3 +#define RDS_RDMA_OTHER_ERROR 4 + +/* + * Common set of flags for all RDMA related structs + */ +#define RDS_RDMA_READWRITE 0x0001 +#define RDS_RDMA_FENCE 0x0002 /* use FENCE for immediate send */ +#define RDS_RDMA_INVALIDATE 0x0004 /* invalidate R_Key after freeing MR */ +#define RDS_RDMA_USE_ONCE 0x0008 /* free MR after use */ +#define RDS_RDMA_DONTWAIT 0x0010 /* Don't wait in SET_BARRIER */ +#define RDS_RDMA_NOTIFY_ME 0x0020 /* Notify when operation completes */ + +#endif /* IB_RDS_H */ -- cgit v1.2.3 From 98c8a60a04316e94ccea8221cf16768ce91bd214 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 17 Feb 2009 13:24:57 +0200 Subject: nl80211: Provide access to STA TX/RX packet counters The TX/RX packet counters are needed to fill in RADIUS Accounting attributes Acct-Output-Packets and Acct-Input-Packets. We already collect the needed information, but only the TX/RX bytes were previously exposed through nl80211. Allow applications to fetch the packet counters, too, to provide more complete support for accounting. Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 5 +++++ include/net/cfg80211.h | 8 ++++++++ net/mac80211/cfg.c | 4 ++++ net/wireless/nl80211.c | 6 ++++++ 4 files changed, 23 insertions(+) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 8802d1bda382..f6e56370ea65 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -526,6 +526,9 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute * containing info as possible, see &enum nl80211_sta_info_txrate. + * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this + * station) */ enum nl80211_sta_info { __NL80211_STA_INFO_INVALID, @@ -537,6 +540,8 @@ enum nl80211_sta_info { NL80211_STA_INFO_PLINK_STATE, NL80211_STA_INFO_SIGNAL, NL80211_STA_INFO_TX_BITRATE, + NL80211_STA_INFO_RX_PACKETS, + NL80211_STA_INFO_TX_PACKETS, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 33f43b0d08fb..8dcc46444037 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -178,6 +178,8 @@ struct station_parameters { * @STATION_INFO_SIGNAL: @signal filled * @STATION_INFO_TX_BITRATE: @tx_bitrate fields are filled * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) + * @STATION_INFO_RX_PACKETS: @rx_packets filled + * @STATION_INFO_TX_PACKETS: @tx_packets filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -188,6 +190,8 @@ enum station_info_flags { STATION_INFO_PLINK_STATE = 1<<5, STATION_INFO_SIGNAL = 1<<6, STATION_INFO_TX_BITRATE = 1<<7, + STATION_INFO_RX_PACKETS = 1<<8, + STATION_INFO_TX_PACKETS = 1<<9, }; /** @@ -235,6 +239,8 @@ struct rate_info { * @plink_state: mesh peer link state * @signal: signal strength of last received packet in dBm * @txrate: current unicast bitrate to this station + * @rx_packets: packets received from this station + * @tx_packets: packets transmitted to this station */ struct station_info { u32 filled; @@ -246,6 +252,8 @@ struct station_info { u8 plink_state; s8 signal; struct rate_info txrate; + u32 rx_packets; + u32 tx_packets; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f453bb7c564b..c43129efc3bf 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -341,11 +341,15 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | + STATION_INFO_RX_PACKETS | + STATION_INFO_TX_PACKETS | STATION_INFO_TX_BITRATE; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->rx_bytes = sta->rx_bytes; sinfo->tx_bytes = sta->tx_bytes; + sinfo->rx_packets = sta->rx_packets; + sinfo->tx_packets = sta->tx_packets; if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { sinfo->filled |= STATION_INFO_SIGNAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 67b18b3a93a0..badccf98074e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1206,6 +1206,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, nla_nest_end(msg, txrate); } + if (sinfo->filled & STATION_INFO_RX_PACKETS) + NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS, + sinfo->rx_packets); + if (sinfo->filled & STATION_INFO_TX_PACKETS) + NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS, + sinfo->tx_packets); nla_nest_end(msg, sinfoattr); return genlmsg_end(msg, hdr); -- cgit v1.2.3 From cabeccbd172cc305f4383f5a4808ae254745275f Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Sat, 28 Feb 2009 04:44:38 +0000 Subject: tcp: kill eff_sacks "cache", the sole user can calculate itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also fixes insignificant bug that would cause sending of stale SACK block (would occur in some corner cases). Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - include/net/tcp.h | 1 - net/ipv4/tcp_input.c | 15 ++------------- net/ipv4/tcp_minisocks.c | 3 +-- net/ipv4/tcp_output.c | 12 ++++++------ 5 files changed, 9 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 0cd99e6baca5..4b86ad71e054 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -218,7 +218,6 @@ struct tcp_options_received { snd_wscale : 4, /* Window scaling received from sender */ rcv_wscale : 4; /* Window scaling to send to receiver */ /* SACKs data */ - u8 eff_sacks; /* Size of SACK array to send with next packet */ u8 num_sacks; /* Number of SACK blocks */ u16 user_mss; /* mss requested by user in ioctl */ u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 0366a559afec..055e4946d4c8 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -926,7 +926,6 @@ extern void tcp_done(struct sock *sk); static inline void tcp_sack_reset(struct tcp_options_received *rx_opt) { rx_opt->dsack = 0; - rx_opt->eff_sacks = 0; rx_opt->num_sacks = 0; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 03f5ede87224..e4442a293eb0 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4099,7 +4099,6 @@ static void tcp_dsack_set(struct sock *sk, u32 seq, u32 end_seq) tp->rx_opt.dsack = 1; tp->duplicate_sack[0].start_seq = seq; tp->duplicate_sack[0].end_seq = end_seq; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + 1; } } @@ -4154,8 +4153,6 @@ static void tcp_sack_maybe_coalesce(struct tcp_sock *tp) * Decrease num_sacks. */ tp->rx_opt.num_sacks--; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + - tp->rx_opt.dsack; for (i = this_sack; i < tp->rx_opt.num_sacks; i++) sp[i] = sp[i + 1]; continue; @@ -4218,7 +4215,6 @@ new_sack: sp->start_seq = seq; sp->end_seq = end_seq; tp->rx_opt.num_sacks++; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; } /* RCV.NXT advances, some SACKs should be eaten. */ @@ -4232,7 +4228,6 @@ static void tcp_sack_remove(struct tcp_sock *tp) /* Empty ofo queue, hence, all the SACKs are eaten. Clear. */ if (skb_queue_empty(&tp->out_of_order_queue)) { tp->rx_opt.num_sacks = 0; - tp->rx_opt.eff_sacks = tp->rx_opt.dsack; return; } @@ -4253,11 +4248,8 @@ static void tcp_sack_remove(struct tcp_sock *tp) this_sack++; sp++; } - if (num_sacks != tp->rx_opt.num_sacks) { + if (num_sacks != tp->rx_opt.num_sacks) tp->rx_opt.num_sacks = num_sacks; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + - tp->rx_opt.dsack; - } } /* This one checks to see if we can put data from the @@ -4333,10 +4325,8 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) TCP_ECN_accept_cwr(tp, skb); - if (tp->rx_opt.dsack) { + if (tp->rx_opt.dsack) tp->rx_opt.dsack = 0; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks; - } /* Queue data for delivery to the user. * Packets in sequence go to the receive queue. @@ -4456,7 +4446,6 @@ drop: if (tcp_is_sack(tp)) { tp->rx_opt.num_sacks = 1; tp->rx_opt.dsack = 0; - tp->rx_opt.eff_sacks = 1; tp->selective_acks[0].start_seq = TCP_SKB_CB(skb)->seq; tp->selective_acks[0].end_seq = TCP_SKB_CB(skb)->end_seq; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index f67effbb102b..bb3d8b35f19a 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -434,9 +434,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->rx_opt.saw_tstamp = 0; newtp->rx_opt.dsack = 0; - newtp->rx_opt.eff_sacks = 0; - newtp->rx_opt.num_sacks = 0; + newtp->urg_data = 0; if (sock_flag(newsk, SOCK_KEEPOPEN)) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 61445b57610c..1555bb73b638 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -441,10 +441,8 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, *ptr++ = htonl(sp[this_sack].end_seq); } - if (tp->rx_opt.dsack) { + if (tp->rx_opt.dsack) tp->rx_opt.dsack = 0; - tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks; - } } } @@ -550,6 +548,7 @@ static unsigned tcp_established_options(struct sock *sk, struct sk_buff *skb, struct tcp_skb_cb *tcb = skb ? TCP_SKB_CB(skb) : NULL; struct tcp_sock *tp = tcp_sk(sk); unsigned size = 0; + unsigned int eff_sacks; #ifdef CONFIG_TCP_MD5SIG *md5 = tp->af_specific->md5_lookup(sk, sk); @@ -568,10 +567,11 @@ static unsigned tcp_established_options(struct sock *sk, struct sk_buff *skb, size += TCPOLEN_TSTAMP_ALIGNED; } - if (unlikely(tp->rx_opt.eff_sacks)) { + eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; + if (unlikely(eff_sacks)) { const unsigned remaining = MAX_TCP_OPTION_SPACE - size; opts->num_sack_blocks = - min_t(unsigned, tp->rx_opt.eff_sacks, + min_t(unsigned, eff_sacks, (remaining - TCPOLEN_SACK_BASE_ALIGNED) / TCPOLEN_SACK_PERBLOCK); size += TCPOLEN_SACK_BASE_ALIGNED + @@ -1418,7 +1418,7 @@ static int tcp_mtu_probe(struct sock *sk) icsk->icsk_mtup.probe_size || inet_csk(sk)->icsk_ca_state != TCP_CA_Open || tp->snd_cwnd < 11 || - tp->rx_opt.eff_sacks) + tp->rx_opt.num_sacks || tp->rx_opt.dsack) return -1; /* Very simple search strategy: just double the MSS. */ -- cgit v1.2.3 From 8987691a4aa6622a1b58bb12c56abaf3d2098fad Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez Date: Sat, 28 Feb 2009 23:42:50 +0000 Subject: wimax/i2400m: allow control of the base-station idle mode timeout For power saving reasons, WiMAX links can be put in idle mode while connected after a certain time of the link not being used for tx or rx. In this mode, the device pages the base-station regularly and when data is ready to be transmitted, the link is revived. This patch allows the user to control the time the device has to be idle before it decides to go to idle mode from a sysfs interace. It also updates the initialization code to acknowledge the module variable 'idle_mode_disabled' when the firmware is a newer version (upcoming 1.4 vs 2.6.29's v1.3). The method for setting the idle mode timeout in the older firmwares is much more limited and can be only done at initialization time. Thus, the sysfs file will return -ENOSYS on older ones. Signed-off-by: Inaky Perez-Gonzalez Signed-off-by: David S. Miller --- drivers/net/wimax/i2400m/Makefile | 1 + drivers/net/wimax/i2400m/control.c | 98 ++++++++++++++++++++++++++++++--- drivers/net/wimax/i2400m/debug-levels.h | 1 + drivers/net/wimax/i2400m/driver.c | 10 ++++ drivers/net/wimax/i2400m/i2400m.h | 29 ++++++++++ drivers/net/wimax/i2400m/sysfs.c | 80 +++++++++++++++++++++++++++ include/linux/wimax/i2400m.h | 10 ++++ 7 files changed, 221 insertions(+), 8 deletions(-) create mode 100644 drivers/net/wimax/i2400m/sysfs.c (limited to 'include/linux') diff --git a/drivers/net/wimax/i2400m/Makefile b/drivers/net/wimax/i2400m/Makefile index 1696e936cf5a..5d9e018d31af 100644 --- a/drivers/net/wimax/i2400m/Makefile +++ b/drivers/net/wimax/i2400m/Makefile @@ -8,6 +8,7 @@ i2400m-y := \ driver.o \ fw.o \ op-rfkill.o \ + sysfs.o \ netdev.o \ tx.o \ rx.o diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c index c8b3a68b72b8..c3968b240d69 100644 --- a/drivers/net/wimax/i2400m/control.c +++ b/drivers/net/wimax/i2400m/control.c @@ -1221,6 +1221,77 @@ none: EXPORT_SYMBOL_GPL(i2400m_set_init_config); +/** + * i2400m_set_idle_timeout - Set the device's idle mode timeout + * + * @i2400m: i2400m device descriptor + * + * @msecs: milliseconds for the timeout to enter idle mode. Between + * 100 to 300000 (5m); 0 to disable. In increments of 100. + * + * After this @msecs of the link being idle (no data being sent or + * received), the device will negotiate with the basestation entering + * idle mode for saving power. The connection is maintained, but + * getting out of it (done in tx.c) will require some negotiation, + * possible crypto re-handshake and a possible DHCP re-lease. + * + * Only available if fw_version >= 0x00090002. + * + * Returns: 0 if ok, < 0 errno code on error. + */ +int i2400m_set_idle_timeout(struct i2400m *i2400m, unsigned msecs) +{ + int result; + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *ack_skb; + struct { + struct i2400m_l3l4_hdr hdr; + struct i2400m_tlv_config_idle_timeout cit; + } *cmd; + const struct i2400m_l3l4_hdr *ack; + size_t ack_len; + char strerr[32]; + + result = -ENOSYS; + if (i2400m_le_v1_3(i2400m)) + goto error_alloc; + result = -ENOMEM; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + goto error_alloc; + cmd->hdr.type = cpu_to_le16(I2400M_MT_GET_STATE); + cmd->hdr.length = cpu_to_le16(sizeof(*cmd) - sizeof(cmd->hdr)); + cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION); + + cmd->cit.hdr.type = + cpu_to_le16(I2400M_TLV_CONFIG_IDLE_TIMEOUT); + cmd->cit.hdr.length = cpu_to_le16(sizeof(cmd->cit.timeout)); + cmd->cit.timeout = cpu_to_le32(msecs); + + ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd)); + if (IS_ERR(ack_skb)) { + dev_err(dev, "Failed to issue 'set idle timeout' command: " + "%ld\n", PTR_ERR(ack_skb)); + result = PTR_ERR(ack_skb); + goto error_msg_to_dev; + } + ack = wimax_msg_data_len(ack_skb, &ack_len); + result = i2400m_msg_check_status(ack, strerr, sizeof(strerr)); + if (result < 0) { + dev_err(dev, "'set idle timeout' (0x%04x) command failed: " + "%d - %s\n", I2400M_MT_GET_STATE, result, strerr); + goto error_cmd_failed; + } + result = 0; + kfree_skb(ack_skb); +error_cmd_failed: +error_msg_to_dev: + kfree(cmd); +error_alloc: + return result; +} + + /** * i2400m_dev_initialize - Initialize the device once communications are ready * @@ -1239,19 +1310,28 @@ int i2400m_dev_initialize(struct i2400m *i2400m) int result; struct device *dev = i2400m_dev(i2400m); struct i2400m_tlv_config_idle_parameters idle_params; + struct i2400m_tlv_config_idle_timeout idle_timeout; const struct i2400m_tlv_hdr *args[9]; unsigned argc = 0; d_fnstart(3, dev, "(i2400m %p)\n", i2400m); - /* Useless for now...might change */ if (i2400m_idle_mode_disabled) { - idle_params.hdr.type = - cpu_to_le16(I2400M_TLV_CONFIG_IDLE_PARAMETERS); - idle_params.hdr.length = cpu_to_le16( - sizeof(idle_params) - sizeof(idle_params.hdr)); - idle_params.idle_timeout = 0; - idle_params.idle_paging_interval = 0; - args[argc++] = &idle_params.hdr; + if (i2400m_le_v1_3(i2400m)) { + idle_params.hdr.type = + cpu_to_le16(I2400M_TLV_CONFIG_IDLE_PARAMETERS); + idle_params.hdr.length = cpu_to_le16( + sizeof(idle_params) - sizeof(idle_params.hdr)); + idle_params.idle_timeout = 0; + idle_params.idle_paging_interval = 0; + args[argc++] = &idle_params.hdr; + } else { + idle_timeout.hdr.type = + cpu_to_le16(I2400M_TLV_CONFIG_IDLE_TIMEOUT); + idle_timeout.hdr.length = cpu_to_le16( + sizeof(idle_timeout) - sizeof(idle_timeout.hdr)); + idle_timeout.timeout = 0; + args[argc++] = &idle_timeout.hdr; + } } result = i2400m_set_init_config(i2400m, args, argc); if (result < 0) @@ -1264,6 +1344,8 @@ int i2400m_dev_initialize(struct i2400m *i2400m) */ result = i2400m_cmd_get_state(i2400m); error: + if (result < 0) + dev_err(dev, "failed to initialize the device: %d\n", result); d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result); return result; } diff --git a/drivers/net/wimax/i2400m/debug-levels.h b/drivers/net/wimax/i2400m/debug-levels.h index 3183baa16a52..48fbfaa0d403 100644 --- a/drivers/net/wimax/i2400m/debug-levels.h +++ b/drivers/net/wimax/i2400m/debug-levels.h @@ -38,6 +38,7 @@ enum d_module { D_SUBMODULE_DECLARE(netdev), D_SUBMODULE_DECLARE(rfkill), D_SUBMODULE_DECLARE(rx), + D_SUBMODULE_DECLARE(sysfs), D_SUBMODULE_DECLARE(tx), }; diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index 69a816e7c5db..f988771bfae0 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c @@ -662,6 +662,11 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED); /* Now setup all that requires a registered net and wimax device. */ + result = sysfs_create_group(&net_dev->dev.kobj, &i2400m_dev_attr_group); + if (result < 0) { + dev_err(dev, "cannot setup i2400m's sysfs: %d\n", result); + goto error_sysfs_setup; + } result = i2400m_debugfs_add(i2400m); if (result < 0) { dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result); @@ -671,6 +676,9 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) return result; error_debugfs_setup: + sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj, + &i2400m_dev_attr_group); +error_sysfs_setup: wimax_dev_rm(&i2400m->wimax_dev); error_wimax_dev_add: i2400m_dev_stop(i2400m); @@ -702,6 +710,8 @@ void i2400m_release(struct i2400m *i2400m) netif_stop_queue(i2400m->wimax_dev.net_dev); i2400m_debugfs_rm(i2400m); + sysfs_remove_group(&i2400m->wimax_dev.net_dev->dev.kobj, + &i2400m_dev_attr_group); wimax_dev_rm(&i2400m->wimax_dev); i2400m_dev_stop(i2400m); unregister_netdev(i2400m->wimax_dev.net_dev); diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 5008cdb12b42..0c60d5c43007 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h @@ -585,6 +585,8 @@ unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr) * Driver / device setup and internal functions */ extern void i2400m_netdev_setup(struct net_device *net_dev); +extern int i2400m_sysfs_setup(struct device_driver *); +extern void i2400m_sysfs_release(struct device_driver *); extern int i2400m_tx_setup(struct i2400m *); extern void i2400m_wake_tx_work(struct work_struct *); extern void i2400m_tx_release(struct i2400m *); @@ -728,6 +730,7 @@ extern struct sk_buff *i2400m_get_device_info(struct i2400m *); extern int i2400m_firmware_check(struct i2400m *); extern int i2400m_set_init_config(struct i2400m *, const struct i2400m_tlv_hdr **, size_t); +extern int i2400m_set_idle_timeout(struct i2400m *, unsigned); static inline struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep) @@ -740,6 +743,32 @@ extern int i2400m_op_rfkill_sw_toggle(struct wimax_dev *, extern void i2400m_report_tlv_rf_switches_status( struct i2400m *, const struct i2400m_tlv_rf_switches_status *); +/* + * Helpers for firmware backwards compability + * + * As we aim to support at least the firmware version that was + * released with the previous kernel/driver release, some code will be + * conditionally executed depending on the firmware version. On each + * release, the code to support fw releases past the last two ones + * will be purged. + * + * By making it depend on this macros, it is easier to keep it a tab + * on what has to go and what not. + */ +static inline +unsigned i2400m_le_v1_3(struct i2400m *i2400m) +{ + /* running fw is lower or v1.3 */ + return i2400m->fw_version <= 0x00090001; +} + +static inline +unsigned i2400m_ge_v1_4(struct i2400m *i2400m) +{ + /* running fw is higher or v1.4 */ + return i2400m->fw_version >= 0x00090002; +} + /* * Do a millisecond-sleep for allowing wireshark to dump all the data diff --git a/drivers/net/wimax/i2400m/sysfs.c b/drivers/net/wimax/i2400m/sysfs.c new file mode 100644 index 000000000000..1237109f251a --- /dev/null +++ b/drivers/net/wimax/i2400m/sysfs.c @@ -0,0 +1,80 @@ +/* + * Intel Wireless WiMAX Connection 2400m + * Sysfs interfaces to show driver and device information + * + * + * Copyright (C) 2007 Intel Corporation + * Inaky Perez-Gonzalez + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include "i2400m.h" + + +#define D_SUBMODULE sysfs +#include "debug-levels.h" + + +/* + * Set the idle timeout (msecs) + * + * FIXME: eventually this should be a common WiMAX stack method, but + * would like to wait to see how other devices manage it. + */ +static +ssize_t i2400m_idle_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + ssize_t result; + struct i2400m *i2400m = net_dev_to_i2400m(to_net_dev(dev)); + unsigned val; + + result = -EINVAL; + if (sscanf(buf, "%u\n", &val) != 1) + goto error_no_unsigned; + if (val != 0 && (val < 100 || val > 300000 || val % 100 != 0)) { + dev_err(dev, "idle_timeout: %u: invalid msecs specification; " + "valid values are 0, 100-300000 in 100 increments\n", + val); + goto error_bad_value; + } + result = i2400m_set_idle_timeout(i2400m, val); + if (result >= 0) + result = size; +error_no_unsigned: +error_bad_value: + return result; +} + +static +DEVICE_ATTR(i2400m_idle_timeout, S_IWUSR, + NULL, i2400m_idle_timeout_store); + +static +struct attribute *i2400m_dev_attrs[] = { + &dev_attr_i2400m_idle_timeout.attr, + NULL, +}; + +struct attribute_group i2400m_dev_attr_group = { + .name = NULL, /* we want them in the same directory */ + .attrs = i2400m_dev_attrs, +}; diff --git a/include/linux/wimax/i2400m.h b/include/linux/wimax/i2400m.h index 74198f5bb4dc..686eeb2b9704 100644 --- a/include/linux/wimax/i2400m.h +++ b/include/linux/wimax/i2400m.h @@ -381,6 +381,7 @@ enum i2400m_tlv { I2400M_TLV_RF_STATUS = 163, I2400M_TLV_DEVICE_RESET_TYPE = 132, I2400M_TLV_CONFIG_IDLE_PARAMETERS = 601, + I2400M_TLV_CONFIG_IDLE_TIMEOUT = 611, }; @@ -509,4 +510,13 @@ struct i2400m_tlv_media_status { __le32 media_status; } __attribute__((packed)); + +/* New in v1.4 */ +struct i2400m_tlv_config_idle_timeout { + struct i2400m_tlv_hdr hdr; + __le32 timeout; /* 100 to 300000 ms [5min], 100 increments + * 0 disabled */ +} __attribute__((packed)); + + #endif /* #ifndef __LINUX__WIMAX__I2400M_H__ */ -- cgit v1.2.3 From 347707baa77d273d79258303e00200d40cf3b323 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sat, 28 Feb 2009 23:42:51 +0000 Subject: wimax: struct device - replace bus_id with dev_name(), dev_set_name() Cc: inaky.perez-gonzalez@intel.com Cc: linux-wimax@intel.com Acked-by: Greg Kroah-Hartman Signed-off-by: Kay Sievers Signed-off-by: Inaky Perez-Gonzalez Signed-off-by: David S. Miller --- drivers/net/wimax/i2400m/driver.c | 2 +- drivers/net/wimax/i2400m/usb-notif.c | 2 +- include/linux/wimax/debug.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index f988771bfae0..e4f1ce5bc294 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c @@ -618,7 +618,7 @@ int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags) d_fnstart(3, dev, "(i2400m %p)\n", i2400m); snprintf(wimax_dev->name, sizeof(wimax_dev->name), - "i2400m-%s:%s", dev->bus->name, dev->bus_id); + "i2400m-%s:%s", dev->bus->name, dev_name(dev)); i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL); if (i2400m->bm_cmd_buf == NULL) { diff --git a/drivers/net/wimax/i2400m/usb-notif.c b/drivers/net/wimax/i2400m/usb-notif.c index 9702c22b2497..6add27c3f35c 100644 --- a/drivers/net/wimax/i2400m/usb-notif.c +++ b/drivers/net/wimax/i2400m/usb-notif.c @@ -102,7 +102,7 @@ int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf, dev_err(dev, "HW BUG? Unknown/unexpected data in notification " "message (%zu bytes)\n", buf_len); snprintf(prefix, sizeof(prefix), "%s %s: ", - dev_driver_string(dev) , dev->bus_id); + dev_driver_string(dev), dev_name(dev)); if (buf_len > 64) { print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, 8, 4, buf, 64, 0); diff --git a/include/linux/wimax/debug.h b/include/linux/wimax/debug.h index ba0c49399a83..c703e0340423 100644 --- a/include/linux/wimax/debug.h +++ b/include/linux/wimax/debug.h @@ -178,7 +178,7 @@ void __d_head(char *head, size_t head_size, WARN_ON(1); } else snprintf(head, head_size, "%s %s: ", - dev_driver_string(dev), dev->bus_id); + dev_driver_string(dev), dev_name(dev)); } -- cgit v1.2.3 From fd5c565c0c04d2716cfdac3f1de3c2261d6a457d Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez Date: Sat, 28 Feb 2009 23:42:52 +0000 Subject: wimax/i2400m: support extended data RX protocol (no need to reallocate skbs) Newer i2400m firmwares (>= v1.4) extend the data RX protocol so that each packet has a 16 byte header. This header is mainly used to implement host reordeing (which is addressed in later commits). However, this header also allows us to overwrite it (once data has been extracted) with an Ethernet header and deliver to the networking stack without having to reallocate the skb (as it happened in fw <= v1.3) to make room for it. - control.c: indicate the device [dev_initialize()] that the driver wants to use the extended data RX protocol. Also involves adding the definition of the needed data types in include/linux/wimax/i2400m.h. - rx.c: handle the new payload type for the extended RX data protocol. Prepares the skb for delivery to netdev.c:i2400m_net_erx(). - netdev.c: Introduce i2400m_net_erx() that adds the fake ethernet address to a prepared skb and delivers it to the networking stack. - cleanup: in most instances in rx.c, the variable 'single' was renamed to 'single_last' for it better conveys its meaning. Signed-off-by: Inaky Perez-Gonzalez Signed-off-by: David S. Miller --- drivers/net/wimax/i2400m/control.c | 9 +++ drivers/net/wimax/i2400m/i2400m.h | 2 + drivers/net/wimax/i2400m/netdev.c | 104 +++++++++++++++++++++++++-------- drivers/net/wimax/i2400m/rx.c | 117 ++++++++++++++++++++++++++++++++++--- include/linux/wimax/i2400m.h | 35 +++++++++++ 5 files changed, 234 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c index c3968b240d69..4073c3e93bd4 100644 --- a/drivers/net/wimax/i2400m/control.c +++ b/drivers/net/wimax/i2400m/control.c @@ -1311,6 +1311,7 @@ int i2400m_dev_initialize(struct i2400m *i2400m) struct device *dev = i2400m_dev(i2400m); struct i2400m_tlv_config_idle_parameters idle_params; struct i2400m_tlv_config_idle_timeout idle_timeout; + struct i2400m_tlv_config_d2h_data_format df; const struct i2400m_tlv_hdr *args[9]; unsigned argc = 0; @@ -1333,6 +1334,14 @@ int i2400m_dev_initialize(struct i2400m *i2400m) args[argc++] = &idle_timeout.hdr; } } + if (i2400m_ge_v1_4(i2400m)) { + df.hdr.type = + cpu_to_le16(I2400M_TLV_CONFIG_D2H_DATA_FORMAT); + df.hdr.length = cpu_to_le16( + sizeof(df) - sizeof(df.hdr)); + df.format = 1; + args[argc++] = &df.hdr; + } result = i2400m_set_init_config(i2400m, args, argc); if (result < 0) goto error; diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 0c60d5c43007..125c30594e63 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h @@ -593,6 +593,8 @@ extern void i2400m_tx_release(struct i2400m *); extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, const void *, int); +extern void i2400m_net_erx(struct i2400m *, struct sk_buff *, + enum i2400m_cs); enum i2400m_pt; extern int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt); diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c index be8be4d0709c..2bdd0cdbb319 100644 --- a/drivers/net/wimax/i2400m/netdev.c +++ b/drivers/net/wimax/i2400m/netdev.c @@ -28,13 +28,12 @@ * space and from the other side. The world is (sadly) configured to * take in only Ethernet devices... * - * Because of this, currently there is an copy-each-rxed-packet - * overhead on the RX path. Each IP packet has to be reallocated to - * add an ethernet header (as there is no space in what we get from - * the device). This is a known drawback and coming versions of the - * device's firmware are being changed to add header space that can be - * used to insert the ethernet header without having to reallocate and - * copy. + * Because of this, when using firmwares <= v1.3, there is an + * copy-each-rxed-packet overhead on the RX path. Each IP packet has + * to be reallocated to add an ethernet header (as there is no space + * in what we get from the device). This is a known drawback and + * firmwares >= 1.4 add header space that can be used to insert the + * ethernet header without having to reallocate and copy. * * TX error handling is tricky; because we have to FIFO/queue the * buffers for transmission (as the hardware likes it aggregated), we @@ -67,7 +66,9 @@ * i2400m_tx_timeout Called when the device times out * * i2400m_net_rx Called by the RX code when a data frame is - * available. + * available (firmware <= 1.3) + * i2400m_net_erx Called by the RX code when a data frame is + * available (firmware >= 1.4). * i2400m_netdev_setup Called to setup all the netdev stuff from * alloc_netdev. */ @@ -396,30 +397,18 @@ void i2400m_tx_timeout(struct net_device *net_dev) * Create a fake ethernet header * * For emulating an ethernet device, every received IP header has to - * be prefixed with an ethernet header. - * - * What we receive has (potentially) many IP packets concatenated with - * no ETH_HLEN bytes prefixed. Thus there is no space for an eth - * header. - * - * We would have to reallocate or do ugly fragment tricks in order to - * add it. - * - * But what we do is use the header space of the RX transaction - * (*msg_hdr) as we don't need it anymore; then we'll point all the - * data skbs there, as they share the same backing store. - * - * We only support IPv4 for v3 firmware. + * be prefixed with an ethernet header. Fake it with the given + * protocol. */ static void i2400m_rx_fake_eth_header(struct net_device *net_dev, - void *_eth_hdr) + void *_eth_hdr, int protocol) { struct ethhdr *eth_hdr = _eth_hdr; memcpy(eth_hdr->h_dest, net_dev->dev_addr, sizeof(eth_hdr->h_dest)); memset(eth_hdr->h_source, 0, sizeof(eth_hdr->h_dest)); - eth_hdr->h_proto = cpu_to_be16(ETH_P_IP); + eth_hdr->h_proto = cpu_to_be16(protocol); } @@ -432,6 +421,13 @@ void i2400m_rx_fake_eth_header(struct net_device *net_dev, * @buf: pointer to the buffer containing the data * @len: buffer's length * + * This is only used now for the v1.3 firmware. It will be deprecated + * in >= 2.6.31. + * + * Note that due to firmware limitations, we don't have space to add + * an ethernet header, so we need to copy each packet. Firmware + * versions >= v1.4 fix this [see i2400m_net_erx()]. + * * We just clone the skb and set it up so that it's skb->data pointer * points to "buf" and it's length. * @@ -478,7 +474,7 @@ void i2400m_net_rx(struct i2400m *i2400m, struct sk_buff *skb_rx, memcpy(skb_put(skb, buf_len), buf, buf_len); } i2400m_rx_fake_eth_header(i2400m->wimax_dev.net_dev, - skb->data - ETH_HLEN); + skb->data - ETH_HLEN, ETH_P_IP); skb_set_mac_header(skb, -ETH_HLEN); skb->dev = i2400m->wimax_dev.net_dev; skb->protocol = htons(ETH_P_IP); @@ -493,6 +489,64 @@ error_skb_realloc: i2400m, buf, buf_len); } + +/* + * i2400m_net_erx - pass a network packet to the stack (extended version) + * + * @i2400m: device descriptor + * @skb: the skb where the packet is - the skb should be set to point + * at the IP packet; this function will add ethernet headers if + * needed. + * @cs: packet type + * + * This is only used now for firmware >= v1.4. Note it is quite + * similar to i2400m_net_rx() (used only for v1.3 firmware). + * + * This function is normally run from a thread context. However, we + * still use netif_rx() instead of netif_receive_skb() as was + * recommended in the mailing list. Reason is in some stress tests + * when sending/receiving a lot of data we seem to hit a softlock in + * the kernel's TCP implementation [aroudn tcp_delay_timer()]. Using + * netif_rx() took care of the issue. + * + * This is, of course, still open to do more research on why running + * with netif_receive_skb() hits this softlock. FIXME. + */ +void i2400m_net_erx(struct i2400m *i2400m, struct sk_buff *skb, + enum i2400m_cs cs) +{ + struct net_device *net_dev = i2400m->wimax_dev.net_dev; + struct device *dev = i2400m_dev(i2400m); + int protocol; + + d_fnstart(2, dev, "(i2400m %p skb %p [%zu] cs %d)\n", + i2400m, skb, skb->len, cs); + switch(cs) { + case I2400M_CS_IPV4_0: + case I2400M_CS_IPV4: + protocol = ETH_P_IP; + i2400m_rx_fake_eth_header(i2400m->wimax_dev.net_dev, + skb->data - ETH_HLEN, ETH_P_IP); + skb_set_mac_header(skb, -ETH_HLEN); + skb->dev = i2400m->wimax_dev.net_dev; + skb->protocol = htons(ETH_P_IP); + net_dev->stats.rx_packets++; + net_dev->stats.rx_bytes += skb->len; + break; + default: + dev_err(dev, "ERX: BUG? CS type %u unsupported\n", cs); + goto error; + + } + d_printf(3, dev, "ERX: receiving %d bytes to the network stack\n", + skb->len); + d_dump(4, dev, skb->data, skb->len); + netif_rx_ni(skb); /* see notes in function header */ +error: + d_fnend(2, dev, "(i2400m %p skb %p [%zu] cs %d) = void\n", + i2400m, skb, skb->len, cs); +} + static const struct net_device_ops i2400m_netdev_ops = { .ndo_open = i2400m_open, .ndo_stop = i2400m_stop, diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c index c62b8c564161..cd525066d4b7 100644 --- a/drivers/net/wimax/i2400m/rx.c +++ b/drivers/net/wimax/i2400m/rx.c @@ -69,6 +69,22 @@ * See tx.c for a deeper description on alignment requirements and * other fun facts of it. * + * DATA PACKETS + * + * In firmwares <= v1.3, data packets have no header for RX, but they + * do for TX (currently unused). + * + * In firmware >= 1.4, RX packets have an extended header (16 + * bytes). This header conveys information for management of host + * reordering of packets (the device offloads storage of the packets + * for reordering to the host). + * + * Currently this information is not used as the current code doesn't + * enable host reordering. + * + * The header is used as dummy space to emulate an ethernet header and + * thus be able to act as an ethernet device without having to reallocate. + * * ROADMAP * * i2400m_rx @@ -76,6 +92,8 @@ * i2400m_rx_pl_descr_check * i2400m_rx_payload * i2400m_net_rx + * i2400m_rx_edata + * i2400m_net_erx * i2400m_rx_ctl * i2400m_msg_size_check * i2400m_report_hook_work [in a workqueue] @@ -264,8 +282,6 @@ error_check: } - - /* * Receive and send up a trace * @@ -314,32 +330,112 @@ error_check: return; } +/* + * Receive and send up an extended data packet + * + * @i2400m: device descriptor + * @skb_rx: skb that contains the extended data packet + * @single_last: 1 if the payload is the only one or the last one of + * the skb. + * @payload: pointer to the packet's data inside the skb + * @size: size of the payload + * + * Starting in v1.4 of the i2400m's firmware, the device can send data + * packets to the host in an extended format that; this incudes a 16 + * byte header (struct i2400m_pl_edata_hdr). Using this header's space + * we can fake ethernet headers for ethernet device emulation without + * having to copy packets around. + * + * This function handles said path. + */ +static +void i2400m_rx_edata(struct i2400m *i2400m, struct sk_buff *skb_rx, + unsigned single_last, const void *payload, size_t size) +{ + struct device *dev = i2400m_dev(i2400m); + const struct i2400m_pl_edata_hdr *hdr = payload; + struct net_device *net_dev = i2400m->wimax_dev.net_dev; + struct sk_buff *skb; + enum i2400m_cs cs; + unsigned reorder_needed; + + d_fnstart(4, dev, "(i2400m %p skb_rx %p single %u payload %p " + "size %zu)\n", i2400m, skb_rx, single_last, payload, size); + if (size < sizeof(*hdr)) { + dev_err(dev, "ERX: HW BUG? message with short header (%zu " + "vs %zu bytes expected)\n", size, sizeof(*hdr)); + goto error; + } + reorder_needed = le32_to_cpu(hdr->reorder & I2400M_REORDER_NEEDED); + cs = hdr->cs; + if (reorder_needed) { + dev_err(dev, "ERX: HW BUG? reorder needed, it was disabled\n"); + goto error; + } + /* ok, so now decide if we want to clone or reuse the skb, + * pull and trim it so the beginning is the space for the eth + * header and pass it to i2400m_net_erx() for the stack */ + if (single_last) { + skb = skb_get(skb_rx); + d_printf(3, dev, "ERX: reusing single payload skb %p\n", skb); + } else { + skb = skb_clone(skb_rx, GFP_KERNEL); + d_printf(3, dev, "ERX: cloning %p\n", skb); + if (skb == NULL) { + dev_err(dev, "ERX: no memory to clone skb\n"); + net_dev->stats.rx_dropped++; + goto error_skb_clone; + } + } + /* now we have to pull and trim so that the skb points to the + * beginning of the IP packet; the netdev part will add the + * ethernet header as needed. */ + BUILD_BUG_ON(ETH_HLEN > sizeof(*hdr)); + skb_pull(skb, payload + sizeof(*hdr) - (void *) skb->data); + skb_trim(skb, (void *) skb_end_pointer(skb) - payload + sizeof(*hdr)); + i2400m_net_erx(i2400m, skb, cs); +error_skb_clone: +error: + d_fnend(4, dev, "(i2400m %p skb_rx %p single %u payload %p " + "size %zu) = void\n", i2400m, skb_rx, single_last, payload, size); + return; +} + + + /* * Act on a received payload * * @i2400m: device instance * @skb_rx: skb where the transaction was received - * @single: 1 if there is only one payload, 0 otherwise + * @single_last: 1 this is the only payload or the last one (so the + * skb can be reused instead of cloned). * @pld: payload descriptor * @payload: payload data * * Upon reception of a payload, look at its guts in the payload - * descriptor and decide what to do with it. + * descriptor and decide what to do with it. If it is a single payload + * skb or if the last skb is a data packet, the skb will be referenced + * and modified (so it doesn't have to be cloned). */ static void i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx, - unsigned single, const struct i2400m_pld *pld, + unsigned single_last, const struct i2400m_pld *pld, const void *payload) { struct device *dev = i2400m_dev(i2400m); size_t pl_size = i2400m_pld_size(pld); enum i2400m_pt pl_type = i2400m_pld_type(pld); + d_printf(7, dev, "RX: received payload type %u, %zu bytes\n", + pl_type, pl_size); + d_dump(8, dev, payload, pl_size); + switch (pl_type) { case I2400M_PT_DATA: d_printf(3, dev, "RX: data payload %zu bytes\n", pl_size); - i2400m_net_rx(i2400m, skb_rx, single, payload, pl_size); + i2400m_net_rx(i2400m, skb_rx, single_last, payload, pl_size); break; case I2400M_PT_CTRL: i2400m_rx_ctl(i2400m, skb_rx, payload, pl_size); @@ -347,6 +443,10 @@ void i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx, case I2400M_PT_TRACE: i2400m_rx_trace(i2400m, payload, pl_size); break; + case I2400M_PT_EDATA: + d_printf(3, dev, "ERX: data payload %zu bytes\n", pl_size); + i2400m_rx_edata(i2400m, skb_rx, single_last, payload, pl_size); + break; default: /* Anything else shouldn't come to the host */ if (printk_ratelimit()) dev_err(dev, "RX: HW BUG? unexpected payload type %u\n", @@ -474,7 +574,7 @@ int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb) const struct i2400m_msg_hdr *msg_hdr; size_t pl_itr, pl_size, skb_len; unsigned long flags; - unsigned num_pls; + unsigned num_pls, single_last; skb_len = skb->len; d_fnstart(4, dev, "(i2400m %p skb %p [size %zu])\n", @@ -503,7 +603,8 @@ int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb) pl_itr, skb->len); if (result < 0) goto error_pl_descr_check; - i2400m_rx_payload(i2400m, skb, num_pls == 1, &msg_hdr->pld[i], + single_last = num_pls == 1 || i == num_pls - 1; + i2400m_rx_payload(i2400m, skb, single_last, &msg_hdr->pld[i], skb->data + pl_itr); pl_itr += ALIGN(pl_size, I2400M_PL_PAD); cond_resched(); /* Don't monopolize */ diff --git a/include/linux/wimax/i2400m.h b/include/linux/wimax/i2400m.h index 686eeb2b9704..ad36e073a70c 100644 --- a/include/linux/wimax/i2400m.h +++ b/include/linux/wimax/i2400m.h @@ -207,6 +207,7 @@ enum i2400m_pt { I2400M_PT_TRACE, /* For device debug */ I2400M_PT_RESET_WARM, /* device reset */ I2400M_PT_RESET_COLD, /* USB[transport] reset, like reconnect */ + I2400M_PT_EDATA, /* Extended RX data */ I2400M_PT_ILLEGAL }; @@ -221,6 +222,32 @@ struct i2400m_pl_data_hdr { } __attribute__((packed)); +/* + * Payload for an extended data packet + * + * New in v1.4 + * + * @cs: the type of data in the packet, as defined per (802.16e + * T11.13.19.1). Currently only 2 (IPv4 packet) supported. + * + * This is prefixed to each and every INCOMING DATA packet. + */ +struct i2400m_pl_edata_hdr { + __le32 reorder; + __u8 cs; + __u8 reserved[11]; +} __attribute__((packed)); + +enum i2400m_cs { + I2400M_CS_IPV4_0 = 0, + I2400M_CS_IPV4 = 2, +}; + +enum i2400m_reorder { + I2400M_REORDER_NEEDED = 0x01, +}; + + /* Misc constants */ enum { I2400M_PL_PAD = 16, /* Payload data size alignment */ @@ -382,6 +409,7 @@ enum i2400m_tlv { I2400M_TLV_DEVICE_RESET_TYPE = 132, I2400M_TLV_CONFIG_IDLE_PARAMETERS = 601, I2400M_TLV_CONFIG_IDLE_TIMEOUT = 611, + I2400M_TLV_CONFIG_D2H_DATA_FORMAT = 614, }; @@ -518,5 +546,12 @@ struct i2400m_tlv_config_idle_timeout { * 0 disabled */ } __attribute__((packed)); +/* New in v1.4 -- for backward compat, will be removed */ +struct i2400m_tlv_config_d2h_data_format { + struct i2400m_tlv_hdr hdr; + __u8 format; /* 0 old format, 1 enhanced */ + __u8 reserved[3]; +} __attribute__((packed)); + #endif /* #ifndef __LINUX__WIMAX__I2400M_H__ */ -- cgit v1.2.3 From c747583d19d5d5147a9f0eae480c1fdbc84c4252 Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez Date: Sat, 28 Feb 2009 23:42:54 +0000 Subject: wimax/i2400m: implement RX reorder support Allow the device to give the driver RX data with reorder information. When that is done, the device will indicate the driver if a packet has to be held in a (sorted) queue. It will also tell the driver when held packets have to be released to the OS. This is done to improve the WiMAX-protocol level retransmission support when missing frames are detected. The code docs provide details about the implementation. In general, this just hooks into the RX path in rx.c; if a packet with the reorder bit in the RX header is detected, the reorder information in the header is extracted and one of the four main reorder operations are executed. In one case (queue) no packet will be delivered to the networking stack, just queued, whereas in the others (reset, update_ws and queue_update_ws), queued packet might be delivered depending on the window start for the specific queue. The modifications to files other than rx.c are: - control.c: during device initialization, enable reordering support if the rx_reorder_disabled module parameter is not enabled - driver.c: expose a rx_reorder_disable module parameter and call i2400m_rx_setup/release() to initialize/shutdown RX reorder support. - i2400m.h: introduce members in 'struct i2400m' needed for implementing reorder support. - linux/i2400m.h: introduce TLVs, commands and constant definitions related to RX reorder Last but not least, the rx reorder code includes an small circular log where the last N reorder operations are recorded to be displayed in case of inconsistency. Otherwise diagnosing issues would be almost impossible. Signed-off-by: Inaky Perez-Gonzalez Signed-off-by: David S. Miller --- drivers/net/wimax/i2400m/control.c | 14 + drivers/net/wimax/i2400m/driver.c | 11 + drivers/net/wimax/i2400m/i2400m.h | 19 +- drivers/net/wimax/i2400m/rx.c | 677 +++++++++++++++++++++++++++++++++++-- include/linux/wimax/i2400m.h | 32 +- 5 files changed, 723 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c index 4073c3e93bd4..b3cadb626fe0 100644 --- a/drivers/net/wimax/i2400m/control.c +++ b/drivers/net/wimax/i2400m/control.c @@ -1312,10 +1312,12 @@ int i2400m_dev_initialize(struct i2400m *i2400m) struct i2400m_tlv_config_idle_parameters idle_params; struct i2400m_tlv_config_idle_timeout idle_timeout; struct i2400m_tlv_config_d2h_data_format df; + struct i2400m_tlv_config_dl_host_reorder dlhr; const struct i2400m_tlv_hdr *args[9]; unsigned argc = 0; d_fnstart(3, dev, "(i2400m %p)\n", i2400m); + /* Disable idle mode? (enabled by default) */ if (i2400m_idle_mode_disabled) { if (i2400m_le_v1_3(i2400m)) { idle_params.hdr.type = @@ -1335,12 +1337,24 @@ int i2400m_dev_initialize(struct i2400m *i2400m) } } if (i2400m_ge_v1_4(i2400m)) { + /* Enable extended RX data format? */ df.hdr.type = cpu_to_le16(I2400M_TLV_CONFIG_D2H_DATA_FORMAT); df.hdr.length = cpu_to_le16( sizeof(df) - sizeof(df.hdr)); df.format = 1; args[argc++] = &df.hdr; + + /* Enable RX data reordering? + * (switch flipped in rx.c:i2400m_rx_setup() after fw upload) */ + if (i2400m->rx_reorder) { + dlhr.hdr.type = + cpu_to_le16(I2400M_TLV_CONFIG_DL_HOST_REORDER); + dlhr.hdr.length = cpu_to_le16( + sizeof(dlhr) - sizeof(dlhr.hdr)); + dlhr.reorder = 1; + args[argc++] = &dlhr.hdr; + } } result = i2400m_set_init_config(i2400m, args, argc); if (result < 0) diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index e4f1ce5bc294..07a54bad237b 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c @@ -76,6 +76,11 @@ MODULE_PARM_DESC(idle_mode_disabled, "If true, the device will not enable idle mode negotiation " "with the base station (when connected) to save power."); +int i2400m_rx_reorder_disabled; /* 0 (rx reorder enabled) by default */ +module_param_named(rx_reorder_disabled, i2400m_rx_reorder_disabled, int, 0644); +MODULE_PARM_DESC(rx_reorder_disabled, + "If true, RX reordering will be disabled."); + /** * i2400m_queue_work - schedule work on a i2400m's queue * @@ -396,6 +401,9 @@ retry: result = i2400m_tx_setup(i2400m); if (result < 0) goto error_tx_setup; + result = i2400m_rx_setup(i2400m); + if (result < 0) + goto error_rx_setup; result = i2400m->bus_dev_start(i2400m); if (result < 0) goto error_bus_dev_start; @@ -430,6 +438,8 @@ error_fw_check: error_create_workqueue: i2400m->bus_dev_stop(i2400m); error_bus_dev_start: + i2400m_rx_release(i2400m); +error_rx_setup: i2400m_tx_release(i2400m); error_tx_setup: error_bootstrap: @@ -477,6 +487,7 @@ void __i2400m_dev_stop(struct i2400m *i2400m) i2400m->ready = 0; destroy_workqueue(i2400m->work_queue); i2400m->bus_dev_stop(i2400m); + i2400m_rx_release(i2400m); i2400m_tx_release(i2400m); wimax_state_change(wimax_dev, WIMAX_ST_DOWN); d_fnend(3, dev, "(i2400m %p) = 0\n", i2400m); diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 125c30594e63..3ae2df38b59a 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h @@ -174,6 +174,7 @@ enum i2400m_reset_type { }; struct i2400m_reset_ctx; +struct i2400m_roq; /** * struct i2400m - descriptor for an Intel 2400m @@ -257,6 +258,9 @@ struct i2400m_reset_ctx; * force this to be the first field so that we can get from * netdev_priv() the right pointer. * + * @rx_reorder: 1 if RX reordering is enabled; this can only be + * set at probe time. + * * @state: device's state (as reported by it) * * @state_wq: waitqueue that is woken up whenever the state changes @@ -313,6 +317,12 @@ struct i2400m_reset_ctx; * * @rx_size_max: buggest RX message received. * + * @rx_roq: RX ReOrder queues. (fw >= v1.4) When packets are received + * out of order, the device will ask the driver to hold certain + * packets until the ones that are received out of order can be + * delivered. Then the driver can release them to the host. See + * drivers/net/i2400m/rx.c for details. + * * @init_mutex: Mutex used for serializing the device bringup * sequence; this way if the device reboots in the middle, we * don't try to do a bringup again while we are tearing down the @@ -377,6 +387,7 @@ struct i2400m { unsigned boot_mode:1; /* is the device in boot mode? */ unsigned sboot:1; /* signed or unsigned fw boot */ unsigned ready:1; /* all probing steps done */ + unsigned rx_reorder:1; /* RX reorder is enabled */ u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */ /* typed u8 so debugfs/u8 can tweak */ enum i2400m_system_state state; @@ -405,10 +416,11 @@ struct i2400m { unsigned tx_pl_num, tx_pl_max, tx_pl_min, tx_num, tx_size_acc, tx_size_min, tx_size_max; - /* RX stats */ + /* RX stuff */ spinlock_t rx_lock; /* protect RX state */ unsigned rx_pl_num, rx_pl_max, rx_pl_min, rx_num, rx_size_acc, rx_size_min, rx_size_max; + struct i2400m_roq *rx_roq; /* not under rx_lock! */ struct mutex msg_mutex; /* serialize command execution */ struct completion msg_completion; @@ -442,6 +454,7 @@ void i2400m_init(struct i2400m *i2400m) wimax_dev_init(&i2400m->wimax_dev); i2400m->boot_mode = 1; + i2400m->rx_reorder = 1; init_waitqueue_head(&i2400m->state_wq); spin_lock_init(&i2400m->tx_lock); @@ -591,6 +604,9 @@ extern int i2400m_tx_setup(struct i2400m *); extern void i2400m_wake_tx_work(struct work_struct *); extern void i2400m_tx_release(struct i2400m *); +extern int i2400m_rx_setup(struct i2400m *); +extern void i2400m_rx_release(struct i2400m *); + extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, const void *, int); extern void i2400m_net_erx(struct i2400m *, struct sk_buff *, @@ -788,6 +804,7 @@ void __i2400m_msleep(unsigned ms) /* Module parameters */ extern int i2400m_idle_mode_disabled; +extern int i2400m_rx_reorder_disabled; #endif /* #ifndef __I2400M_H__ */ diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c index cd525066d4b7..02419bfd64b5 100644 --- a/drivers/net/wimax/i2400m/rx.c +++ b/drivers/net/wimax/i2400m/rx.c @@ -39,7 +39,7 @@ * - Use skb_clone(), break up processing in chunks * - Split transport/device specific * - Make buffer size dynamic to exert less memory pressure - * + * - RX reorder support * * This handles the RX path. * @@ -77,14 +77,42 @@ * In firmware >= 1.4, RX packets have an extended header (16 * bytes). This header conveys information for management of host * reordering of packets (the device offloads storage of the packets - * for reordering to the host). - * - * Currently this information is not used as the current code doesn't - * enable host reordering. + * for reordering to the host). Read below for more information. * * The header is used as dummy space to emulate an ethernet header and * thus be able to act as an ethernet device without having to reallocate. * + * DATA RX REORDERING + * + * Starting in firmware v1.4, the device can deliver packets for + * delivery with special reordering information; this allows it to + * more effectively do packet management when some frames were lost in + * the radio traffic. + * + * Thus, for RX packets that come out of order, the device gives the + * driver enough information to queue them properly and then at some + * point, the signal to deliver the whole (or part) of the queued + * packets to the networking stack. There are 16 such queues. + * + * This only happens when a packet comes in with the "need reorder" + * flag set in the RX header. When such bit is set, the following + * operations might be indicated: + * + * - reset queue: send all queued packets to the OS + * + * - queue: queue a packet + * + * - update ws: update the queue's window start and deliver queued + * packets that meet the criteria + * + * - queue & update ws: queue a packet, update the window start and + * deliver queued packets that meet the criteria + * + * (delivery criteria: the packet's [normalized] sequence number is + * lower than the new [normalized] window start). + * + * See the i2400m_roq_*() functions for details. + * * ROADMAP * * i2400m_rx @@ -94,6 +122,17 @@ * i2400m_net_rx * i2400m_rx_edata * i2400m_net_erx + * i2400m_roq_reset + * i2400m_net_erx + * i2400m_roq_queue + * __i2400m_roq_queue + * i2400m_roq_update_ws + * __i2400m_roq_update_ws + * i2400m_net_erx + * i2400m_roq_queue_update_ws + * __i2400m_roq_queue + * __i2400m_roq_update_ws + * i2400m_net_erx * i2400m_rx_ctl * i2400m_msg_size_check * i2400m_report_hook_work [in a workqueue] @@ -330,6 +369,469 @@ error_check: return; } + +/* + * Reorder queue data stored on skb->cb while the skb is queued in the + * reorder queues. + */ +struct i2400m_roq_data { + unsigned sn; /* Serial number for the skb */ + enum i2400m_cs cs; /* packet type for the skb */ +}; + + +/* + * ReOrder Queue + * + * @ws: Window Start; sequence number where the current window start + * is for this queue + * @queue: the skb queue itself + * @log: circular ring buffer used to log information about the + * reorder process in this queue that can be displayed in case of + * error to help diagnose it. + * + * This is the head for a list of skbs. In the skb->cb member of the + * skb when queued here contains a 'struct i2400m_roq_data' were we + * store the sequence number (sn) and the cs (packet type) coming from + * the RX payload header from the device. + */ +struct i2400m_roq +{ + unsigned ws; + struct sk_buff_head queue; + struct i2400m_roq_log *log; +}; + + +static +void __i2400m_roq_init(struct i2400m_roq *roq) +{ + roq->ws = 0; + skb_queue_head_init(&roq->queue); +} + + +static +unsigned __i2400m_roq_index(struct i2400m *i2400m, struct i2400m_roq *roq) +{ + return ((unsigned long) roq - (unsigned long) i2400m->rx_roq) + / sizeof(*roq); +} + + +/* + * Normalize a sequence number based on the queue's window start + * + * nsn = (sn - ws) % 2048 + * + * Note that if @sn < @roq->ws, we still need a positive number; %'s + * sign is implementation specific, so we normalize it by adding 2048 + * to bring it to be positive. + */ +static +unsigned __i2400m_roq_nsn(struct i2400m_roq *roq, unsigned sn) +{ + int r; + r = ((int) sn - (int) roq->ws) % 2048; + if (r < 0) + r += 2048; + return r; +} + + +/* + * Circular buffer to keep the last N reorder operations + * + * In case something fails, dumb then to try to come up with what + * happened. + */ +enum { + I2400M_ROQ_LOG_LENGTH = 32, +}; + +struct i2400m_roq_log { + struct i2400m_roq_log_entry { + enum i2400m_ro_type type; + unsigned ws, count, sn, nsn, new_ws; + } entry[I2400M_ROQ_LOG_LENGTH]; + unsigned in, out; +}; + + +/* Print a log entry */ +static +void i2400m_roq_log_entry_print(struct i2400m *i2400m, unsigned index, + unsigned e_index, + struct i2400m_roq_log_entry *e) +{ + struct device *dev = i2400m_dev(i2400m); + + switch(e->type) { + case I2400M_RO_TYPE_RESET: + dev_err(dev, "q#%d reset ws %u cnt %u sn %u/%u" + " - new nws %u\n", + index, e->ws, e->count, e->sn, e->nsn, e->new_ws); + break; + case I2400M_RO_TYPE_PACKET: + dev_err(dev, "q#%d queue ws %u cnt %u sn %u/%u\n", + index, e->ws, e->count, e->sn, e->nsn); + break; + case I2400M_RO_TYPE_WS: + dev_err(dev, "q#%d update_ws ws %u cnt %u sn %u/%u" + " - new nws %u\n", + index, e->ws, e->count, e->sn, e->nsn, e->new_ws); + break; + case I2400M_RO_TYPE_PACKET_WS: + dev_err(dev, "q#%d queue_update_ws ws %u cnt %u sn %u/%u" + " - new nws %u\n", + index, e->ws, e->count, e->sn, e->nsn, e->new_ws); + break; + default: + dev_err(dev, "q#%d BUG? entry %u - unknown type %u\n", + index, e_index, e->type); + break; + } +} + + +static +void i2400m_roq_log_add(struct i2400m *i2400m, + struct i2400m_roq *roq, enum i2400m_ro_type type, + unsigned ws, unsigned count, unsigned sn, + unsigned nsn, unsigned new_ws) +{ + struct i2400m_roq_log_entry *e; + unsigned cnt_idx; + int index = __i2400m_roq_index(i2400m, roq); + + /* if we run out of space, we eat from the end */ + if (roq->log->in - roq->log->out == I2400M_ROQ_LOG_LENGTH) + roq->log->out++; + cnt_idx = roq->log->in++ % I2400M_ROQ_LOG_LENGTH; + e = &roq->log->entry[cnt_idx]; + + e->type = type; + e->ws = ws; + e->count = count; + e->sn = sn; + e->nsn = nsn; + e->new_ws = new_ws; + + if (d_test(1)) + i2400m_roq_log_entry_print(i2400m, index, cnt_idx, e); +} + + +/* Dump all the entries in the FIFO and reinitialize it */ +static +void i2400m_roq_log_dump(struct i2400m *i2400m, struct i2400m_roq *roq) +{ + unsigned cnt, cnt_idx; + struct i2400m_roq_log_entry *e; + int index = __i2400m_roq_index(i2400m, roq); + + BUG_ON(roq->log->out > roq->log->in); + for (cnt = roq->log->out; cnt < roq->log->in; cnt++) { + cnt_idx = cnt % I2400M_ROQ_LOG_LENGTH; + e = &roq->log->entry[cnt_idx]; + i2400m_roq_log_entry_print(i2400m, index, cnt_idx, e); + memset(e, 0, sizeof(*e)); + } + roq->log->in = roq->log->out = 0; +} + + +/* + * Backbone for the queuing of an skb (by normalized sequence number) + * + * @i2400m: device descriptor + * @roq: reorder queue where to add + * @skb: the skb to add + * @sn: the sequence number of the skb + * @nsn: the normalized sequence number of the skb (pre-computed by the + * caller from the @sn and @roq->ws). + * + * We try first a couple of quick cases: + * + * - the queue is empty + * - the skb would be appended to the queue + * + * These will be the most common operations. + * + * If these fail, then we have to do a sorted insertion in the queue, + * which is the slowest path. + * + * We don't have to acquire a reference count as we are going to own it. + */ +static +void __i2400m_roq_queue(struct i2400m *i2400m, struct i2400m_roq *roq, + struct sk_buff *skb, unsigned sn, unsigned nsn) +{ + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *skb_itr; + struct i2400m_roq_data *roq_data_itr, *roq_data; + unsigned nsn_itr; + + d_fnstart(4, dev, "(i2400m %p roq %p skb %p sn %u nsn %u)\n", + i2400m, roq, skb, sn, nsn); + + roq_data = (struct i2400m_roq_data *) &skb->cb; + BUILD_BUG_ON(sizeof(*roq_data) > sizeof(skb->cb)); + roq_data->sn = sn; + d_printf(3, dev, "ERX: roq %p [ws %u] nsn %d sn %u\n", + roq, roq->ws, nsn, roq_data->sn); + + /* Queues will be empty on not-so-bad environments, so try + * that first */ + if (skb_queue_empty(&roq->queue)) { + d_printf(2, dev, "ERX: roq %p - first one\n", roq); + __skb_queue_head(&roq->queue, skb); + goto out; + } + /* Now try append, as most of the operations will be that */ + skb_itr = skb_peek_tail(&roq->queue); + roq_data_itr = (struct i2400m_roq_data *) &skb_itr->cb; + nsn_itr = __i2400m_roq_nsn(roq, roq_data_itr->sn); + /* NSN bounds assumed correct (checked when it was queued) */ + if (nsn >= nsn_itr) { + d_printf(2, dev, "ERX: roq %p - appended after %p (nsn %d sn %u)\n", + roq, skb_itr, nsn_itr, roq_data_itr->sn); + __skb_queue_tail(&roq->queue, skb); + goto out; + } + /* None of the fast paths option worked. Iterate to find the + * right spot where to insert the packet; we know the queue is + * not empty, so we are not the first ones; we also know we + * are not going to be the last ones. The list is sorted, so + * we have to insert before the the first guy with an nsn_itr + * greater that our nsn. */ + skb_queue_walk(&roq->queue, skb_itr) { + roq_data_itr = (struct i2400m_roq_data *) &skb_itr->cb; + nsn_itr = __i2400m_roq_nsn(roq, roq_data_itr->sn); + /* NSN bounds assumed correct (checked when it was queued) */ + if (nsn_itr > nsn) { + d_printf(2, dev, "ERX: roq %p - queued before %p " + "(nsn %d sn %u)\n", roq, skb_itr, nsn_itr, + roq_data_itr->sn); + __skb_queue_before(&roq->queue, skb_itr, skb); + goto out; + } + } + /* If we get here, that is VERY bad -- print info to help + * diagnose and crash it */ + dev_err(dev, "SW BUG? failed to insert packet\n"); + dev_err(dev, "ERX: roq %p [ws %u] skb %p nsn %d sn %u\n", + roq, roq->ws, skb, nsn, roq_data->sn); + skb_queue_walk(&roq->queue, skb_itr) { + roq_data_itr = (struct i2400m_roq_data *) &skb_itr->cb; + nsn_itr = __i2400m_roq_nsn(roq, roq_data_itr->sn); + /* NSN bounds assumed correct (checked when it was queued) */ + dev_err(dev, "ERX: roq %p skb_itr %p nsn %d sn %u\n", + roq, skb_itr, nsn_itr, roq_data_itr->sn); + } + BUG(); +out: + d_fnend(4, dev, "(i2400m %p roq %p skb %p sn %u nsn %d) = void\n", + i2400m, roq, skb, sn, nsn); + return; +} + + +/* + * Backbone for the update window start operation + * + * @i2400m: device descriptor + * @roq: Reorder queue + * @sn: New sequence number + * + * Updates the window start of a queue; when doing so, it must deliver + * to the networking stack all the queued skb's whose normalized + * sequence number is lower than the new normalized window start. + */ +static +unsigned __i2400m_roq_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq, + unsigned sn) +{ + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *skb_itr, *tmp_itr; + struct i2400m_roq_data *roq_data_itr; + unsigned new_nws, nsn_itr; + + new_nws = __i2400m_roq_nsn(roq, sn); + if (unlikely(new_nws >= 1024) && d_test(1)) { + dev_err(dev, "SW BUG? __update_ws new_nws %u (sn %u ws %u)\n", + new_nws, sn, roq->ws); + WARN_ON(1); + i2400m_roq_log_dump(i2400m, roq); + } + skb_queue_walk_safe(&roq->queue, skb_itr, tmp_itr) { + roq_data_itr = (struct i2400m_roq_data *) &skb_itr->cb; + nsn_itr = __i2400m_roq_nsn(roq, roq_data_itr->sn); + /* NSN bounds assumed correct (checked when it was queued) */ + if (nsn_itr < new_nws) { + d_printf(2, dev, "ERX: roq %p - release skb %p " + "(nsn %u/%u new nws %u)\n", + roq, skb_itr, nsn_itr, roq_data_itr->sn, + new_nws); + __skb_unlink(skb_itr, &roq->queue); + i2400m_net_erx(i2400m, skb_itr, roq_data_itr->cs); + } + else + break; /* rest of packets all nsn_itr > nws */ + } + roq->ws = sn; + return new_nws; +} + + +/* + * Reset a queue + * + * @i2400m: device descriptor + * @cin: Queue Index + * + * Deliver all the packets and reset the window-start to zero. Name is + * kind of misleading. + */ +static +void i2400m_roq_reset(struct i2400m *i2400m, struct i2400m_roq *roq) +{ + struct device *dev = i2400m_dev(i2400m); + struct sk_buff *skb_itr, *tmp_itr; + struct i2400m_roq_data *roq_data_itr; + + d_fnstart(2, dev, "(i2400m %p roq %p)\n", i2400m, roq); + i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_RESET, + roq->ws, skb_queue_len(&roq->queue), + ~0, ~0, 0); + skb_queue_walk_safe(&roq->queue, skb_itr, tmp_itr) { + roq_data_itr = (struct i2400m_roq_data *) &skb_itr->cb; + d_printf(2, dev, "ERX: roq %p - release skb %p (sn %u)\n", + roq, skb_itr, roq_data_itr->sn); + __skb_unlink(skb_itr, &roq->queue); + i2400m_net_erx(i2400m, skb_itr, roq_data_itr->cs); + } + roq->ws = 0; + d_fnend(2, dev, "(i2400m %p roq %p) = void\n", i2400m, roq); + return; +} + + +/* + * Queue a packet + * + * @i2400m: device descriptor + * @cin: Queue Index + * @skb: containing the packet data + * @fbn: First block number of the packet in @skb + * @lbn: Last block number of the packet in @skb + * + * The hardware is asking the driver to queue a packet for later + * delivery to the networking stack. + */ +static +void i2400m_roq_queue(struct i2400m *i2400m, struct i2400m_roq *roq, + struct sk_buff * skb, unsigned lbn) +{ + struct device *dev = i2400m_dev(i2400m); + unsigned nsn, len; + + d_fnstart(2, dev, "(i2400m %p roq %p skb %p lbn %u) = void\n", + i2400m, roq, skb, lbn); + len = skb_queue_len(&roq->queue); + nsn = __i2400m_roq_nsn(roq, lbn); + if (unlikely(nsn >= 1024)) { + dev_err(dev, "SW BUG? queue nsn %d (lbn %u ws %u)\n", + nsn, lbn, roq->ws); + i2400m_roq_log_dump(i2400m, roq); + i2400m->bus_reset(i2400m, I2400M_RT_WARM); + } else { + __i2400m_roq_queue(i2400m, roq, skb, lbn, nsn); + i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_PACKET, + roq->ws, len, lbn, nsn, ~0); + } + d_fnend(2, dev, "(i2400m %p roq %p skb %p lbn %u) = void\n", + i2400m, roq, skb, lbn); + return; +} + + +/* + * Update the window start in a reorder queue and deliver all skbs + * with a lower window start + * + * @i2400m: device descriptor + * @roq: Reorder queue + * @sn: New sequence number + */ +static +void i2400m_roq_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq, + unsigned sn) +{ + struct device *dev = i2400m_dev(i2400m); + unsigned old_ws, nsn, len; + + d_fnstart(2, dev, "(i2400m %p roq %p sn %u)\n", i2400m, roq, sn); + old_ws = roq->ws; + len = skb_queue_len(&roq->queue); + nsn = __i2400m_roq_update_ws(i2400m, roq, sn); + i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_WS, + old_ws, len, sn, nsn, roq->ws); + d_fnstart(2, dev, "(i2400m %p roq %p sn %u) = void\n", i2400m, roq, sn); + return; +} + + +/* + * Queue a packet and update the window start + * + * @i2400m: device descriptor + * @cin: Queue Index + * @skb: containing the packet data + * @fbn: First block number of the packet in @skb + * @sn: Last block number of the packet in @skb + * + * Note that unlike i2400m_roq_update_ws(), which sets the new window + * start to @sn, in here we'll set it to @sn + 1. + */ +static +void i2400m_roq_queue_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq, + struct sk_buff * skb, unsigned sn) +{ + struct device *dev = i2400m_dev(i2400m); + unsigned nsn, old_ws, len; + + d_fnstart(2, dev, "(i2400m %p roq %p skb %p sn %u)\n", + i2400m, roq, skb, sn); + len = skb_queue_len(&roq->queue); + nsn = __i2400m_roq_nsn(roq, sn); + old_ws = roq->ws; + if (unlikely(nsn >= 1024)) { + dev_err(dev, "SW BUG? queue_update_ws nsn %u (sn %u ws %u)\n", + nsn, sn, roq->ws); + i2400m_roq_log_dump(i2400m, roq); + i2400m->bus_reset(i2400m, I2400M_RT_WARM); + } else { + /* if the queue is empty, don't bother as we'd queue + * it and inmediately unqueue it -- just deliver it */ + if (len == 0) { + struct i2400m_roq_data *roq_data; + roq_data = (struct i2400m_roq_data *) &skb->cb; + i2400m_net_erx(i2400m, skb, roq_data->cs); + } + else { + __i2400m_roq_queue(i2400m, roq, skb, sn, nsn); + __i2400m_roq_update_ws(i2400m, roq, sn + 1); + } + i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_PACKET_WS, + old_ws, len, sn, nsn, roq->ws); + } + d_fnend(2, dev, "(i2400m %p roq %p skb %p sn %u) = void\n", + i2400m, roq, skb, sn); + return; +} + + /* * Receive and send up an extended data packet * @@ -347,6 +849,28 @@ error_check: * having to copy packets around. * * This function handles said path. + * + * + * Receive and send up an extended data packet that requires no reordering + * + * @i2400m: device descriptor + * @skb_rx: skb that contains the extended data packet + * @single_last: 1 if the payload is the only one or the last one of + * the skb. + * @payload: pointer to the packet's data (past the actual extended + * data payload header). + * @size: size of the payload + * + * Pass over to the networking stack a data packet that might have + * reordering requirements. + * + * This needs to the decide if the skb in which the packet is + * contained can be reused or if it needs to be cloned. Then it has to + * be trimmed in the edges so that the beginning is the space for eth + * header and then pass it to i2400m_net_erx() for the stack + * + * Assumes the caller has verified the sanity of the payload (size, + * etc) already. */ static void i2400m_rx_edata(struct i2400m *i2400m, struct sk_buff *skb_rx, @@ -357,53 +881,86 @@ void i2400m_rx_edata(struct i2400m *i2400m, struct sk_buff *skb_rx, struct net_device *net_dev = i2400m->wimax_dev.net_dev; struct sk_buff *skb; enum i2400m_cs cs; - unsigned reorder_needed; + u32 reorder; + unsigned ro_needed, ro_type, ro_cin, ro_sn; + struct i2400m_roq *roq; + struct i2400m_roq_data *roq_data; - d_fnstart(4, dev, "(i2400m %p skb_rx %p single %u payload %p " + BUILD_BUG_ON(ETH_HLEN > sizeof(*hdr)); + + d_fnstart(2, dev, "(i2400m %p skb_rx %p single %u payload %p " "size %zu)\n", i2400m, skb_rx, single_last, payload, size); if (size < sizeof(*hdr)) { dev_err(dev, "ERX: HW BUG? message with short header (%zu " "vs %zu bytes expected)\n", size, sizeof(*hdr)); goto error; } - reorder_needed = le32_to_cpu(hdr->reorder & I2400M_REORDER_NEEDED); - cs = hdr->cs; - if (reorder_needed) { - dev_err(dev, "ERX: HW BUG? reorder needed, it was disabled\n"); - goto error; - } - /* ok, so now decide if we want to clone or reuse the skb, - * pull and trim it so the beginning is the space for the eth - * header and pass it to i2400m_net_erx() for the stack */ + if (single_last) { skb = skb_get(skb_rx); - d_printf(3, dev, "ERX: reusing single payload skb %p\n", skb); + d_printf(3, dev, "ERX: skb %p reusing\n", skb); } else { skb = skb_clone(skb_rx, GFP_KERNEL); - d_printf(3, dev, "ERX: cloning %p\n", skb); if (skb == NULL) { dev_err(dev, "ERX: no memory to clone skb\n"); net_dev->stats.rx_dropped++; goto error_skb_clone; } + d_printf(3, dev, "ERX: skb %p cloned from %p\n", skb, skb_rx); } /* now we have to pull and trim so that the skb points to the * beginning of the IP packet; the netdev part will add the - * ethernet header as needed. */ - BUILD_BUG_ON(ETH_HLEN > sizeof(*hdr)); + * ethernet header as needed - we know there is enough space + * because we checked in i2400m_rx_edata(). */ skb_pull(skb, payload + sizeof(*hdr) - (void *) skb->data); - skb_trim(skb, (void *) skb_end_pointer(skb) - payload + sizeof(*hdr)); - i2400m_net_erx(i2400m, skb, cs); + skb_trim(skb, (void *) skb_end_pointer(skb) - payload - sizeof(*hdr)); + + reorder = le32_to_cpu(hdr->reorder); + ro_needed = reorder & I2400M_RO_NEEDED; + cs = hdr->cs; + if (ro_needed) { + ro_type = (reorder >> I2400M_RO_TYPE_SHIFT) & I2400M_RO_TYPE; + ro_cin = (reorder >> I2400M_RO_CIN_SHIFT) & I2400M_RO_CIN; + ro_sn = (reorder >> I2400M_RO_SN_SHIFT) & I2400M_RO_SN; + + roq = &i2400m->rx_roq[ro_cin]; + roq_data = (struct i2400m_roq_data *) &skb->cb; + roq_data->sn = ro_sn; + roq_data->cs = cs; + d_printf(2, dev, "ERX: reorder needed: " + "type %u cin %u [ws %u] sn %u/%u len %zuB\n", + ro_type, ro_cin, roq->ws, ro_sn, + __i2400m_roq_nsn(roq, ro_sn), size); + d_dump(2, dev, payload, size); + switch(ro_type) { + case I2400M_RO_TYPE_RESET: + i2400m_roq_reset(i2400m, roq); + kfree_skb(skb); /* no data here */ + break; + case I2400M_RO_TYPE_PACKET: + i2400m_roq_queue(i2400m, roq, skb, ro_sn); + break; + case I2400M_RO_TYPE_WS: + i2400m_roq_update_ws(i2400m, roq, ro_sn); + kfree_skb(skb); /* no data here */ + break; + case I2400M_RO_TYPE_PACKET_WS: + i2400m_roq_queue_update_ws(i2400m, roq, skb, ro_sn); + break; + default: + dev_err(dev, "HW BUG? unknown reorder type %u\n", ro_type); + } + } + else + i2400m_net_erx(i2400m, skb, cs); error_skb_clone: error: - d_fnend(4, dev, "(i2400m %p skb_rx %p single %u payload %p " + d_fnend(2, dev, "(i2400m %p skb_rx %p single %u payload %p " "size %zu) = void\n", i2400m, skb_rx, single_last, payload, size); return; } - - /* * Act on a received payload * @@ -632,3 +1189,73 @@ error_msg_hdr_check: return result; } EXPORT_SYMBOL_GPL(i2400m_rx); + + +/* + * Initialize the RX queue and infrastructure + * + * This sets up all the RX reordering infrastructures, which will not + * be used if reordering is not enabled or if the firmware does not + * support it. The device is told to do reordering in + * i2400m_dev_initialize(), where it also looks at the value of the + * i2400m->rx_reorder switch before taking a decission. + * + * Note we allocate the roq queues in one chunk and the actual logging + * support for it (logging) in another one and then we setup the + * pointers from the first to the last. + */ +int i2400m_rx_setup(struct i2400m *i2400m) +{ + int result = 0; + struct device *dev = i2400m_dev(i2400m); + + i2400m->rx_reorder = i2400m_rx_reorder_disabled? 0 : 1; + if (i2400m->rx_reorder) { + unsigned itr; + size_t size; + struct i2400m_roq_log *rd; + + result = -ENOMEM; + + size = sizeof(i2400m->rx_roq[0]) * (I2400M_RO_CIN + 1); + i2400m->rx_roq = kzalloc(size, GFP_KERNEL); + if (i2400m->rx_roq == NULL) { + dev_err(dev, "RX: cannot allocate %zu bytes for " + "reorder queues\n", size); + goto error_roq_alloc; + } + + size = sizeof(*i2400m->rx_roq[0].log) * (I2400M_RO_CIN + 1); + rd = kzalloc(size, GFP_KERNEL); + if (rd == NULL) { + dev_err(dev, "RX: cannot allocate %zu bytes for " + "reorder queues log areas\n", size); + result = -ENOMEM; + goto error_roq_log_alloc; + } + + for(itr = 0; itr < I2400M_RO_CIN + 1; itr++) { + __i2400m_roq_init(&i2400m->rx_roq[itr]); + i2400m->rx_roq[itr].log = &rd[itr]; + } + } + return 0; + +error_roq_log_alloc: + kfree(i2400m->rx_roq); +error_roq_alloc: + return result; +} + + +/* Tear down the RX queue and infrastructure */ +void i2400m_rx_release(struct i2400m *i2400m) +{ + if (i2400m->rx_reorder) { + unsigned itr; + for(itr = 0; itr < I2400M_RO_CIN + 1; itr++) + __skb_queue_purge(&i2400m->rx_roq[itr].queue); + kfree(i2400m->rx_roq[0].log); + kfree(i2400m->rx_roq); + } +} diff --git a/include/linux/wimax/i2400m.h b/include/linux/wimax/i2400m.h index ad36e073a70c..d5148a7889a6 100644 --- a/include/linux/wimax/i2400m.h +++ b/include/linux/wimax/i2400m.h @@ -225,15 +225,16 @@ struct i2400m_pl_data_hdr { /* * Payload for an extended data packet * - * New in v1.4 + * New in fw v1.4 * + * @reorder: if this payload has to be reorder or not (and how) * @cs: the type of data in the packet, as defined per (802.16e * T11.13.19.1). Currently only 2 (IPv4 packet) supported. * * This is prefixed to each and every INCOMING DATA packet. */ struct i2400m_pl_edata_hdr { - __le32 reorder; + __le32 reorder; /* bits defined in i2400m_ro */ __u8 cs; __u8 reserved[11]; } __attribute__((packed)); @@ -243,8 +244,23 @@ enum i2400m_cs { I2400M_CS_IPV4 = 2, }; -enum i2400m_reorder { - I2400M_REORDER_NEEDED = 0x01, +enum i2400m_ro { + I2400M_RO_NEEDED = 0x01, + I2400M_RO_TYPE = 0x03, + I2400M_RO_TYPE_SHIFT = 1, + I2400M_RO_CIN = 0x0f, + I2400M_RO_CIN_SHIFT = 4, + I2400M_RO_FBN = 0x07ff, + I2400M_RO_FBN_SHIFT = 8, + I2400M_RO_SN = 0x07ff, + I2400M_RO_SN_SHIFT = 21, +}; + +enum i2400m_ro_type { + I2400M_RO_TYPE_RESET = 0, + I2400M_RO_TYPE_PACKET, + I2400M_RO_TYPE_WS, + I2400M_RO_TYPE_PACKET_WS, }; @@ -410,6 +426,7 @@ enum i2400m_tlv { I2400M_TLV_CONFIG_IDLE_PARAMETERS = 601, I2400M_TLV_CONFIG_IDLE_TIMEOUT = 611, I2400M_TLV_CONFIG_D2H_DATA_FORMAT = 614, + I2400M_TLV_CONFIG_DL_HOST_REORDER = 615, }; @@ -553,5 +570,12 @@ struct i2400m_tlv_config_d2h_data_format { __u8 reserved[3]; } __attribute__((packed)); +/* New in v1.4 */ +struct i2400m_tlv_config_dl_host_reorder { + struct i2400m_tlv_hdr hdr; + __u8 reorder; /* 0 disabled, 1 enabled */ + __u8 reserved[3]; +} __attribute__((packed)); + #endif /* #ifndef __LINUX__WIMAX__I2400M_H__ */ -- cgit v1.2.3 From d3a21be86c178964167aa54c39a01260d33e7509 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 2 Mar 2009 03:15:58 -0800 Subject: skbuff.h: fix timestamps kernel-doc Fix skbuff.h kernel-doc for timestamps: must include "struct" keyword, otherwise there are kernel-doc errors: Error(linux-next-20090227//include/linux/skbuff.h:161): cannot understand prototype: 'struct skb_shared_hwtstamps ' Error(linux-next-20090227//include/linux/skbuff.h:177): cannot understand prototype: 'union skb_shared_tx ' Signed-off-by: Randy Dunlap Signed-off-by: David S. Miller --- include/linux/skbuff.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 61ce97a8b868..1f659e8c2b88 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -135,8 +135,7 @@ struct skb_frag_struct { #define HAVE_HW_TIME_STAMP /** - * skb_shared_hwtstamps - hardware time stamps - * + * struct skb_shared_hwtstamps - hardware time stamps * @hwtstamp: hardware time stamp transformed into duration * since arbitrary point in time * @syststamp: hwtstamp transformed to system time base @@ -164,8 +163,7 @@ struct skb_shared_hwtstamps { }; /** - * skb_shared_tx - instructions for time stamping of outgoing packets - * + * struct skb_shared_tx - instructions for time stamping of outgoing packets * @hardware: generate hardware time stamp * @software: generate software time stamp * @in_progress: device driver is going to provide -- cgit v1.2.3 From 0c5c2d3089068d4aa378f7a40d2b5ad9d4f52ce8 Mon Sep 17 00:00:00 2001 From: Eric Biederman Date: Wed, 4 Mar 2009 00:03:08 -0800 Subject: neigh: Allow for user space users of the neighbour table Currently it is possible to do just about everything with the arp table from user space except treat an entry like you are using it. To that end implement and a flag NTF_USE that when set in a netwlink update request treats the neighbour table entry like the kernel does on the output path. This allows user space applications to share the kernel's arp cache. Signed-off-by: Eric Biederman Signed-off-by: David S. Miller --- include/linux/neighbour.h | 1 + net/core/neighbour.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/neighbour.h b/include/linux/neighbour.h index 8730d5dae1bc..12c9de138451 100644 --- a/include/linux/neighbour.h +++ b/include/linux/neighbour.h @@ -31,6 +31,7 @@ enum * Neighbor Cache Entry Flags */ +#define NTF_USE 0x01 #define NTF_PROXY 0x08 /* == ATF_PUBL */ #define NTF_ROUTER 0x80 diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 417b6d739fb7..a1cbce7fdae5 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1654,7 +1654,11 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) flags &= ~NEIGH_UPDATE_F_OVERRIDE; } - err = neigh_update(neigh, lladdr, ndm->ndm_state, flags); + if (ndm->ndm_flags & NTF_USE) { + neigh_event_send(neigh, NULL); + err = 0; + } else + err = neigh_update(neigh, lladdr, ndm->ndm_state, flags); neigh_release(neigh); goto out_dev_put; } -- cgit v1.2.3 From e79c1ba84c68de9161d541bd2bcc8ea65c89955c Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 27 Feb 2009 16:59:05 +0100 Subject: ssb: Add SPROM fallback support This adds SSB functionality to register a fallback SPROM image from the architecture setup code. Weird architectures exist that have half-assed SSB devices without SPROM attached to their PCI busses. The architecture can register a fallback SPROM image that is used if no SPROM is found on the SSB device. Signed-off-by: Michael Buesch Cc: Florian Fainelli Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 14 +++++++++++++- drivers/ssb/sprom.c | 36 ++++++++++++++++++++++++++++++++++++ drivers/ssb/ssb_private.h | 1 + include/linux/ssb/ssb.h | 4 ++++ 4 files changed, 54 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index c958ac16423c..40ea41762247 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -564,6 +564,7 @@ static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, static int ssb_pci_sprom_get(struct ssb_bus *bus, struct ssb_sprom *sprom) { + const struct ssb_sprom *fallback; int err = -ENOMEM; u16 *buf; @@ -583,12 +584,23 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus, bus->sprom_size = SSB_SPROMSIZE_WORDS_R4; sprom_do_read(bus, buf); err = sprom_check_crc(buf, bus->sprom_size); - if (err) + if (err) { + /* All CRC attempts failed. + * Maybe there is no SPROM on the device? + * If we have a fallback, use that. */ + fallback = ssb_get_fallback_sprom(); + if (fallback) { + memcpy(sprom, fallback, sizeof(*sprom)); + err = 0; + goto out_free; + } ssb_printk(KERN_WARNING PFX "WARNING: Invalid" " SPROM CRC (corrupt SPROM)\n"); + } } err = sprom_extract(bus, sprom, buf, bus->sprom_size); +out_free: kfree(buf); out: return err; diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c index 3668edb39315..8943015a3eef 100644 --- a/drivers/ssb/sprom.c +++ b/drivers/ssb/sprom.c @@ -14,6 +14,9 @@ #include "ssb_private.h" +static const struct ssb_sprom *fallback_sprom; + + static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, size_t sprom_size_words) { @@ -131,3 +134,36 @@ out: return res; return err ? err : count; } + +/** + * ssb_arch_set_fallback_sprom - Set a fallback SPROM for use if no SPROM is found. + * + * @sprom: The SPROM data structure to register. + * + * With this function the architecture implementation may register a fallback + * SPROM data structure. The fallback is only used for PCI based SSB devices, + * where no valid SPROM can be found in the shadow registers. + * + * This function is useful for weird architectures that have a half-assed SSB device + * hardwired to their PCI bus. + * + * Note that it does only work with PCI attached SSB devices. PCMCIA devices currently + * don't use this fallback. + * Architectures must provide the SPROM for native SSB devices anyway, + * so the fallback also isn't used for native devices. + * + * This function is available for architecture code, only. So it is not exported. + */ +int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom) +{ + if (fallback_sprom) + return -EEXIST; + fallback_sprom = sprom; + + return 0; +} + +const struct ssb_sprom *ssb_get_fallback_sprom(void) +{ + return fallback_sprom; +} diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index ebc32d8fe15f..57fa482abb94 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -131,6 +131,7 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, const char *buf, size_t count, int (*sprom_check_crc)(const u16 *sprom, size_t size), int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom)); +extern const struct ssb_sprom *ssb_get_fallback_sprom(void); /* core.c */ diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 17d9b58f6379..5ae8fa22d331 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -339,6 +339,10 @@ extern int ssb_bus_pcmciabus_register(struct ssb_bus *bus, extern void ssb_bus_unregister(struct ssb_bus *bus); +/* Set a fallback SPROM. + * See kdoc at the function definition for complete documentation. */ +extern int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom); + /* Suspend a SSB bus. * Call this from the parent bus suspend routine. */ extern int ssb_bus_suspend(struct ssb_bus *bus); -- cgit v1.2.3 From ead2ceb0ec9f85cff19c43b5cdb2f8a054484431 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Wed, 11 Mar 2009 09:49:55 +0000 Subject: Network Drop Monitor: Adding kfree_skb_clean for non-drops and modifying end-of-line points for skbs Signed-off-by: Neil Horman include/linux/skbuff.h | 4 +++- net/core/datagram.c | 2 +- net/core/skbuff.c | 22 ++++++++++++++++++++++ net/ipv4/arp.c | 2 +- net/ipv4/udp.c | 2 +- net/packet/af_packet.c | 2 +- 6 files changed, 29 insertions(+), 5 deletions(-) Signed-off-by: David S. Miller --- include/linux/skbuff.h | 4 +++- net/core/datagram.c | 2 +- net/core/skbuff.c | 22 ++++++++++++++++++++++ net/ipv4/arp.c | 2 +- net/ipv4/udp.c | 2 +- net/packet/af_packet.c | 2 +- 6 files changed, 29 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 1f659e8c2b88..1fbab2ae613c 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -421,6 +421,7 @@ extern void skb_dma_unmap(struct device *dev, struct sk_buff *skb, #endif extern void kfree_skb(struct sk_buff *skb); +extern void consume_skb(struct sk_buff *skb); extern void __kfree_skb(struct sk_buff *skb); extern struct sk_buff *__alloc_skb(unsigned int size, gfp_t priority, int fclone, int node); @@ -459,7 +460,8 @@ extern int skb_to_sgvec(struct sk_buff *skb, extern int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer); extern int skb_pad(struct sk_buff *skb, int pad); -#define dev_kfree_skb(a) kfree_skb(a) +#define dev_kfree_skb(a) consume_skb(a) +#define dev_consume_skb(a) kfree_skb_clean(a) extern void skb_over_panic(struct sk_buff *skb, int len, void *here); extern void skb_under_panic(struct sk_buff *skb, int len, diff --git a/net/core/datagram.c b/net/core/datagram.c index 5e2ac0c4b07c..d0de644b378d 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -208,7 +208,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, void skb_free_datagram(struct sock *sk, struct sk_buff *skb) { - kfree_skb(skb); + consume_skb(skb); sk_mem_reclaim_partial(sk); } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index e5e2111a397d..6acbf9e79eb1 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -65,6 +65,7 @@ #include #include +#include #include "kmap_skb.h" @@ -442,10 +443,31 @@ void kfree_skb(struct sk_buff *skb) smp_rmb(); else if (likely(!atomic_dec_and_test(&skb->users))) return; + trace_kfree_skb(skb, __builtin_return_address(0)); __kfree_skb(skb); } EXPORT_SYMBOL(kfree_skb); +/** + * consume_skb - free an skbuff + * @skb: buffer to free + * + * Drop a ref to the buffer and free it if the usage count has hit zero + * Functions identically to kfree_skb, but kfree_skb assumes that the frame + * is being dropped after a failure and notes that + */ +void consume_skb(struct sk_buff *skb) +{ + if (unlikely(!skb)) + return; + if (likely(atomic_read(&skb->users) == 1)) + smp_rmb(); + else if (likely(!atomic_dec_and_test(&skb->users))) + return; + __kfree_skb(skb); +} +EXPORT_SYMBOL(consume_skb); + /** * skb_recycle_check - check if skb can be reused for receive * @skb: buffer diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 3d67d1ffed77..9c220323f353 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -892,7 +892,7 @@ static int arp_process(struct sk_buff *skb) out: if (in_dev) in_dev_put(in_dev); - kfree_skb(skb); + consume_skb(skb); return 0; } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 4bd178a111d5..05b7abb99f69 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1184,7 +1184,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, sk = sknext; } while (sknext); } else - kfree_skb(skb); + consume_skb(skb); spin_unlock(&hslot->lock); return 0; } diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index d8cc006fac45..74776de523ec 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -584,7 +584,7 @@ drop_n_restore: skb->len = skb_len; } drop: - kfree_skb(skb); + consume_skb(skb); return 0; } -- cgit v1.2.3 From 9a8afc8d3962f3ed26fd6b56db34133860ed1e72 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Wed, 11 Mar 2009 09:51:26 +0000 Subject: Network Drop Monitor: Adding drop monitor implementation & Netlink protocol Signed-off-by: Neil Horman include/linux/net_dropmon.h | 56 +++++++++ net/core/drop_monitor.c | 263 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+) Signed-off-by: David S. Miller --- include/linux/net_dropmon.h | 56 ++++++++++ net/core/drop_monitor.c | 263 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 include/linux/net_dropmon.h create mode 100644 net/core/drop_monitor.c (limited to 'include/linux') diff --git a/include/linux/net_dropmon.h b/include/linux/net_dropmon.h new file mode 100644 index 000000000000..0217fb81a630 --- /dev/null +++ b/include/linux/net_dropmon.h @@ -0,0 +1,56 @@ +#ifndef __NET_DROPMON_H +#define __NET_DROPMON_H + +#include + +struct net_dm_drop_point { + __u8 pc[8]; + __u32 count; +}; + +#define NET_DM_CFG_VERSION 0 +#define NET_DM_CFG_ALERT_COUNT 1 +#define NET_DM_CFG_ALERT_DELAY 2 +#define NET_DM_CFG_MAX 3 + +struct net_dm_config_entry { + __u32 type; + __u64 data __attribute__((aligned(8))); +}; + +struct net_dm_config_msg { + __u32 entries; + struct net_dm_config_entry options[0]; +}; + +struct net_dm_alert_msg { + __u32 entries; + struct net_dm_drop_point points[0]; +}; + +struct net_dm_user_msg { + union { + struct net_dm_config_msg user; + struct net_dm_alert_msg alert; + } u; +}; + + +/* These are the netlink message types for this protocol */ + +enum { + NET_DM_CMD_UNSPEC = 0, + NET_DM_CMD_ALERT, + NET_DM_CMD_CONFIG, + NET_DM_CMD_START, + NET_DM_CMD_STOP, + _NET_DM_CMD_MAX, +}; + +#define NET_DM_CMD_MAX (_NET_DM_CMD_MAX - 1) + +/* + * Our group identifiers + */ +#define NET_DM_GRP_ALERT 1 +#endif diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c new file mode 100644 index 000000000000..9fd0dc3cca99 --- /dev/null +++ b/net/core/drop_monitor.c @@ -0,0 +1,263 @@ +/* + * Monitoring code for network dropped packet alerts + * + * Copyright (C) 2009 Neil Horman + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define TRACE_ON 1 +#define TRACE_OFF 0 + +static void send_dm_alert(struct work_struct *unused); + + +/* + * Globals, our netlink socket pointer + * and the work handle that will send up + * netlink alerts + */ +struct sock *dm_sock; + +struct per_cpu_dm_data { + struct work_struct dm_alert_work; + struct sk_buff *skb; + atomic_t dm_hit_count; + struct timer_list send_timer; +}; + +static struct genl_family net_drop_monitor_family = { + .id = GENL_ID_GENERATE, + .hdrsize = 0, + .name = "NET_DM", + .version = 1, + .maxattr = NET_DM_CMD_MAX, +}; + +static DEFINE_PER_CPU(struct per_cpu_dm_data, dm_cpu_data); + +static int dm_hit_limit = 64; +static int dm_delay = 1; + + +static void reset_per_cpu_data(struct per_cpu_dm_data *data) +{ + size_t al; + struct net_dm_alert_msg *msg; + + al = sizeof(struct net_dm_alert_msg); + al += dm_hit_limit * sizeof(struct net_dm_drop_point); + data->skb = genlmsg_new(al, GFP_KERNEL); + genlmsg_put(data->skb, 0, 0, &net_drop_monitor_family, + 0, NET_DM_CMD_ALERT); + msg = __nla_reserve_nohdr(data->skb, sizeof(struct net_dm_alert_msg)); + memset(msg, 0, al); + atomic_set(&data->dm_hit_count, dm_hit_limit); +} + +static void send_dm_alert(struct work_struct *unused) +{ + struct sk_buff *skb; + struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data); + + /* + * Grab the skb we're about to send + */ + skb = data->skb; + + /* + * Replace it with a new one + */ + reset_per_cpu_data(data); + + /* + * Ship it! + */ + genlmsg_multicast(skb, 0, NET_DM_GRP_ALERT, GFP_KERNEL); + +} + +/* + * This is the timer function to delay the sending of an alert + * in the event that more drops will arrive during the + * hysteresis period. Note that it operates under the timer interrupt + * so we don't need to disable preemption here + */ +static void sched_send_work(unsigned long unused) +{ + struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data); + + schedule_work(&data->dm_alert_work); +} + +static void trace_kfree_skb_hit(struct sk_buff *skb, void *location) +{ + struct net_dm_alert_msg *msg; + struct nlmsghdr *nlh; + int i; + struct per_cpu_dm_data *data = &__get_cpu_var(dm_cpu_data); + + + if (!atomic_add_unless(&data->dm_hit_count, -1, 0)) { + /* + * we're already at zero, discard this hit + */ + goto out; + } + + nlh = (struct nlmsghdr *)data->skb->data; + msg = genlmsg_data(nlmsg_data(nlh)); + for (i = 0; i < msg->entries; i++) { + if (!memcmp(&location, msg->points[i].pc, sizeof(void *))) { + msg->points[i].count++; + goto out; + } + } + + /* + * We need to create a new entry + */ + __nla_reserve_nohdr(data->skb, sizeof(struct net_dm_drop_point)); + memcpy(msg->points[msg->entries].pc, &location, sizeof(void *)); + msg->points[msg->entries].count = 1; + msg->entries++; + + if (!timer_pending(&data->send_timer)) { + data->send_timer.expires = jiffies + dm_delay * HZ; + add_timer_on(&data->send_timer, smp_processor_id()); + } + +out: + return; +} + +static int set_all_monitor_traces(int state) +{ + int rc = 0; + + switch (state) { + case TRACE_ON: + rc |= register_trace_kfree_skb(trace_kfree_skb_hit); + break; + case TRACE_OFF: + rc |= unregister_trace_kfree_skb(trace_kfree_skb_hit); + + tracepoint_synchronize_unregister(); + break; + default: + rc = 1; + break; + } + + if (rc) + return -EINPROGRESS; + return rc; +} + + +static int net_dm_cmd_config(struct sk_buff *skb, + struct genl_info *info) +{ + return -ENOTSUPP; +} + +static int net_dm_cmd_trace(struct sk_buff *skb, + struct genl_info *info) +{ + switch (info->genlhdr->cmd) { + case NET_DM_CMD_START: + return set_all_monitor_traces(TRACE_ON); + break; + case NET_DM_CMD_STOP: + return set_all_monitor_traces(TRACE_OFF); + break; + } + + return -ENOTSUPP; +} + + +static struct genl_ops dropmon_ops[] = { + { + .cmd = NET_DM_CMD_CONFIG, + .doit = net_dm_cmd_config, + }, + { + .cmd = NET_DM_CMD_START, + .doit = net_dm_cmd_trace, + }, + { + .cmd = NET_DM_CMD_STOP, + .doit = net_dm_cmd_trace, + }, +}; + +static int __init init_net_drop_monitor(void) +{ + int cpu; + int rc, i, ret; + struct per_cpu_dm_data *data; + printk(KERN_INFO "Initalizing network drop monitor service\n"); + + if (sizeof(void *) > 8) { + printk(KERN_ERR "Unable to store program counters on this arch, Drop monitor failed\n"); + return -ENOSPC; + } + + if (genl_register_family(&net_drop_monitor_family) < 0) { + printk(KERN_ERR "Could not create drop monitor netlink family\n"); + return -EFAULT; + } + + rc = -EFAULT; + + for (i = 0; i < ARRAY_SIZE(dropmon_ops); i++) { + ret = genl_register_ops(&net_drop_monitor_family, + &dropmon_ops[i]); + if (ret) { + printk(KERN_CRIT "failed to register operation %d\n", + dropmon_ops[i].cmd); + goto out_unreg; + } + } + + rc = 0; + + for_each_present_cpu(cpu) { + data = &per_cpu(dm_cpu_data, cpu); + reset_per_cpu_data(data); + INIT_WORK(&data->dm_alert_work, send_dm_alert); + init_timer(&data->send_timer); + data->send_timer.data = cpu; + data->send_timer.function = sched_send_work; + } + goto out; + +out_unreg: + genl_unregister_family(&net_drop_monitor_family); +out: + return rc; +} + +late_initcall(init_net_drop_monitor); -- cgit v1.2.3 From 273ae44b9cb9443e0b5265cdc99f127ddb95c8db Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Wed, 11 Mar 2009 09:53:16 +0000 Subject: Network Drop Monitor: Adding Build changes to enable drop monitor Network Drop Monitor: Adding Build changes to enable drop monitor Signed-off-by: Neil Horman include/linux/Kbuild | 1 + net/Kconfig | 11 +++++++++++ net/core/Makefile | 1 + 3 files changed, 13 insertions(+) Signed-off-by: David S. Miller --- include/linux/Kbuild | 1 + net/Kconfig | 11 +++++++++++ net/core/Makefile | 1 + 3 files changed, 13 insertions(+) (limited to 'include/linux') diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 106c3ba50844..e9581fd9fb66 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -115,6 +115,7 @@ header-y += mqueue.h header-y += mtio.h header-y += ncp_no.h header-y += neighbour.h +header-y += net_dropmon.h header-y += netfilter_arp.h header-y += netrom.h header-y += nfs2.h diff --git a/net/Kconfig b/net/Kconfig index 6b39ede3b1b1..c9fdcd7e71ea 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -222,6 +222,17 @@ config NET_TCPPROBE To compile this code as a module, choose M here: the module will be called tcp_probe. +config NET_DROP_MONITOR + boolean "Network packet drop alerting service" + depends on INET && EXPERIMENTAL && TRACEPOINTS + ---help--- + This feature provides an alerting service to userspace in the + event that packets are discarded in the network stack. Alerts + are broadcast via netlink socket to any listening user space + process. If you don't need network drop alerts, or if you are ok + just checking the various proc files and other utilities for + drop statistics, say N here. + endmenu endmenu diff --git a/net/core/Makefile b/net/core/Makefile index d47092bc525c..796f46eece5f 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -18,4 +18,5 @@ obj-$(CONFIG_NETPOLL) += netpoll.o obj-$(CONFIG_NET_DMA) += user_dma.o obj-$(CONFIG_FIB_RULES) += fib_rules.o obj-$(CONFIG_TRACEPOINTS) += net-traces.o +obj-$(CONFIG_NET_DROP_MONITOR) += drop_monitor.o -- cgit v1.2.3 From a390d1f379cf821248b735f43d2e1147ebb8241d Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Fri, 13 Mar 2009 15:41:19 -0700 Subject: phylib: convert state_queue work to delayed_work It closes a race in phy_stop_machine when reprogramming of phy_timer (from phy_state_machine) happens between del_timer_sync and cancel_work_sync. Without this change it could lead to crash if phy_device would be freed after phy_stop_machine (timer would fire and schedule freed work). Signed-off-by: Marcin Slusarz Acked-by: Jean Delvare Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 41 +++++++++++------------------------------ include/linux/phy.h | 3 +-- 2 files changed, 12 insertions(+), 32 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index e4ede6080c9d..58b73b08dde0 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -414,7 +414,6 @@ EXPORT_SYMBOL(phy_start_aneg); static void phy_change(struct work_struct *work); static void phy_state_machine(struct work_struct *work); -static void phy_timer(unsigned long data); /** * phy_start_machine - start PHY state machine tracking @@ -434,11 +433,8 @@ void phy_start_machine(struct phy_device *phydev, { phydev->adjust_state = handler; - INIT_WORK(&phydev->state_queue, phy_state_machine); - init_timer(&phydev->phy_timer); - phydev->phy_timer.function = &phy_timer; - phydev->phy_timer.data = (unsigned long) phydev; - mod_timer(&phydev->phy_timer, jiffies + HZ); + INIT_DELAYED_WORK(&phydev->state_queue, phy_state_machine); + schedule_delayed_work(&phydev->state_queue, jiffies + HZ); } /** @@ -451,8 +447,7 @@ void phy_start_machine(struct phy_device *phydev, */ void phy_stop_machine(struct phy_device *phydev) { - del_timer_sync(&phydev->phy_timer); - cancel_work_sync(&phydev->state_queue); + cancel_delayed_work_sync(&phydev->state_queue); mutex_lock(&phydev->lock); if (phydev->state > PHY_UP) @@ -680,11 +675,9 @@ static void phy_change(struct work_struct *work) if (err) goto irq_enable_err; - /* Stop timer and run the state queue now. The work function for - * state_queue will start the timer up again. - */ - del_timer(&phydev->phy_timer); - schedule_work(&phydev->state_queue); + /* reschedule state queue work to run as soon as possible */ + cancel_delayed_work_sync(&phydev->state_queue); + schedule_delayed_work(&phydev->state_queue, 0); return; @@ -761,14 +754,13 @@ EXPORT_SYMBOL(phy_start); /** * phy_state_machine - Handle the state machine * @work: work_struct that describes the work to be done - * - * Description: Scheduled by the state_queue workqueue each time - * phy_timer is triggered. */ static void phy_state_machine(struct work_struct *work) { + struct delayed_work *dwork = + container_of(work, struct delayed_work, work); struct phy_device *phydev = - container_of(work, struct phy_device, state_queue); + container_of(dwork, struct phy_device, state_queue); int needs_aneg = 0; int err = 0; @@ -946,17 +938,6 @@ static void phy_state_machine(struct work_struct *work) if (err < 0) phy_error(phydev); - mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ); -} - -/* PHY timer which schedules the state machine work */ -static void phy_timer(unsigned long data) -{ - struct phy_device *phydev = (struct phy_device *)data; - - /* - * PHY I/O operations can potentially sleep so we ensure that - * it's done from a process context - */ - schedule_work(&phydev->state_queue); + schedule_delayed_work(&phydev->state_queue, + jiffies + PHY_STATE_TIME * HZ); } diff --git a/include/linux/phy.h b/include/linux/phy.h index d7e54d98869f..32cf14a4b034 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -315,8 +315,7 @@ struct phy_device { /* Interrupt and Polling infrastructure */ struct work_struct phy_queue; - struct work_struct state_queue; - struct timer_list phy_timer; + struct delayed_work state_queue; atomic_t irq_disable; struct mutex lock; -- cgit v1.2.3 From 9c705260feea6ae329bc6b6d5f6d2ef0227eda0a Mon Sep 17 00:00:00 2001 From: Gabriele Paoloni Date: Fri, 13 Mar 2009 16:09:12 -0700 Subject: ppp: ppp_mp_explode() redesign I found the PPP subsystem to not work properly when connecting channels with different speeds to the same bundle. Problem Description: As the "ppp_mp_explode" function fragments the sk_buff buffer evenly among the PPP channels that are connected to a certain PPP unit to make up a bundle, if we are transmitting using an upper layer protocol that requires an Ack before sending the next packet (like TCP/IP for example), we will have a bandwidth bottleneck on the slowest channel of the bundle. Let's clarify by an example. Let's consider a scenario where we have two PPP links making up a bundle: a slow link (10KB/sec) and a fast link (1000KB/sec) working at the best (full bandwidth). On the top we have a TCP/IP stack sending a 1000 Bytes sk_buff buffer down to the PPP subsystem. The "ppp_mp_explode" function will divide the buffer in two fragments of 500B each (we are neglecting all the headers, crc, flags etc?.). Before the TCP/IP stack sends out the next buffer, it will have to wait for the ACK response from the remote peer, so it will have to wait for both fragments to have been sent over the two PPP links, received by the remote peer and reconstructed. The resulting behaviour is that, rather than having a bundle working @1010KB/sec (the sum of the channels bandwidths), we'll have a bundle working @20KB/sec (the double of the slowest channels bandwidth). Problem Solution: The problem has been solved by redesigning the "ppp_mp_explode" function in such a way to make it split the sk_buff buffer according to the speeds of the underlying PPP channels (the speeds of the serial interfaces respectively attached to the PPP channels). Referring to the above example, the redesigned "ppp_mp_explode" function will now divide the 1000 Bytes buffer into two fragments whose sizes are set according to the speeds of the channels where they are going to be sent on (e.g . 10 Byets on 10KB/sec channel and 990 Bytes on 1000KB/sec channel). The reworked function grants the same performances of the original one in optimal working conditions (i.e. a bundle made up of PPP links all working at the same speed), while greatly improving performances on the bundles made up of channels working at different speeds. Signed-off-by: Gabriele Paoloni Signed-off-by: David S. Miller --- drivers/net/ppp_async.c | 3 + drivers/net/ppp_generic.c | 211 +++++++++++++++++++++++++------------------- drivers/net/ppp_synctty.c | 3 + include/linux/ppp_channel.h | 2 +- 4 files changed, 127 insertions(+), 92 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index 5de6fedd1d76..6de8399d6dd9 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -157,6 +157,7 @@ ppp_asynctty_open(struct tty_struct *tty) { struct asyncppp *ap; int err; + int speed; if (tty->ops->write == NULL) return -EOPNOTSUPP; @@ -187,6 +188,8 @@ ppp_asynctty_open(struct tty_struct *tty) ap->chan.private = ap; ap->chan.ops = &async_ops; ap->chan.mtu = PPP_MRU; + speed = tty_get_baud_rate(tty); + ap->chan.speed = speed; err = ppp_register_channel(&ap->chan); if (err) goto out_free; diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 42d455578453..8ee91421db12 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -167,6 +167,7 @@ struct channel { u8 avail; /* flag used in multilink stuff */ u8 had_frag; /* >= 1 fragments have been sent */ u32 lastseq; /* MP: last sequence # received */ + int speed; /* speed of the corresponding ppp channel*/ #endif /* CONFIG_PPP_MULTILINK */ }; @@ -1307,138 +1308,181 @@ ppp_push(struct ppp *ppp) */ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) { - int len, fragsize; - int i, bits, hdrlen, mtu; - int flen; - int navail, nfree; - int nbigger; + int len, totlen; + int i, bits, hdrlen, mtu; + int flen; + int navail, nfree, nzero; + int nbigger; + int totspeed; + int totfree; unsigned char *p, *q; struct list_head *list; struct channel *pch; struct sk_buff *frag; struct ppp_channel *chan; - nfree = 0; /* # channels which have no packet already queued */ + totspeed = 0; /*total bitrate of the bundle*/ + nfree = 0; /* # channels which have no packet already queued */ navail = 0; /* total # of usable channels (not deregistered) */ + nzero = 0; /* number of channels with zero speed associated*/ + totfree = 0; /*total # of channels available and + *having no queued packets before + *starting the fragmentation*/ + hdrlen = (ppp->flags & SC_MP_XSHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN; - i = 0; - list_for_each_entry(pch, &ppp->channels, clist) { + i = 0; + list_for_each_entry(pch, &ppp->channels, clist) { navail += pch->avail = (pch->chan != NULL); - if (pch->avail) { + pch->speed = pch->chan->speed; + if (pch->avail) { if (skb_queue_empty(&pch->file.xq) || - !pch->had_frag) { - pch->avail = 2; - ++nfree; - } - if (!pch->had_frag && i < ppp->nxchan) - ppp->nxchan = i; + !pch->had_frag) { + if (pch->speed == 0) + nzero++; + else + totspeed += pch->speed; + + pch->avail = 2; + ++nfree; + ++totfree; + } + if (!pch->had_frag && i < ppp->nxchan) + ppp->nxchan = i; } ++i; } - /* - * Don't start sending this packet unless at least half of - * the channels are free. This gives much better TCP - * performance if we have a lot of channels. + * Don't start sending this packet unless at least half of + * the channels are free. This gives much better TCP + * performance if we have a lot of channels. */ - if (nfree == 0 || nfree < navail / 2) - return 0; /* can't take now, leave it in xmit_pending */ + if (nfree == 0 || nfree < navail / 2) + return 0; /* can't take now, leave it in xmit_pending */ /* Do protocol field compression (XXX this should be optional) */ - p = skb->data; - len = skb->len; + p = skb->data; + len = skb->len; if (*p == 0) { ++p; --len; } - /* - * Decide on fragment size. - * We create a fragment for each free channel regardless of - * how small they are (i.e. even 0 length) in order to minimize - * the time that it will take to detect when a channel drops - * a fragment. - */ - fragsize = len; - if (nfree > 1) - fragsize = DIV_ROUND_UP(fragsize, nfree); - /* nbigger channels get fragsize bytes, the rest get fragsize-1, - except if nbigger==0, then they all get fragsize. */ - nbigger = len % nfree; - - /* skip to the channel after the one we last used - and start at that one */ + totlen = len; + nbigger = len % nfree; + + /* skip to the channel after the one we last used + and start at that one */ list = &ppp->channels; - for (i = 0; i < ppp->nxchan; ++i) { + for (i = 0; i < ppp->nxchan; ++i) { list = list->next; - if (list == &ppp->channels) { - i = 0; + if (list == &ppp->channels) { + i = 0; break; } } - /* create a fragment for each channel */ + /* create a fragment for each channel */ bits = B; - while (nfree > 0 || len > 0) { + while (nfree > 0 && len > 0) { list = list->next; - if (list == &ppp->channels) { - i = 0; + if (list == &ppp->channels) { + i = 0; continue; } - pch = list_entry(list, struct channel, clist); + pch = list_entry(list, struct channel, clist); ++i; if (!pch->avail) continue; /* - * Skip this channel if it has a fragment pending already and - * we haven't given a fragment to all of the free channels. + * Skip this channel if it has a fragment pending already and + * we haven't given a fragment to all of the free channels. */ if (pch->avail == 1) { - if (nfree > 0) + if (nfree > 0) continue; } else { - --nfree; pch->avail = 1; } /* check the channel's mtu and whether it is still attached. */ spin_lock_bh(&pch->downl); if (pch->chan == NULL) { - /* can't use this channel, it's being deregistered */ + /* can't use this channel, it's being deregistered */ + if (pch->speed == 0) + nzero--; + else + totspeed -= pch->speed; + spin_unlock_bh(&pch->downl); pch->avail = 0; - if (--navail == 0) + totlen = len; + totfree--; + nfree--; + if (--navail == 0) break; continue; } /* - * Create a fragment for this channel of - * min(max(mtu+2-hdrlen, 4), fragsize, len) bytes. - * If mtu+2-hdrlen < 4, that is a ridiculously small - * MTU, so we use mtu = 2 + hdrlen. + *if the channel speed is not set divide + *the packet evenly among the free channels; + *otherwise divide it according to the speed + *of the channel we are going to transmit on + */ + if (pch->speed == 0) { + flen = totlen/nfree ; + if (nbigger > 0) { + flen++; + nbigger--; + } + } else { + flen = (((totfree - nzero)*(totlen + hdrlen*totfree)) / + ((totspeed*totfree)/pch->speed)) - hdrlen; + if (nbigger > 0) { + flen += ((totfree - nzero)*pch->speed)/totspeed; + nbigger -= ((totfree - nzero)*pch->speed)/ + totspeed; + } + } + nfree--; + + /* + *check if we are on the last channel or + *we exceded the lenght of the data to + *fragment + */ + if ((nfree == 0) || (flen > len)) + flen = len; + /* + *it is not worth to tx on slow channels: + *in that case from the resulting flen according to the + *above formula will be equal or less than zero. + *Skip the channel in this case */ - if (fragsize > len) - fragsize = len; - flen = fragsize; - mtu = pch->chan->mtu + 2 - hdrlen; - if (mtu < 4) - mtu = 4; + if (flen <= 0) { + pch->avail = 2; + spin_unlock_bh(&pch->downl); + continue; + } + + mtu = pch->chan->mtu + 2 - hdrlen; + if (mtu < 4) + mtu = 4; if (flen > mtu) flen = mtu; - if (flen == len && nfree == 0) - bits |= E; - frag = alloc_skb(flen + hdrlen + (flen == 0), GFP_ATOMIC); + if (flen == len) + bits |= E; + frag = alloc_skb(flen + hdrlen + (flen == 0), GFP_ATOMIC); if (!frag) goto noskb; - q = skb_put(frag, flen + hdrlen); + q = skb_put(frag, flen + hdrlen); - /* make the MP header */ + /* make the MP header */ q[0] = PPP_MP >> 8; q[1] = PPP_MP; if (ppp->flags & SC_MP_XSHORTSEQ) { - q[2] = bits + ((ppp->nxseq >> 8) & 0xf); + q[2] = bits + ((ppp->nxseq >> 8) & 0xf); q[3] = ppp->nxseq; } else { q[2] = bits; @@ -1447,43 +1491,28 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) q[5] = ppp->nxseq; } - /* - * Copy the data in. - * Unfortunately there is a bug in older versions of - * the Linux PPP multilink reconstruction code where it - * drops 0-length fragments. Therefore we make sure the - * fragment has at least one byte of data. Any bytes - * we add in this situation will end up as padding on the - * end of the reconstructed packet. - */ - if (flen == 0) - *skb_put(frag, 1) = 0; - else - memcpy(q + hdrlen, p, flen); + memcpy(q + hdrlen, p, flen); /* try to send it down the channel */ chan = pch->chan; - if (!skb_queue_empty(&pch->file.xq) || - !chan->ops->start_xmit(chan, frag)) + if (!skb_queue_empty(&pch->file.xq) || + !chan->ops->start_xmit(chan, frag)) skb_queue_tail(&pch->file.xq, frag); - pch->had_frag = 1; + pch->had_frag = 1; p += flen; - len -= flen; + len -= flen; ++ppp->nxseq; bits = 0; spin_unlock_bh(&pch->downl); - - if (--nbigger == 0 && fragsize > 0) - --fragsize; } - ppp->nxchan = i; + ppp->nxchan = i; return 1; noskb: spin_unlock_bh(&pch->downl); if (ppp->debug & 1) - printk(KERN_ERR "PPP: no memory (fragment)\n"); + printk(KERN_ERR "PPP: no memory (fragment)\n"); ++ppp->dev->stats.tx_errors; ++ppp->nxseq; return 1; /* abandon the frame */ diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index 3ea791d16b00..d2fa2db13586 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -206,6 +206,7 @@ ppp_sync_open(struct tty_struct *tty) { struct syncppp *ap; int err; + int speed; if (tty->ops->write == NULL) return -EOPNOTSUPP; @@ -234,6 +235,8 @@ ppp_sync_open(struct tty_struct *tty) ap->chan.ops = &sync_ops; ap->chan.mtu = PPP_MRU; ap->chan.hdrlen = 2; /* for A/C bytes */ + speed = tty_get_baud_rate(tty); + ap->chan.speed = speed; err = ppp_register_channel(&ap->chan); if (err) goto out_free; diff --git a/include/linux/ppp_channel.h b/include/linux/ppp_channel.h index 9d64bdf14770..0d3fa63e90ea 100644 --- a/include/linux/ppp_channel.h +++ b/include/linux/ppp_channel.h @@ -40,8 +40,8 @@ struct ppp_channel { int mtu; /* max transmit packet size */ int hdrlen; /* amount of headroom channel needs */ void *ppp; /* opaque to channel */ - /* the following are not used at present */ int speed; /* transfer rate (bytes/second) */ + /* the following is not used at present */ int latency; /* overhead time in milliseconds */ }; -- cgit v1.2.3 From 8bdd663aba341c15cd2fa9dbd7061b8b387964dc Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 15 Mar 2009 19:59:13 -0700 Subject: net: reorder fields of struct socket On x86_64, its rather unfortunate that "wait_queue_head_t wait" field of "struct socket" spans two cache lines (assuming a 64 bytes cache line in current cpus) offsetof(struct socket, wait)=0x30 sizeof(wait_queue_head_t)=0x18 This might explain why Kenny Chang noticed that his multicast workload was performing bad with 64 bit kernels, since more cache lines ping pongs were involved. This litle patch moves "wait" field next "fasync_list" so that both fields share a single cache line, to speedup sock_def_readable() Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/net.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/net.h b/include/linux/net.h index 4515efae4c39..4fc2ffd527f9 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -129,11 +129,15 @@ struct socket { socket_state state; short type; unsigned long flags; - const struct proto_ops *ops; + /* + * Please keep fasync_list & wait fields in the same cache line + */ struct fasync_struct *fasync_list; + wait_queue_head_t wait; + struct file *file; struct sock *sk; - wait_queue_head_t wait; + const struct proto_ops *ops; }; struct vm_area_struct; -- cgit v1.2.3 From 0c54b85f2828128274f319a1eb3ce7f604fe2a53 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Sat, 14 Mar 2009 14:23:05 +0000 Subject: tcp: simplify tcp_current_mss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's very little need for most of the callsites to get tp->xmit_goal_size updated. That will cost us divide as is, so slice the function in two. Also, the only users of the tp->xmit_goal_size are directly behind tcp_current_mss(), so there's no need to store that variable into tcp_sock at all! The drop of xmit_goal_size currently leaves 16-bit hole and some reorganization would again be necessary to change that (but I'm aiming to fill that hole with u16 xmit_goal_size_segs to cache the results of the remaining divide to get that tso on regression). Bring xmit_goal_size parts into tcp.c Signed-off-by: Ilpo Järvinen Cc: Evgeniy Polyakov Cc: Ingo Molnar Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - include/net/tcp.h | 13 +++++++++++-- net/ipv4/tcp.c | 43 +++++++++++++++++++++++++++++++++++-------- net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_output.c | 41 +++++++---------------------------------- 5 files changed, 54 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4b86ad71e054..ad2021ccc55a 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -248,7 +248,6 @@ struct tcp_sock { /* inet_connection_sock has to be the first member of tcp_sock */ struct inet_connection_sock inet_conn; u16 tcp_header_len; /* Bytes of tcp header to send */ - u16 xmit_size_goal; /* Goal for segmenting output packets */ /* * Header prediction flags diff --git a/include/net/tcp.h b/include/net/tcp.h index 255ca35bea05..e54c76d75495 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -481,7 +481,16 @@ static inline void tcp_clear_xmit_timers(struct sock *sk) } extern unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu); -extern unsigned int tcp_current_mss(struct sock *sk, int large); +extern unsigned int tcp_current_mss(struct sock *sk); + +/* Bound MSS / TSO packet size with the half of the window */ +static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize) +{ + if (tp->max_window && pktsize > (tp->max_window >> 1)) + return max(tp->max_window >> 1, 68U - tp->tcp_header_len); + else + return pktsize; +} /* tcp.c */ extern void tcp_get_info(struct sock *, struct tcp_info *); @@ -822,7 +831,7 @@ static inline void tcp_push_pending_frames(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); - __tcp_push_pending_frames(sk, tcp_current_mss(sk, 1), tp->nonagle); + __tcp_push_pending_frames(sk, tcp_current_mss(sk), tp->nonagle); } static inline void tcp_init_wl(struct tcp_sock *tp, u32 seq) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index d3f9beee74c0..886596ff0aae 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -661,6 +661,37 @@ struct sk_buff *sk_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp) return NULL; } +static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, + int large_allowed) +{ + struct tcp_sock *tp = tcp_sk(sk); + u32 xmit_size_goal; + + xmit_size_goal = mss_now; + + if (large_allowed && sk_can_gso(sk)) { + xmit_size_goal = ((sk->sk_gso_max_size - 1) - + inet_csk(sk)->icsk_af_ops->net_header_len - + inet_csk(sk)->icsk_ext_hdr_len - + tp->tcp_header_len); + + xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal); + xmit_size_goal -= (xmit_size_goal % mss_now); + } + + return xmit_size_goal; +} + +static int tcp_send_mss(struct sock *sk, int *size_goal, int flags) +{ + int mss_now; + + mss_now = tcp_current_mss(sk); + *size_goal = tcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); + + return mss_now; +} + static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags) { @@ -677,8 +708,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); - mss_now = tcp_current_mss(sk, !(flags&MSG_OOB)); - size_goal = tp->xmit_size_goal; + mss_now = tcp_send_mss(sk, &size_goal, flags); copied = 0; err = -EPIPE; @@ -761,8 +791,7 @@ wait_for_memory: if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) goto do_error; - mss_now = tcp_current_mss(sk, !(flags&MSG_OOB)); - size_goal = tp->xmit_size_goal; + mss_now = tcp_send_mss(sk, &size_goal, flags); } out: @@ -844,8 +873,7 @@ int tcp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, /* This should be in poll */ clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); - mss_now = tcp_current_mss(sk, !(flags&MSG_OOB)); - size_goal = tp->xmit_size_goal; + mss_now = tcp_send_mss(sk, &size_goal, flags); /* Ok commence sending. */ iovlen = msg->msg_iovlen; @@ -1007,8 +1035,7 @@ wait_for_memory: if ((err = sk_stream_wait_memory(sk, &timeo)) != 0) goto do_error; - mss_now = tcp_current_mss(sk, !(flags&MSG_OOB)); - size_goal = tp->xmit_size_goal; + mss_now = tcp_send_mss(sk, &size_goal, flags); } } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 311c30f73ee4..fae78e3eccc4 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2864,7 +2864,7 @@ void tcp_simple_retransmit(struct sock *sk) const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; - unsigned int mss = tcp_current_mss(sk, 0); + unsigned int mss = tcp_current_mss(sk); u32 prior_lost = tp->lost_out; tcp_for_write_queue(skb, sk) { diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 325658039139..c1f259d2d33b 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -921,7 +921,7 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) * factor and mss. */ if (tcp_skb_pcount(skb) > 1) - tcp_set_skb_tso_segs(sk, skb, tcp_current_mss(sk, 1)); + tcp_set_skb_tso_segs(sk, skb, tcp_current_mss(sk)); return 0; } @@ -982,15 +982,6 @@ void tcp_mtup_init(struct sock *sk) icsk->icsk_mtup.probe_size = 0; } -/* Bound MSS / TSO packet size with the half of the window */ -static int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize) -{ - if (tp->max_window && pktsize > (tp->max_window >> 1)) - return max(tp->max_window >> 1, 68U - tp->tcp_header_len); - else - return pktsize; -} - /* This function synchronize snd mss to current pmtu/exthdr set. tp->rx_opt.user_mss is mss set by user by TCP_MAXSEG. It does NOT counts @@ -1037,22 +1028,17 @@ unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu) /* Compute the current effective MSS, taking SACKs and IP options, * and even PMTU discovery events into account. */ -unsigned int tcp_current_mss(struct sock *sk, int large_allowed) +unsigned int tcp_current_mss(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); struct dst_entry *dst = __sk_dst_get(sk); u32 mss_now; - u16 xmit_size_goal; - int doing_tso = 0; unsigned header_len; struct tcp_out_options opts; struct tcp_md5sig_key *md5; mss_now = tp->mss_cache; - if (large_allowed && sk_can_gso(sk)) - doing_tso = 1; - if (dst) { u32 mtu = dst_mtu(dst); if (mtu != inet_csk(sk)->icsk_pmtu_cookie) @@ -1070,19 +1056,6 @@ unsigned int tcp_current_mss(struct sock *sk, int large_allowed) mss_now -= delta; } - xmit_size_goal = mss_now; - - if (doing_tso) { - xmit_size_goal = ((sk->sk_gso_max_size - 1) - - inet_csk(sk)->icsk_af_ops->net_header_len - - inet_csk(sk)->icsk_ext_hdr_len - - tp->tcp_header_len); - - xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal); - xmit_size_goal -= (xmit_size_goal % mss_now); - } - tp->xmit_size_goal = xmit_size_goal; - return mss_now; } @@ -1264,7 +1237,7 @@ int tcp_may_send_now(struct sock *sk) struct sk_buff *skb = tcp_send_head(sk); return (skb && - tcp_snd_test(sk, skb, tcp_current_mss(sk, 1), + tcp_snd_test(sk, skb, tcp_current_mss(sk), (tcp_skb_is_last(sk, skb) ? tp->nonagle : TCP_NAGLE_PUSH))); } @@ -1421,7 +1394,7 @@ static int tcp_mtu_probe(struct sock *sk) return -1; /* Very simple search strategy: just double the MSS. */ - mss_now = tcp_current_mss(sk, 0); + mss_now = tcp_current_mss(sk); probe_size = 2 * tp->mss_cache; size_needed = probe_size + (tp->reordering + 1) * tp->mss_cache; if (probe_size > tcp_mtu_to_mss(sk, icsk->icsk_mtup.search_high)) { @@ -1903,7 +1876,7 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk)) return -EHOSTUNREACH; /* Routing failure or similar. */ - cur_mss = tcp_current_mss(sk, 0); + cur_mss = tcp_current_mss(sk); /* If receiver has shrunk his window, and skb is out of * new window, do not retransmit it. The exception is the @@ -2111,7 +2084,7 @@ void tcp_send_fin(struct sock *sk) * unsent frames. But be careful about outgoing SACKS * and IP options. */ - mss_now = tcp_current_mss(sk, 1); + mss_now = tcp_current_mss(sk); if (tcp_send_head(sk) != NULL) { TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN; @@ -2523,7 +2496,7 @@ int tcp_write_wakeup(struct sock *sk) if ((skb = tcp_send_head(sk)) != NULL && before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp))) { int err; - unsigned int mss = tcp_current_mss(sk, 0); + unsigned int mss = tcp_current_mss(sk); unsigned int seg_size = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; if (before(tp->pushed_seq, TCP_SKB_CB(skb)->end_seq)) -- cgit v1.2.3 From 2a3a041c4e2c1685e668b280c121a5a40a029a03 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Sat, 14 Mar 2009 22:45:16 +0000 Subject: tcp: cache result of earlier divides when mss-aligning things MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The results is very unlikely change every so often so we hardly need to divide again after doing that once for a connection. Yet, if divide still becomes necessary we detect that and do the right thing and again settle for non-divide state. Takes the u16 space which was previously taken by the plain xmit_size_goal. This should take care part of the tso vs non-tso difference we found earlier. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 + net/ipv4/tcp.c | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index ad2021ccc55a..9d5078bd23a3 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -248,6 +248,7 @@ struct tcp_sock { /* inet_connection_sock has to be the first member of tcp_sock */ struct inet_connection_sock inet_conn; u16 tcp_header_len; /* Bytes of tcp header to send */ + u16 xmit_size_goal_segs; /* Goal for segmenting output packets */ /* * Header prediction flags diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 886596ff0aae..0db9f3b984f7 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -665,7 +665,7 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, int large_allowed) { struct tcp_sock *tp = tcp_sk(sk); - u32 xmit_size_goal; + u32 xmit_size_goal, old_size_goal; xmit_size_goal = mss_now; @@ -676,7 +676,17 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, tp->tcp_header_len); xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal); - xmit_size_goal -= (xmit_size_goal % mss_now); + + /* We try hard to avoid divides here */ + old_size_goal = tp->xmit_size_goal_segs * mss_now; + + if (likely(old_size_goal <= xmit_size_goal && + old_size_goal + mss_now > xmit_size_goal)) { + xmit_size_goal = old_size_goal; + } else { + tp->xmit_size_goal_segs = xmit_size_goal / mss_now; + xmit_size_goal = tp->xmit_size_goal_segs * mss_now; + } } return xmit_size_goal; -- cgit v1.2.3 From acc738fec03bdaa5b77340c32a82fbfedaaabef0 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 16 Mar 2009 15:35:29 +0100 Subject: netfilter: xtables: avoid pointer to self Commit 784544739a25c30637397ace5489eeb6e15d7d49 (netfilter: iptables: lock free counters) broke a number of modules whose rule data referenced itself. A reallocation would not reestablish the correct references, so it is best to use a separate struct that does not fall under RCU. Signed-off-by: Jan Engelhardt Signed-off-by: Patrick McHardy --- include/linux/netfilter/xt_limit.h | 9 ++++---- include/linux/netfilter/xt_quota.h | 4 +++- include/linux/netfilter/xt_statistic.h | 7 +++--- net/netfilter/xt_limit.c | 40 ++++++++++++++++++++++++---------- net/netfilter/xt_quota.c | 31 ++++++++++++++++++++------ net/netfilter/xt_statistic.c | 28 +++++++++++++++++++----- 6 files changed, 88 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_limit.h b/include/linux/netfilter/xt_limit.h index b3ce65375ecb..fda222c7953b 100644 --- a/include/linux/netfilter/xt_limit.h +++ b/include/linux/netfilter/xt_limit.h @@ -4,6 +4,8 @@ /* timings are in milliseconds. */ #define XT_LIMIT_SCALE 10000 +struct xt_limit_priv; + /* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490 seconds, or one every 59 hours. */ struct xt_rateinfo { @@ -11,11 +13,10 @@ struct xt_rateinfo { u_int32_t burst; /* Period multiplier for upper limit. */ /* Used internally by the kernel */ - unsigned long prev; - u_int32_t credit; + unsigned long prev; /* moved to xt_limit_priv */ + u_int32_t credit; /* moved to xt_limit_priv */ u_int32_t credit_cap, cost; - /* Ugly, ugly fucker. */ - struct xt_rateinfo *master; + struct xt_limit_priv *master; }; #endif /*_XT_RATE_H*/ diff --git a/include/linux/netfilter/xt_quota.h b/include/linux/netfilter/xt_quota.h index 4c8368d781e5..8dc89dfc1361 100644 --- a/include/linux/netfilter/xt_quota.h +++ b/include/linux/netfilter/xt_quota.h @@ -6,13 +6,15 @@ enum xt_quota_flags { }; #define XT_QUOTA_MASK 0x1 +struct xt_quota_priv; + struct xt_quota_info { u_int32_t flags; u_int32_t pad; /* Used internally by the kernel */ aligned_u64 quota; - struct xt_quota_info *master; + struct xt_quota_priv *master; }; #endif /* _XT_QUOTA_H */ diff --git a/include/linux/netfilter/xt_statistic.h b/include/linux/netfilter/xt_statistic.h index 3d38bc975048..8f521ab49ef7 100644 --- a/include/linux/netfilter/xt_statistic.h +++ b/include/linux/netfilter/xt_statistic.h @@ -13,6 +13,8 @@ enum xt_statistic_flags { }; #define XT_STATISTIC_MASK 0x1 +struct xt_statistic_priv; + struct xt_statistic_info { u_int16_t mode; u_int16_t flags; @@ -23,11 +25,10 @@ struct xt_statistic_info { struct { u_int32_t every; u_int32_t packet; - /* Used internally by the kernel */ - u_int32_t count; + u_int32_t count; /* unused */ } nth; } u; - struct xt_statistic_info *master __attribute__((aligned(8))); + struct xt_statistic_priv *master __attribute__((aligned(8))); }; #endif /* _XT_STATISTIC_H */ diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c index c908d69a5595..2e8089ecd0af 100644 --- a/net/netfilter/xt_limit.c +++ b/net/netfilter/xt_limit.c @@ -14,6 +14,11 @@ #include #include +struct xt_limit_priv { + unsigned long prev; + uint32_t credit; +}; + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Herve Eychenne "); MODULE_DESCRIPTION("Xtables: rate-limit match"); @@ -60,18 +65,18 @@ static DEFINE_SPINLOCK(limit_lock); static bool limit_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - struct xt_rateinfo *r = - ((const struct xt_rateinfo *)par->matchinfo)->master; + const struct xt_rateinfo *r = par->matchinfo; + struct xt_limit_priv *priv = r->master; unsigned long now = jiffies; spin_lock_bh(&limit_lock); - r->credit += (now - xchg(&r->prev, now)) * CREDITS_PER_JIFFY; - if (r->credit > r->credit_cap) - r->credit = r->credit_cap; + priv->credit += (now - xchg(&priv->prev, now)) * CREDITS_PER_JIFFY; + if (priv->credit > r->credit_cap) + priv->credit = r->credit_cap; - if (r->credit >= r->cost) { + if (priv->credit >= r->cost) { /* We're not limited. */ - r->credit -= r->cost; + priv->credit -= r->cost; spin_unlock_bh(&limit_lock); return true; } @@ -95,6 +100,7 @@ user2credits(u_int32_t user) static bool limit_mt_check(const struct xt_mtchk_param *par) { struct xt_rateinfo *r = par->matchinfo; + struct xt_limit_priv *priv; /* Check for overflow. */ if (r->burst == 0 @@ -104,19 +110,30 @@ static bool limit_mt_check(const struct xt_mtchk_param *par) return false; } - /* For SMP, we only want to use one set of counters. */ - r->master = r; + priv = kmalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + /* For SMP, we only want to use one set of state. */ + r->master = priv; if (r->cost == 0) { /* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies * 128. */ - r->prev = jiffies; - r->credit = user2credits(r->avg * r->burst); /* Credits full. */ + priv->prev = jiffies; + priv->credit = user2credits(r->avg * r->burst); /* Credits full. */ r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */ r->cost = user2credits(r->avg); } return true; } +static void limit_mt_destroy(const struct xt_mtdtor_param *par) +{ + const struct xt_rateinfo *info = par->matchinfo; + + kfree(info->master); +} + #ifdef CONFIG_COMPAT struct compat_xt_rateinfo { u_int32_t avg; @@ -167,6 +184,7 @@ static struct xt_match limit_mt_reg __read_mostly = { .family = NFPROTO_UNSPEC, .match = limit_mt, .checkentry = limit_mt_check, + .destroy = limit_mt_destroy, .matchsize = sizeof(struct xt_rateinfo), #ifdef CONFIG_COMPAT .compatsize = sizeof(struct compat_xt_rateinfo), diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c index c84fce5e0f3e..01dd07b764ec 100644 --- a/net/netfilter/xt_quota.c +++ b/net/netfilter/xt_quota.c @@ -9,6 +9,10 @@ #include #include +struct xt_quota_priv { + uint64_t quota; +}; + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sam Johnston "); MODULE_DESCRIPTION("Xtables: countdown quota match"); @@ -20,18 +24,20 @@ static DEFINE_SPINLOCK(quota_lock); static bool quota_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - struct xt_quota_info *q = - ((const struct xt_quota_info *)par->matchinfo)->master; + struct xt_quota_info *q = (void *)par->matchinfo; + struct xt_quota_priv *priv = q->master; bool ret = q->flags & XT_QUOTA_INVERT; spin_lock_bh("a_lock); - if (q->quota >= skb->len) { - q->quota -= skb->len; + if (priv->quota >= skb->len) { + priv->quota -= skb->len; ret = !ret; } else { /* we do not allow even small packets from now on */ - q->quota = 0; + priv->quota = 0; } + /* Copy quota back to matchinfo so that iptables can display it */ + q->quota = priv->quota; spin_unlock_bh("a_lock); return ret; @@ -43,17 +49,28 @@ static bool quota_mt_check(const struct xt_mtchk_param *par) if (q->flags & ~XT_QUOTA_MASK) return false; - /* For SMP, we only want to use one set of counters. */ - q->master = q; + + q->master = kmalloc(sizeof(*q->master), GFP_KERNEL); + if (q->master == NULL) + return -ENOMEM; + return true; } +static void quota_mt_destroy(const struct xt_mtdtor_param *par) +{ + const struct xt_quota_info *q = par->matchinfo; + + kfree(q->master); +} + static struct xt_match quota_mt_reg __read_mostly = { .name = "quota", .revision = 0, .family = NFPROTO_UNSPEC, .match = quota_mt, .checkentry = quota_mt_check, + .destroy = quota_mt_destroy, .matchsize = sizeof(struct xt_quota_info), .me = THIS_MODULE, }; diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c index 0d75141139d5..d8c0f8f1a78e 100644 --- a/net/netfilter/xt_statistic.c +++ b/net/netfilter/xt_statistic.c @@ -16,6 +16,10 @@ #include #include +struct xt_statistic_priv { + uint32_t count; +}; + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Patrick McHardy "); MODULE_DESCRIPTION("Xtables: statistics-based matching (\"Nth\", random)"); @@ -27,7 +31,7 @@ static DEFINE_SPINLOCK(nth_lock); static bool statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) { - struct xt_statistic_info *info = (void *)par->matchinfo; + const struct xt_statistic_info *info = par->matchinfo; bool ret = info->flags & XT_STATISTIC_INVERT; switch (info->mode) { @@ -36,10 +40,9 @@ statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) ret = !ret; break; case XT_STATISTIC_MODE_NTH: - info = info->master; spin_lock_bh(&nth_lock); - if (info->u.nth.count++ == info->u.nth.every) { - info->u.nth.count = 0; + if (info->master->count++ == info->u.nth.every) { + info->master->count = 0; ret = !ret; } spin_unlock_bh(&nth_lock); @@ -56,16 +59,31 @@ static bool statistic_mt_check(const struct xt_mtchk_param *par) if (info->mode > XT_STATISTIC_MODE_MAX || info->flags & ~XT_STATISTIC_MASK) return false; - info->master = info; + + info->master = kzalloc(sizeof(*info->master), GFP_KERNEL); + if (info->master == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": Out of memory\n"); + return false; + } + info->master->count = info->u.nth.count; + return true; } +static void statistic_mt_destroy(const struct xt_mtdtor_param *par) +{ + const struct xt_statistic_info *info = par->matchinfo; + + kfree(info->master); +} + static struct xt_match xt_statistic_mt_reg __read_mostly = { .name = "statistic", .revision = 0, .family = NFPROTO_UNSPEC, .match = statistic_mt, .checkentry = statistic_mt_check, + .destroy = statistic_mt_destroy, .matchsize = sizeof(struct xt_statistic_info), .me = THIS_MODULE, }; -- cgit v1.2.3 From 0269ea4937343536ec7e85649932bc8c9686ea78 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 16 Mar 2009 17:10:36 +0100 Subject: netfilter: xtables: add cluster match This patch adds the iptables cluster match. This match can be used to deploy gateway and back-end load-sharing clusters. The cluster can be composed of 32 nodes maximum (although I have only tested this with two nodes, so I cannot tell what is the real scalability limit of this solution in terms of cluster nodes). Assuming that all the nodes see all packets (see below for an example on how to do that if your switch does not allow this), the cluster match decides if this node has to handle a packet given: (jhash(source IP) % total_nodes) & node_mask For related connections, the master conntrack is used. The following is an example of its use to deploy a gateway cluster composed of two nodes (where this is the node 1): iptables -I PREROUTING -t mangle -i eth1 -m cluster \ --cluster-total-nodes 2 --cluster-local-node 1 \ --cluster-proc-name eth1 -j MARK --set-mark 0xffff iptables -A PREROUTING -t mangle -i eth1 \ -m mark ! --mark 0xffff -j DROP iptables -A PREROUTING -t mangle -i eth2 -m cluster \ --cluster-total-nodes 2 --cluster-local-node 1 \ --cluster-proc-name eth2 -j MARK --set-mark 0xffff iptables -A PREROUTING -t mangle -i eth2 \ -m mark ! --mark 0xffff -j DROP And the following commands to make all nodes see the same packets: ip maddr add 01:00:5e:00:01:01 dev eth1 ip maddr add 01:00:5e:00:01:02 dev eth2 arptables -I OUTPUT -o eth1 --h-length 6 \ -j mangle --mangle-mac-s 01:00:5e:00:01:01 arptables -I INPUT -i eth1 --h-length 6 \ --destination-mac 01:00:5e:00:01:01 \ -j mangle --mangle-mac-d 00:zz:yy:xx:5a:27 arptables -I OUTPUT -o eth2 --h-length 6 \ -j mangle --mangle-mac-s 01:00:5e:00:01:02 arptables -I INPUT -i eth2 --h-length 6 \ --destination-mac 01:00:5e:00:01:02 \ -j mangle --mangle-mac-d 00:zz:yy:xx:5a:27 In the case of TCP connections, pickup facility has to be disabled to avoid marking TCP ACK packets coming in the reply direction as valid. echo 0 > /proc/sys/net/netfilter/nf_conntrack_tcp_loose BTW, some final notes: * This match mangles the skbuff pkt_type in case that it detects PACKET_MULTICAST for a non-multicast address. This may be done in a PKTTYPE target for this sole purpose. * This match supersedes the CLUSTERIP target. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy --- include/linux/netfilter/Kbuild | 1 + include/linux/netfilter/xt_cluster.h | 15 ++++ net/netfilter/Kconfig | 16 ++++ net/netfilter/Makefile | 1 + net/netfilter/xt_cluster.c | 164 +++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+) create mode 100644 include/linux/netfilter/xt_cluster.h create mode 100644 net/netfilter/xt_cluster.c (limited to 'include/linux') diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index 947b47d7f6c0..af9d2fb97212 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild @@ -21,6 +21,7 @@ header-y += xt_connbytes.h header-y += xt_connlimit.h header-y += xt_connmark.h header-y += xt_conntrack.h +header-y += xt_cluster.h header-y += xt_dccp.h header-y += xt_dscp.h header-y += xt_esp.h diff --git a/include/linux/netfilter/xt_cluster.h b/include/linux/netfilter/xt_cluster.h new file mode 100644 index 000000000000..5e0a0d07b526 --- /dev/null +++ b/include/linux/netfilter/xt_cluster.h @@ -0,0 +1,15 @@ +#ifndef _XT_CLUSTER_MATCH_H +#define _XT_CLUSTER_MATCH_H + +enum xt_cluster_flags { + XT_CLUSTER_F_INV = (1 << 0) +}; + +struct xt_cluster_match_info { + u_int32_t total_nodes; + u_int32_t node_mask; + u_int32_t hash_seed; + u_int32_t flags; +}; + +#endif /* _XT_CLUSTER_MATCH_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index cdbaaff6d0d6..2562d05dbaf5 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -527,6 +527,22 @@ config NETFILTER_XT_TARGET_TCPOPTSTRIP This option adds a "TCPOPTSTRIP" target, which allows you to strip TCP options from TCP packets. +config NETFILTER_XT_MATCH_CLUSTER + tristate '"cluster" match support' + depends on NF_CONNTRACK + depends on NETFILTER_ADVANCED + ---help--- + This option allows you to build work-load-sharing clusters of + network servers/stateful firewalls without having a dedicated + load-balancing router/server/switch. Basically, this match returns + true when the packet must be handled by this cluster node. Thus, + all nodes see all packets and this match decides which node handles + what packets. The work-load sharing algorithm is based on source + address hashing. + + If you say Y or M here, try `iptables -m cluster --help` for + more information. + config NETFILTER_XT_MATCH_COMMENT tristate '"comment" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 7a9b8397573a..6282060fbda9 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o # matches +obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c new file mode 100644 index 000000000000..ad5bd890e4e8 --- /dev/null +++ b/net/netfilter/xt_cluster.c @@ -0,0 +1,164 @@ +/* + * (C) 2008-2009 Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +static inline u_int32_t nf_ct_orig_ipv4_src(const struct nf_conn *ct) +{ + return ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; +} + +static inline const void *nf_ct_orig_ipv6_src(const struct nf_conn *ct) +{ + return ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip6; +} + +static inline u_int32_t +xt_cluster_hash_ipv4(u_int32_t ip, const struct xt_cluster_match_info *info) +{ + return jhash_1word(ip, info->hash_seed); +} + +static inline u_int32_t +xt_cluster_hash_ipv6(const void *ip, const struct xt_cluster_match_info *info) +{ + return jhash2(ip, NF_CT_TUPLE_L3SIZE / sizeof(__u32), info->hash_seed); +} + +static inline u_int32_t +xt_cluster_hash(const struct nf_conn *ct, + const struct xt_cluster_match_info *info) +{ + u_int32_t hash = 0; + + switch(nf_ct_l3num(ct)) { + case AF_INET: + hash = xt_cluster_hash_ipv4(nf_ct_orig_ipv4_src(ct), info); + break; + case AF_INET6: + hash = xt_cluster_hash_ipv6(nf_ct_orig_ipv6_src(ct), info); + break; + default: + WARN_ON(1); + break; + } + return (((u64)hash * info->total_nodes) >> 32); +} + +static inline bool +xt_cluster_is_multicast_addr(const struct sk_buff *skb, u_int8_t family) +{ + bool is_multicast = false; + + switch(family) { + case NFPROTO_IPV4: + is_multicast = ipv4_is_multicast(ip_hdr(skb)->daddr); + break; + case NFPROTO_IPV6: + is_multicast = ipv6_addr_type(&ipv6_hdr(skb)->daddr) & + IPV6_ADDR_MULTICAST; + break; + default: + WARN_ON(1); + break; + } + return is_multicast; +} + +static bool +xt_cluster_mt(const struct sk_buff *skb, const struct xt_match_param *par) +{ + struct sk_buff *pskb = (struct sk_buff *)skb; + const struct xt_cluster_match_info *info = par->matchinfo; + const struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + unsigned long hash; + + /* This match assumes that all nodes see the same packets. This can be + * achieved if the switch that connects the cluster nodes support some + * sort of 'port mirroring'. However, if your switch does not support + * this, your cluster nodes can reply ARP request using a multicast MAC + * address. Thus, your switch will flood the same packets to the + * cluster nodes with the same multicast MAC address. Using a multicast + * link address is a RFC 1812 (section 3.3.2) violation, but this works + * fine in practise. + * + * Unfortunately, if you use the multicast MAC address, the link layer + * sets skbuff's pkt_type to PACKET_MULTICAST, which is not accepted + * by TCP and others for packets coming to this node. For that reason, + * this match mangles skbuff's pkt_type if it detects a packet + * addressed to a unicast address but using PACKET_MULTICAST. Yes, I + * know, matches should not alter packets, but we are doing this here + * because we would need to add a PKTTYPE target for this sole purpose. + */ + if (!xt_cluster_is_multicast_addr(skb, par->family) && + skb->pkt_type == PACKET_MULTICAST) { + pskb->pkt_type = PACKET_HOST; + } + + ct = nf_ct_get(skb, &ctinfo); + if (ct == NULL) + return false; + + if (ct == &nf_conntrack_untracked) + return false; + + if (ct->master) + hash = xt_cluster_hash(ct->master, info); + else + hash = xt_cluster_hash(ct, info); + + return !!((1 << hash) & info->node_mask) ^ + !!(info->flags & XT_CLUSTER_F_INV); +} + +static bool xt_cluster_mt_checkentry(const struct xt_mtchk_param *par) +{ + struct xt_cluster_match_info *info = par->matchinfo; + + if (info->node_mask >= (1 << info->total_nodes)) { + printk(KERN_ERR "xt_cluster: this node mask cannot be " + "higher than the total number of nodes\n"); + return false; + } + return true; +} + +static struct xt_match xt_cluster_match __read_mostly = { + .name = "cluster", + .family = NFPROTO_UNSPEC, + .match = xt_cluster_mt, + .checkentry = xt_cluster_mt_checkentry, + .matchsize = sizeof(struct xt_cluster_match_info), + .me = THIS_MODULE, +}; + +static int __init xt_cluster_mt_init(void) +{ + return xt_register_match(&xt_cluster_match); +} + +static void __exit xt_cluster_mt_fini(void) +{ + xt_unregister_match(&xt_cluster_match); +} + +MODULE_AUTHOR("Pablo Neira Ayuso "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Xtables: hash-based cluster match"); +MODULE_ALIAS("ipt_cluster"); +MODULE_ALIAS("ip6t_cluster"); +module_init(xt_cluster_mt_init); +module_exit(xt_cluster_mt_fini); -- cgit v1.2.3 From d1c76af9e2434fac3add561e26c61b06503de986 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 16 Mar 2009 10:50:02 -0700 Subject: GRO: Move netpoll checks to correct location As my netpoll fix for net doesn't really work for net-next, we need this update to move the checks into the right place. As it stands we may pass freed skbs to netpoll_receive_skb. This patch also introduces a netpoll_rx_on function to avoid GRO completely if we're invoked through netpoll. This might seem paranoid but as netpoll may have an external receive hook it's better to be safe than sorry. I don't think we need this for 2.6.29 though since there's nothing immediately broken by it. This patch also moves the GRO_* return values to netdevice.h since VLAN needs them too (I tried to avoid this originally but alas this seems to be the easiest way out). This fixes a bug in VLAN where it continued to use the old return value 2 instead of the correct GRO_DROP. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/netdevice.h | 8 ++++++++ include/linux/netpoll.h | 11 +++++++++++ net/8021q/vlan_core.c | 11 ++++------- net/core/dev.c | 17 +++-------------- 4 files changed, 26 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 493b065f76d7..be3ebd7e8ce5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -330,6 +330,14 @@ enum NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */ }; +enum { + GRO_MERGED, + GRO_MERGED_FREE, + GRO_HELD, + GRO_NORMAL, + GRO_DROP, +}; + extern void __napi_schedule(struct napi_struct *n); static inline int napi_disable_pending(struct napi_struct *n) diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index e38d3c9dccda..de99025f2c5d 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -63,6 +63,13 @@ static inline int netpoll_rx(struct sk_buff *skb) return ret; } +static inline int netpoll_rx_on(struct sk_buff *skb) +{ + struct netpoll_info *npinfo = skb->dev->npinfo; + + return npinfo && (npinfo->rx_np || npinfo->rx_flags); +} + static inline int netpoll_receive_skb(struct sk_buff *skb) { if (!list_empty(&skb->dev->napi_list)) @@ -99,6 +106,10 @@ static inline int netpoll_rx(struct sk_buff *skb) { return 0; } +static inline int netpoll_rx_on(struct sk_buff *skb) +{ + return 0; +} static inline int netpoll_receive_skb(struct sk_buff *skb) { return 0; diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 2d6e405fc498..6227248597c4 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -79,6 +79,9 @@ static int vlan_gro_common(struct napi_struct *napi, struct vlan_group *grp, { struct sk_buff *p; + if (netpoll_rx_on(skb)) + return GRO_NORMAL; + if (skb_bond_should_drop(skb)) goto drop; @@ -98,7 +101,7 @@ static int vlan_gro_common(struct napi_struct *napi, struct vlan_group *grp, return dev_gro_receive(napi, skb); drop: - return 2; + return GRO_DROP; } int vlan_gro_receive(struct napi_struct *napi, struct vlan_group *grp, @@ -106,9 +109,6 @@ int vlan_gro_receive(struct napi_struct *napi, struct vlan_group *grp, { skb_gro_reset_offset(skb); - if (netpoll_receive_skb(skb)) - return NET_RX_DROP; - return napi_skb_finish(vlan_gro_common(napi, grp, vlan_tci, skb), skb); } EXPORT_SYMBOL(vlan_gro_receive); @@ -121,9 +121,6 @@ int vlan_gro_frags(struct napi_struct *napi, struct vlan_group *grp, if (!skb) return NET_RX_DROP; - if (netpoll_receive_skb(skb)) - return NET_RX_DROP; - return napi_frags_finish(napi, skb, vlan_gro_common(napi, grp, vlan_tci, skb)); } diff --git a/net/core/dev.c b/net/core/dev.c index 033d7ca28e6e..7bd3c29c5a78 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -135,14 +135,6 @@ /* This should be increased if a protocol with a bigger head is added. */ #define GRO_MAX_HEAD (MAX_HEADER + 128) -enum { - GRO_MERGED, - GRO_MERGED_FREE, - GRO_HELD, - GRO_NORMAL, - GRO_DROP, -}; - /* * The list of packet types we will receive (as opposed to discard) * and the routines to invoke. @@ -2474,6 +2466,9 @@ static int __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) { struct sk_buff *p; + if (netpoll_rx_on(skb)) + return GRO_NORMAL; + for (p = napi->gro_list; p; p = p->next) { NAPI_GRO_CB(p)->same_flow = !compare_ether_header( skb_mac_header(p), skb_gro_mac_header(skb)); @@ -2487,9 +2482,6 @@ int napi_skb_finish(int ret, struct sk_buff *skb) { int err = NET_RX_SUCCESS; - if (netpoll_receive_skb(skb)) - return NET_RX_DROP; - switch (ret) { case GRO_NORMAL: return netif_receive_skb(skb); @@ -2587,9 +2579,6 @@ int napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb, int ret) { int err = NET_RX_SUCCESS; - if (netpoll_receive_skb(skb)) - return NET_RX_DROP; - switch (ret) { case GRO_NORMAL: case GRO_HELD: -- cgit v1.2.3 From 7db90f4a25bd4184f3d36dfa4f512f53b0448da7 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 9 Mar 2009 22:07:41 -0400 Subject: cfg80211: move enum reg_set_by to nl80211.h We do this so we can later inform userspace who set the regulatory domain and provide details of the request. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 2 +- drivers/net/wireless/ath9k/regd.c | 31 ++++++++------- drivers/net/wireless/ath9k/regd.h | 3 +- include/linux/nl80211.h | 19 +++++++++ include/net/cfg80211.h | 24 ++---------- net/wireless/core.c | 2 +- net/wireless/core.h | 3 +- net/wireless/reg.c | 82 +++++++++++++++++++++------------------ 8 files changed, 90 insertions(+), 76 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 1d6b05c0d800..e9b3f365f099 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1670,7 +1670,7 @@ int ath_attach(u16 devid, struct ath_softc *sc) } wiphy_apply_custom_regulatory(hw->wiphy, regd); ath9k_reg_apply_radar_flags(hw->wiphy); - ath9k_reg_apply_world_flags(hw->wiphy, REGDOM_SET_BY_DRIVER); + ath9k_reg_apply_world_flags(hw->wiphy, NL80211_REGDOM_SET_BY_DRIVER); INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c index ff0afc02f3ce..b8f9b6d6bec4 100644 --- a/drivers/net/wireless/ath9k/regd.c +++ b/drivers/net/wireless/ath9k/regd.c @@ -168,8 +168,9 @@ static bool ath9k_is_radar_freq(u16 center_freq) * received a beacon on a channel we can enable active scan and * adhoc (or beaconing). */ -static void ath9k_reg_apply_beaconing_flags(struct wiphy *wiphy, - enum reg_set_by setby) +static void ath9k_reg_apply_beaconing_flags( + struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) { enum ieee80211_band band; struct ieee80211_supported_band *sband; @@ -194,7 +195,7 @@ static void ath9k_reg_apply_beaconing_flags(struct wiphy *wiphy, (ch->flags & IEEE80211_CHAN_RADAR)) continue; - if (setby == REGDOM_SET_BY_COUNTRY_IE) { + if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { r = freq_reg_info(wiphy, ch->center_freq, &bandwidth, ®_rule); if (r) @@ -226,8 +227,9 @@ static void ath9k_reg_apply_beaconing_flags(struct wiphy *wiphy, } /* Allows active scan scan on Ch 12 and 13 */ -static void ath9k_reg_apply_active_scan_flags(struct wiphy *wiphy, - enum reg_set_by setby) +static void ath9k_reg_apply_active_scan_flags( + struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) { struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; @@ -241,7 +243,7 @@ static void ath9k_reg_apply_active_scan_flags(struct wiphy *wiphy, * If no country IE has been received always enable active scan * on these channels. This is only done for specific regulatory SKUs */ - if (setby != REGDOM_SET_BY_COUNTRY_IE) { + if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { ch = &sband->channels[11]; /* CH 12 */ if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; @@ -308,7 +310,8 @@ void ath9k_reg_apply_radar_flags(struct wiphy *wiphy) } } -void ath9k_reg_apply_world_flags(struct wiphy *wiphy, enum reg_set_by setby) +void ath9k_reg_apply_world_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath_wiphy *aphy = hw->priv; @@ -320,11 +323,11 @@ void ath9k_reg_apply_world_flags(struct wiphy *wiphy, enum reg_set_by setby) case 0x63: case 0x66: case 0x67: - ath9k_reg_apply_beaconing_flags(wiphy, setby); + ath9k_reg_apply_beaconing_flags(wiphy, initiator); break; case 0x68: - ath9k_reg_apply_beaconing_flags(wiphy, setby); - ath9k_reg_apply_active_scan_flags(wiphy, setby); + ath9k_reg_apply_beaconing_flags(wiphy, initiator); + ath9k_reg_apply_active_scan_flags(wiphy, initiator); break; } return; @@ -340,11 +343,11 @@ int ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) ath9k_reg_apply_radar_flags(wiphy); switch (request->initiator) { - case REGDOM_SET_BY_DRIVER: - case REGDOM_SET_BY_CORE: - case REGDOM_SET_BY_USER: + case NL80211_REGDOM_SET_BY_DRIVER: + case NL80211_REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_USER: break; - case REGDOM_SET_BY_COUNTRY_IE: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: if (ath9k_is_world_regd(sc->sc_ah)) ath9k_reg_apply_world_flags(wiphy, request->initiator); break; diff --git a/drivers/net/wireless/ath9k/regd.h b/drivers/net/wireless/ath9k/regd.h index d48160d0c0e9..8f885f3bc8df 100644 --- a/drivers/net/wireless/ath9k/regd.h +++ b/drivers/net/wireless/ath9k/regd.h @@ -236,7 +236,8 @@ enum CountryCode { bool ath9k_is_world_regd(struct ath_hw *ah); const struct ieee80211_regdomain *ath9k_world_regdomain(struct ath_hw *ah); const struct ieee80211_regdomain *ath9k_default_world_regdomain(void); -void ath9k_reg_apply_world_flags(struct wiphy *wiphy, enum reg_set_by setby); +void ath9k_reg_apply_world_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator); void ath9k_reg_apply_radar_flags(struct wiphy *wiphy); int ath9k_regd_init(struct ath_hw *ah); bool ath9k_regd_is_eeprom_valid(struct ath_hw *ah); diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index f6e56370ea65..c0fd432b57dc 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -672,6 +672,25 @@ enum nl80211_bitrate_attr { NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 }; +/** + * enum nl80211_initiator - Indicates the initiator of a reg domain request + * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world + * regulatory domain. + * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the + * regulatory domain. + * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the + * wireless core it thinks its knows the regulatory domain we should be in. + * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an + * 802.11 country information element with regulatory information it + * thinks we should consider. + */ +enum nl80211_reg_initiator { + NL80211_REGDOM_SET_BY_CORE, + NL80211_REGDOM_SET_BY_USER, + NL80211_REGDOM_SET_BY_DRIVER, + NL80211_REGDOM_SET_BY_COUNTRY_IE, +}; + /** * enum nl80211_reg_rule_attr - regulatory rule attributes * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f195ea460811..50f3fd9ff524 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -348,28 +348,10 @@ struct bss_parameters { u8 basic_rates_len; }; -/** - * enum reg_set_by - Indicates who is trying to set the regulatory domain - * @REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world regulatory domain. - * @REGDOM_SET_BY_USER: User asked the wireless core to set the - * regulatory domain. - * @REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the wireless core - * it thinks its knows the regulatory domain we should be in. - * @REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an 802.11 country - * information element with regulatory information it thinks we - * should consider. - */ -enum reg_set_by { - REGDOM_SET_BY_CORE, - REGDOM_SET_BY_USER, - REGDOM_SET_BY_DRIVER, - REGDOM_SET_BY_COUNTRY_IE, -}; - /** * enum environment_cap - Environment parsed from country IE * @ENVIRON_ANY: indicates country IE applies to both indoor and - * outdoor operation. + * outdoor operation. * @ENVIRON_INDOOR: indicates country IE applies only to indoor operation * @ENVIRON_OUTDOOR: indicates country IE applies only to outdoor operation */ @@ -388,7 +370,7 @@ enum environment_cap { * and potentially inform users of which devices specifically * cased the conflicts. * @initiator: indicates who sent this request, could be any of - * of those set in reg_set_by, %REGDOM_SET_BY_* + * of those set in nl80211_reg_initiator (%NL80211_REGDOM_SET_BY_*) * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested * regulatory domain. We have a few special codes: * 00 - World regulatory domain @@ -405,7 +387,7 @@ enum environment_cap { */ struct regulatory_request { int wiphy_idx; - enum reg_set_by initiator; + enum nl80211_reg_initiator initiator; char alpha2[2]; bool intersect; u32 country_ie_checksum; diff --git a/net/wireless/core.c b/net/wireless/core.c index dd7f222919fe..c939f5ee065e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -350,7 +350,7 @@ int wiphy_register(struct wiphy *wiphy) mutex_lock(&cfg80211_mutex); /* set up regulatory info */ - wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE); + wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); res = device_add(&drv->wiphy.dev); if (res) diff --git a/net/wireless/core.h b/net/wireless/core.h index f6c53f5807f4..6acd483a61f8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -136,7 +136,8 @@ extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, char *newname); void ieee80211_set_bitrate_flags(struct wiphy *wiphy); -void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby); +void wiphy_update_regulatory(struct wiphy *wiphy, + enum nl80211_reg_initiator setby); void cfg80211_bss_expire(struct cfg80211_registered_device *dev); void cfg80211_bss_age(struct cfg80211_registered_device *dev, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 47ff44751b70..68fde6d33dc3 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -857,8 +857,8 @@ static int freq_reg_info_regd(struct wiphy *wiphy, * Follow the driver's regulatory domain, if present, unless a country * IE has been processed or a user wants to help complaince further */ - if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE && - last_request->initiator != REGDOM_SET_BY_USER && + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + last_request->initiator != NL80211_REGDOM_SET_BY_USER && wiphy->regd) regd = wiphy->regd; @@ -943,7 +943,8 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, * http://tinyurl.com/11d-clarification */ if (r == -ERANGE && - last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { + last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) { #ifdef CONFIG_CFG80211_REG_DEBUG printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz " "intact on %s - no rule found in band on " @@ -956,7 +957,8 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, * for the band so we respect its band definitions */ #ifdef CONFIG_CFG80211_REG_DEBUG - if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) + if (last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) printk(KERN_DEBUG "cfg80211: Disabling " "channel %d MHz on %s due to " "Country IE\n", @@ -970,7 +972,7 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, power_rule = ®_rule->power_rule; - if (last_request->initiator == REGDOM_SET_BY_DRIVER && + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && request_wiphy && request_wiphy == wiphy && request_wiphy->strict_regulatory) { /* @@ -1011,11 +1013,12 @@ static void handle_band(struct wiphy *wiphy, enum ieee80211_band band) handle_channel(wiphy, band, i); } -static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) +static bool ignore_reg_update(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) { if (!last_request) return true; - if (setby == REGDOM_SET_BY_CORE && + if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->custom_regulatory) return true; /* @@ -1028,12 +1031,12 @@ static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) return false; } -static void update_all_wiphy_regulatory(enum reg_set_by setby) +static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) { struct cfg80211_registered_device *drv; list_for_each_entry(drv, &cfg80211_drv_list, list) - wiphy_update_regulatory(&drv->wiphy, setby); + wiphy_update_regulatory(&drv->wiphy, initiator); } static void handle_reg_beacon(struct wiphy *wiphy, @@ -1124,7 +1127,7 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) if (is_world_regdom(cfg80211_regdomain->alpha2) || (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) return true; - if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE && + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy->custom_regulatory) return true; return false; @@ -1138,11 +1141,12 @@ static void reg_process_beacons(struct wiphy *wiphy) wiphy_update_beacon_reg(wiphy); } -void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) +void wiphy_update_regulatory(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) { enum ieee80211_band band; - if (ignore_reg_update(wiphy, setby)) + if (ignore_reg_update(wiphy, initiator)) goto out; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) @@ -1255,15 +1259,16 @@ static int ignore_request(struct wiphy *wiphy, return 0; switch (pending_request->initiator) { - case REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_CORE: return -EINVAL; - case REGDOM_SET_BY_COUNTRY_IE: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); if (unlikely(!is_an_alpha2(pending_request->alpha2))) return -EINVAL; - if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { + if (last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) { if (last_wiphy != wiphy) { /* * Two cards with two APs claiming different @@ -1284,8 +1289,8 @@ static int ignore_request(struct wiphy *wiphy, return -EALREADY; } return REG_INTERSECT; - case REGDOM_SET_BY_DRIVER: - if (last_request->initiator == REGDOM_SET_BY_CORE) { + case NL80211_REGDOM_SET_BY_DRIVER: + if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { if (is_old_static_regdom(cfg80211_regdomain)) return 0; if (regdom_changes(pending_request->alpha2)) @@ -1298,28 +1303,28 @@ static int ignore_request(struct wiphy *wiphy, * back in or if you add a new device for which the previously * loaded card also agrees on the regulatory domain. */ - if (last_request->initiator == REGDOM_SET_BY_DRIVER && + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !regdom_changes(pending_request->alpha2)) return -EALREADY; return REG_INTERSECT; - case REGDOM_SET_BY_USER: - if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) + case NL80211_REGDOM_SET_BY_USER: + if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) return REG_INTERSECT; /* * If the user knows better the user should set the regdom * to their country before the IE is picked up */ - if (last_request->initiator == REGDOM_SET_BY_USER && + if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && last_request->intersect) return -EOPNOTSUPP; /* * Process user requests only after previous user/driver/core * requests have been processed */ - if (last_request->initiator == REGDOM_SET_BY_CORE || - last_request->initiator == REGDOM_SET_BY_DRIVER || - last_request->initiator == REGDOM_SET_BY_USER) { + if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || + last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || + last_request->initiator == NL80211_REGDOM_SET_BY_USER) { if (regdom_changes(last_request->alpha2)) return -EAGAIN; } @@ -1359,7 +1364,8 @@ static int __regulatory_hint(struct wiphy *wiphy, r = ignore_request(wiphy, pending_request); if (r == REG_INTERSECT) { - if (pending_request->initiator == REGDOM_SET_BY_DRIVER) { + if (pending_request->initiator == + NL80211_REGDOM_SET_BY_DRIVER) { r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); if (r) { kfree(pending_request); @@ -1374,7 +1380,8 @@ static int __regulatory_hint(struct wiphy *wiphy, * wiphy */ if (r == -EALREADY && - pending_request->initiator == REGDOM_SET_BY_DRIVER) { + pending_request->initiator == + NL80211_REGDOM_SET_BY_DRIVER) { r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); if (r) { kfree(pending_request); @@ -1425,7 +1432,7 @@ static void reg_process_hint(struct regulatory_request *reg_request) if (wiphy_idx_valid(reg_request->wiphy_idx)) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); - if (reg_request->initiator == REGDOM_SET_BY_DRIVER && + if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { kfree(reg_request); goto out; @@ -1439,7 +1446,7 @@ out: mutex_unlock(&cfg80211_mutex); } -/* Processes regulatory hints, this is all the REGDOM_SET_BY_* */ +/* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */ static void reg_process_pending_hints(void) { struct regulatory_request *reg_request; @@ -1523,7 +1530,7 @@ static int regulatory_hint_core(const char *alpha2) request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; - request->initiator = REGDOM_SET_BY_CORE; + request->initiator = NL80211_REGDOM_SET_BY_CORE; queue_regulatory_request(request); @@ -1544,7 +1551,7 @@ int regulatory_hint_user(const char *alpha2) request->wiphy_idx = WIPHY_IDX_STALE; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; - request->initiator = REGDOM_SET_BY_USER, + request->initiator = NL80211_REGDOM_SET_BY_USER, queue_regulatory_request(request); @@ -1570,7 +1577,7 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; - request->initiator = REGDOM_SET_BY_DRIVER; + request->initiator = NL80211_REGDOM_SET_BY_DRIVER; queue_regulatory_request(request); @@ -1719,7 +1726,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, request->wiphy_idx = get_wiphy_idx(wiphy); request->alpha2[0] = rd->alpha2[0]; request->alpha2[1] = rd->alpha2[1]; - request->initiator = REGDOM_SET_BY_COUNTRY_IE; + request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; request->country_ie_checksum = checksum; request->country_ie_env = env; @@ -1827,7 +1834,8 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) if (is_intersected_alpha2(rd->alpha2)) { - if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) { + if (last_request->initiator == + NL80211_REGDOM_SET_BY_COUNTRY_IE) { struct cfg80211_registered_device *drv; drv = cfg80211_drv_by_wiphy_idx( last_request->wiphy_idx); @@ -1919,7 +1927,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * rd is non static (it means CRDA was present and was used last) * and the pending request came in from a country IE */ - if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) { + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { /* * If someone else asked us to change the rd lets only bother * checking if the alpha2 changes if CRDA was already called @@ -1951,7 +1959,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) if (!last_request->intersect) { int r; - if (last_request->initiator != REGDOM_SET_BY_DRIVER) { + if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { reset_regdomains(); cfg80211_regdomain = rd; return 0; @@ -1975,7 +1983,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) /* Intersection requires a bit more work */ - if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) { + if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { intersected_rd = regdom_intersect(rd, cfg80211_regdomain); if (!intersected_rd) @@ -1986,7 +1994,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) * However if a driver requested this specific regulatory * domain we keep it for its private use */ - if (last_request->initiator == REGDOM_SET_BY_DRIVER) + if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) request_wiphy->regd = rd; else kfree(rd); -- cgit v1.2.3 From 73d54c9e74c4d8ee8a41bc516f481f0f754eca32 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 9 Mar 2009 22:07:42 -0400 Subject: cfg80211: add regulatory netlink multicast group This allows us to send to userspace "regulatory" events. For now we just send an event when we change regulatory domains. We also notify userspace when devices are using their own custom world roaming regulatory domains. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/linux/nl80211.h | 48 ++++++++++++++++++++++++++++++++++++++ net/wireless/core.c | 11 +++++++++ net/wireless/nl80211.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 5 ++++ net/wireless/reg.c | 13 ++++++++++- 5 files changed, 138 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c0fd432b57dc..f33aa08dd9b3 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -150,6 +150,17 @@ * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, * partial scan results may be available * + * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain + * has been changed and provides details of the request information + * that caused the change such as who initiated the regulatory request + * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx + * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if + * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or + * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain + * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is + * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on + * to (%NL80211_ATTR_REG_ALPHA2). + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -204,6 +215,8 @@ enum nl80211_commands { NL80211_CMD_NEW_SCAN_RESULTS, NL80211_CMD_SCAN_ABORTED, + NL80211_CMD_REG_CHANGE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -218,6 +231,8 @@ enum nl80211_commands { #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS #define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE +#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE + /** * enum nl80211_attrs - nl80211 netlink attributes * @@ -329,6 +344,11 @@ enum nl80211_commands { * messages carried the same generation number) * @NL80211_ATTR_BSS: scan result BSS * + * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain + * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* + * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently + * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -403,6 +423,9 @@ enum nl80211_attrs { NL80211_ATTR_SCAN_GENERATION, NL80211_ATTR_BSS, + NL80211_ATTR_REG_INITIATOR, + NL80211_ATTR_REG_TYPE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -420,6 +443,8 @@ enum nl80211_attrs { #define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE #define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE #define NL80211_ATTR_IE NL80211_ATTR_IE +#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR +#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 @@ -691,6 +716,29 @@ enum nl80211_reg_initiator { NL80211_REGDOM_SET_BY_COUNTRY_IE, }; +/** + * enum nl80211_reg_type - specifies the type of regulatory domain + * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains + * to a specific country. When this is set you can count on the + * ISO / IEC 3166 alpha2 country code being valid. + * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory + * domain. + * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom + * driver specific world regulatory domain. These do not apply system-wide + * and are only applicable to the individual devices which have requested + * them to be applied. + * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product + * of an intersection between two regulatory domains -- the previously + * set regulatory domain on the system and the last accepted regulatory + * domain request to be processed. + */ +enum nl80211_reg_type { + NL80211_REGDOM_TYPE_COUNTRY, + NL80211_REGDOM_TYPE_WORLD, + NL80211_REGDOM_TYPE_CUSTOM_WORLD, + NL80211_REGDOM_TYPE_INTERSECTION, +}; + /** * enum nl80211_reg_rule_attr - regulatory rule attributes * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional diff --git a/net/wireless/core.c b/net/wireless/core.c index c939f5ee065e..17fe39049740 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -365,6 +365,17 @@ int wiphy_register(struct wiphy *wiphy) if (IS_ERR(drv->wiphy.debugfsdir)) drv->wiphy.debugfsdir = NULL; + if (wiphy->custom_regulatory) { + struct regulatory_request request; + + request.wiphy_idx = get_wiphy_idx(wiphy); + request.initiator = NL80211_REGDOM_SET_BY_DRIVER; + request.alpha2[0] = '9'; + request.alpha2[1] = '9'; + + nl80211_send_reg_change_event(&request); + } + res = 0; out_unlock: mutex_unlock(&cfg80211_mutex); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 531bb67cf502..8ac3d26014a8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2739,6 +2739,9 @@ static struct genl_multicast_group nl80211_config_mcgrp = { static struct genl_multicast_group nl80211_scan_mcgrp = { .name = "scan", }; +static struct genl_multicast_group nl80211_regulatory_mcgrp = { + .name = "regulatory", +}; /* notification functions */ @@ -2818,6 +2821,61 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); } +/* + * This can happen on global regulatory changes or device specific settings + * based on custom world regulatory domains. + */ +void nl80211_send_reg_change_event(struct regulatory_request *request) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + /* Userspace can always count this one always being set */ + NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator); + + if (request->alpha2[0] == '0' && request->alpha2[1] == '0') + NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, + NL80211_REGDOM_TYPE_WORLD); + else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') + NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, + NL80211_REGDOM_TYPE_CUSTOM_WORLD); + else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') || + request->intersect) + NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, + NL80211_REGDOM_TYPE_INTERSECTION); + else { + NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, + NL80211_REGDOM_TYPE_COUNTRY); + NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2); + } + + if (wiphy_idx_valid(request->wiphy_idx)) + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL); + + return; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + /* initialisation/exit functions */ int nl80211_init(void) @@ -2842,6 +2900,10 @@ int nl80211_init(void) if (err) goto err_out; + err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp); + if (err) + goto err_out; + return 0; err_out: genl_unregister_family(&nl80211_fam); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 69787b621365..e65a3c38c52f 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -11,6 +11,7 @@ extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, struct net_device *netdev); extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, struct net_device *netdev); +extern void nl80211_send_reg_change_event(struct regulatory_request *request); #else static inline int nl80211_init(void) { @@ -31,6 +32,10 @@ static inline void nl80211_send_scan_aborted( struct cfg80211_registered_device *rdev, struct net_device *netdev) {} +static inline void +nl80211_send_reg_change_event(struct regulatory_request *request) +{ +} #endif /* CONFIG_NL80211 */ #endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 68fde6d33dc3..eb8b8ed16155 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -41,6 +41,7 @@ #include #include "core.h" #include "reg.h" +#include "nl80211.h" /* Receipt of information from last regulatory request */ static struct regulatory_request *last_request; @@ -1403,8 +1404,16 @@ new_request: pending_request = NULL; /* When r == REG_INTERSECT we do need to call CRDA */ - if (r < 0) + if (r < 0) { + /* + * Since CRDA will not be called in this case as we already + * have applied the requested regulatory domain before we just + * inform userspace we have processed the request + */ + if (r == -EALREADY) + nl80211_send_reg_change_event(last_request); return r; + } /* * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled @@ -2084,6 +2093,8 @@ int set_regdom(const struct ieee80211_regdomain *rd) print_regdomain(cfg80211_regdomain); + nl80211_send_reg_change_event(last_request); + return r; } -- cgit v1.2.3 From 2e1ab634bf013792d8803ec57c7a428a76f50028 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 19 Mar 2009 23:49:41 -0700 Subject: rtnetlink: add new value for DHCP added routes To improve manageability, it would be good to be able to disambiguate routes added by administrator from those added by DHCP client. The only necessary kernel change is to add value to rtnetlink include file so iproute2 utility can use it. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 35a07c830f79..ba3254ecf7fb 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -217,6 +217,7 @@ enum #define RTPROT_DNROUTED 13 /* DECnet routing daemon */ #define RTPROT_XORP 14 /* XORP */ #define RTPROT_NTK 15 /* Netsukuku */ +#define RTPROT_DHCP 16 /* DHCP client */ /* rtm_scope -- cgit v1.2.3 From 5e140dfc1fe87eae27846f193086724806b33c7d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 20 Mar 2009 01:33:32 -0700 Subject: net: reorder struct Qdisc for better SMP performance dev_queue_xmit() needs to dirty fields "state", "q", "bstats" and "qstats" On x86_64 arch, they currently span three cache lines, involving more cache line ping pongs than necessary, making longer holding of queue spinlock. We can reduce this to one cache line, by grouping all read-mostly fields at the beginning of structure. (Or should I say, all highly modified fields at the end :) ) Before patch : offsetof(struct Qdisc, state)=0x38 offsetof(struct Qdisc, q)=0x48 offsetof(struct Qdisc, bstats)=0x80 offsetof(struct Qdisc, qstats)=0x90 sizeof(struct Qdisc)=0xc8 After patch : offsetof(struct Qdisc, state)=0x80 offsetof(struct Qdisc, q)=0x88 offsetof(struct Qdisc, bstats)=0xa0 offsetof(struct Qdisc, qstats)=0xac sizeof(struct Qdisc)=0xc0 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/gen_stats.h | 2 +- include/net/sch_generic.h | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/gen_stats.h b/include/linux/gen_stats.h index 13f4e74609ac..0ffa41df0ee8 100644 --- a/include/linux/gen_stats.h +++ b/include/linux/gen_stats.h @@ -22,7 +22,7 @@ struct gnet_stats_basic { __u64 bytes; __u32 packets; -}; +} __attribute__ ((packed)); /** * struct gnet_stats_rate_est - rate estimator diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 3d78a4d22460..964ffa0d8815 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -49,18 +49,10 @@ struct Qdisc int padded; struct Qdisc_ops *ops; struct qdisc_size_table *stab; + struct list_head list; u32 handle; u32 parent; atomic_t refcnt; - unsigned long state; - struct sk_buff *gso_skb; - struct sk_buff_head q; - struct netdev_queue *dev_queue; - struct Qdisc *next_sched; - struct list_head list; - - struct gnet_stats_basic bstats; - struct gnet_stats_queue qstats; struct gnet_stats_rate_est rate_est; int (*reshape_fail)(struct sk_buff *skb, struct Qdisc *q); @@ -71,6 +63,17 @@ struct Qdisc * and it will live until better solution will be invented. */ struct Qdisc *__parent; + struct netdev_queue *dev_queue; + struct Qdisc *next_sched; + + struct sk_buff *gso_skb; + /* + * For performance sake on SMP, we put highly modified fields at the end + */ + unsigned long state; + struct sk_buff_head q; + struct gnet_stats_basic bstats; + struct gnet_stats_queue qstats; }; struct Qdisc_class_ops -- cgit v1.2.3 From 9247744e5eaa29aecee5342a0c8694187a6aadcd Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sat, 21 Mar 2009 13:39:26 -0700 Subject: skb: expose and constify hash primitives Some minor changes to queue hashing: 1. Use const on accessor functions 2. Export skb_tx_hash for use in drivers (see ixgbe) Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/skbuff.h | 9 ++++++--- net/core/dev.c | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 1fbab2ae613c..bb1981fd60f3 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1969,7 +1969,7 @@ static inline void skb_set_queue_mapping(struct sk_buff *skb, u16 queue_mapping) skb->queue_mapping = queue_mapping; } -static inline u16 skb_get_queue_mapping(struct sk_buff *skb) +static inline u16 skb_get_queue_mapping(const struct sk_buff *skb) { return skb->queue_mapping; } @@ -1984,16 +1984,19 @@ static inline void skb_record_rx_queue(struct sk_buff *skb, u16 rx_queue) skb->queue_mapping = rx_queue + 1; } -static inline u16 skb_get_rx_queue(struct sk_buff *skb) +static inline u16 skb_get_rx_queue(const struct sk_buff *skb) { return skb->queue_mapping - 1; } -static inline bool skb_rx_queue_recorded(struct sk_buff *skb) +static inline bool skb_rx_queue_recorded(const struct sk_buff *skb) { return (skb->queue_mapping != 0); } +extern u16 skb_tx_hash(const struct net_device *dev, + const struct sk_buff *skb); + #ifdef CONFIG_XFRM static inline struct sec_path *skb_sec_path(struct sk_buff *skb) { diff --git a/net/core/dev.c b/net/core/dev.c index ca212acd3348..fdb9973b82a6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1725,7 +1725,7 @@ out_kfree_skb: static u32 skb_tx_hashrnd; -static u16 skb_tx_hash(struct net_device *dev, struct sk_buff *skb) +u16 skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb) { u32 hash; @@ -1740,6 +1740,7 @@ static u16 skb_tx_hash(struct net_device *dev, struct sk_buff *skb) return (u16) (((u64) hash * dev->real_num_tx_queues) >> 32); } +EXPORT_SYMBOL(skb_tx_hash); static struct netdev_queue *dev_pick_tx(struct net_device *dev, struct sk_buff *skb) -- cgit v1.2.3 From 777baa4711c6b8373f4e03a3a558d44a6b046d7a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 20 Mar 2009 19:35:54 +0000 Subject: usbnet: support net_device_ops Use net_device_ops for usbnet device, and export for use by other derived drivers. Signed-off-by: Stephen Hemminger Acked-by: David Brownell Signed-off-by: David S. Miller --- drivers/net/usb/usbnet.c | 31 +++++++++++++++++++++++-------- include/linux/usb/usbnet.h | 5 +++++ 2 files changed, 28 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 084141692245..659654f45880 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -223,7 +223,7 @@ EXPORT_SYMBOL_GPL(usbnet_skb_return); * *-------------------------------------------------------------------------*/ -static int usbnet_change_mtu (struct net_device *net, int new_mtu) +int usbnet_change_mtu (struct net_device *net, int new_mtu) { struct usbnet *dev = netdev_priv(net); int ll_mtu = new_mtu + net->hard_header_len; @@ -246,6 +246,7 @@ static int usbnet_change_mtu (struct net_device *net, int new_mtu) return 0; } +EXPORT_SYMBOL_GPL(usbnet_change_mtu); /*-------------------------------------------------------------------------*/ @@ -540,7 +541,7 @@ EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs); // precondition: never called in_interrupt -static int usbnet_stop (struct net_device *net) +int usbnet_stop (struct net_device *net) { struct usbnet *dev = netdev_priv(net); int temp; @@ -584,6 +585,7 @@ static int usbnet_stop (struct net_device *net) return 0; } +EXPORT_SYMBOL_GPL(usbnet_stop); /*-------------------------------------------------------------------------*/ @@ -591,7 +593,7 @@ static int usbnet_stop (struct net_device *net) // precondition: never called in_interrupt -static int usbnet_open (struct net_device *net) +int usbnet_open (struct net_device *net) { struct usbnet *dev = netdev_priv(net); int retval; @@ -666,6 +668,7 @@ done: done_nopm: return retval; } +EXPORT_SYMBOL_GPL(usbnet_open); /*-------------------------------------------------------------------------*/ @@ -900,7 +903,7 @@ static void tx_complete (struct urb *urb) /*-------------------------------------------------------------------------*/ -static void usbnet_tx_timeout (struct net_device *net) +void usbnet_tx_timeout (struct net_device *net) { struct usbnet *dev = netdev_priv(net); @@ -909,10 +912,11 @@ static void usbnet_tx_timeout (struct net_device *net) // FIXME: device recovery -- reset? } +EXPORT_SYMBOL_GPL(usbnet_tx_timeout); /*-------------------------------------------------------------------------*/ -static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) +int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) { struct usbnet *dev = netdev_priv(net); int length; @@ -995,7 +999,7 @@ drop: } return retval; } - +EXPORT_SYMBOL_GPL(usbnet_start_xmit); /*-------------------------------------------------------------------------*/ @@ -1102,6 +1106,15 @@ void usbnet_disconnect (struct usb_interface *intf) } EXPORT_SYMBOL_GPL(usbnet_disconnect); +static const struct net_device_ops usbnet_netdev_ops = { + .ndo_open = usbnet_open, + .ndo_stop = usbnet_stop, + .ndo_start_xmit = usbnet_start_xmit, + .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_change_mtu = usbnet_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; /*-------------------------------------------------------------------------*/ @@ -1171,12 +1184,14 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) net->features |= NETIF_F_HIGHDMA; #endif - net->change_mtu = usbnet_change_mtu; + net->netdev_ops = &usbnet_netdev_ops; +#ifdef CONFIG_COMPAT_NET_DEV_OPS net->hard_start_xmit = usbnet_start_xmit; net->open = usbnet_open; net->stop = usbnet_stop; - net->watchdog_timeo = TX_TIMEOUT_JIFFIES; net->tx_timeout = usbnet_tx_timeout; +#endif + net->watchdog_timeo = TX_TIMEOUT_JIFFIES; net->ethtool_ops = &usbnet_ethtool_ops; // allow device-specific bind/init procedures diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 7d3822243074..36fabb95c7d3 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -176,6 +176,11 @@ struct skb_data { /* skb->cb is one of these */ size_t length; }; +extern int usbnet_open (struct net_device *net); +extern int usbnet_stop (struct net_device *net); +extern int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net); +extern void usbnet_tx_timeout (struct net_device *net); +extern int usbnet_change_mtu (struct net_device *net, int new_mtu); extern int usbnet_get_endpoints(struct usbnet *, struct usb_interface *); extern void usbnet_defer_kevent (struct usbnet *, int); -- cgit v1.2.3 From dd5b6ce6fd465eab90357711c8e8124dc3a31ff0 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 23 Mar 2009 13:21:06 +0100 Subject: nefilter: nfnetlink: add nfnetlink_set_err and use it in ctnetlink This patch adds nfnetlink_set_err() to propagate the error to netlink broadcast listener in case of memory allocation errors in the message building. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy --- include/linux/netfilter/nfnetlink.h | 1 + net/netfilter/nf_conntrack_netlink.c | 2 ++ net/netfilter/nfnetlink.c | 6 ++++++ net/netlink/af_netlink.c | 1 + 4 files changed, 10 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 7d8e0455ccac..135e5cfe68a2 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -76,6 +76,7 @@ extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); extern int nfnetlink_has_listeners(unsigned int group); extern int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo); +extern void nfnetlink_set_err(u32 pid, u32 group, int error); extern int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags); extern void nfnl_lock(void); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index d1fe9d15ac5c..1b75c9efb0eb 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -518,6 +518,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this, nla_put_failure: rcu_read_unlock(); nlmsg_failure: + nfnetlink_set_err(0, group, -ENOBUFS); kfree_skb(skb); return NOTIFY_DONE; } @@ -1514,6 +1515,7 @@ static int ctnetlink_expect_event(struct notifier_block *this, nla_put_failure: rcu_read_unlock(); nlmsg_failure: + nfnetlink_set_err(0, 0, -ENOBUFS); kfree_skb(skb); return NOTIFY_DONE; } diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 9c0ba17a1ddb..2785d66a7e38 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -113,6 +113,12 @@ int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo) } EXPORT_SYMBOL_GPL(nfnetlink_send); +void nfnetlink_set_err(u32 pid, u32 group, int error) +{ + netlink_set_err(nfnl, pid, group, error); +} +EXPORT_SYMBOL_GPL(nfnetlink_set_err); + int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags) { return netlink_unicast(nfnl, skb, pid, flags); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6ee69c27f806..5b33879c6422 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1106,6 +1106,7 @@ void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code) read_unlock(&nl_table_lock); } +EXPORT_SYMBOL(netlink_set_err); /* must be called with netlink table grabbed */ static void netlink_update_socket_mc(struct netlink_sock *nlk, -- cgit v1.2.3 From 38938bfe3489394e2eed5e40c9bb8f66a2ce1405 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 24 Mar 2009 16:37:55 -0700 Subject: netlink: add NETLINK_NO_ENOBUFS socket flag This patch adds the NETLINK_NO_ENOBUFS socket flag. This flag can be used by unicast and broadcast listeners to avoid receiving ENOBUFS errors. Generally speaking, ENOBUFS errors are useful to notify two things to the listener: a) You may increase the receiver buffer size via setsockopt(). b) You have lost messages, you may be out of sync. In some cases, ignoring ENOBUFS errors can be useful. For example: a) nfnetlink_queue: this subsystem does not have any sort of resync method and you can decide to ignore ENOBUFS once you have set a given buffer size. b) ctnetlink: you can use this together with the socket flag NETLINK_BROADCAST_SEND_ERROR to stop getting ENOBUFS errors as you do not need to resync (packets whose event are not delivered are drop to provide reliable logging and state-synchronization). Moreover, the use of NETLINK_NO_ENOBUFS also reduces a "go up, go down" effect in terms of performance which is due to the netlink congestion control when the listener cannot back off. The effect is the following: 1) throughput rate goes up and netlink messages are inserted in the receiver buffer. 2) Then, netlink buffer fills and overruns (set on nlk->state bit 0). 3) While the listener empties the receiver buffer, netlink keeps dropping messages. Thus, throughput goes dramatically down. 4) Then, once the listener has emptied the buffer (nlk->state bit 0 is set off), goto step 1. This effect is easy to trigger with netlink broadcast under heavy load, and it is more noticeable when using a big receiver buffer. You can find some results in [1] that show this problem. [1] http://1984.lsi.us.es/linux/netlink/ This patch also includes the use of sk_drop to account the number of netlink messages drop due to overrun. This value is shown in /proc/net/netlink. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- include/linux/netlink.h | 1 + net/netlink/af_netlink.c | 38 ++++++++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 1e6bf995435c..5ba398e90304 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -104,6 +104,7 @@ struct nlmsgerr #define NETLINK_DROP_MEMBERSHIP 2 #define NETLINK_PKTINFO 3 #define NETLINK_BROADCAST_ERROR 4 +#define NETLINK_NO_ENOBUFS 5 struct nl_pktinfo { diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index b73d4e61c5ac..8b6bbb3032b0 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -86,6 +86,7 @@ struct netlink_sock { #define NETLINK_KERNEL_SOCKET 0x1 #define NETLINK_RECV_PKTINFO 0x2 #define NETLINK_BROADCAST_SEND_ERROR 0x4 +#define NETLINK_RECV_NO_ENOBUFS 0x8 static inline struct netlink_sock *nlk_sk(struct sock *sk) { @@ -717,10 +718,15 @@ static int netlink_getname(struct socket *sock, struct sockaddr *addr, static void netlink_overrun(struct sock *sk) { - if (!test_and_set_bit(0, &nlk_sk(sk)->state)) { - sk->sk_err = ENOBUFS; - sk->sk_error_report(sk); + struct netlink_sock *nlk = nlk_sk(sk); + + if (!(nlk->flags & NETLINK_RECV_NO_ENOBUFS)) { + if (!test_and_set_bit(0, &nlk_sk(sk)->state)) { + sk->sk_err = ENOBUFS; + sk->sk_error_report(sk); + } } + atomic_inc(&sk->sk_drops); } static struct sock *netlink_getsockbypid(struct sock *ssk, u32 pid) @@ -1182,6 +1188,15 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, nlk->flags &= ~NETLINK_BROADCAST_SEND_ERROR; err = 0; break; + case NETLINK_NO_ENOBUFS: + if (val) { + nlk->flags |= NETLINK_RECV_NO_ENOBUFS; + clear_bit(0, &nlk->state); + wake_up_interruptible(&nlk->wait); + } else + nlk->flags &= ~NETLINK_RECV_NO_ENOBUFS; + err = 0; + break; default: err = -ENOPROTOOPT; } @@ -1224,6 +1239,16 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname, return -EFAULT; err = 0; break; + case NETLINK_NO_ENOBUFS: + if (len < sizeof(int)) + return -EINVAL; + len = sizeof(int); + val = nlk->flags & NETLINK_RECV_NO_ENOBUFS ? 1 : 0; + if (put_user(len, optlen) || + put_user(val, optval)) + return -EFAULT; + err = 0; + break; default: err = -ENOPROTOOPT; } @@ -1879,12 +1904,12 @@ static int netlink_seq_show(struct seq_file *seq, void *v) if (v == SEQ_START_TOKEN) seq_puts(seq, "sk Eth Pid Groups " - "Rmem Wmem Dump Locks\n"); + "Rmem Wmem Dump Locks Drops\n"); else { struct sock *s = v; struct netlink_sock *nlk = nlk_sk(s); - seq_printf(seq, "%p %-3d %-6d %08x %-8d %-8d %p %d\n", + seq_printf(seq, "%p %-3d %-6d %08x %-8d %-8d %p %-8d %-8d\n", s, s->sk_protocol, nlk->pid, @@ -1892,7 +1917,8 @@ static int netlink_seq_show(struct seq_file *seq, void *v) atomic_read(&s->sk_rmem_alloc), atomic_read(&s->sk_wmem_alloc), nlk->cb, - atomic_read(&s->sk_refcnt) + atomic_read(&s->sk_refcnt), + atomic_read(&s->sk_drops) ); } -- cgit v1.2.3