* Re: 2.6.24-rc4-mm1 - BUG in tcp_fragment
From: Ilpo Järvinen @ 2007-12-13 23:00 UTC (permalink / raw)
To: Cedric Le Goater, David Miller; +Cc: Andrew Morton, LKML, Netdev
In-Reply-To: <47616FA6.1010607@fr.ibm.com>
On Thu, 13 Dec 2007, Cedric Le Goater wrote:
> I got this one while compiling on NFS.
>
> C.
>
> kernel BUG at /home/legoater/linux/2.6.24-rc4-mm1/include/net/tcp.h:1480!
I'm not exactly sure what patches you have applied and which patches are
not, with rc4-mm1 there are two patches (first one was incomplete, I
assume you had at least that one based on your other mail) to really fix
the issues in (__|)tcp_reset_fack_counts(...). However, there seems to be
so much breakage that I have a bit trouble to decide where to start...
The situation seems bit scary :-).
So, I might soon prepare a revert patch for most of the questionable
TCP parts and ask Dave to apply it (and drop them fully during next
rebase) unless I suddently figure something out soon which explains
all/most of the problems, then return to drawing board. ...As it seems
that the cumulative ACK processing problem discovered later on (having
rather cumbersome solution with skbs only) will make part of the work
that's currently in net-2.6.25 quite useless/duplicate effort. But thanks
anyway for reporting these.
--
i.
^ permalink raw reply
* Re: [RFC] net: napi fix
From: Jarek Poplawski @ 2007-12-13 22:58 UTC (permalink / raw)
To: David Miller
Cc: auke-jan.h.kok, gallatin, joonwpark81, netdev, linux-kernel,
jgarzik, shemminger, jesse.brandeburg
In-Reply-To: <20071213.143425.142750733.davem@davemloft.net>
David Miller wrote, On 12/13/2007 11:34 PM:
> From: Jarek Poplawski <jarkao2@gmail.com>
> Date: Thu, 13 Dec 2007 23:28:41 +0100
>
>> ...I'm afraid I can't understand: I mean doing the same but without
>> passing this info with 'work == weight': if driver sends this info,
>> why it can't instead call something like napi_continue() with
>> this list_move_tail() (and probably additional local_irq_disable()/
>> enble() - but since it's unlikely()?) which looks much more readable,
>> and saves one whole unlikely if ()?
>
> Because the poll list is private to net_rx_action() and we don't
> want to expose implementation details like that to every
> ->poll() implementation.
So, it seems 'we' failed e.g. exposing napi_complete()...
OK, no offense, I'll only mention at the end that there is
always a possibility to redefine such a function to {} with any
change of implementation.
Jarek P.
^ permalink raw reply
* Re: [RFC] net: napi fix
From: David Miller @ 2007-12-13 22:34 UTC (permalink / raw)
To: jarkao2
Cc: auke-jan.h.kok, gallatin, joonwpark81, netdev, linux-kernel,
jgarzik, shemminger, jesse.brandeburg
In-Reply-To: <4761B219.1030303@gmail.com>
From: Jarek Poplawski <jarkao2@gmail.com>
Date: Thu, 13 Dec 2007 23:28:41 +0100
> ...I'm afraid I can't understand: I mean doing the same but without
> passing this info with 'work == weight': if driver sends this info,
> why it can't instead call something like napi_continue() with
> this list_move_tail() (and probably additional local_irq_disable()/
> enble() - but since it's unlikely()?) which looks much more readable,
> and saves one whole unlikely if ()?
Because the poll list is private to net_rx_action() and we don't
want to expose implementation details like that to every
->poll() implementation.
^ permalink raw reply
* Re: [RFC] net: napi fix
From: Jarek Poplawski @ 2007-12-13 22:28 UTC (permalink / raw)
To: David Miller
Cc: auke-jan.h.kok, gallatin, joonwpark81, netdev, linux-kernel,
jgarzik, shemminger, jesse.brandeburg
In-Reply-To: <20071213.123727.224454625.davem@davemloft.net>
David Miller wrote, On 12/13/2007 09:37 PM:
...
> For example, if we export the list handling widget into the ->poll()
> routines, god help the person who wants to change how the poll list is
> managed in net_rx_action() :-/
...I'm afraid I can't understand: I mean doing the same but without
passing this info with 'work == weight': if driver sends this info,
why it can't instead call something like napi_continue() with
this list_move_tail() (and probably additional local_irq_disable()/
enble() - but since it's unlikely()?) which looks much more readable,
and saves one whole unlikely if ()?
Jarek P.
^ permalink raw reply
* RE: What was the reason for 2.6.22 SMP kernels to change how sendmsg is called?
From: Kevin Wilson @ 2007-12-13 22:00 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20071213.131753.156591181.davem@davemloft.net>
I see your point but it just so happens it is a GPL'd driver, as is all of our Linux code we produce for our hardware. Granted it is out of tree, and after you saw it you would want it to stay that way. However, I would have sent you the whole thing if that is a pre-req to cordial exchanges on this list.
Nonetheless, a somewhat recent change in your tree, that I could not pinpoint on my own, caused the driver to stop functioning properly. So after much searching in git/google/sources with no luck, I decided to ask for a little assistance, maybe just a hint as to where the culprit may be in the tree so I could investigate for myself. For SNGs I tried the method that now works but I am still at a loss as to (can't find) what changes in the tree caused it to fail.
Does that now clarify/help matters any?
Regards,
Kevin
-----Original Message-----
From: David Miller [mailto:davem@davemloft.net]
Sent: Thursday, December 13, 2007 15:18
To: Kevin Wilson
Cc: netdev@vger.kernel.org
Subject: Re: What was the reason for 2.6.22 SMP kernels to change how
sendmsg is called?
From: "Kevin Wilson" <Kevin.Wilson@comtrol.com>
Date: Thu, 13 Dec 2007 14:54:31 -0600
> The 2nd method fixes the kernel oops I reported. Thanks to all those
> that assisted me (0) with my first post to this list (see below)
> ... uh, oh yeah, did I mention that number would tally up to ZERO
> people. ;-}
Because the knowledgable folks here have zero interest in helping
people with out-of-tree-likely-binary-only modules, because in such
cases you have our code but we don't have your's.
Have a nice day.
^ permalink raw reply
* Re: [RFC] net: napi fix
From: Jarek Poplawski @ 2007-12-13 21:55 UTC (permalink / raw)
To: Stephen Hemminger
Cc: David Miller, auke-jan.h.kok, gallatin, joonwpark81, netdev,
linux-kernel, jgarzik, jesse.brandeburg
In-Reply-To: <476198F9.4020004@linux-foundation.org>
Stephen Hemminger wrote, On 12/13/2007 09:41 PM:
> David Miller wrote:
>> From: Jarek Poplawski <jarkao2@gmail.com>
>> Date: Thu, 13 Dec 2007 21:16:12 +0100
>>
>>
>>> I see in a nearby thread you would prefer to save some work to drivers
>>> (like this netif_running() check), but I think this all is at the cost
>>> of flexibility, and there will probably appear new problems, when a
>>> driver simply can't wait till the next poll (which btw. looks strange
>>> with all these hotplugging, usb and powersaving).
>>>
>> As someone who has actually had to edit the NAPI support of _EVERY_
>> single driver in the tree I can tell you that code duplication and
>> subtle semantic differences are a huge issue.
>>
>> And when you talk about driver flexibility, it's wise to mention that
>> this comes at the expense of flexibility in the core implmentation.
>> For example, if we export the list handling widget into the ->poll()
>> routines, god help the person who wants to change how the poll list is
>> managed in net_rx_action() :-/
>>
>> So we don't want to export datastructure details like that to the
>> driver.
(I hope you both don't mind I save some 'paper' and do this
2 in 1...)
So, you've seen a few drivers, know this much better than me, and
maybe even thought why they all so unnecessarily different... Of
course, if you think that despite those differences they all can
work with simpler napi api then OK (until they don't have to do
any cheating, like with this 'work' here).
> Also, most of the drivers should/could be doing the same thing. It is
> seems that
> driver writers just want to get "creative" and do things differently.
> The code is
> cleaner, safer, and less buggy if every device uses the interface in the
> same way.
>
> When I did the initial pass on this, I didn't see a single variation on
> NAPI usage
> that was better than the simple "get N packets and return" variation.
> But Dave
> did way more detailed grunt work on this.
It seems there are some differences in thinking what is simple/complex.
I think drivers' developers are used to controlling their devices, so
they know better when to turn on/off interrupts. So, maybe similar model
could be appropriate here. Sometimes doing more looks simpler than doing
less and remembering how and when the rest will be done (like
this netif_running() test). But I hope I'm wrong here, and this will
work after all!
Cheers,
Jarek P.
^ permalink raw reply
* Re: What was the reason for 2.6.22 SMP kernels to change how sendmsg is called?
From: David Miller @ 2007-12-13 21:17 UTC (permalink / raw)
To: Kevin.Wilson; +Cc: netdev
In-Reply-To: <B1F576FE19C55C43B8D2E31F061B50C04E60B1@exchange.comtrol.com>
From: "Kevin Wilson" <Kevin.Wilson@comtrol.com>
Date: Thu, 13 Dec 2007 14:54:31 -0600
> The 2nd method fixes the kernel oops I reported. Thanks to all those
> that assisted me (0) with my first post to this list (see below)
> ... uh, oh yeah, did I mention that number would tally up to ZERO
> people. ;-}
Because the knowledgable folks here have zero interest in helping
people with out-of-tree-likely-binary-only modules, because in such
cases you have our code but we don't have your's.
Have a nice day.
^ permalink raw reply
* Re: [PATCH] PS3: gelic: Add wireless support for PS3
From: Dan Williams @ 2007-12-13 21:13 UTC (permalink / raw)
To: Masakazu Mokuno
Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA,
geoffrey.levand-mEdOJwZ7QcZBDgjK7y7TUQ, Geert Uytterhoeven
In-Reply-To: <20071213211626.BF8E.MOKUNO-DfbDroY8Xu1L9jVzuh4AOg@public.gmane.org>
On Thu, 2007-12-13 at 21:27 +0900, Masakazu Mokuno wrote:
> Hi all,
>
> PS3: gelic: Add wireless support for PS3
>
> This is the re-worked (rewritten) version wireless support driver for
> PS3. The formar one was submitted to net-dev ML in June 2007.
>
> Major differnces from the previous one are:
> - The new driver has separate ethX interface from ethernet.
> They share same one MAC address.
> - Thus we can use both ethernet and wireless simultaenously.
> - The new driver returns AP's cipher information by the common IE
> format in the scan information.
> - Cipher selection is done via the common wirelee extension way
>
> This patch depends on the patch set I submitted to netdev ML
> (http://marc.info/?l=linux-netdev&m=119754603814092)
>
> Thanks for reviewing!
One more question; does the driver work with wpa_supplicant for WPA, or
does the firmware capture the EAPOL frames and handle the 4 way
handshake internally? Ideally the firmware would have the ability to
pass those frames up unmodified so the driver would at least have a
_hope_ of 802.1x capability. Does the firmware handle Dynamic WEP at
all?
Basically, what happens when the AP you've just associated with starts
sending you EAPOL traffic to start the 802.1x process?
Dan
> Signed-off-by: Masakazu Mokuno <mokuno-DfbDroY8Xu1L9jVzuh4AOg@public.gmane.org>
> ---
> drivers/net/Kconfig | 10
> drivers/net/Makefile | 3
> drivers/net/ps3_gelic_net.c | 18
> drivers/net/ps3_gelic_net.h | 6
> drivers/net/ps3_gelic_wireless.c | 2750 +++++++++++++++++++++++++++++++++++++++
> drivers/net/ps3_gelic_wireless.h | 329 ++++
> 6 files changed, 3114 insertions(+), 2 deletions(-)
>
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -2302,6 +2302,16 @@ config GELIC_NET
> To compile this driver as a module, choose M here: the
> module will be called ps3_gelic.
>
> +config GELIC_WIRELESS
> + bool "PS3 Wireless support"
> + depends on GELIC_NET
> + help
> + This option adds the support for the wireless feature of PS3.
> + If you have the wireless-less model of PS3 or have no plan to
> + use wireless feature, disabling this option saves memory. As
> + the driver automatically distinguishes the models, you can
> + safely enable this option even if you have a wireless-less model.
> +
> config GIANFAR
> tristate "Gianfar Ethernet"
> depends on 85xx || 83xx || PPC_86xx
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -66,7 +66,8 @@ obj-$(CONFIG_BNX2) += bnx2.o
> spidernet-y += spider_net.o spider_net_ethtool.o
> obj-$(CONFIG_SPIDER_NET) += spidernet.o sungem_phy.o
> obj-$(CONFIG_GELIC_NET) += ps3_gelic.o
> -ps3_gelic-objs += ps3_gelic_net.o
> +gelic_wireless-$(CONFIG_GELIC_WIRELESS) += ps3_gelic_wireless.o
> +ps3_gelic-objs += ps3_gelic_net.o $(gelic_wireless-y)
> obj-$(CONFIG_TC35815) += tc35815.o
> obj-$(CONFIG_SKGE) += skge.o
> obj-$(CONFIG_SKY2) += sky2.o
> --- a/drivers/net/ps3_gelic_net.c
> +++ b/drivers/net/ps3_gelic_net.c
> @@ -46,9 +46,10 @@
> #include <asm/lv1call.h>
>
> #include "ps3_gelic_net.h"
> +#include "ps3_gelic_wireless.h"
>
> #define DRV_NAME "Gelic Network Driver"
> -#define DRV_VERSION "1.1"
> +#define DRV_VERSION "2.0"
>
> MODULE_AUTHOR("SCE Inc.");
> MODULE_DESCRIPTION("Gelic Network driver");
> @@ -1122,6 +1123,12 @@ static irqreturn_t gelic_card_interrupt(
> if (status & GELIC_CARD_PORT_STATUS_CHANGED)
> gelic_card_get_ether_port_status(card, 1);
>
> +#ifdef CONFIG_GELIC_WIRELESS
> + if (status & (GELIC_CARD_WLAN_EVENT_RECEIVED |
> + GELIC_CARD_WLAN_COMMAND_COMPLETED))
> + gelic_wl_interrupt(card->netdev[GELIC_PORT_WIRELESS], status);
> +#endif
> +
> return IRQ_HANDLED;
> }
>
> @@ -1603,6 +1610,12 @@ static int ps3_gelic_driver_probe(struct
> goto fail_setup_netdev;
> }
>
> +#ifdef CONFIG_GELIC_WIRELESS
> + if (gelic_wl_driver_probe(card)) {
> + dev_dbg(&dev->core, "%s: WL init failed\n", __func__);
> + goto fail_setup_netdev;
> + }
> +#endif
> pr_debug("%s: done\n", __func__);
> return 0;
>
> @@ -1642,6 +1655,9 @@ static int ps3_gelic_driver_remove(struc
> struct net_device *netdev0;
> pr_debug("%s: called\n", __func__);
>
> +#ifdef CONFIG_GELIC_WIRELESS
> + gelic_wl_driver_remove(card);
> +#endif
> /* stop interrupt */
> gelic_card_set_irq_mask(card, 0);
>
> --- a/drivers/net/ps3_gelic_net.h
> +++ b/drivers/net/ps3_gelic_net.h
> @@ -57,6 +57,8 @@
> #define GELIC_CARD_RX_PROTECTION_ERR 0x0000000004000000L
> #define GELIC_CARD_TX_TCP_UDP_CHECKSUM_ERR 0x0000000008000000L
> #define GELIC_CARD_PORT_STATUS_CHANGED 0x0000000020000000L
> +#define GELIC_CARD_WLAN_EVENT_RECEIVED 0x0000000040000000L
> +#define GELIC_CARD_WLAN_COMMAND_COMPLETED 0x0000000080000000L
> /* INT 0 */
> #define GELIC_CARD_TX_FLAGGED_DESCR 0x0004000000000000L
> #define GELIC_CARD_RX_FLAGGED_DESCR 0x0040000000000000L
> @@ -180,6 +182,10 @@ enum gelic_lv1_net_control_code {
> GELIC_LV1_GET_ETH_PORT_STATUS = 2,
> GELIC_LV1_SET_NEGOTIATION_MODE = 3,
> GELIC_LV1_GET_VLAN_ID = 4,
> + GELIC_LV1_GET_CHANNEL = 6,
> + GELIC_LV1_POST_WLAN_CMD = 9,
> + GELIC_LV1_GET_WLAN_CMD_RESULT = 10,
> + GELIC_LV1_GET_WLAN_EVENT = 11
> };
>
> /* status returened from GET_ETH_PORT_STATUS */
> --- /dev/null
> +++ b/drivers/net/ps3_gelic_wireless.c
> @@ -0,0 +1,2750 @@
> +/*
> + * PS3 gelic network driver.
> + *
> + * Copyright (C) 2007 Sony Computer Entertainment Inc.
> + * Copyright 2007 Sony Corporation
> + *
> + * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#undef DEBUG
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +
> +#include <linux/etherdevice.h>
> +#include <linux/ethtool.h>
> +#include <linux/if_vlan.h>
> +
> +#include <linux/in.h>
> +#include <linux/ip.h>
> +#include <linux/tcp.h>
> +#include <linux/wireless.h>
> +#include <linux/ctype.h>
> +#include <linux/string.h>
> +#include <net/iw_handler.h>
> +#include <net/ieee80211.h>
> +
> +#include <linux/dma-mapping.h>
> +#include <net/checksum.h>
> +#include <asm/firmware.h>
> +#include <asm/ps3.h>
> +#include <asm/lv1call.h>
> +
> +#include "ps3_gelic_net.h"
> +#include "ps3_gelic_wireless.h"
> +
> +
> +static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan);
> +static int gelic_wl_try_associate(struct net_device *netdev);
> +
> +/*
> + * tables
> + */
> +
> +/* 802.11b/g channel to freq in MHz */
> +static const int channel_freq[] = {
> + 2412, 2417, 2422, 2427, 2432,
> + 2437, 2442, 2447, 2452, 2457,
> + 2462, 2467, 2472, 2484
> +};
> +#define NUM_CHANNELS ARRAY_SIZE(channel_freq)
> +
> +/* in bps */
> +static const int bitrate_list[] = {
> + 1000000,
> + 2000000,
> + 5500000,
> + 11000000,
> + 6000000,
> + 9000000,
> + 12000000,
> + 18000000,
> + 24000000,
> + 36000000,
> + 48000000,
> + 54000000
> +};
> +#define NUM_BITRATES ARRAY_SIZE(bitrate_list)
> +
> +/*
> + * wpa2 support requires the hypervisor version 2.0 or later
> + */
> +static inline int wpa2_capable(void)
> +{
> + return (0 <= ps3_compare_firmware_version(2, 0, 0));
> +}
> +
> +static inline int precise_ie(void)
> +{
> + return 0; /* FIXME */
> +}
> +/*
> + * post_eurus_cmd helpers
> + */
> +struct eurus_cmd_arg_info {
> + int pre_arg; /* command requres arg1, arg2 at POST COMMAND */
> + int post_arg; /* command requires arg1, arg2 at GET_RESULT */
> +};
> +
> +static const struct eurus_cmd_arg_info cmd_info[GELIC_EURUS_CMD_MAX_INDEX] = {
> + [GELIC_EURUS_CMD_SET_COMMON_CFG] = { .pre_arg = 1},
> + [GELIC_EURUS_CMD_SET_WEP_CFG] = { .pre_arg = 1},
> + [GELIC_EURUS_CMD_SET_WPA_CFG] = { .pre_arg = 1},
> + [GELIC_EURUS_CMD_GET_COMMON_CFG] = { .post_arg = 1},
> + [GELIC_EURUS_CMD_GET_WEP_CFG] = { .post_arg = 1},
> + [GELIC_EURUS_CMD_GET_WPA_CFG] = { .post_arg = 1},
> + [GELIC_EURUS_CMD_GET_RSSI_CFG] = { .post_arg = 1},
> + [GELIC_EURUS_CMD_GET_SCAN] = { .post_arg = 1},
> +};
> +
> +#ifdef DEBUG
> +static const char *cmdstr(enum gelic_eurus_command ix)
> +{
> + switch (ix) {
> + case GELIC_EURUS_CMD_ASSOC:
> + return "ASSOC";
> + case GELIC_EURUS_CMD_DISASSOC:
> + return "DISASSOC";
> + case GELIC_EURUS_CMD_START_SCAN:
> + return "SCAN";
> + case GELIC_EURUS_CMD_GET_SCAN:
> + return "GET SCAN";
> + case GELIC_EURUS_CMD_SET_COMMON_CFG:
> + return "SET_COMMON_CFG";
> + case GELIC_EURUS_CMD_GET_COMMON_CFG:
> + return "GET_COMMON_CFG";
> + case GELIC_EURUS_CMD_SET_WEP_CFG:
> + return "SET_WEP_CFG";
> + case GELIC_EURUS_CMD_GET_WEP_CFG:
> + return "GET_WEP_CFG";
> + case GELIC_EURUS_CMD_SET_WPA_CFG:
> + return "SET_WPA_CFG";
> + case GELIC_EURUS_CMD_GET_WPA_CFG:
> + return "GET_WPA_CFG";
> + case GELIC_EURUS_CMD_GET_RSSI_CFG:
> + return "GET_RSSI";
> + default:
> + break;
> + }
> + return "";
> +};
> +#else
> +static inline const char *cmdstr(enum gelic_eurus_command ix)
> +{
> + return "";
> +}
> +#endif
> +
> +/* synchronously do eurus commands */
> +static void gelic_eurus_sync_cmd_worker(struct work_struct *work)
> +{
> + struct gelic_eurus_cmd *cmd;
> + struct gelic_card *card;
> + struct gelic_wl_info *wl;
> +
> + u64 arg1, arg2;
> +
> + pr_debug("%s: <-\n", __func__);
> + cmd = container_of(work, struct gelic_eurus_cmd, work);
> + BUG_ON(cmd_info[cmd->cmd].pre_arg &&
> + cmd_info[cmd->cmd].post_arg);
> + wl = cmd->wl;
> + card = port_to_card(wl_port(wl));
> +
> + if (cmd_info[cmd->cmd].pre_arg) {
> + arg1 = ps3_mm_phys_to_lpar(__pa(cmd->buffer));
> + arg2 = cmd->buf_size;
> + } else {
> + arg1 = 0;
> + arg2 = 0;
> + }
> + init_completion(&wl->cmd_done_intr);
> + pr_debug("%s: cmd='%s' start\n", __func__, cmdstr(cmd->cmd));
> + cmd->status = lv1_net_control(bus_id(card), dev_id(card),
> + GELIC_LV1_POST_WLAN_CMD,
> + cmd->cmd, arg1, arg2,
> + &cmd->tag, &cmd->size);
> + if (cmd->status) {
> + complete(&cmd->done);
> + pr_info("%s: cmd issue failed\n", __func__);
> + return;
> + }
> +
> + wait_for_completion(&wl->cmd_done_intr);
> +
> + if (cmd_info[cmd->cmd].post_arg) {
> + arg1 = ps3_mm_phys_to_lpar(__pa(cmd->buffer));
> + arg2 = cmd->buf_size;
> + } else {
> + arg1 = 0;
> + arg2 = 0;
> + }
> +
> + cmd->status = lv1_net_control(bus_id(card), dev_id(card),
> + GELIC_LV1_GET_WLAN_CMD_RESULT,
> + cmd->tag, arg1, arg2,
> + &cmd->cmd_status, &cmd->size);
> +#ifdef DEBUG
> + if (cmd->status || cmd->cmd_status) {
> + pr_debug("%s: cmd done tag=%#lx arg1=%#lx, arg2=%#lx\n", __func__,
> + cmd->tag, arg1, arg2);
> + pr_debug("%s: cmd done status=%#x cmd_status=%#lx size=%#lx\n",
> + __func__, cmd->status, cmd->cmd_status, cmd->size);
> + }
> +#endif
> + complete(&cmd->done);
> + pr_debug("%s: cmd='%s' done\n", __func__, cmdstr(cmd->cmd));
> +}
> +
> +static struct gelic_eurus_cmd *gelic_eurus_sync_cmd(struct gelic_wl_info *wl,
> + unsigned int eurus_cmd,
> + void *buffer,
> + unsigned int buf_size)
> +{
> + struct gelic_eurus_cmd *cmd;
> +
> + /* allocate cmd */
> + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
> + if (!cmd)
> + return NULL;
> +
> + /* initialize members */
> + cmd->cmd = eurus_cmd;
> + cmd->buffer = buffer;
> + cmd->buf_size = buf_size;
> + cmd->wl = wl;
> + INIT_WORK(&cmd->work, gelic_eurus_sync_cmd_worker);
> + init_completion(&cmd->done);
> + queue_work(wl->eurus_cmd_queue, &cmd->work);
> +
> + /* wait for command completion */
> + wait_for_completion(&cmd->done);
> +
> + return cmd;
> +}
> +
> +static u32 gelic_wl_get_link(struct net_device *netdev)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
> + u32 ret;
> +
> + pr_debug("%s: <-\n", __func__);
> + down(&wl->assoc_stat_lock);
> + if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
> + ret = 1;
> + else
> + ret = 0;
> + up(&wl->assoc_stat_lock);
> + pr_debug("%s: ->\n", __func__);
> + return ret;
> +}
> +
> +/*
> + * wireless extension handlers and helpers
> + */
> +
> +/* SIOGIWNAME */
> +static int gelic_wl_get_name(struct net_device *dev,
> + struct iw_request_info *info,
> + union iwreq_data *iwreq, char *extra)
> +{
> + strcpy(iwreq->name, "IEEE 802.11bg");
> + return 0;
> +}
> +
> +static void gelic_wl_get_ch_info(struct gelic_wl_info *wl)
> +{
> + struct gelic_card *card = port_to_card(wl_port(wl));
> + u64 ch_info_raw, tmp;
> + int status;
> +
> + if (!test_and_set_bit(GELIC_WL_STAT_CH_INFO, &wl->stat)) {
> + status = lv1_net_control(bus_id(card), dev_id(card),
> + GELIC_LV1_GET_CHANNEL, 0, 0, 0,
> + &ch_info_raw,
> + &tmp);
> + /* some fw versions may return error */
> + if (status) {
> + if (status != LV1_NO_ENTRY)
> + pr_info("%s: available ch unknown\n", __func__);
> + wl->ch_info = 0x07ff;/* 11 ch */
> + } else
> + /* 16 bits of MSB has available channels */
> + wl->ch_info = ch_info_raw >> 48;
> + }
> + return;
> +}
> +
> +/* SIOGIWRANGE */
> +static int gelic_wl_get_range(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *iwreq, char *extra)
> +{
> + struct iw_point *point = &iwreq->data;
> + struct iw_range *range = (struct iw_range *)extra;
> + struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
> + unsigned int i, chs;
> +
> + pr_debug("%s: <-\n", __func__);
> + point->length = sizeof(struct iw_range);
> + memset(range, 0, sizeof(struct iw_range));
> +
> + range->we_version_compiled = WIRELESS_EXT;
> + range->we_version_source = 22;
> +
> + /* available channels and frequencies */
> + gelic_wl_get_ch_info(wl);
> +
> + for (i = 0, chs = 0;
> + i < NUM_CHANNELS && chs < IW_MAX_FREQUENCIES; i++)
> + if (wl->ch_info & (1 << i)) {
> + range->freq[chs].i = i + 1;
> + range->freq[chs].m = channel_freq[i];
> + range->freq[chs].e = 6;
> + chs++;
> + }
> + range->num_frequency = chs;
> + range->old_num_frequency = chs;
> + range->num_channels = chs;
> + range->old_num_channels = chs;
> +
> + /* bitrates */
> + for (i = 0; i < NUM_BITRATES; i++)
> + range->bitrate[i] = bitrate_list[i];
> + range->num_bitrates = i;
> +
> + /* signal levels */
> + range->max_qual.qual = 100; /* relative value */
> + range->max_qual.level = 100;
> + range->avg_qual.qual = 50;
> + range->avg_qual.level = 50;
> + range->sensitivity = 0;
> +
> + /* Event capability */
> + IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
> + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
> + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
> +
> + /* encryption capability */
> + range->enc_capa = IW_ENC_CAPA_WPA |
> + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
> + if (wpa2_capable())
> + range->enc_capa |= IW_ENC_CAPA_WPA2;
> + range->encoding_size[0] = 5; /* 40bit WEP */
> + range->encoding_size[1] = 13; /* 104bit WEP */
> + range->encoding_size[2] = 32; /* WPA-PSK */
> + range->num_encoding_sizes = 3;
> + range->max_encoding_tokens = GELIC_WEP_KEYS;
> +
> + pr_debug("%s: ->\n", __func__);
> + return 0;
> +
> +}
> +
> +/* SIOC{G,S}IWSCAN */
> +static int gelic_wl_set_scan(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *wrqu, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> +
> + return gelic_wl_start_scan(wl, 1);
> +}
> +
> +#define OUI_LEN 3
> +static const u8 rsn_oui[OUI_LEN] = { 0x00, 0x0f, 0xac };
> +static const u8 wpa_oui[OUI_LEN] = { 0x00, 0x50, 0xf2 };
> +
> +/*
> + * synthesize WPA/RSN IE data
> + * See WiFi WPA specification and IEEE 802.11-2007 7.3.2.25
> + * for the format
> + */
> +static size_t gelic_wl_synthesize_ie(u8 *buf,
> + struct gelic_eurus_scan_info *scan)
> +{
> +
> + const u8 *oui_header;
> + u8 *start = buf;
> + int rsn;
> + int ccmp;
> +
> + pr_debug("%s: <- sec=%16x\n", __func__, scan->security);
> + switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_MASK) {
> + case GELIC_EURUS_SCAN_SEC_WPA:
> + rsn = 0;
> + break;
> + case GELIC_EURUS_SCAN_SEC_WPA2:
> + rsn = 1;
> + break;
> + default:
> + /* WEP or none. No IE returned */
> + return 0;
> + }
> +
> + switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_WPA_MASK) {
> + case GELIC_EURUS_SCAN_SEC_WPA_TKIP:
> + ccmp = 0;
> + break;
> + case GELIC_EURUS_SCAN_SEC_WPA_AES:
> + ccmp = 1;
> + break;
> + default:
> + if (rsn) {
> + ccmp = 1;
> + pr_info("%s: no cipher info. defaulted to CCMP\n",
> + __func__);
> + } else {
> + ccmp = 0;
> + pr_info("%s: no cipher info. defaulted to TKIP\n",
> + __func__);
> + }
> + }
> +
> + if (rsn)
> + oui_header = rsn_oui;
> + else
> + oui_header = wpa_oui;
> +
> + /* element id */
> + if (rsn)
> + *buf++ = MFIE_TYPE_RSN;
> + else
> + *buf++ = MFIE_TYPE_GENERIC;
> +
> + /* length filed; set later */
> + buf++;
> +
> + /* wpa special header */
> + if (!rsn) {
> + memcpy(buf, wpa_oui, OUI_LEN);
> + buf += OUI_LEN;
> + *buf++ = 0x01;
> + }
> +
> + /* version */
> + *buf++ = 0x01; /* version 1.0 */
> + *buf++ = 0x00;
> +
> + /* group cipher */
> + memcpy(buf, oui_header, OUI_LEN);
> + buf += OUI_LEN;
> +
> + if (ccmp)
> + *buf++ = 0x04; /* CCMP */
> + else
> + *buf++ = 0x02; /* TKIP */
> +
> + /* pairwise key count always 1 */
> + *buf++ = 0x01;
> + *buf++ = 0x00;
> +
> + /* pairwise key suit */
> + memcpy(buf, oui_header, OUI_LEN);
> + buf += OUI_LEN;
> + if (ccmp)
> + *buf++ = 0x04; /* CCMP */
> + else
> + *buf++ = 0x02; /* TKIP */
> +
> + /* AKM count is 1 */
> + *buf++ = 0x01;
> + *buf++ = 0x00;
> +
> + /* AKM suite is assumed as PSK*/
> + memcpy(buf, oui_header, OUI_LEN);
> + buf += OUI_LEN;
> + *buf++ = 0x02; /* PSK */
> +
> + /* RSN capabilities is 0 */
> + *buf++ = 0x00;
> + *buf++ = 0x00;
> +
> + /* set length field */
> + start[1] = (buf - start - 2);
> +
> + pr_debug("%s: ->\n", __func__);
> + return (buf - start);
> +}
> +
> +struct ie_item {
> + u8 *data;
> + u8 len;
> +};
> +
> +struct ie_info {
> + struct ie_item wpa;
> + struct ie_item rsn;
> +};
> +
> +static void gelic_wl_parse_ie(u8 *data, size_t len,
> + struct ie_info *ie_info)
> +{
> + size_t data_left = len;
> + u8 *pos = data;
> + u8 item_len;
> + u8 item_id;
> +
> + pr_debug("%s: data=%p len=%ld \n", __func__,
> + data, len);
> + memset(ie_info, 0, sizeof(struct ie_info));
> +
> + while (0 < data_left) {
> + item_id = *pos++;
> + item_len = *pos++;
> +
> + switch (item_id) {
> + case MFIE_TYPE_GENERIC:
> + if (!memcmp(pos, wpa_oui, OUI_LEN) &&
> + pos[OUI_LEN] == 0x01) {
> + ie_info->wpa.data = pos - 2;
> + ie_info->wpa.len = item_len + 2;
> + }
> + break;
> + case MFIE_TYPE_RSN:
> + ie_info->rsn.data = pos - 2;
> + /* length includes the header */
> + ie_info->rsn.len = item_len + 2;
> + break;
> + default:
> + pr_debug("%s: ignore %#x,%d\n", __func__,
> + item_id, item_len);
> + break;
> + }
> + pos += item_len;
> + data_left -= item_len + 2;
> + }
> + pr_debug("%s: wpa=%p,%d wpa2=%p,%d\n", __func__,
> + ie_info->wpa.data, ie_info->wpa.len,
> + ie_info->rsn.data, ie_info->rsn.len);
> +}
> +
> +
> +/*
> + * translate the scan informations from hypervisor to a
> + * independent format
> + */
> +static char *gelic_wl_translate_scan(struct net_device *netdev,
> + char *ev,
> + char *stop,
> + struct gelic_wl_scan_info *network)
> +{
> + struct iw_event iwe;
> + struct gelic_eurus_scan_info *scan = network->hwinfo;
> + char *tmp;
> + u8 rate;
> + unsigned int i, j, len;
> + u8 buf[MAX_WPA_IE_LEN];
> +
> + pr_debug("%s: <-\n", __func__);
> +
> + /* first entry should be AP's mac address */
> + iwe.cmd = SIOCGIWAP;
> + iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
> + memcpy(iwe.u.ap_addr.sa_data, &scan->bssid[2], ETH_ALEN);
> + ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_ADDR_LEN);
> +
> + /* ESSID */
> + iwe.cmd = SIOCGIWESSID;
> + iwe.u.data.flags = 1;
> + iwe.u.data.length = strnlen(scan->essid, 32);
> + ev = iwe_stream_add_point(ev, stop, &iwe, scan->essid);
> +
> + /* FREQUENCY */
> + iwe.cmd = SIOCGIWFREQ;
> + iwe.u.freq.m = be16_to_cpu(scan->channel);
> + iwe.u.freq.e = 0; /* table value in MHz */
> + iwe.u.freq.i = 0;
> + ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_FREQ_LEN);
> +
> + /* RATES */
> + iwe.cmd = SIOCGIWRATE;
> + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
> + /* to stuff multiple values in one event */
> + tmp = ev + IW_EV_LCP_LEN;
> + /* put them in ascendant order (older is first) */
> + i = 0;
> + j = 0;
> + pr_debug("%s: rates=%d rate=%d\n", __func__,
> + network->rate_len, network->rate_ext_len);
> + while (i < network->rate_len) {
> + if (j < network->rate_ext_len &&
> + ((scan->ext_rate[j] & 0x7f) < (scan->rate[i] & 0x7f)))
> + rate = scan->ext_rate[j++] & 0x7f;
> + else
> + rate = scan->rate[i++] & 0x7f;
> + iwe.u.bitrate.value = rate * 500000; /* 500kbps unit */
> + tmp = iwe_stream_add_value(ev, tmp, stop, &iwe,
> + IW_EV_PARAM_LEN);
> + }
> + while (j < network->rate_ext_len) {
> + iwe.u.bitrate.value = (scan->ext_rate[j++] & 0x7f) * 500000;
> + tmp = iwe_stream_add_value(ev, tmp, stop, &iwe,
> + IW_EV_PARAM_LEN);
> + }
> + /* Check if we added any rate */
> + if (IW_EV_LCP_LEN < (tmp - ev))
> + ev = tmp;
> +
> + /* ENCODE */
> + iwe.cmd = SIOCGIWENCODE;
> + if (be16_to_cpu(scan->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;
> + ev = iwe_stream_add_point(ev, stop, &iwe, scan->essid);
> +
> + /* MODE */
> + iwe.cmd = SIOCGIWMODE;
> + if (be16_to_cpu(scan->capability) &
> + (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
> + if (be16_to_cpu(scan->capability) & WLAN_CAPABILITY_ESS)
> + iwe.u.mode = IW_MODE_MASTER;
> + else
> + iwe.u.mode = IW_MODE_ADHOC;
> + ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_UINT_LEN);
> + }
> +
> + /* QUAL */
> + iwe.cmd = IWEVQUAL;
> + iwe.u.qual.updated = IW_QUAL_ALL_UPDATED |
> + IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
> + iwe.u.qual.level = be16_to_cpu(scan->rssi);
> + iwe.u.qual.qual = be16_to_cpu(scan->rssi);
> + iwe.u.qual.noise = 0;
> + ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_QUAL_LEN);
> +
> + /* RSN */
> + memset(&iwe, 0, sizeof(iwe));
> + if (be16_to_cpu(scan->size) <= sizeof(*scan)) {
> + /* If wpa[2] capable station, synthesize IE and put it */
> + len = gelic_wl_synthesize_ie(buf, scan);
> + if (len) {
> + iwe.cmd = IWEVGENIE;
> + iwe.u.data.length = len;
> + ev = iwe_stream_add_point(ev, stop, &iwe, buf);
> + }
> + } else {
> + /* this scan info has IE data */
> + struct ie_info ie_info;
> + size_t data_len;
> +
> + data_len = be16_to_cpu(scan->size) - sizeof(*scan);
> +
> + gelic_wl_parse_ie(scan->elements, data_len, &ie_info);
> +
> + if (ie_info.wpa.len && (ie_info.wpa.len <= sizeof(buf))) {
> + memcpy(buf, ie_info.wpa.data, ie_info.wpa.len);
> + iwe.cmd = IWEVGENIE;
> + iwe.u.data.length = ie_info.wpa.len;
> + ev = iwe_stream_add_point(ev, stop, &iwe, buf);
> + }
> +
> + if (ie_info.rsn.len && (ie_info.rsn.len <= sizeof(buf))) {
> + memset(&iwe, 0, sizeof(iwe));
> + memcpy(buf, ie_info.rsn.data, ie_info.rsn.len);
> + iwe.cmd = IWEVGENIE;
> + iwe.u.data.length = ie_info.rsn.len;
> + ev = iwe_stream_add_point(ev, stop, &iwe, buf);
> + }
> + }
> +
> + pr_debug("%s: ->\n", __func__);
> + return ev;
> +}
> +
> +
> +static int gelic_wl_get_scan(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *wrqu, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + struct gelic_wl_scan_info *scan_info;
> + char *ev = extra;
> + char *stop = ev + wrqu->data.length;
> + int ret = 0;
> + unsigned long this_time = jiffies;
> +
> + pr_debug("%s: <-\n", __func__);
> + if (down_interruptible(&wl->scan_lock))
> + return -EAGAIN;
> +
> + switch (wl->scan_stat) {
> + case GELIC_WL_SCAN_STAT_SCANNING:
> + /* If a scan in progress, caller should call me again */
> + ret = -EAGAIN;
> + goto out;
> + break;
> +
> + case GELIC_WL_SCAN_STAT_INIT:
> + /* last scan request failed or never issued */
> + ret = -ENODEV;
> + goto out;
> + break;
> + case GELIC_WL_SCAN_STAT_GOT_LIST:
> + /* ok, use current list */
> + break;
> + }
> +
> + list_for_each_entry(scan_info, &wl->network_list, list) {
> + if (wl->scan_age == 0 ||
> + time_after(scan_info->last_scanned + wl->scan_age,
> + this_time))
> + ev = gelic_wl_translate_scan(netdev, ev, stop,
> + scan_info);
> + else
> + pr_debug("%s:entry too old\n", __func__);
> +
> + if (stop - ev <= IW_EV_ADDR_LEN) {
> + ret = -E2BIG;
> + goto out;
> + }
> + }
> +
> + wrqu->data.length = ev - extra;
> + wrqu->data.flags = 0;
> +out:
> + up(&wl->scan_lock);
> + pr_debug("%s: -> %d %d\n", __func__, ret, wrqu->data.length);
> + return ret;
> +}
> +
> +#ifdef DEBUG
> +static void scan_list_dump(struct gelic_wl_info *wl)
> +{
> + struct gelic_wl_scan_info *scan_info;
> + int i;
> + DECLARE_MAC_BUF(mac);
> +
> + i = 0;
> + list_for_each_entry(scan_info, &wl->network_list, list) {
> + pr_debug("%s: item %d\n", __func__, i++);
> + pr_debug("valid=%d eurusindex=%d last=%lx\n",
> + scan_info->valid, scan_info->eurus_index,
> + scan_info->last_scanned);
> + pr_debug("r_len=%d r_ext_len=%d essid_len=%d\n",
> + scan_info->rate_len, scan_info->rate_ext_len,
> + scan_info->essid_len);
> + /* -- */
> + pr_debug("bssid=%s\n",
> + print_mac(mac, &scan_info->hwinfo->bssid[2]));
> + pr_debug("essid=%s\n", scan_info->hwinfo->essid);
> + }
> +}
> +#endif
> +
> +static int gelic_wl_set_auth(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct iw_param *param = &data->param;
> + struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
> + unsigned long irqflag;
> + int ret = 0;
> +
> + pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX);
> + spin_lock_irqsave(&wl->lock, irqflag);
> + switch (param->flags & IW_AUTH_INDEX) {
> + case IW_AUTH_WPA_VERSION:
> + if (param->value & IW_AUTH_WPA_VERSION_DISABLED) {
> + pr_debug("%s: NO WPA selected\n", __func__);
> + wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
> + wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
> + }
> + if (param->value & IW_AUTH_WPA_VERSION_WPA) {
> + pr_debug("%s: WPA version 1 selected\n", __func__);
> + wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA;
> + wl->group_cipher_method = GELIC_WL_CIPHER_TKIP;
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP;
> + wl->auth_method = GELIC_EURUS_AUTH_OPEN;
> + }
> + if (param->value & IW_AUTH_WPA_VERSION_WPA2) {
> + /*
> + * As the hypervisor may not tell the cipher
> + * information of the AP if it is WPA2,
> + * you will not decide suitable cipher from
> + * its beacon.
> + * You should have knowledge about the AP's
> + * cipher infomation in other method prior to
> + * the association.
> + */
> + if (!precise_ie())
> + pr_info("%s: WPA2 may not work\n", __func__);
> + if (wpa2_capable()) {
> + wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA2;
> + wl->group_cipher_method = GELIC_WL_CIPHER_AES;
> + wl->pairwise_cipher_method =
> + GELIC_WL_CIPHER_AES;
> + wl->auth_method = GELIC_EURUS_AUTH_OPEN;
> + } else
> + ret = -EINVAL;
> + }
> + break;
> +
> + case IW_AUTH_CIPHER_PAIRWISE:
> + if (param->value &
> + (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) {
> + pr_debug("%s: WEP selected\n", __func__);
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
> + }
> + if (param->value & IW_AUTH_CIPHER_TKIP) {
> + pr_debug("%s: TKIP selected\n", __func__);
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP;
> + }
> + if (param->value & IW_AUTH_CIPHER_CCMP) {
> + pr_debug("%s: CCMP selected\n", __func__);
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_AES;
> + }
> + if (param->value & IW_AUTH_CIPHER_NONE) {
> + pr_debug("%s: no auth selected\n", __func__);
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
> + }
> + break;
> + case IW_AUTH_CIPHER_GROUP:
> + if (param->value &
> + (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) {
> + pr_debug("%s: WEP selected\n", __func__);
> + wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
> + }
> + if (param->value & IW_AUTH_CIPHER_TKIP) {
> + pr_debug("%s: TKIP selected\n", __func__);
> + wl->group_cipher_method = GELIC_WL_CIPHER_TKIP;
> + }
> + if (param->value & IW_AUTH_CIPHER_CCMP) {
> + pr_debug("%s: CCMP selected\n", __func__);
> + wl->group_cipher_method = GELIC_WL_CIPHER_AES;
> + }
> + if (param->value & IW_AUTH_CIPHER_NONE) {
> + pr_debug("%s: no auth selected\n", __func__);
> + wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
> + }
> + break;
> + case IW_AUTH_80211_AUTH_ALG:
> + if (param->value & IW_AUTH_ALG_SHARED_KEY) {
> + pr_debug("%s: shared key specified\n", __func__);
> + wl->auth_method = GELIC_EURUS_AUTH_SHARED;
> + } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
> + pr_debug("%s: open system specified\n", __func__);
> + wl->auth_method = GELIC_EURUS_AUTH_OPEN;
> + } else
> + ret = -EINVAL;
> + break;
> +
> + case IW_AUTH_WPA_ENABLED:
> + if (param->value) {
> + pr_debug("%s: WPA enabled\n", __func__);
> + wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA;
> + } else {
> + pr_debug("%s: WPA disabled\n", __func__);
> + wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
> + }
> + break;
> +
> + case IW_AUTH_KEY_MGMT:
> + if (param->value & IW_AUTH_KEY_MGMT_PSK)
> + break;
> + /* intentionally fall through */
> + default:
> + ret = -EOPNOTSUPP;
> + break;
> + };
> +
> + if (!ret)
> + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
> +
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + pr_debug("%s: -> %d\n", __func__, ret);
> + return ret;
> +}
> +
> +static int gelic_wl_get_auth(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *iwreq, char *extra)
> +{
> + struct iw_param *param = &iwreq->param;
> + struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
> + unsigned long irqflag;
> + int ret = 0;
> +
> + pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX);
> + spin_lock_irqsave(&wl->lock, irqflag);
> + switch (param->flags & IW_AUTH_INDEX) {
> + case IW_AUTH_WPA_VERSION:
> + switch (wl->wpa_level) {
> + case GELIC_WL_WPA_LEVEL_WPA:
> + param->value |= IW_AUTH_WPA_VERSION_WPA;
> + break;
> + case GELIC_WL_WPA_LEVEL_WPA2:
> + param->value |= IW_AUTH_WPA_VERSION_WPA2;
> + break;
> + default:
> + param->value |= IW_AUTH_WPA_VERSION_DISABLED;
> + }
> + break;
> +
> + case IW_AUTH_80211_AUTH_ALG:
> + if (wl->auth_method == GELIC_EURUS_AUTH_SHARED)
> + param->value = IW_AUTH_ALG_SHARED_KEY;
> + else if (wl->auth_method == GELIC_EURUS_AUTH_OPEN)
> + param->value = IW_AUTH_ALG_OPEN_SYSTEM;
> + break;
> +
> + case IW_AUTH_WPA_ENABLED:
> + switch (wl->wpa_level) {
> + case GELIC_WL_WPA_LEVEL_WPA:
> + case GELIC_WL_WPA_LEVEL_WPA2:
> + param->value = 1;
> + break;
> + default:
> + param->value = 0;
> + break;
> + }
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + }
> +
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + pr_debug("%s: -> %d\n", __func__, ret);
> + return ret;
> +}
> +
> +/* SIOC{S,G}IWESSID */
> +static int gelic_wl_set_essid(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + unsigned long irqflag;
> +
> + pr_debug("%s: <- l=%d f=%d\n", __func__,
> + data->essid.length, data->essid.flags);
> + if (IW_ESSID_MAX_SIZE < data->essid.length)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&wl->lock, irqflag);
> + if (data->essid.flags) {
> + wl->essid_len = data->essid.length;
> + memcpy(wl->essid, extra, wl->essid_len);
> + pr_debug("%s: essid = '%s'\n", __func__, extra);
> + set_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat);
> + } else {
> + pr_debug("%s: ESSID any \n", __func__);
> + clear_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat);
> + }
> + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> +
> +
> + gelic_wl_try_associate(netdev); /* FIXME */
> + pr_debug("%s: -> \n", __func__);
> + return 0;
> +}
> +
> +static int gelic_wl_get_essid(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + unsigned long irqflag;
> +
> + pr_debug("%s: <- \n", __func__);
> + down(&wl->assoc_stat_lock);
> + spin_lock_irqsave(&wl->lock, irqflag);
> + if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat) ||
> + wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) {
> + memcpy(extra, wl->essid, wl->essid_len);
> + data->essid.length = wl->essid_len;
> + data->essid.flags = 1;
> + } else
> + data->essid.flags = 0;
> +
> + up(&wl->assoc_stat_lock);
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + pr_debug("%s: -> len=%d \n", __func__, data->essid.length);
> +
> + return 0;
> +}
> +
> +/* SIO{S,G}IWENCODE */
> +static int gelic_wl_set_encode(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + struct iw_point *enc = &data->encoding;
> + __u16 flags;
> + unsigned int irqflag;
> + int key_index, index_specified;
> + int ret = 0;
> +
> + pr_debug("%s: <- \n", __func__);
> + flags = enc->flags & IW_ENCODE_FLAGS;
> + key_index = enc->flags & IW_ENCODE_INDEX;
> +
> + pr_debug("%s: key_index = %d\n", __func__, key_index);
> + pr_debug("%s: key_len = %d\n", __func__, enc->length);
> + pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
> +
> + if (GELIC_WEP_KEYS < key_index)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&wl->lock, irqflag);
> + if (key_index) {
> + index_specified = 1;
> + key_index--;
> + } else {
> + index_specified = 0;
> + key_index = wl->current_key;
> + }
> +
> + if (flags & IW_ENCODE_NOKEY) {
> + /* if just IW_ENCODE_NOKEY, change current key index */
> + if (!flags && index_specified) {
> + wl->current_key = key_index;
> + goto done;
> + }
> +
> + if (flags & IW_ENCODE_DISABLED) {
> + if (!index_specified) {
> + /* disable encryption */
> + wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
> + wl->pairwise_cipher_method =
> + GELIC_WL_CIPHER_NONE;
> + /* invalidate all key */
> + wl->key_enabled = 0;
> + } else
> + clear_bit(key_index, &wl->key_enabled);
> + }
> +
> + if (flags & IW_ENCODE_OPEN)
> + wl->auth_method = GELIC_EURUS_AUTH_OPEN;
> + if (flags & IW_ENCODE_RESTRICTED) {
> + pr_info("%s: shared key mode enabled\n", __func__);
> + wl->auth_method = GELIC_EURUS_AUTH_SHARED;
> + }
> + } else {
> + if (IW_ENCODING_TOKEN_MAX < enc->length) {
> + ret = -EINVAL;
> + goto done;
> + }
> + wl->key_len[key_index] = enc->length;
> + memcpy(wl->key[key_index], extra, enc->length);
> + set_bit(key_index, &wl->key_enabled);
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
> + wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
> + }
> + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
> +done:
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + pr_debug("%s: -> \n", __func__);
> + return ret;
> +}
> +
> +static int gelic_wl_get_encode(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + struct iw_point *enc = &data->encoding;
> + unsigned int irqflag;
> + unsigned int key_index, index_specified;
> + int ret = 0;
> +
> + pr_debug("%s: <- \n", __func__);
> + key_index = enc->flags & IW_ENCODE_INDEX;
> + pr_debug("%s: flag=%#x point=%p len=%d extra=%p\n", __func__,
> + enc->flags, enc->pointer, enc->length, extra);
> + if (GELIC_WEP_KEYS < key_index)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&wl->lock, irqflag);
> + if (key_index) {
> + index_specified = 1;
> + key_index--;
> + } else {
> + index_specified = 0;
> + key_index = wl->current_key;
> + }
> +
> + if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
> + switch (wl->auth_method) {
> + case GELIC_EURUS_AUTH_OPEN:
> + enc->flags = IW_ENCODE_OPEN;
> + break;
> + case GELIC_EURUS_AUTH_SHARED:
> + enc->flags = IW_ENCODE_RESTRICTED;
> + break;
> + }
> + } else
> + enc->flags = IW_ENCODE_DISABLED;
> +
> + if (test_bit(key_index, &wl->key_enabled)) {
> + if (enc->length < wl->key_len[key_index]) {
> + ret = -EINVAL;
> + goto done;
> + }
> + enc->length = wl->key_len[key_index];
> + memcpy(extra, wl->key[key_index], wl->key_len[key_index]);
> + } else {
> + enc->length = 0;
> + enc->flags |= IW_ENCODE_NOKEY;
> + }
> + enc->flags |= key_index + 1;
> + pr_debug("%s: -> flag=%x len=%d\n", __func__,
> + enc->flags, enc->length);
> +
> +done:
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + return ret;
> +}
> +
> +/* SIOC{S,G}IWAP */
> +static int gelic_wl_set_ap(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + unsigned long irqflag;
> +
> + pr_debug("%s: <-\n", __func__);
> + if (data->ap_addr.sa_family != ARPHRD_ETHER)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&wl->lock, irqflag);
> + if (is_valid_ether_addr(data->ap_addr.sa_data)) {
> + memcpy(wl->bssid, data->ap_addr.sa_data,
> + ETH_ALEN);
> + set_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat);
> + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
> + pr_debug("%s: bss=%02x:%02x:%02x:%02x:%02x:%02x\n",
> + __func__,
> + wl->bssid[0], wl->bssid[1],
> + wl->bssid[2], wl->bssid[3],
> + wl->bssid[4], wl->bssid[5]);
> + } else {
> + pr_debug("%s: clear bssid\n", __func__);
> + clear_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat);
> + memset(wl->bssid, 0, ETH_ALEN);
> + }
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + pr_debug("%s: ->\n", __func__);
> + return 0;
> +}
> +
> +static int gelic_wl_get_ap(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + unsigned long irqflag;
> +
> + pr_debug("%s: <-\n", __func__);
> + down(&wl->assoc_stat_lock);
> + spin_lock_irqsave(&wl->lock, irqflag);
> + if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) {
> + data->ap_addr.sa_family = ARPHRD_ETHER;
> + memcpy(data->ap_addr.sa_data, wl->active_bssid,
> + ETH_ALEN);
> + } else
> + memset(data->ap_addr.sa_data, 0, ETH_ALEN);
> +
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + up(&wl->assoc_stat_lock);
> + pr_debug("%s: ->\n", __func__);
> + return 0;
> +}
> +
> +/* SIOC{S,G}IWENCODEEXT */
> +static int gelic_wl_set_encodeext(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + struct iw_point *enc = &data->encoding;
> + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
> + __u16 alg;
> + __u16 flags;
> + unsigned int irqflag;
> + int key_index;
> + int ret = 0;
> +
> + pr_debug("%s: <- \n", __func__);
> + flags = enc->flags & IW_ENCODE_FLAGS;
> + alg = ext->alg;
> + key_index = enc->flags & IW_ENCODE_INDEX;
> +
> + pr_debug("%s: key_index = %d\n", __func__, key_index);
> + pr_debug("%s: key_len = %d\n", __func__, enc->length);
> + pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
> + pr_debug("%s: ext_flag=%x\n", __func__, ext->ext_flags);
> + pr_debug("%s: ext_key_len=%x\n", __func__, ext->key_len);
> +
> + if (GELIC_WEP_KEYS < key_index)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&wl->lock, irqflag);
> + if (key_index)
> + key_index--;
> + else
> + key_index = wl->current_key;
> +
> + if (!enc->length && (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
> + /* reques to change default key index */
> + pr_debug("%s: request to change default key to %d\n",
> + __func__, key_index);
> + wl->current_key = key_index;
> + goto done;
> + }
> +
> + if (alg == IW_ENCODE_ALG_NONE || (flags & IW_ENCODE_DISABLED)) {
> + pr_debug("%s: alg disabled\n", __func__);
> + wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
> + wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
> + wl->auth_method = GELIC_EURUS_AUTH_OPEN; /* should be open */
> + } else if (alg == IW_ENCODE_ALG_WEP) {
> + pr_debug("%s: WEP requested\n", __func__);
> + if (flags & IW_ENCODE_OPEN) {
> + pr_debug("%s: open key mode\n", __func__);
> + wl->auth_method = GELIC_EURUS_AUTH_OPEN;
> + }
> + if (flags & IW_ENCODE_RESTRICTED) {
> + pr_debug("%s: shared key mode\n", __func__);
> + wl->auth_method = GELIC_EURUS_AUTH_SHARED;
> + }
> + if (IW_ENCODING_TOKEN_MAX < ext->key_len) {
> + pr_info("%s: key is too long %d\n", __func__,
> + ext->key_len);
> + ret = -EINVAL;
> + goto done;
> + }
> + /* OK, update the key */
> + wl->key_len[key_index] = ext->key_len;
> + memset(wl->key[key_index], 0, IW_ENCODING_TOKEN_MAX);
> + memcpy(wl->key[key_index], ext->key, ext->key_len);
> + set_bit(key_index, &wl->key_enabled);
> + /* remember wep info changed */
> + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
> + } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
> + pr_debug("%s: TKIP/CCMP requested alg=%d\n", __func__, alg);
> + /* check key length */
> + if (IW_ENCODING_TOKEN_MAX < ext->key_len) {
> + pr_info("%s: key is too long %d\n", __func__,
> + ext->key_len);
> + ret = -EINVAL;
> + goto done;
> + }
> + if (alg == IW_ENCODE_ALG_CCMP) {
> + pr_debug("%s: AES selected\n", __func__);
> + wl->group_cipher_method = GELIC_WL_CIPHER_AES;
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_AES;
> + wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA2;
> + } else {
> + pr_debug("%s: TKIP selected, WPA forced\n", __func__);
> + wl->group_cipher_method = GELIC_WL_CIPHER_TKIP;
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP;
> + /* FIXME: how do we do if WPA2 + TKIP? */
> + wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA;
> + }
> + if (flags & IW_ENCODE_RESTRICTED)
> + BUG();
> + wl->auth_method = GELIC_EURUS_AUTH_OPEN;
> + /* We should use same key for both and unicast */
> + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
> + pr_debug("%s: group key \n", __func__);
> + else
> + pr_debug("%s: unicast key \n", __func__);
> + /* OK, update the key */
> + wl->key_len[key_index] = ext->key_len;
> + memset(wl->key[key_index], 0, IW_ENCODING_TOKEN_MAX);
> + memcpy(wl->key[key_index], ext->key, ext->key_len);
> + set_bit(key_index, &wl->key_enabled);
> + /* remember info changed */
> + set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
> + }
> +done:
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + pr_debug("%s: -> \n", __func__);
> + return ret;
> +}
> +
> +static int gelic_wl_get_encodeext(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + struct iw_point *enc = &data->encoding;
> + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
> + unsigned int irqflag;
> + int key_index;
> + int ret = 0;
> + int max_key_len;
> +
> + pr_debug("%s: <- \n", __func__);
> +
> + max_key_len = enc->length - sizeof(struct iw_encode_ext);
> + if (max_key_len < 0)
> + return -EINVAL;
> + key_index = enc->flags & IW_ENCODE_INDEX;
> +
> + pr_debug("%s: key_index = %d\n", __func__, key_index);
> + pr_debug("%s: key_len = %d\n", __func__, enc->length);
> + pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
> +
> + if (GELIC_WEP_KEYS < key_index)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&wl->lock, irqflag);
> + if (key_index)
> + key_index--;
> + else
> + key_index = wl->current_key;
> +
> + memset(ext, 0, sizeof(struct iw_encode_ext));
> + switch (wl->group_cipher_method) {
> + case GELIC_WL_CIPHER_WEP:
> + ext->alg = IW_ENCODE_ALG_WEP;
> + enc->flags |= IW_ENCODE_ENABLED;
> + break;
> + case GELIC_WL_CIPHER_TKIP:
> + ext->alg = IW_ENCODE_ALG_TKIP;
> + enc->flags |= IW_ENCODE_ENABLED;
> + break;
> + case GELIC_WL_CIPHER_AES:
> + ext->alg = IW_ENCODE_ALG_CCMP;
> + enc->flags |= IW_ENCODE_ENABLED;
> + break;
> + case GELIC_WL_CIPHER_NONE:
> + default:
> + ext->alg = IW_ENCODE_ALG_NONE;
> + enc->flags |= IW_ENCODE_NOKEY;
> + break;
> + }
> +
> + if (!(enc->flags & IW_ENCODE_NOKEY)) {
> + if (max_key_len < wl->key_len[key_index]) {
> + ret = -E2BIG;
> + goto out;
> + }
> + if (test_bit(key_index, &wl->key_enabled))
> + memcpy(ext->key, wl->key[key_index],
> + wl->key_len[key_index]);
> + else
> + pr_debug("%s: disabled key requested ix=%d\n",
> + __func__, key_index);
> + }
> +out:
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + pr_debug("%s: -> \n", __func__);
> + return ret;
> +}
> +/* SIOC{S,G}IWMODE */
> +static int gelic_wl_set_mode(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + __u32 mode = data->mode;
> + int ret;
> +
> + pr_debug("%s: <- \n", __func__);
> + if (mode == IW_MODE_INFRA)
> + ret = 0;
> + else
> + ret = -EOPNOTSUPP;
> + pr_debug("%s: -> %d\n", __func__, ret);
> + return ret;
> +}
> +
> +static int gelic_wl_get_mode(struct net_device *netdev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + __u32 *mode = &data->mode;
> + pr_debug("%s: <- \n", __func__);
> + *mode = IW_MODE_INFRA;
> + pr_debug("%s: ->\n", __func__);
> + return 0;
> +}
> +
> +/* SIOCIWFIRSTPRIV */
> +static int hex2bin(u8 *str, u8 *bin, unsigned int len)
> +{
> + unsigned int i;
> + static unsigned char *hex = "0123456789ABCDEF";
> + unsigned char *p, *q;
> + u8 tmp;
> +
> + if (len != WPA_PSK_LEN * 2)
> + return -EINVAL;
> +
> + for (i = 0; i < WPA_PSK_LEN * 2; i += 2) {
> + p = strchr(hex, toupper(str[i]));
> + q = strchr(hex, toupper(str[i + 1]));
> + if (!p || !q) {
> + pr_info("%s: unconvertible PSK digit=%d\n",
> + __func__, i);
> + return -EINVAL;
> + }
> + tmp = ((p - hex) << 4) + (q - hex);
> + *bin++ = tmp;
> + }
> + return 0;
> +};
> +
> +int gelic_wl_priv_set_psk(struct net_device *net_dev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(net_dev));
> + unsigned int len;
> + unsigned int irqflag;
> + int ret = 0;
> +
> + pr_debug("%s:<- len=%d\n", __func__, data->data.length);
> + len = data->data.length - 1;
> + if (len <= 2)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&wl->lock, irqflag);
> + if (extra[0] == '"' && extra[len - 1] == '"') {
> + pr_debug("%s: passphrase mode\n", __func__);
> + /* pass phrase */
> + if (GELIC_WL_EURUS_PSK_MAX_LEN < (len - 2)) {
> + pr_info("%s: passphrase too long\n", __func__);
> + ret = -E2BIG;
> + goto out;
> + }
> + memset(wl->psk, 0, sizeof(wl->psk));
> + wl->psk_len = len - 2;
> + memcpy(wl->psk, &(extra[1]), wl->psk_len);
> + wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE;
> + } else {
> + ret = hex2bin(extra, wl->psk, len);
> + if (ret)
> + goto out;
> + wl->psk_len = WPA_PSK_LEN;
> + wl->psk_type = GELIC_EURUS_WPA_PSK_BIN;
> + }
> + set_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat);
> +out:
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + pr_debug("%s:->\n", __func__);
> + return ret;
> +}
> +
> +static int gelic_wl_priv_get_psk(struct net_device *net_dev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(net_dev));
> + char *p;
> + unsigned int irqflag;
> + unsigned int i;
> +
> + pr_debug("%s:<-\n", __func__);
> + if (!capable(CAP_NET_ADMIN))
> + return -EPERM;
> +
> + spin_lock_irqsave(&wl->lock, irqflag);
> + p = extra;
> + if (test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat)) {
> + if (wl->psk_type == GELIC_EURUS_WPA_PSK_BIN) {
> + for (i = 0; i < wl->psk_len; i++) {
> + sprintf(p, "%02xu", wl->psk[i]);
> + p += 2;
> + }
> + *p = '\0';
> + data->data.length = wl->psk_len * 2;
> + } else {
> + *p++ = '"';
> + memcpy(p, wl->psk, wl->psk_len);
> + p += wl->psk_len;
> + *p++ = '"';
> + *p = '\0';
> + data->data.length = wl->psk_len + 2;
> + }
> + } else
> + /* no psk set */
> + data->data.length = 0;
> + spin_unlock_irqrestore(&wl->lock, irqflag);
> + pr_debug("%s:-> %d\n", __func__, data->data.length);
> + return 0;
> +}
> +
> +/* SIOCGIWNICKN */
> +static int gelic_wl_get_nick(struct net_device *net_dev,
> + struct iw_request_info *info,
> + union iwreq_data *data, char *extra)
> +{
> + strcpy(extra, "gelic_wl");
> + data->data.length = strlen(extra);
> + data->data.flags = 1;
> + return 0;
> +}
> +
> +
> +/* --- */
> +
> +static struct iw_statistics *gelic_wl_get_wireless_stats(
> + struct net_device *netdev)
> +{
> +
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + struct gelic_eurus_cmd *cmd;
> + struct iw_statistics *is;
> + struct gelic_eurus_rssi_info *rssi;
> +
> + pr_debug("%s: <-\n", __func__);
> +
> + is = &wl->iwstat;
> + memset(is, 0, sizeof(*is));
> + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_RSSI_CFG,
> + wl->buf, sizeof(*rssi));
> + if (cmd && !cmd->status && !cmd->cmd_status) {
> + rssi = wl->buf;
> + is->qual.level = be16_to_cpu(rssi->rssi);
> + is->qual.updated = IW_QUAL_LEVEL_UPDATED |
> + IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
> + } else
> + /* not associated */
> + is->qual.updated = IW_QUAL_ALL_INVALID;
> +
> + kfree(cmd);
> + pr_debug("%s: ->\n", __func__);
> + return is;
> +}
> +
> +/*
> + * scanning helpers
> + */
> +static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan)
> +{
> + struct gelic_eurus_cmd *cmd;
> + int ret = 0;
> +
> + pr_debug("%s: <- always=%d\n", __func__, always_scan);
> + if (down_interruptible(&wl->scan_lock))
> + return -ERESTARTSYS;
> +
> + /*
> + * If already a scan in progress, do not trigger more
> + */
> + if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) {
> + pr_debug("%s: scanning now\n", __func__);
> + goto out;
> + }
> +
> + init_completion(&wl->scan_done);
> + /*
> + * If we have already a bss list, don't try to get new
> + */
> + if (!always_scan && wl->scan_stat == GELIC_WL_SCAN_STAT_GOT_LIST) {
> + pr_debug("%s: already has the list\n", __func__);
> + complete(&wl->scan_done);
> + goto out;
> + }
> + /*
> + * issue start scan request
> + */
> + wl->scan_stat = GELIC_WL_SCAN_STAT_SCANNING;
> + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_START_SCAN,
> + NULL, 0);
> + if (!cmd || cmd->status || cmd->cmd_status) {
> + wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
> + complete(&wl->scan_done);
> + ret = -ENOMEM;
> + goto out;
> + }
> + kfree(cmd);
> +out:
> + up(&wl->scan_lock);
> + pr_debug("%s: ->\n", __func__);
> + return ret;
> +}
> +
> +/*
> + * retrieve scan result from the chip (hypervisor)
> + * this function is invoked by schedule work.
> + */
> +static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl)
> +{
> + struct gelic_eurus_cmd *cmd = NULL;
> + struct gelic_wl_scan_info *target, *tmp;
> + struct gelic_wl_scan_info *oldest = NULL;
> + struct gelic_eurus_scan_info *scan_info;
> + unsigned int scan_info_size;
> + union iwreq_data data;
> + unsigned long this_time = jiffies;
> + unsigned int data_len, i, found, r;
> + DECLARE_MAC_BUF(mac);
> +
> + pr_debug("%s:start\n", __func__);
> + down(&wl->scan_lock);
> +
> + if (wl->scan_stat != GELIC_WL_SCAN_STAT_SCANNING) {
> + /*
> + * stop() may be called while scanning, ignore result
> + */
> + pr_debug("%s: scan complete when stat != scanning(%d)\n",
> + __func__, wl->scan_stat);
> + goto out;
> + }
> +
> + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_SCAN,
> + wl->buf, PAGE_SIZE);
> + if (!cmd || cmd->status || cmd->cmd_status) {
> + wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
> + pr_info("%s:cmd failed\n", __func__);
> + kfree(cmd);
> + goto out;
> + }
> + data_len = cmd->size;
> + pr_debug("%s: data_len = %d\n", __func__, data_len);
> + kfree(cmd);
> +
> + /* OK, bss list retrieved */
> + wl->scan_stat = GELIC_WL_SCAN_STAT_GOT_LIST;
> +
> + /* mark all entries are old */
> + list_for_each_entry_safe(target, tmp, &wl->network_list, list) {
> + target->valid = 0;
> + /* expire too old entries */
> + if (time_before(target->last_scanned + wl->scan_age,
> + this_time)) {
> + kfree(target->hwinfo);
> + target->hwinfo = NULL;
> + list_move_tail(&target->list, &wl->network_free_list);
> + }
> + }
> +
> + /* put them in the newtork_list */
> + scan_info = wl->buf;
> + scan_info_size = 0;
> + i = 0;
> + while (scan_info_size < data_len) {
> + pr_debug("%s:size=%d bssid=%s scan_info=%p\n", __func__,
> + be16_to_cpu(scan_info->size),
> + print_mac(mac, &scan_info->bssid[2]), scan_info);
> + found = 0;
> + oldest = NULL;
> + list_for_each_entry(target, &wl->network_list, list) {
> + if (!compare_ether_addr(&target->hwinfo->bssid[2],
> + &scan_info->bssid[2])) {
> + found = 1;
> + pr_debug("%s: same BBS found scanned list\n",
> + __func__);
> + break;
> + }
> + if (!oldest ||
> + (target->last_scanned < oldest->last_scanned))
> + oldest = target;
> + }
> +
> + if (!found) {
> + /* not found in the list */
> + if (list_empty(&wl->network_free_list)) {
> + /* expire oldest */
> + target = oldest;
> + } else {
> + target = list_entry(wl->network_free_list.next,
> + struct gelic_wl_scan_info,
> + list);
> + }
> + }
> +
> + /* update the item */
> + target->last_scanned = this_time;
> + target->valid = 1;
> + target->eurus_index = i;
> + kfree(target->hwinfo);
> + target->hwinfo = kzalloc(be16_to_cpu(scan_info->size),
> + GFP_KERNEL);
> + if (!target->hwinfo) {
> + pr_info("%s: kzalloc failed\n", __func__);
> + i++;
> + scan_info_size += be16_to_cpu(scan_info->size);
> + scan_info = (void *)scan_info +
> + be16_to_cpu(scan_info->size);
> + continue;
> + }
> + /* copy hw scan info */
> + memcpy(target->hwinfo, scan_info, scan_info->size);
> + target->essid_len = strnlen(scan_info->essid,
> + sizeof(scan_info->essid));
> + target->rate_len = 0;
> + for (r = 0; r < MAX_RATES_LENGTH; r++)
> + if (scan_info->rate[r])
> + target->rate_len++;
> + if (8 < target->rate_len)
> + pr_info("%s: AP returns %d rates\n", __func__,
> + target->rate_len);
> + target->rate_ext_len = 0;
> + for (r = 0; r < MAX_RATES_EX_LENGTH; r++)
> + if (scan_info->ext_rate[r])
> + target->rate_ext_len++;
> + list_move_tail(&target->list, &wl->network_list);
> + /* bump pointer */
> + i++;
> + scan_info_size += be16_to_cpu(scan_info->size);
> + scan_info = (void *)scan_info + be16_to_cpu(scan_info->size);
> + }
> + memset(&data, 0, sizeof(data));
> + wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWSCAN, &data,
> + NULL);
> +out:
> + complete(&wl->scan_done);
> + up(&wl->scan_lock);
> + pr_debug("%s:end\n", __func__);
> +}
> +
> +/*
> + * Select an appropriate bss from current scan list regarding
> + * current settings from userspace.
> + * The caller must hold wl->scan_lock,
> + * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST
> + */
> +static void update_best(struct gelic_wl_scan_info **best,
> + struct gelic_wl_scan_info *candid,
> + int *best_weight,
> + int *weight)
> +{
> + if (*best_weight < ++(*weight)) {
> + *best_weight = *weight;
> + *best = candid;
> + }
> +}
> +
> +static
> +struct gelic_wl_scan_info *gelic_wl_find_best_bss(struct gelic_wl_info *wl)
> +{
> + struct gelic_wl_scan_info *scan_info;
> + struct gelic_wl_scan_info *best_bss;
> + int weight, best_weight;
> + u16 security;
> + DECLARE_MAC_BUF(mac);
> +
> + pr_debug("%s: <-\n", __func__);
> +
> + best_bss = NULL;
> + best_weight = 0;
> +
> + list_for_each_entry(scan_info, &wl->network_list, list) {
> + pr_debug("%s: station %p\n", __func__, scan_info);
> +
> + if (!scan_info->valid) {
> + pr_debug("%s: station invalid\n", __func__);
> + continue;
> + }
> +
> + /* If bss specified, check it only */
> + if (test_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat)) {
> + if (!compare_ether_addr(&scan_info->hwinfo->bssid[2],
> + wl->bssid)) {
> + best_bss = scan_info;
> + pr_debug("%s: bssid matched\n", __func__);
> + break;
> + } else {
> + pr_debug("%s: bssid unmached\n", __func__);
> + continue;
> + }
> + }
> +
> + weight = 0;
> +
> + /* security */
> + security = be16_to_cpu(scan_info->hwinfo->security) &
> + GELIC_EURUS_SCAN_SEC_MASK;
> + if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) {
> + if (security == GELIC_EURUS_SCAN_SEC_WPA2)
> + update_best(&best_bss, scan_info,
> + &best_weight, &weight);
> + else
> + continue;
> + } else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA) {
> + if (security == GELIC_EURUS_SCAN_SEC_WPA)
> + update_best(&best_bss, scan_info,
> + &best_weight, &weight);
> + else
> + continue;
> + } else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_NONE &&
> + wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
> + if (security == GELIC_EURUS_SCAN_SEC_WEP)
> + update_best(&best_bss, scan_info,
> + &best_weight, &weight);
> + else
> + continue;
> + }
> +
> + /* If ESSID is set, check it */
> + if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) {
> + if ((scan_info->essid_len == wl->essid_len) &&
> + !strncmp(wl->essid,
> + scan_info->hwinfo->essid,
> + scan_info->essid_len))
> + update_best(&best_bss, scan_info,
> + &best_weight, &weight);
> + else
> + continue;
> + }
> + }
> +
> +#ifdef DEBUG
> + pr_debug("%s: -> bss=%p\n", __func__, best_bss);
> + if (best_bss) {
> + pr_debug("%s:addr=%s\n", __func__,
> + print_mac(mac, &best_bss->hwinfo->bssid[2]));
> + }
> +#endif
> + return best_bss;
> +}
> +
> +/*
> + * Setup WEP configuration to the chip
> + * The caller must hold wl->scan_lock,
> + * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST
> + */
> +static int gelic_wl_do_wep_setup(struct gelic_wl_info *wl)
> +{
> + unsigned int i;
> + struct gelic_eurus_wep_cfg *wep;
> + struct gelic_eurus_cmd *cmd;
> + int wep104 = 0;
> + int have_key = 0;
> + int ret = 0;
> +
> + pr_debug("%s: <-\n", __func__);
> + /* we can assume no one should uses the buffer */
> + wep = wl->buf;
> + memset(wep, 0, sizeof(*wep));
> +
> + if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
> + pr_debug("%s: WEP mode\n", __func__);
> + for (i = 0; i < GELIC_WEP_KEYS; i++) {
> + if (!test_bit(i, &wl->key_enabled))
> + continue;
> +
> + pr_debug("%s: key#%d enabled\n", __func__, i);
> + have_key = 1;
> + if (wl->key_len[i] == 13)
> + wep104 = 1;
> + else if (wl->key_len[i] != 5) {
> + pr_info("%s: wrong wep key[%d]=%d\n",
> + __func__, i, wl->key_len[i]);
> + ret = -EINVAL;
> + goto out;
> + }
> + memcpy(wep->key[i], wl->key[i], wl->key_len[i]);
> + }
> +
> + if (!have_key) {
> + pr_info("%s: all wep key disabled\n", __func__);
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + if (wep104) {
> + pr_debug("%s: 104bit key\n", __func__);
> + wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_104BIT);
> + } else {
> + pr_debug("%s: 40bit key\n", __func__);
> + wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_40BIT);
> + }
> + } else {
> + pr_debug("%s: NO encryption\n", __func__);
> + wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_NONE);
> + }
> +
> + /* issue wep setup */
> + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WEP_CFG,
> + wep, sizeof(*wep));
> + if (!cmd)
> + ret = -ENOMEM;
> + else if (cmd->status || cmd->cmd_status)
> + ret = -ENXIO;
> +
> + kfree(cmd);
> +out:
> + pr_debug("%s: ->\n", __func__);
> + return ret;
> +}
> +
> +#ifdef DEBUG
> +static const char *wpasecstr(enum gelic_eurus_wpa_security sec)
> +{
> + switch (sec) {
> + case GELIC_EURUS_WPA_SEC_NONE:
> + return "NONE";
> + break;
> + case GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP:
> + return "WPA_TKIP_TKIP";
> + break;
> + case GELIC_EURUS_WPA_SEC_WPA_TKIP_AES:
> + return "WPA_TKIP_AES";
> + break;
> + case GELIC_EURUS_WPA_SEC_WPA_AES_AES:
> + return "WPA_AES_AES";
> + break;
> + case GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP:
> + return "WPA2_TKIP_TKIP";
> + break;
> + case GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES:
> + return "WPA2_TKIP_AES";
> + break;
> + case GELIC_EURUS_WPA_SEC_WPA2_AES_AES:
> + return "WPA2_AES_AES";
> + break;
> + }
> + return "";
> +};
> +#endif
> +
> +static int gelic_wl_do_wpa_setup(struct gelic_wl_info *wl)
> +{
> + struct gelic_eurus_wpa_cfg *wpa;
> + struct gelic_eurus_cmd *cmd;
> + u16 security;
> + int ret = 0;
> +
> + pr_debug("%s: <-\n", __func__);
> + /* we can assume no one should uses the buffer */
> + wpa = wl->buf;
> + memset(wpa, 0, sizeof(*wpa));
> +
> + if (!test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat))
> + pr_info("%s: PSK not configured yet\n", __func__);
> +
> + /* copy key */
> + memcpy(wpa->psk, wl->psk, wl->psk_len);
> +
> + /* set security level */
> + if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) {
> + if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) {
> + security = GELIC_EURUS_WPA_SEC_WPA2_AES_AES;
> + } else {
> + if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES &&
> + precise_ie())
> + security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES;
> + else
> + security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP;
> + }
> + } else {
> + if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) {
> + security = GELIC_EURUS_WPA_SEC_WPA_AES_AES;
> + } else {
> + if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES &&
> + precise_ie())
> + security = GELIC_EURUS_WPA_SEC_WPA_TKIP_AES;
> + else
> + security = GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP;
> + }
> + }
> + wpa->security = cpu_to_be16(security);
> +
> + /* PSK type */
> + wpa->psk_type = cpu_to_be16(wl->psk_type);
> +#ifdef DEBUG
> + pr_debug("%s: sec=%s psktype=%s\nn", __func__,
> + wpasecstr(wpa->security),
> + (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ?
> + "BIN" : "passphrase");
> +#if 0
> + /*
> + * don't enable here if you plan to submit
> + * the debug log because this dumps your precious
> + * passphrase/key.
> + */
> + pr_debug("%s: psk=%s\n",
> + (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ?
> + (char *)"N/A" : (char *)wpa->psk);
> +#endif
> +#endif
> + /* issue wpa setup */
> + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WPA_CFG,
> + wpa, sizeof(*wpa));
> + if (!cmd)
> + ret = -ENOMEM;
> + else if (cmd->status || cmd->cmd_status)
> + ret = -ENXIO;
> + kfree(cmd);
> + pr_debug("%s: --> %d\n", __func__, ret);
> + return ret;
> +}
> +
> +/*
> + * Start association. caller must hold assoc_stat_lock
> + */
> +static int gelic_wl_associate_bss(struct gelic_wl_info *wl,
> + struct gelic_wl_scan_info *bss)
> +{
> + struct gelic_eurus_cmd *cmd;
> + struct gelic_eurus_common_cfg *common;
> + int ret = 0;
> + unsigned long rc;
> +
> + pr_debug("%s: <-\n", __func__);
> +
> + /* do common config */
> + common = wl->buf;
> + memset(common, 0, sizeof(*common));
> + common->bss_type = cpu_to_be16(GELIC_EURUS_BSS_INFRA);
> + common->op_mode = cpu_to_be16(GELIC_EURUS_OPMODE_11BG);
> +
> + common->scan_index = cpu_to_be16(bss->eurus_index);
> + switch (wl->auth_method) {
> + case GELIC_EURUS_AUTH_OPEN:
> + common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_OPEN);
> + break;
> + case GELIC_EURUS_AUTH_SHARED:
> + common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_SHARED);
> + break;
> + }
> +
> +#ifdef DEBUG
> + scan_list_dump(wl);
> +#endif
> + pr_debug("%s: common cfg index=%d bsstype=%d auth=%d\n", __func__,
> + be16_to_cpu(common->scan_index),
> + be16_to_cpu(common->bss_type),
> + be16_to_cpu(common->auth_method));
> +
> + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_COMMON_CFG,
> + common, sizeof(*common));
> + if (!cmd || cmd->status || cmd->cmd_status) {
> + ret = -ENOMEM;
> + kfree(cmd);
> + goto out;
> + }
> + kfree(cmd);
> +
> + /* WEP/WPA */
> + switch (wl->wpa_level) {
> + case GELIC_WL_WPA_LEVEL_NONE:
> + /* If WEP or no security, setup WEP config */
> + ret = gelic_wl_do_wep_setup(wl);
> + break;
> + case GELIC_WL_WPA_LEVEL_WPA:
> + case GELIC_WL_WPA_LEVEL_WPA2:
> + ret = gelic_wl_do_wpa_setup(wl);
> + break;
> + };
> +
> + if (ret) {
> + pr_debug("%s: WEP/WPA setup failed %d\n", __func__,
> + ret);
> + }
> +
> + /* start association */
> + init_completion(&wl->assoc_done);
> + wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATING;
> + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_ASSOC,
> + NULL, 0);
> + if (!cmd || cmd->status || cmd->cmd_status) {
> + pr_debug("%s: assoc request failed\n", __func__);
> + wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
> + kfree(cmd);
> + ret = -ENOMEM;
> + goto out;
> + }
> + kfree(cmd);
> +
> + /* wait for connected event */
> + rc = wait_for_completion_timeout(&wl->assoc_done, HZ * 4);/*FIXME*/
> +
> + if (!rc) {
> + /* timeouted. Maybe key or cyrpt mode is wrong */
> + pr_info("%s: connect timeout \n", __func__);
> + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC,
> + NULL, 0);
> + kfree(cmd);
> + wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
> + ret = -ENXIO;
> + } else {
> + union iwreq_data data;
> +
> + wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATED;
> + /* copy bssid */
> + memcpy(wl->active_bssid, &bss->hwinfo->bssid[2], ETH_ALEN);
> +
> + /* send event */
> + memset(&data, 0, sizeof(data));
> + memcpy(data.ap_addr.sa_data, wl->active_bssid, ETH_ALEN);
> + data.ap_addr.sa_family = ARPHRD_ETHER;
> + wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWAP,
> + &data, NULL);
> + pr_info("%s: connected\n", __func__);
> + }
> +out:
> + pr_debug("%s: ->\n", __func__);
> + return ret;
> +}
> +
> +/*
> + * connected event
> + */
> +static void gelic_wl_connected_event(struct gelic_wl_info *wl,
> + u64 event)
> +{
> + u64 desired_event = 0;
> +
> + switch (wl->wpa_level) {
> + case GELIC_WL_WPA_LEVEL_NONE:
> + desired_event = GELIC_LV1_WL_EVENT_CONNECTED;
> + break;
> + case GELIC_WL_WPA_LEVEL_WPA:
> + case GELIC_WL_WPA_LEVEL_WPA2:
> + desired_event = GELIC_LV1_WL_EVENT_WPA_CONNECTED;
> + break;
> + }
> +
> + if (desired_event == event) {
> + pr_debug("%s: completed \n", __func__);
> + complete(&wl->assoc_done);
> + netif_carrier_on(port_to_netdev(wl_port(wl)));
> + } else
> + pr_debug("%s: event %#lx under wpa\n",
> + __func__, event);
> +}
> +
> +/*
> + * disconnect event
> + */
> +static void gelic_wl_disconnect_event(struct gelic_wl_info *wl,
> + u64 event)
> +{
> + struct gelic_eurus_cmd *cmd;
> + union iwreq_data data;
> + int lock;
> +
> + /*
> + * If we fall here in the middle of association,
> + * associate_bss() should be waiting for complation of
> + * wl->assoc_done.
> + * As it waits with timeout, just leave assoc_done
> + * uncompleted, then it terminates with timeout
> + */
> + if (down_trylock(&wl->assoc_stat_lock)) {
> + pr_debug("%s: already locked\n", __func__);
> + lock = 0;
> + } else {
> + pr_debug("%s: obtain lock\n", __func__);
> + lock = 1;
> + }
> +
> + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0);
> + kfree(cmd);
> +
> + /* send disconnected event to the supplicant */
> + if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) {
> + memset(&data, 0, sizeof(data));
> + data.ap_addr.sa_family = ARPHRD_ETHER;
> + wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWAP,
> + &data, NULL);
> + }
> + wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
> + netif_carrier_off(port_to_netdev(wl_port(wl)));
> +
> + if (lock)
> + up(&wl->assoc_stat_lock);
> +}
> +/*
> + * event worker
> + */
> +#ifdef DEBUG
> +static const char *eventstr(enum gelic_lv1_wl_event event)
> +{
> + static char buf[32];
> + char *ret;
> + if (event & GELIC_LV1_WL_EVENT_DEVICE_READY)
> + ret = "EURUS_READY";
> + else if (event & GELIC_LV1_WL_EVENT_SCAN_COMPLETED)
> + ret = "SCAN_COMPLETED";
> + else if (event & GELIC_LV1_WL_EVENT_DEAUTH)
> + ret = "DEAUTH";
> + else if (event & GELIC_LV1_WL_EVENT_BEACON_LOST)
> + ret = "BEACON_LOST";
> + else if (event & GELIC_LV1_WL_EVENT_CONNECTED)
> + ret = "CONNECTED";
> + else if (event & GELIC_LV1_WL_EVENT_WPA_CONNECTED)
> + ret = "WPA_CONNECTED";
> + else if (event & GELIC_LV1_WL_EVENT_WPA_ERROR)
> + ret = "WPA_ERROR";
> + else {
> + sprintf(buf, "Unknown(%#x)", event);
> + ret = buf;
> + }
> + return ret;
> +}
> +#else
> +static const char *eventstr(enum gelic_lv1_wl_event event)
> +{
> + return 0;
> +}
> +#endif
> +static void gelic_wl_event_worker(struct work_struct *work)
> +{
> + struct gelic_wl_info *wl;
> + struct gelic_port *port;
> + u64 event, tmp;
> + int status;
> +
> + pr_debug("%s:start\n", __func__);
> + wl = container_of(work, struct gelic_wl_info, event_work.work);
> + port = wl_port(wl);
> + while (1) {
> + status = lv1_net_control(bus_id(port->card), dev_id(port->card),
> + GELIC_LV1_GET_WLAN_EVENT, 0, 0, 0,
> + &event, &tmp);
> + if (status) {
> + if (status != LV1_NO_ENTRY)
> + pr_debug("%s:wlan event failed %d\n",
> + __func__, status);
> + /* got all events */
> + pr_debug("%s:end\n", __func__);
> + return;
> + }
> + pr_debug("%s: event=%s\n", __func__, eventstr(event));
> + switch (event) {
> + case GELIC_LV1_WL_EVENT_SCAN_COMPLETED:
> + gelic_wl_scan_complete_event(wl);
> + break;
> + case GELIC_LV1_WL_EVENT_BEACON_LOST:
> + case GELIC_LV1_WL_EVENT_DEAUTH:
> + gelic_wl_disconnect_event(wl, event);
> + break;
> + case GELIC_LV1_WL_EVENT_CONNECTED:
> + case GELIC_LV1_WL_EVENT_WPA_CONNECTED:
> + gelic_wl_connected_event(wl, event);
> + break;
> + default:
> + break;
> + }
> + } /* while */
> +}
> +/*
> + * association worker
> + */
> +static void gelic_wl_assoc_worker(struct work_struct *work)
> +{
> + struct gelic_wl_info *wl;
> +
> + struct gelic_wl_scan_info *best_bss;
> + int ret;
> +
> + wl = container_of(work, struct gelic_wl_info, assoc_work.work);
> +
> + down(&wl->assoc_stat_lock);
> +
> + if (wl->assoc_stat != GELIC_WL_ASSOC_STAT_DISCONN)
> + goto out;
> +
> + ret = gelic_wl_start_scan(wl, 0);
> + if (ret == -ERESTARTSYS) {
> + pr_debug("%s: scan start failed association\n", __func__);
> + schedule_delayed_work(&wl->assoc_work, HZ/10); /*FIXME*/
> + goto out;
> + } else if (ret) {
> + pr_info("%s: scan prerequisite failed\n", __func__);
> + goto out;
> + }
> +
> + /*
> + * Wait for bss scan completion
> + * If we have scan list already, gelic_wl_start_scan()
> + * returns OK and raises the complete. Thus,
> + * it's ok to wait unconditionally here
> + */
> + wait_for_completion(&wl->scan_done);
> +
> + pr_debug("%s: scan done\n", __func__);
> + down(&wl->scan_lock);
> + if (wl->scan_stat != GELIC_WL_SCAN_STAT_GOT_LIST) {
> + pr_info("%s: no scan list. association failed\n", __func__);
> + goto scan_lock_out;
> + }
> +
> + /* find best matching bss */
> + best_bss = gelic_wl_find_best_bss(wl);
> + if (!best_bss) {
> + pr_info("%s: no bss matched. association failed\n", __func__);
> + goto scan_lock_out;
> + }
> +
> + /* ok, do association */
> + ret = gelic_wl_associate_bss(wl, best_bss);
> + if (ret)
> + pr_info("%s: association failed %d\n", __func__, ret);
> +scan_lock_out:
> + up(&wl->scan_lock);
> +out:
> + up(&wl->assoc_stat_lock);
> +}
> +/*
> + * Interrupt handler
> + * Called from the ethernet interrupt handler
> + * Processes wireless specific virtual interrupts only
> + */
> +void gelic_wl_interrupt(struct net_device *netdev, u64 status)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> +
> + if (status & GELIC_CARD_WLAN_COMMAND_COMPLETED) {
> + pr_debug("%s:cmd complete\n", __func__);
> + complete(&wl->cmd_done_intr);
> + }
> +
> + if (status & GELIC_CARD_WLAN_EVENT_RECEIVED) {
> + pr_debug("%s:event received\n", __func__);
> + queue_delayed_work(wl->event_queue, &wl->event_work, 0);
> + }
> +}
> +/*
> + * driver helpers
> + */
> +#define IW_IOCTL(n) [(n) - SIOCSIWCOMMIT]
> +static const iw_handler gelic_wl_wext_handler[] =
> +{
> + IW_IOCTL(SIOCGIWNAME) = gelic_wl_get_name,
> + IW_IOCTL(SIOCGIWRANGE) = gelic_wl_get_range,
> + IW_IOCTL(SIOCSIWSCAN) = gelic_wl_set_scan,
> + IW_IOCTL(SIOCGIWSCAN) = gelic_wl_get_scan,
> + IW_IOCTL(SIOCSIWAUTH) = gelic_wl_set_auth,
> + IW_IOCTL(SIOCGIWAUTH) = gelic_wl_get_auth,
> + IW_IOCTL(SIOCSIWESSID) = gelic_wl_set_essid,
> + IW_IOCTL(SIOCGIWESSID) = gelic_wl_get_essid,
> + IW_IOCTL(SIOCSIWENCODE) = gelic_wl_set_encode,
> + IW_IOCTL(SIOCGIWENCODE) = gelic_wl_get_encode,
> + IW_IOCTL(SIOCSIWAP) = gelic_wl_set_ap,
> + IW_IOCTL(SIOCGIWAP) = gelic_wl_get_ap,
> + IW_IOCTL(SIOCSIWENCODEEXT) = gelic_wl_set_encodeext,
> + IW_IOCTL(SIOCGIWENCODEEXT) = gelic_wl_get_encodeext,
> + IW_IOCTL(SIOCSIWMODE) = gelic_wl_set_mode,
> + IW_IOCTL(SIOCGIWMODE) = gelic_wl_get_mode,
> + IW_IOCTL(SIOCGIWNICKN) = gelic_wl_get_nick,
> +};
> +
> +static struct iw_priv_args gelic_wl_private_args[] =
> +{
> + {
> + .cmd = GELIC_WL_PRIV_SET_PSK,
> + .set_args = IW_PRIV_TYPE_CHAR |
> + (GELIC_WL_EURUS_PSK_MAX_LEN + 2),
> + .name = "set_psk"
> + },
> + {
> + .cmd = GELIC_WL_PRIV_GET_PSK,
> + .get_args = IW_PRIV_TYPE_CHAR |
> + (GELIC_WL_EURUS_PSK_MAX_LEN + 2),
> + .name = "get_psk"
> + }
> +};
> +
> +static const iw_handler gelic_wl_private_handler[] =
> +{
> + gelic_wl_priv_set_psk,
> + gelic_wl_priv_get_psk,
> +};
> +
> +static const struct iw_handler_def gelic_wl_wext_handler_def = {
> + .num_standard = ARRAY_SIZE(gelic_wl_wext_handler),
> + .standard = gelic_wl_wext_handler,
> + .get_wireless_stats = gelic_wl_get_wireless_stats,
> + .num_private = ARRAY_SIZE(gelic_wl_private_handler),
> + .num_private_args = ARRAY_SIZE(gelic_wl_private_args),
> + .private = gelic_wl_private_handler,
> + .private_args = gelic_wl_private_args,
> +};
> +
> +static struct net_device *gelic_wl_alloc(struct gelic_card *card)
> +{
> + struct net_device *netdev;
> + struct gelic_port *port;
> + struct gelic_wl_info *wl;
> + unsigned int i;
> +
> + pr_debug("%s:start\n", __func__);
> + netdev = alloc_etherdev(sizeof(struct gelic_port) +
> + sizeof(struct gelic_wl_info));
> + pr_debug("%s: netdev =%p card=%p \np", __func__, netdev, card);
> + if (!netdev)
> + return NULL;
> +
> + port = netdev_priv(netdev);
> + port->netdev = netdev;
> + port->card = card;
> + port->type = GELIC_PORT_WIRELESS;
> +
> + wl = port_wl(port);
> + pr_debug("%s: wl=%p port=%p\n", __func__, wl, port);
> +
> + /* allocate scan list */
> + wl->networks = kzalloc(sizeof(struct gelic_wl_scan_info) *
> + GELIC_WL_BSS_MAX_ENT, GFP_KERNEL);
> +
> + if (!wl->networks)
> + goto fail_bss;
> +
> + wl->eurus_cmd_queue = create_singlethread_workqueue("gelic_cmd");
> + if (!wl->eurus_cmd_queue)
> + goto fail_cmd_workqueue;
> +
> + wl->event_queue = create_singlethread_workqueue("gelic_event");
> + if (!wl->event_queue)
> + goto fail_event_workqueue;
> +
> + INIT_LIST_HEAD(&wl->network_free_list);
> + INIT_LIST_HEAD(&wl->network_list);
> + for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++)
> + list_add_tail(&wl->networks[i].list,
> + &wl->network_free_list);
> + init_completion(&wl->cmd_done_intr);
> +
> + INIT_DELAYED_WORK(&wl->event_work, gelic_wl_event_worker);
> + INIT_DELAYED_WORK(&wl->assoc_work, gelic_wl_assoc_worker);
> + init_MUTEX(&wl->scan_lock);
> + init_MUTEX(&wl->assoc_stat_lock);
> +
> + init_completion(&wl->scan_done);
> + /* for the case that no scan request is issued and stop() is called */
> + complete(&wl->scan_done);
> +
> + spin_lock_init(&wl->lock);
> +
> + wl->scan_age = 5*HZ; /* FIXME */
> +
> + /* buffer for receiving scanned list etc */
> + BUILD_BUG_ON(PAGE_SIZE <
> + sizeof(struct gelic_eurus_scan_info) *
> + GELIC_EURUS_MAX_SCAN);
> + wl->buf = (void *)get_zeroed_page(GFP_KERNEL);
> + if (!wl->buf) {
> + pr_info("%s:buffer allocation failed\n", __func__);
> + goto fail_getpage;
> + }
> + pr_debug("%s:end\n", __func__);
> + return netdev;
> +
> +fail_getpage:
> + destroy_workqueue(wl->event_queue);
> +fail_event_workqueue:
> + destroy_workqueue(wl->eurus_cmd_queue);
> +fail_cmd_workqueue:
> + kfree(wl->networks);
> +fail_bss:
> + free_netdev(netdev);
> + pr_debug("%s:end error\n", __func__);
> + return NULL;
> +
> +}
> +
> +void gelic_wl_free(struct gelic_wl_info *wl)
> +{
> + struct gelic_wl_scan_info *scan_info;
> + unsigned int i;
> +
> + pr_debug("%s: <-\n", __func__);
> +
> + pr_debug("%s: destroy queues\n", __func__);
> + destroy_workqueue(wl->eurus_cmd_queue);
> + destroy_workqueue(wl->event_queue);
> +
> + scan_info = wl->networks;
> + for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++, scan_info++)
> + kfree(scan_info->hwinfo);
> + kfree(wl->networks);
> +
> + free_netdev(port_to_netdev(wl_port(wl)));
> +
> + pr_debug("%s: ->\n", __func__);
> +}
> +
> +int gelic_wl_try_associate(struct net_device *netdev)
> +{
> + struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
> + int ret = -1;
> + unsigned int i;
> +
> + pr_debug("%s: <-\n", __func__);
> +
> + /* check constraits for start association */
> + /* for no access restriction AP */
> + if (wl->group_cipher_method == GELIC_WL_CIPHER_NONE) {
> + if (test_bit(GELIC_WL_STAT_CONFIGURED,
> + &wl->stat))
> + goto do_associate;
> + else {
> + pr_debug("%s: no wep, not configured\n", __func__);
> + return ret;
> + }
> + }
> +
> + /* for WEP, one of four keys should be set */
> + if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
> + /* one of keys set */
> + for (i = 0; i < GELIC_WEP_KEYS; i++) {
> + if (test_bit(i, &wl->key_enabled))
> + goto do_associate;
> + }
> + pr_debug("%s: WEP, but no key specified\n", __func__);
> + return ret;
> + }
> +
> + /* for WPA[2], psk should be set */
> + if ((wl->group_cipher_method == GELIC_WL_CIPHER_TKIP) ||
> + (wl->group_cipher_method == GELIC_WL_CIPHER_AES)) {
> + if (test_bit(GELIC_WL_STAT_WPA_PSK_SET,
> + &wl->stat))
> + goto do_associate;
> + else {
> + pr_debug("%s: AES/TKIP, but PSK not configured\n",
> + __func__);
> + return ret;
> + }
> + }
> +
> +do_associate:
> + ret = schedule_delayed_work(&wl->assoc_work, 0);
> + pr_debug("%s: start association work %d\n", __func__, ret);
> + return ret;
> +}
> +
> +/*
> + * netdev handlers
> + */
> +static int gelic_wl_open(struct net_device *netdev)
> +{
> + struct gelic_card *card = netdev_card(netdev);
> +
> + pr_debug("%s:->%p\n", __func__, netdev);
> +
> + gelic_card_up(card);
> +
> + /* try to associate */
> + gelic_wl_try_associate(netdev);
> +
> + netif_start_queue(netdev);
> +
> + pr_debug("%s:<-\n", __func__);
> + return 0;
> +}
> +
> +/*
> + * reset state machine
> + */
> +static int gelic_wl_reset_state(struct gelic_wl_info *wl)
> +{
> + struct gelic_wl_scan_info *target;
> + struct gelic_wl_scan_info *tmp;
> +
> + /* empty scan list */
> + list_for_each_entry_safe(target, tmp, &wl->network_list, list) {
> + list_move_tail(&target->list, &wl->network_free_list);
> + }
> + wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
> +
> + /* clear configuration */
> + wl->auth_method = GELIC_EURUS_AUTH_OPEN;
> + wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
> + wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
> + wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
> +
> + wl->key_enabled = 0;
> + wl->current_key = 0;
> +
> + wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE;
> + wl->psk_len = 0;
> +
> + wl->essid_len = 0;
> + memset(wl->essid, 0, sizeof(wl->essid));
> + memset(wl->bssid, 0, sizeof(wl->bssid));
> + memset(wl->active_bssid, 0, sizeof(wl->active_bssid));
> +
> + wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
> +
> + memset(&wl->iwstat, 0, sizeof(wl->iwstat));
> + /* all status bit clear */
> + wl->stat = 0;
> + return 0;
> +}
> +
> +/*
> + * Tell eurus to terminate association
> + */
> +static void gelic_wl_disconnect(struct net_device *netdev)
> +{
> + struct gelic_port *port = netdev_priv(netdev);
> + struct gelic_wl_info *wl = port_wl(port);
> + struct gelic_eurus_cmd *cmd;
> + union iwreq_data data;
> +
> + /*
> + * If scann process is running on chip,
> + * further requests will be rejected
> + */
> + if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING)
> + wait_for_completion_timeout(&wl->scan_done, HZ);
> +
> + cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0);
> + kfree(cmd);
> + memset(&data, 0, sizeof(data));
> + data.ap_addr.sa_family = ARPHRD_ETHER;
> + wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWAP,
> + &data, NULL);
> +};
> +
> +static int gelic_wl_stop(struct net_device *netdev)
> +{
> + struct gelic_port *port = netdev_priv(netdev);
> + struct gelic_wl_info *wl = port_wl(port);
> + struct gelic_card *card = netdev_card(netdev);
> +
> + pr_debug("%s:<-\n", __func__);
> +
> + /*
> + * Cancel pending association work.
> + * event work can run after netdev down
> + */
> + cancel_delayed_work(&wl->assoc_work);
> +
> + if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
> + gelic_wl_disconnect(netdev);
> +
> + /* reset our state machine */
> + gelic_wl_reset_state(wl);
> +
> + netif_stop_queue(netdev);
> +
> + gelic_card_down(card);
> +
> + pr_debug("%s:->\n", __func__);
> + return 0;
> +}
> +
> +/* -- */
> +
> +static struct ethtool_ops gelic_wl_ethtool_ops = {
> + .get_drvinfo = gelic_net_get_drvinfo,
> + .get_link = gelic_wl_get_link,
> + .get_tx_csum = ethtool_op_get_tx_csum,
> + .set_tx_csum = ethtool_op_set_tx_csum,
> + .get_rx_csum = gelic_net_get_rx_csum,
> + .set_rx_csum = gelic_net_set_rx_csum,
> +};
> +
> +static void gelic_wl_setup_netdev_ops(struct net_device *netdev)
> +{
> + struct gelic_wl_info *wl;
> + wl = port_wl(netdev_priv(netdev));
> + BUG_ON(!wl);
> + netdev->open = &gelic_wl_open;
> + netdev->stop = &gelic_wl_stop;
> + netdev->hard_start_xmit = &gelic_net_xmit;
> + netdev->set_multicast_list = &gelic_net_set_multi;
> + netdev->change_mtu = &gelic_net_change_mtu;
> + netdev->wireless_data = &wl->wireless_data;
> + netdev->wireless_handlers = &gelic_wl_wext_handler_def;
> + /* tx watchdog */
> + netdev->tx_timeout = &gelic_net_tx_timeout;
> + netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT;
> +
> + netdev->ethtool_ops = &gelic_wl_ethtool_ops;
> +#ifdef CONFIG_NET_POLL_CONTROLLER
> + netdev->poll_controller = gelic_net_poll_controller;
> +#endif
> +}
> +
> +/*
> + * driver probe/remove
> + */
> +int gelic_wl_driver_probe(struct gelic_card *card)
> +{
> + int ret;
> + struct net_device *netdev;
> +
> + pr_debug("%s:start\n", __func__);
> +
> + if (ps3_compare_firmware_version(1, 6, 0) < 0)
> + return 0;
> + if (!card->vlan[GELIC_PORT_WIRELESS].tx)
> + return 0;
> +
> + /* alloc netdevice for wireless */
> + netdev = gelic_wl_alloc(card);
> + if (!netdev)
> + return -ENOMEM;
> +
> + /* setup net_device structure */
> + gelic_wl_setup_netdev_ops(netdev);
> +
> + /* setup some of net_device and register it */
> + ret = gelic_net_setup_netdev(netdev, card);
> + if (ret)
> + goto fail_setup;
> + card->netdev[GELIC_PORT_WIRELESS] = netdev;
> +
> + /* add enable wireless interrupt */
> + card->irq_mask |= GELIC_CARD_WLAN_EVENT_RECEIVED |
> + GELIC_CARD_WLAN_COMMAND_COMPLETED;
> + /* to allow wireless commands while both interfaces are down */
> + gelic_card_set_irq_mask(card, GELIC_CARD_WLAN_EVENT_RECEIVED |
> + GELIC_CARD_WLAN_COMMAND_COMPLETED);
> + pr_debug("%s:end\n", __func__);
> + return 0;
> +
> +fail_setup:
> + gelic_wl_free(port_wl(netdev_port(netdev)));
> +
> + return ret;
> +}
> +
> +int gelic_wl_driver_remove(struct gelic_card *card)
> +{
> + struct gelic_wl_info *wl;
> + struct net_device *netdev;
> +
> + pr_debug("%s:start\n", __func__);
> +
> + if (ps3_compare_firmware_version(1, 6, 0) < 0)
> + return 0;
> + if (!card->vlan[GELIC_PORT_WIRELESS].tx)
> + return 0;
> +
> + netdev = card->netdev[GELIC_PORT_WIRELESS];
> + wl = port_wl(netdev_priv(netdev));
> +
> + /* if the interface was not up, but associated */
> + if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
> + gelic_wl_disconnect(netdev);
> +
> + complete(&wl->cmd_done_intr);
> +
> + /* cancel all work queue */
> + cancel_delayed_work(&wl->assoc_work);
> + cancel_delayed_work(&wl->event_work);
> + flush_workqueue(wl->eurus_cmd_queue);
> + flush_workqueue(wl->event_queue);
> +
> + unregister_netdev(netdev);
> +
> + /* disable wireless interrupt */
> + pr_debug("%s: disable intr\n", __func__);
> + card->irq_mask &= ~(GELIC_CARD_WLAN_EVENT_RECEIVED |
> + GELIC_CARD_WLAN_COMMAND_COMPLETED);
> + /* free bss list, netdev*/
> + gelic_wl_free(wl);
> + pr_debug("%s:end\n", __func__);
> + return 0;
> +}
> --- /dev/null
> +++ b/drivers/net/ps3_gelic_wireless.h
> @@ -0,0 +1,329 @@
> +/*
> + * PS3 gelic network driver.
> + *
> + * Copyright (C) 2007 Sony Computer Entertainment Inc.
> + * Copyright 2007 Sony Corporation
> + *
> + * 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.
> + *
> + * 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.
> + */
> +#ifndef _GELIC_WIRELESS_H
> +#define _GELIC_WIRELESS_H
> +
> +#include <linux/wireless.h>
> +#include <net/iw_handler.h>
> +
> +
> +/* return value from GELIC_LV1_GET_WLAN_EVENT netcontrol */
> +enum gelic_lv1_wl_event {
> + GELIC_LV1_WL_EVENT_DEVICE_READY = 0x01, /* Eurus ready */
> + GELIC_LV1_WL_EVENT_SCAN_COMPLETED = 0x02, /* Scan has completed */
> + GELIC_LV1_WL_EVENT_DEAUTH = 0x04, /* Deauthed by the AP */
> + GELIC_LV1_WL_EVENT_BEACON_LOST = 0x08, /* Beacon lost detected */
> + GELIC_LV1_WL_EVENT_CONNECTED = 0x10, /* Connected to AP */
> + GELIC_LV1_WL_EVENT_WPA_CONNECTED = 0x20, /* WPA connection */
> + GELIC_LV1_WL_EVENT_WPA_ERROR = 0x40, /* MIC error */
> +};
> +
> +/* arguments for GELIC_LV1_POST_WLAN_COMMAND netcontrol */
> +enum gelic_eurus_command {
> + GELIC_EURUS_CMD_ASSOC = 1, /* association start */
> + GELIC_EURUS_CMD_DISASSOC = 2, /* disassociate */
> + GELIC_EURUS_CMD_START_SCAN = 3, /* scan start */
> + GELIC_EURUS_CMD_GET_SCAN = 4, /* get scan result */
> + GELIC_EURUS_CMD_SET_COMMON_CFG = 5, /* set common config */
> + GELIC_EURUS_CMD_GET_COMMON_CFG = 6, /* set common config */
> + GELIC_EURUS_CMD_SET_WEP_CFG = 7, /* set WEP config */
> + GELIC_EURUS_CMD_GET_WEP_CFG = 8, /* get WEP config */
> + GELIC_EURUS_CMD_SET_WPA_CFG = 9, /* set WPA config */
> + GELIC_EURUS_CMD_GET_WPA_CFG = 10, /* get WPA config */
> + GELIC_EURUS_CMD_GET_RSSI_CFG = 11, /* get RSSI info. */
> + GELIC_EURUS_CMD_MAX_INDEX
> +};
> +
> +/* for GELIC_EURUS_CMD_COMMON_CFG */
> +enum gelic_eurus_bss_type {
> + GELIC_EURUS_BSS_INFRA = 0,
> + GELIC_EURUS_BSS_ADHOC = 1, /* not supported */
> +};
> +
> +enum gelic_eurus_auth_method {
> + GELIC_EURUS_AUTH_OPEN = 0, /* FIXME: WLAN_AUTH_OPEN */
> + GELIC_EURUS_AUTH_SHARED = 1, /* not supported */
> +};
> +
> +enum gelic_eurus_opmode {
> + GELIC_EURUS_OPMODE_11BG = 0, /* 802.11b/g */
> + GELIC_EURUS_OPMODE_11B = 1, /* 802.11b only */
> + GELIC_EURUS_OPMODE_11G = 2, /* 802.11g only */
> +};
> +
> +struct gelic_eurus_common_cfg {
> + /* all fields are big endian */
> + u16 scan_index;
> + u16 bss_type; /* infra or adhoc */
> + u16 auth_method; /* shared key or open */
> + u16 op_mode; /* B/G */
> +} __attribute__((packed));
> +
> +
> +/* for GELIC_EURUS_CMD_WEP_CFG */
> +enum gelic_eurus_wep_security {
> + GELIC_EURUS_WEP_SEC_NONE = 0,
> + GELIC_EURUS_WEP_SEC_40BIT = 1,
> + GELIC_EURUS_WEP_SEC_104BIT = 2,
> +};
> +
> +struct gelic_eurus_wep_cfg {
> + /* all fields are big endian */
> + u16 security;
> + u8 key[4][16];
> +} __attribute__((packed));
> +
> +/* for GELIC_EURUS_CMD_WPA_CFG */
> +enum gelic_eurus_wpa_security {
> + GELIC_EURUS_WPA_SEC_NONE = 0x0000,
> + /* group=TKIP, pairwise=TKIP */
> + GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP = 0x0001,
> + /* group=AES, pairwise=AES */
> + GELIC_EURUS_WPA_SEC_WPA_AES_AES = 0x0002,
> + /* group=TKIP, pairwise=TKIP */
> + GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP = 0x0004,
> + /* group=AES, pairwise=AES */
> + GELIC_EURUS_WPA_SEC_WPA2_AES_AES = 0x0008,
> + /* group=TKIP, pairwise=AES */
> + GELIC_EURUS_WPA_SEC_WPA_TKIP_AES = 0x0010,
> + /* group=TKIP, pairwise=AES */
> + GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES = 0x0020,
> +};
> +
> +enum gelic_eurus_wpa_psk_type {
> + GELIC_EURUS_WPA_PSK_PASSPHRASE = 0, /* passphrase string */
> + GELIC_EURUS_WPA_PSK_BIN = 1, /* 32 bytes binary key */
> +};
> +
> +#define GELIC_WL_EURUS_PSK_MAX_LEN 64
> +#define WPA_PSK_LEN 32 /* WPA spec says 256bit */
> +
> +struct gelic_eurus_wpa_cfg {
> + /* all fields are big endian */
> + u16 security;
> + u16 psk_type; /* psk key encoding type */
> + u8 psk[GELIC_WL_EURUS_PSK_MAX_LEN]; /* psk key; hex or passphrase */
> +} __attribute__((packed));
> +
> +/* for GELIC_EURUS_CMD_{START,GET}_SCAN */
> +enum gelic_eurus_scan_capability {
> + GELIC_EURUS_SCAN_CAP_ADHOC = 0x0000,
> + GELIC_EURUS_SCAN_CAP_INFRA = 0x0001,
> + GELIC_EURUS_SCAN_CAP_MASK = 0x0001,
> +};
> +
> +enum gelic_eurus_scan_sec_type {
> + GELIC_EURUS_SCAN_SEC_NONE = 0x0000,
> + GELIC_EURUS_SCAN_SEC_WEP = 0x0100,
> + GELIC_EURUS_SCAN_SEC_WPA = 0x0200,
> + GELIC_EURUS_SCAN_SEC_WPA2 = 0x0400,
> + GELIC_EURUS_SCAN_SEC_MASK = 0x0f00,
> +};
> +
> +enum gelic_eurus_scan_sec_wep_type {
> + GELIC_EURUS_SCAN_SEC_WEP_UNKNOWN = 0x0000,
> + GELIC_EURUS_SCAN_SEC_WEP_40 = 0x0001,
> + GELIC_EURUS_SCAN_SEC_WEP_104 = 0x0002,
> + GELIC_EURUS_SCAN_SEC_WEP_MASK = 0x0003,
> +};
> +
> +enum gelic_eurus_scan_sec_wpa_type {
> + GELIC_EURUS_SCAN_SEC_WPA_UNKNOWN = 0x0000,
> + GELIC_EURUS_SCAN_SEC_WPA_TKIP = 0x0001,
> + GELIC_EURUS_SCAN_SEC_WPA_AES = 0x0002,
> + GELIC_EURUS_SCAN_SEC_WPA_MASK = 0x0003,
> +};
> +
> +/*
> + * hw BSS information structure returned from GELIC_EURUS_CMD_GET_SCAN
> + */
> +struct gelic_eurus_scan_info {
> + /* all fields are big endian */
> + __be16 size;
> + __be16 rssi; /* percentage */
> + __be16 channel; /* channel number */
> + __be16 beacon_period; /* FIXME: in msec unit */
> + __be16 capability;
> + __be16 security;
> + u8 bssid[8]; /* last ETH_ALEN are valid. bssid[0],[1] are unused */
> + u8 essid[32]; /* IW_ESSID_MAX_SIZE */
> + u8 rate[16]; /* first MAX_RATES_LENGTH(12) are valid */
> + u8 ext_rate[16]; /* first MAX_RATES_EX_LENGTH(16) are valid */
> + __be32 reserved1;
> + __be32 reserved2;
> + __be32 reserved3;
> + __be32 reserved4;
> + u8 elements[0]; /* ie */
> +} __attribute__ ((packed));
> +
> +/* the hypervisor returns bbs up to 16 */
> +#define GELIC_EURUS_MAX_SCAN (16)
> +struct gelic_wl_scan_info {
> + struct list_head list;
> + struct gelic_eurus_scan_info *hwinfo;
> +
> + int valid; /* set 1 if this entry was in latest scanned list
> + * from Eurus */
> + unsigned int eurus_index; /* index in the Eurus list */
> + unsigned long last_scanned; /* acquired time */
> +
> + unsigned int rate_len;
> + unsigned int rate_ext_len;
> + unsigned int essid_len;
> +};
> +
> +/* for GELIC_EURUS_CMD_GET_RSSI */
> +struct gelic_eurus_rssi_info {
> + /* big endian */
> + __be16 rssi;
> +} __attribute__ ((packed));
> +
> +
> +/* for 'stat' member of gelic_wl_info */
> +enum gelic_wl_info_status_bit {
> + GELIC_WL_STAT_CONFIGURED,
> + GELIC_WL_STAT_CH_INFO, /* ch info aquired */
> + GELIC_WL_STAT_ESSID_SET, /* ESSID specified by userspace */
> + GELIC_WL_STAT_BSSID_SET, /* BSSID specified by userspace */
> + GELIC_WL_STAT_WPA_PSK_SET, /* PMK specified by userspace */
> + GELIC_WL_STAT_WPA_LEVEL_SET, /* WEP or WPA[2] selected */
> +};
> +
> +/* for 'scan_stat' member of gelic_wl_info */
> +enum gelic_wl_scan_state {
> + /* just initialized or get last scan result failed */
> + GELIC_WL_SCAN_STAT_INIT,
> + /* scan request issued, accepted or chip is scanning */
> + GELIC_WL_SCAN_STAT_SCANNING,
> + /* scan results retrieved */
> + GELIC_WL_SCAN_STAT_GOT_LIST,
> +};
> +
> +/* for 'cipher_method' */
> +enum gelic_wl_cipher_method {
> + GELIC_WL_CIPHER_NONE,
> + GELIC_WL_CIPHER_WEP,
> + GELIC_WL_CIPHER_TKIP,
> + GELIC_WL_CIPHER_AES,
> +};
> +
> +/* for 'wpa_level' */
> +enum gelic_wl_wpa_level {
> + GELIC_WL_WPA_LEVEL_NONE,
> + GELIC_WL_WPA_LEVEL_WPA,
> + GELIC_WL_WPA_LEVEL_WPA2,
> +};
> +
> +/* for 'assoc_stat' */
> +enum gelic_wl_assoc_state {
> + GELIC_WL_ASSOC_STAT_DISCONN,
> + GELIC_WL_ASSOC_STAT_ASSOCIATING,
> + GELIC_WL_ASSOC_STAT_ASSOCIATED,
> +};
> +/* part of private data alloc_etherdev() allocated */
> +#define GELIC_WEP_KEYS 4
> +struct gelic_wl_info {
> + /* bss list */
> + struct semaphore scan_lock;
> + struct list_head network_list;
> + struct list_head network_free_list;
> + struct gelic_wl_scan_info *networks;
> +
> + unsigned long scan_age; /* last scanned time */
> + enum gelic_wl_scan_state scan_stat;
> + struct completion scan_done;
> +
> + /* eurus command queue */
> + struct workqueue_struct *eurus_cmd_queue;
> + struct completion cmd_done_intr;
> +
> + /* eurus event handling */
> + struct workqueue_struct *event_queue;
> + struct delayed_work event_work;
> +
> + /* wl status bits */
> + unsigned long stat;
> + enum gelic_eurus_auth_method auth_method; /* open/shared */
> + enum gelic_wl_cipher_method group_cipher_method;
> + enum gelic_wl_cipher_method pairwise_cipher_method;
> + enum gelic_wl_wpa_level wpa_level; /* wpa/wpa2 */
> +
> + /* association handling */
> + struct semaphore assoc_stat_lock;
> + struct delayed_work assoc_work;
> + enum gelic_wl_assoc_state assoc_stat;
> + struct completion assoc_done;
> +
> + spinlock_t lock;
> + u16 ch_info; /* available channels. bit0 = ch1 */
> + /* WEP keys */
> + u8 key[GELIC_WEP_KEYS][IW_ENCODING_TOKEN_MAX];
> + unsigned long key_enabled;
> + unsigned int key_len[GELIC_WEP_KEYS];
> + unsigned int current_key;
> + /* WWPA PSK */
> + u8 psk[GELIC_WL_EURUS_PSK_MAX_LEN];
> + enum gelic_eurus_wpa_psk_type psk_type;
> + unsigned int psk_len;
> +
> + u8 essid[IW_ESSID_MAX_SIZE];
> + u8 bssid[ETH_ALEN]; /* userland requested */
> + u8 active_bssid[ETH_ALEN]; /* associated bssid */
> + unsigned int essid_len;
> +
> + /* buffer for hypervisor IO */
> + void *buf;
> +
> + struct iw_public_data wireless_data;
> + struct iw_statistics iwstat;
> +};
> +
> +#define GELIC_WL_BSS_MAX_ENT 32
> +#define GELIC_WL_ASSOC_RETRY 50
> +static inline struct gelic_port *wl_port(struct gelic_wl_info *wl)
> +{
> + return container_of((void *)wl, struct gelic_port, priv);
> +}
> +static inline struct gelic_wl_info *port_wl(struct gelic_port *port)
> +{
> + return port_priv(port);
> +}
> +
> +struct gelic_eurus_cmd {
> + struct work_struct work;
> + struct gelic_wl_info *wl;
> + unsigned int cmd; /* command code */
> + u64 tag;
> + u64 size;
> + void *buffer;
> + unsigned int buf_size;
> + struct completion done;
> + int status;
> + u64 cmd_status;
> +};
> +
> +/* private ioctls to pass PSK */
> +#define GELIC_WL_PRIV_SET_PSK (SIOCIWFIRSTPRIV + 0)
> +#define GELIC_WL_PRIV_GET_PSK (SIOCIWFIRSTPRIV + 1)
> +
> +extern int gelic_wl_driver_probe(struct gelic_card *card);
> +extern int gelic_wl_driver_remove(struct gelic_card *card);
> +extern void gelic_wl_interrupt(struct net_device *netdev, u64 status);
> +#endif /* _GELIC_WIRELESS_H */
>
^ permalink raw reply
* What was the reason for 2.6.22 SMP kernels to change how sendmsg is called?
From: Kevin Wilson @ 2007-12-13 20:54 UTC (permalink / raw)
To: netdev
In SMP kernels 2.6.21 and prior you could use a SOCK's sendmsg() call via the PROTO structure directly. e.g., sock->sk_prot->sendmsg().
Now in 2.6.22 and later kernels you must use the higher level SOCKET to make a call to PROTO_OPS then to sendmsg(). e.g., socket->ops->sendmsg().
Would someone please clue me in as to what source changes caused previously working driver code to go belly up? (ref original post below) I tried finding it in git but I don't think this was intentional but rather a side effect of some other change made between .21 & .22.
The 2nd method fixes the kernel oops I reported. Thanks to all those that assisted me (0) with my first post to this list (see below) ... uh, oh yeah, did I mention that number would tally up to ZERO people. ;-}
Thanks,
Kevin
***********************************************************************
-----Original Message-----
From: netdev-owner@vger.kernel.org
[mailto:netdev-owner@vger.kernel.org]On Behalf Of Kevin Wilson
Sent: Tuesday, December 11, 2007 16:51
To: netdev@vger.kernel.org
Subject: sk_prot->sendmsg(...) giving me a kernel oops on kernels past
2.6.20
I've searched everywhere (including this list) for a report but couldn't
find anything that can tell me what implementation step(s) I am missing.
Essentially all the kernels past 2.6.20 gives me a kernel oops on the
sendmsg code.
I know the sk_buff changes worked things over quite a bit but I have (or
thought I did) those changes accounted for in our driver. Are there any
specific implementation changes we need to add that we didn't need prior
before we now use sendmsg()? Below is the general gist of the code I am
executing, any pointers to what I am missing would be very welcome.
Thanks a bunch.
Kevin
***************************************
done in userland and passed via ioctl:
***************************************
sk = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
tcp_params.boxid = boxIndex;
tcp_params.sk = sk;
tcp_params.rbuf = malloc(4096);
tcp_params.rbuf_size = 4096;
tcp_params.wbuf = malloc(1460);
tcp_params.wbuf_size = 1460;
rc = ioctl(fd, SI_SET_TCP, &tcp_params);
***************************************
done in kernel space:
***************************************
struct tcp_sock *tp;
struct sock *mysock;
tp = sockfd_lookup(tcp_params.sk, &i);
mysock = tp->sk;
mysock->sk_prot->sendmsg(...); <-- OOPS HERE
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RFC] mac80211: clean up frame receive handling
From: John W. Linville @ 2007-12-13 20:49 UTC (permalink / raw)
To: Johannes Berg
Cc: linux-wireless, netdev, Michael Wu, Tomas Winkler, Jouni Malinen
In-Reply-To: <1197483844.6558.158.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
On Wed, Dec 12, 2007 at 07:24:04PM +0100, Johannes Berg wrote:
> @@ -1014,6 +992,24 @@ ieee80211_drop_unencrypted(struct ieee80
> return 0;
> }
>
> +static bool ieee80211_frame_allowed(struct ieee80211_txrx_data *rx)
> +{
> + static const u8 pae_group_addr[ETH_ALEN]
> + = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
> + struct ethhdr *ehdr = (struct ethhdr *)rx->skb->data;
> +
> + if (rx->skb->protocol == htons(ETH_P_PAE) &&
> + (compare_ether_addr(ehdr->h_dest, pae_group_addr) == 0 ||
> + compare_ether_addr(ehdr->h_dest, rx->dev->dev_addr) == 0))
> + return true;
Should you reverse these two compare_ether_addr calls?
rx->dev->dev_addr seems more likely for any given packet. It probably
makes little difference but it seems like checking for that first
would still be better.
John
--
John W. Linville
linville-2XuSBdqkA4R54TAoqtyWWQ@public.gmane.org
^ permalink raw reply
* Re: [RFC] net: napi fix
From: Stephen Hemminger @ 2007-12-13 20:41 UTC (permalink / raw)
To: David Miller
Cc: jarkao2, auke-jan.h.kok, gallatin, joonwpark81, netdev,
linux-kernel, jgarzik, jesse.brandeburg
In-Reply-To: <20071213.123727.224454625.davem@davemloft.net>
David Miller wrote:
> From: Jarek Poplawski <jarkao2@gmail.com>
> Date: Thu, 13 Dec 2007 21:16:12 +0100
>
>
>> I see in a nearby thread you would prefer to save some work to drivers
>> (like this netif_running() check), but I think this all is at the cost
>> of flexibility, and there will probably appear new problems, when a
>> driver simply can't wait till the next poll (which btw. looks strange
>> with all these hotplugging, usb and powersaving).
>>
>
> As someone who has actually had to edit the NAPI support of _EVERY_
> single driver in the tree I can tell you that code duplication and
> subtle semantic differences are a huge issue.
>
> And when you talk about driver flexibility, it's wise to mention that
> this comes at the expense of flexibility in the core implmentation.
> For example, if we export the list handling widget into the ->poll()
> routines, god help the person who wants to change how the poll list is
> managed in net_rx_action() :-/
>
> So we don't want to export datastructure details like that to the
> driver.
>
Also, most of the drivers should/could be doing the same thing. It is
seems that
driver writers just want to get "creative" and do things differently.
The code is
cleaner, safer, and less buggy if every device uses the interface in the
same way.
When I did the initial pass on this, I didn't see a single variation on
NAPI usage
that was better than the simple "get N packets and return" variation.
But Dave
did way more detailed grunt work on this.
^ permalink raw reply
* Re: [3/4] DST: Network state machine.
From: Dmitry Monakhov @ 2007-12-13 20:43 UTC (permalink / raw)
To: Evgeniy Polyakov; +Cc: lkml, netdev, linux-fsdevel
In-Reply-To: <11972872511269@2ka.mipt.ru>
[-- Attachment #1: Type: text/plain, Size: 42481 bytes --]
On 14:47 Mon 10 Dec , Evgeniy Polyakov wrote:
>
> Network state machine.
>
> Includes network async processing state machine and related tasks.
Hi, I've tried to play a little bit with DST and discover huge memory
leak. Every read request from remote node result in bio + bio's pages leak.
Data flow:
->kst_export_ready ## prepare and submit bio
->generic_make_request(bio) ## submit it
->kst_export_read_end_io ## block layer call bio_end_io callback
->kst_thread_process_state ## process ready requests
->kst_data_callback
->kst_data_process_bio ## submit pages to network layer
->kst_complete_req
->kst_bio_endio
->kst_export_read_end_io ## WoW we calling the same bio_end_io
## callback twice
->dst_free_request(req); ## request will be destroyed but it's bio
## and all bio's pages wasn't released.
We may release bio's pages after it was sent to network, it is safe because
sendpage() already called get_page(). I've attached simple patch which
this this.
>
> Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
>
>
> diff --git a/drivers/block/dst/kst.c b/drivers/block/dst/kst.c
> new file mode 100644
> index 0000000..8fa3387
> --- /dev/null
> +++ b/drivers/block/dst/kst.c
> @@ -0,0 +1,1513 @@
> +/*
> + * 2007+ Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>> + * 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
> + * (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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +#include <linux/socket.h>
> +#include <linux/kthread.h>
> +#include <linux/net.h>
> +#include <linux/in.h>
> +#include <linux/poll.h>
> +#include <linux/bio.h>
> +#include <linux/dst.h>
> +
> +#include <net/sock.h>
> +
> +struct kst_poll_helper
> +{
> + poll_table pt;
> + struct kst_state *st;
> +};
> +
> +static LIST_HEAD(kst_worker_list);
> +static DEFINE_MUTEX(kst_worker_mutex);
> +
> +/*
> + * This function creates bound socket for local export node.
> + */
> +static int kst_sock_create(struct kst_state *st, struct saddr *addr,
> + int type, int proto, int backlog)
> +{
> + int err;
> +
> + err = sock_create(addr->sa_family, type, proto, &st->socket);
> + if (err)
> + goto err_out_exit;
> +
> + err = st->socket->ops->bind(st->socket, (struct sockaddr *)addr,
> + addr->sa_data_len);
> +
> + err = st->socket->ops->listen(st->socket, backlog);
> + if (err)
> + goto err_out_release;
> +
> + st->socket->sk->sk_allocation = GFP_NOIO;
> +
> + return 0;
> +
> +err_out_release:
> + sock_release(st->socket);
> +err_out_exit:
> + return err;
> +}
> +
> +static void kst_sock_release(struct kst_state *st)
> +{
> + if (st->socket) {
> + sock_release(st->socket);
> + st->socket = NULL;
> + }
> +}
> +
> +void kst_wake(struct kst_state *st)
> +{
> + if (st) {
> + struct kst_worker *w = st->node->w;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&w->ready_lock, flags);
> + if (list_empty(&st->ready_entry))
> + list_add_tail(&st->ready_entry, &w->ready_list);
> + spin_unlock_irqrestore(&w->ready_lock, flags);
> +
> + wake_up(&w->wait);
> + }
> +}
> +EXPORT_SYMBOL_GPL(kst_wake);
> +
> +/*
> + * Polling machinery.
> + */
> +static int kst_state_wake_callback(wait_queue_t *wait, unsigned mode,
> + int sync, void *key)
> +{
> + struct kst_state *st = container_of(wait, struct kst_state, wait);
> + kst_wake(st);
> + return 1;
> +}
> +
> +static void kst_queue_func(struct file *file, wait_queue_head_t *whead,
> + poll_table *pt)
> +{
> + struct kst_state *st = container_of(pt, struct kst_poll_helper, pt)->st;
> +
> + st->whead = whead;
> + init_waitqueue_func_entry(&st->wait, kst_state_wake_callback);
> + add_wait_queue(whead, &st->wait);
> +}
> +
> +static void kst_poll_exit(struct kst_state *st)
> +{
> + if (st->whead) {
> + remove_wait_queue(st->whead, &st->wait);
> + st->whead = NULL;
> + }
> +}
> +
> +/*
> + * This function removes request from state tree and ordering list.
> + */
> +void kst_del_req(struct dst_request *req)
> +{
> + list_del_init(&req->request_list_entry);
> +}
> +EXPORT_SYMBOL_GPL(kst_del_req);
> +
> +static struct dst_request *kst_req_first(struct kst_state *st)
> +{
> + struct dst_request *req = NULL;
> +
> + if (!list_empty(&st->request_list))
> + req = list_entry(st->request_list.next, struct dst_request,
> + request_list_entry);
> + return req;
> +}
> +
> +/*
> + * This function dequeues first request from the queue and tree.
> + */
> +static struct dst_request *kst_dequeue_req(struct kst_state *st)
> +{
> + struct dst_request *req;
> +
> + mutex_lock(&st->request_lock);
> + req = kst_req_first(st);
> + if (req)
> + kst_del_req(req);
> + mutex_unlock(&st->request_lock);
> + return req;
> +}
> +
> +/*
> + * This function enqueues request into tree, indexed by start of the request,
> + * and also puts request into ordered queue.
> + */
> +int kst_enqueue_req(struct kst_state *st, struct dst_request *req)
> +{
> + if (unlikely(req->flags & DST_REQ_CHECK_QUEUE)) {
> + struct dst_request *r;
> +
> + list_for_each_entry(r, &st->request_list, request_list_entry) {
> + if (bio_rw(r->bio) != bio_rw(req->bio))
> + continue;
> +
> + if (r->start >= req->start + req->size)
> + continue;
> +
> + if (r->start + r->size <= req->start)
> + continue;
> +
> + return -EEXIST;
> + }
> + }
> +
> + list_add_tail(&req->request_list_entry, &st->request_list);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(kst_enqueue_req);
> +
> +/*
> + * BIOs for local exporting node are freed via this function.
> + */
> +static void kst_export_put_bio(struct bio *bio)
> +{
> + int i;
> + struct bio_vec *bv;
> +
> + dprintk("%s: bio: %p, size: %u, idx: %d, num: %d, req: %p.\n",
> + __func__, bio, bio->bi_size, bio->bi_idx,
> + bio->bi_vcnt, bio->bi_private);
> +
> + bio_for_each_segment(bv, bio, i)
> + __free_page(bv->bv_page);
> + bio_put(bio);
> +}
> +
> +/*
> + * This is a generic request completion function for requests,
> + * queued for async processing.
> + * If it is local export node, state machine is different,
> + * see details below.
> + */
> +void kst_complete_req(struct dst_request *req, int err)
> +{
> + dprintk("%s: bio: %p, req: %p, size: %llu, orig_size: %llu, "
> + "bi_size: %u, err: %d, flags: %u.\n",
> + __func__, req->bio, req, req->size, req->orig_size,
> + req->bio->bi_size, err, req->flags);
> +
> + if (req->flags & DST_REQ_EXPORT) {
> + if (err || !(req->flags & DST_REQ_EXPORT_WRITE)) {
> + req->bio_endio(req, err);
> + goto out;
> + }
> +
> + req->bio->bi_rw = WRITE;
> + generic_make_request(req->bio);
> + } else {
> + req->bio_endio(req, err);
> + }
> +out:
> + dst_free_request(req);
> +}
> +EXPORT_SYMBOL_GPL(kst_complete_req);
> +
> +static void kst_flush_requests(struct kst_state *st)
> +{
> + struct dst_request *req;
> +
> + while ((req = kst_dequeue_req(st)) != NULL)
> + kst_complete_req(req, -EIO);
> +}
> +
> +static int kst_poll_init(struct kst_state *st)
> +{
> + struct kst_poll_helper ph;
> +
> + ph.st = st;
> + init_poll_funcptr(&ph.pt, &kst_queue_func);
> +
> + st->socket->ops->poll(NULL, st->socket, &ph.pt);
> + return 0;
> +}
> +
> +/*
> + * Main state creation function.
> + * It creates new state according to given operations
> + * and links it into worker structure and node.
> + */
> +static struct kst_state *kst_state_init(struct dst_node *node,
> + unsigned int permissions,
> + struct kst_state_ops *ops, void *data)
> +{
> + struct kst_state *st;
> + int err;
> +
> + st = kzalloc(sizeof(struct kst_state), GFP_KERNEL);
> + if (!st)
> + return ERR_PTR(-ENOMEM);
> +
> + st->permissions = permissions;
> + st->node = node;
> + st->ops = ops;
> + INIT_LIST_HEAD(&st->ready_entry);
> + INIT_LIST_HEAD(&st->entry);
> + INIT_LIST_HEAD(&st->request_list);
> + mutex_init(&st->request_lock);
> +
> + err = st->ops->init(st, data);
> + if (err)
> + goto err_out_free;
> + mutex_lock(&node->w->state_mutex);
> + list_add_tail(&st->entry, &node->w->state_list);
> + mutex_unlock(&node->w->state_mutex);
> +
> + kst_wake(st);
> +
> + return st;
> +
> +err_out_free:
> + kfree(st);
> + return ERR_PTR(err);
> +}
> +
> +/*
> + * This function is called when node is removed,
> + * or when state is destroyed for connected to local exporting
> + * node client.
> + */
> +void kst_state_exit(struct kst_state *st)
> +{
> + struct kst_worker *w = st->node->w;
> +
> + mutex_lock(&w->state_mutex);
> + list_del_init(&st->entry);
> + mutex_unlock(&w->state_mutex);
> +
> + st->ops->exit(st);
> +
> + if (st == st->node->state)
> + st->node->state = NULL;
> +
> + kfree(st);
> +}
> +
> +static int kst_error(struct kst_state *st, int err)
> +{
> + if ((err == -ECONNRESET || err == -EPIPE) && st->ops->recovery)
> + err = st->ops->recovery(st, err);
> +
> + return st->node->st->alg->ops->error(st, err);
> +}
> +
> +/*
> + * This is main state processing function.
> + * It tries to complete request and invoke appropriate
> + * callbacks in case of errors or successfull operation finish.
> + */
> +static int kst_thread_process_state(struct kst_state *st)
> +{
> + int err, empty;
> + unsigned int revents;
> + struct dst_request *req, *tmp;
> +
> + mutex_lock(&st->request_lock);
> + if (st->ops->ready) {
> + err = st->ops->ready(st);
> + if (err) {
> + mutex_unlock(&st->request_lock);
> + if (err < 0)
> + kst_state_exit(st);
> + return err;
> + }
> + }
> +
> + err = 0;
> + empty = 1;
> + req = NULL;
> + list_for_each_entry_safe(req, tmp, &st->request_list, request_list_entry) {
> + empty = 0;
> + revents = st->socket->ops->poll(st->socket->file,
> + st->socket, NULL);
> + if (!revents)
> + break;
> + err = req->callback(req, revents);
> + if (req->size && !err)
> + err = 1;
> +
> + if (err < 0 || !req->size) {
> + if (!req->size)
> + err = 0;
> + kst_del_req(req);
> + kst_complete_req(req, err);
> + }
> +
> + if (err)
> + break;
> + }
> +
> + dprintk("%s: broke the loop: err: %d, list_empty: %d.\n",
> + __func__, err, list_empty(&st->request_list));
> + mutex_unlock(&st->request_lock);
> +
> + if (err < 0) {
> + dprintk("%s: req: %p, err: %d, st: %p, node->state: %p.\n",
> + __func__, req, err, st, st->node->state);
> +
> + if (st != st->node->state) {
> + /*
> + * Accepted client has state not related to storage
> + * node, so it must be freed explicitely.
> + * We do not try to fix clients connections to local
> + * export nodes, just drop the client.
> + */
> +
> + kst_state_exit(st);
> + return err;
> + }
> +
> + err = kst_error(st, err);
> + if (err)
> + return err;
> +
> + kst_wake(st);
> + }
> +
> + if (list_empty(&st->request_list) && !empty)
> + kst_wake(st);
> +
> + return err;
> +}
> +
> +/*
> + * Main worker thread - one per storage.
> + */
> +static int kst_thread_func(void *data)
> +{
> + struct kst_worker *w = data;
> + struct kst_state *st;
> + unsigned long flags;
> + int err = 0;
> +
> + while (!kthread_should_stop()) {
> + wait_event_interruptible_timeout(w->wait,
> + !list_empty(&w->ready_list) ||
> + kthread_should_stop(),
> + HZ);
> +
> + st = NULL;
> + spin_lock_irqsave(&w->ready_lock, flags);
> + if (!list_empty(&w->ready_list)) {
> + st = list_entry(w->ready_list.next, struct kst_state,
> + ready_entry);
> + list_del_init(&st->ready_entry);
> + }
> + spin_unlock_irqrestore(&w->ready_lock, flags);
> +
> + if (!st)
> + continue;
> +
> + err = kst_thread_process_state(st);
> + }
> +
> + return err;
> +}
> +
> +/*
> + * Worker initialization - this object will host andprocess all states,
> + * which in turn host requests for remote targets.
> + */
> +struct kst_worker *kst_worker_init(int id)
> +{
> + struct kst_worker *w;
> + int err;
> +
> + w = kzalloc(sizeof(struct kst_worker), GFP_KERNEL);
> + if (!w)
> + return ERR_PTR(-ENOMEM);
> +
> + w->id = id;
> + init_waitqueue_head(&w->wait);
> + spin_lock_init(&w->ready_lock);
> + mutex_init(&w->state_mutex);
> +
> + INIT_LIST_HEAD(&w->ready_list);
> + INIT_LIST_HEAD(&w->state_list);
> +
> + w->req_pool = mempool_create_slab_pool(256, dst_request_cache);
> + if (!w->req_pool) {
> + err = -ENOMEM;
> + goto err_out_free;
> + }
> +
> + w->thread = kthread_run(&kst_thread_func, w, "kst%d", w->id);
> + if (IS_ERR(w->thread)) {
> + err = PTR_ERR(w->thread);
> + goto err_out_destroy;
> + }
> +
> + mutex_lock(&kst_worker_mutex);
> + list_add_tail(&w->entry, &kst_worker_list);
> + mutex_unlock(&kst_worker_mutex);
> +
> + return w;
> +
> +err_out_destroy:
> + mempool_destroy(w->req_pool);
> +err_out_free:
> + kfree(w);
> + return ERR_PTR(err);
> +}
> +
> +void kst_worker_exit(struct kst_worker *w)
> +{
> + struct kst_state *st, *n;
> +
> + mutex_lock(&kst_worker_mutex);
> + list_del(&w->entry);
> + mutex_unlock(&kst_worker_mutex);
> +
> + kthread_stop(w->thread);
> +
> + list_for_each_entry_safe(st, n, &w->state_list, entry) {
> + kst_state_exit(st);
> + }
> +
> + mempool_destroy(w->req_pool);
> + kfree(w);
> +}
> +
> +/*
> + * Common state exit callback.
> + * Removes itself from worker's list of states,
> + * releases socket and flushes all requests.
> + */
> +static void kst_common_exit(struct kst_state *st)
> +{
> + unsigned long flags;
> +
> + kst_poll_exit(st);
> +
> + spin_lock_irqsave(&st->node->w->ready_lock, flags);
> + list_del_init(&st->ready_entry);
> + spin_unlock_irqrestore(&st->node->w->ready_lock, flags);
> +
> + kst_flush_requests(st);
> + kst_sock_release(st);
> +}
> +
> +/*
> + * Listen socket contains security attributes in request_list,
> + * so it can not be flushed via usual way.
> + */
> +static void kst_listen_flush(struct kst_state *st)
> +{
> + struct dst_secure *s, *tmp;
> +
> + list_for_each_entry_safe(s, tmp, &st->request_list, sec_entry) {
> + list_del(&s->sec_entry);
> + kfree(s);
> + }
> +}
> +
> +static void kst_listen_exit(struct kst_state *st)
> +{
> + kst_listen_flush(st);
> + kst_common_exit(st);
> +}
> +
> +/*
> + * BIO vector receiving function - does not block, but may sleep because
> + * of scheduling policy.
> + */
> +static int kst_data_recv_bio_vec(struct kst_state *st, struct bio_vec *bv,
> + unsigned int offset, unsigned int size)
> +{
> + struct msghdr msg;
> + struct kvec iov;
> + void *kaddr;
> + int err;
> +
> + kaddr = kmap(bv->bv_page);
> +
> + iov.iov_base = kaddr + bv->bv_offset + offset;
> + iov.iov_len = size;
> +
> + msg.msg_iov = (struct iovec *)&iov;
> + msg.msg_iovlen = 1;
> + msg.msg_name = NULL;
> + msg.msg_namelen = 0;
> + msg.msg_control = NULL;
> + msg.msg_controllen = 0;
> + msg.msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL;
> +
> + err = kernel_recvmsg(st->socket, &msg, &iov, 1, iov.iov_len,
> + msg.msg_flags);
> + kunmap(bv->bv_page);
> +
> + return err;
> +}
> +
> +/*
> + * BIO vector sending function - does not block, but may sleep because
> + * of scheduling policy.
> + */
> +static int kst_data_send_bio_vec(struct kst_state *st, struct bio_vec *bv,
> + unsigned int offset, unsigned int size)
> +{
> + return kernel_sendpage(st->socket, bv->bv_page,
> + bv->bv_offset + offset, size,
> + MSG_DONTWAIT | MSG_NOSIGNAL);
> +}
> +
> +static int kst_data_send_bio_vec_slow(struct kst_state *st, struct bio_vec *bv,
> + unsigned int offset, unsigned int size)
> +{
> + struct msghdr msg;
> + struct kvec iov;
> + void *addr;
> + int err;
> +
> + addr = kmap(bv->bv_page);
> + iov.iov_base = addr + bv->bv_offset + offset;
> + iov.iov_len = size;
> +
> + msg.msg_iov = (struct iovec *)&iov;
> + msg.msg_iovlen = 1;
> + msg.msg_name = NULL;
> + msg.msg_namelen = 0;
> + msg.msg_control = NULL;
> + msg.msg_controllen = 0;
> + msg.msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL;
> +
> + err = kernel_sendmsg(st->socket, &msg, &iov, 1, iov.iov_len);
> + kunmap(bv->bv_page);
> +
> + return err;
> +}
> +
> +static u32 dst_csum_bvec(struct bio_vec *bv, unsigned int offset, unsigned int size)
> +{
> + void *addr;
> + u32 csum;
> +
> + addr = kmap_atomic(bv->bv_page, KM_USER0);
> + csum = dst_csum_data(addr + bv->bv_offset + offset, size);
> + kunmap_atomic(addr, KM_USER0);
> +
> + return csum;
> +}
> +
> +typedef int (*kst_data_process_bio_vec_t)(struct kst_state *st,
> + struct bio_vec *bv, unsigned int offset, unsigned int size);
> +
> +/*
> + * @req: processing request.
> + * Contains BIO and all related to its processing info.
> + *
> + * This function sends or receives requested number of pages from given BIO.
> + *
> + * In case of errors negative value is returned and @size,
> + * @index and @off are set to the:
> + * - number of bytes not yet processed (i.e. the rest of the bytes to be
> + * processed).
> + * - index of the last bio_vec started to be processed (header sent).
> + * - offset of the first byte to be processed in the bio_vec.
> + *
> + * If there are no errors, zero is returned.
> + * -EAGAIN is not an error and is transformed into zero return value,
> + * called must check if @size is zero, in that case whole BIO is processed
> + * and thus req->bio_endio() can be called, othervise new request must be allocated
> + * to be processed later.
> + */
> +static int kst_data_process_bio(struct dst_request *req)
> +{
> + int err = -ENOSPC;
> + struct dst_remote_request r;
> + kst_data_process_bio_vec_t func;
> + unsigned int cur_size;
> + int use_csum = test_bit(DST_NODE_USE_CSUM, &req->node->flags);
> +
> + if (bio_rw(req->bio) == WRITE) {
> + int i;
> +
> + func = kst_data_send_bio_vec;
> + for (i=req->idx; i<req->num; ++i) {
> + struct bio_vec *bv = bio_iovec_idx(req->bio, i);
> +
> + if (PageSlab(bv->bv_page)) {
> + func = kst_data_send_bio_vec_slow;
> + break;
> + }
> + }
> + r.cmd = cpu_to_be32(DST_WRITE);
> + } else {
> + r.cmd = cpu_to_be32(DST_READ);
> + func = kst_data_recv_bio_vec;
> + }
> +
> + dprintk("%s: start: [%c], start: %llu, idx: %d, num: %d, "
> + "size: %llu, offset: %u, flags: %x, use_csum: %d.\n",
> + __func__, (bio_rw(req->bio) == WRITE)?'W':'R',
> + req->start, req->idx, req->num, req->size, req->offset,
> + req->flags, use_csum);
> +
> + while (req->idx < req->num) {
> + struct bio_vec *bv = bio_iovec_idx(req->bio, req->idx);
> +
> + cur_size = min_t(u64, bv->bv_len - req->offset, req->size);
> +
> + dprintk("%s: page: %p, slab: %d, count: %d, max: %d, off: %u, len: %u, req->offset: %u, "
> + "req->size: %llu, cur_size: %u, flags: %x, "
> + "use_csum: %d, req->csum: %x.\n",
> + __func__, bv->bv_page, PageSlab(bv->bv_page),
> + atomic_read(&bv->bv_page->_count), req->bio->bi_vcnt,
> + bv->bv_offset, bv->bv_len,
> + req->offset, req->size, cur_size,
> + req->flags, use_csum, req->tmp_csum);
> +
> + if (cur_size == 0) {
> + printk(KERN_ERR "%s: %d/%d: start: %llu, "
> + "bv_offset: %u, bv_len: %u, "
> + "req_offset: %u, req_size: %llu, "
> + "req: %p, bio: %p, err: %d.\n",
> + __func__, req->idx, req->num, req->start,
> + bv->bv_offset, bv->bv_len,
> + req->offset, req->size,
> + req, req->bio, err);
> + BUG();
> + }
> +
> + if (!(req->flags & DST_REQ_HEADER_SENT)) {
> + r.sector = cpu_to_be64(req->start);
> + r.offset = cpu_to_be32(bv->bv_offset + req->offset);
> + r.size = cpu_to_be32(cur_size);
> + r.csum = 0;
> +
> + if (use_csum && bio_rw(req->bio) == WRITE &&
> + !req->tmp_offset) {
> + req->tmp_offset = req->offset;
> + r.csum = cpu_to_be32(dst_csum_bvec(bv,
> + req->offset, cur_size));
> + }
> +
> + err = dst_data_send_header(req->state->socket, &r);
> + dprintk("%s: %d/%d: sending header: cmd: %u, start: %llu, "
> + "bv_offset: %u, bv_len: %u, "
> + "a offset: %u, offset: %u, "
> + "cur_size: %u, err: %d.\n",
> + __func__, req->idx, req->num, be32_to_cpu(r.cmd),
> + req->start, bv->bv_offset, bv->bv_len,
> + bv->bv_offset + req->offset,
> + req->offset, cur_size, err);
> +
> + if (err != sizeof(struct dst_remote_request)) {
> + if (err >= 0)
> + err = -EINVAL;
> + break;
> + }
> +
> + req->flags |= DST_REQ_HEADER_SENT;
> + }
> +
> + if (use_csum && (bio_rw(req->bio) != WRITE) &&
> + !(req->flags & DST_REQ_CHEKSUM_RECV)) {
> + struct dst_remote_request tmp_req;
> +
> + err = dst_data_recv_header(req->state->socket, &tmp_req, 0);
> + dprintk("%s: %d/%d: receiving header: start: %llu, "
> + "bv_offset: %u, bv_len: %u, "
> + "a offset: %u, offset: %u, "
> + "cur_size: %u, err: %d.\n",
> + __func__, req->idx, req->num,
> + req->start, bv->bv_offset, bv->bv_len,
> + bv->bv_offset + req->offset,
> + req->offset, cur_size, err);
> +
> + if (err != sizeof(struct dst_remote_request)) {
> + if (err >= 0)
> + err = -EINVAL;
> + break;
> + }
> +
> + if (req->tmp_csum) {
> + printk("%s: req: %p, old csum: %x, new: %x.\n",
> + __func__, req, req->tmp_csum,
> + be32_to_cpu(tmp_req.csum));
> + BUG_ON(1);
> + }
> +
> + dprintk("%s: req: %p, old csum: %x, new: %x.\n",
> + __func__, req, req->tmp_csum,
> + be32_to_cpu(tmp_req.csum));
> + req->tmp_csum = be32_to_cpu(tmp_req.csum);
> +
> + req->flags |= DST_REQ_CHEKSUM_RECV;
> + }
> +
> + err = func(req->state, bv, req->offset, cur_size);
> + if (err <= 0)
> + break;
> +
> + req->offset += err;
> + req->size -= err;
> +
> + if (req->offset != bv->bv_len) {
> + dprintk("%s: %d/%d: this: start: %llu, bv_offset: %u, "
> + "bv_len: %u, offset: %u, "
> + "cur_size: %u, err: %d.\n",
> + __func__, req->idx, req->num, req->start,
> + bv->bv_offset, bv->bv_len,
> + req->offset, cur_size, err);
> + err = -EAGAIN;
> + break;
> + }
> +
> + if (use_csum && bio_rw(req->bio) != WRITE) {
> + u32 csum = dst_csum_bvec(bv, req->tmp_offset,
> + bv->bv_len - req->tmp_offset);
> +
> + dprintk("%s: req: %p, csum: %x, received csum: %x.\n",
> + __func__, req, csum, req->tmp_csum);
> +
> + if (csum != req->tmp_csum) {
> + printk("%s: %d/%d: broken checksum: start: %llu, "
> + "bv_offset: %u, bv_len: %u, "
> + "a offset: %u, offset: %u, "
> + "cur_size: %u, orig_size: %llu.\n",
> + __func__, req->idx, req->num,
> + req->start, bv->bv_offset, bv->bv_len,
> + bv->bv_offset + req->offset,
> + req->offset, cur_size, req->orig_size);
> + printk("%s: broken checksum: req: %p, csum: %x, "
> + "should be: %x, flags: %x, "
> + "req->tmp_offset: %u, rw: %lu.\n",
> + __func__, req, csum, req->tmp_csum,
> + req->flags, req->tmp_offset, bio_rw(req->bio));
> +
> + req->offset -= err;
> + req->size += err;
> +
> + err = -EREMOTEIO;
> + break;
> + }
> + }
> +
> + req->offset = 0;
> + req->idx++;
> + req->flags &= ~(DST_REQ_HEADER_SENT | DST_REQ_CHEKSUM_RECV);
> + req->tmp_csum = 0;
> + req->start += to_sector(bv->bv_len);
> + }
> +
> + if (err <= 0 && err != -EAGAIN) {
> + if (err == 0)
> + err = -ECONNRESET;
> + } else
> + err = 0;
> +
> + if (err < 0 || (req->idx == req->num && req->size)) {
> + dprintk("%s: return: idx: %d, num: %d, offset: %u, "
> + "size: %llu, err: %d.\n",
> + __func__, req->idx, req->num, req->offset,
> + req->size, err);
> + }
> + dprintk("%s: end: start: %llu, idx: %d, num: %d, "
> + "size: %llu, offset: %u.\n",
> + __func__, req->start, req->idx, req->num,
> + req->size, req->offset);
> +
> + return err;
> +}
> +
> +void kst_bio_endio(struct dst_request *req, int err)
> +{
> + if (err && printk_ratelimit())
> + printk("%s: freeing bio: %p, bi_size: %u, "
> + "orig_size: %llu, req: %p, err: %d.\n",
> + __func__, req->bio, req->bio->bi_size, req->orig_size,
> + req, err);
> + bio_endio(req->bio, req->orig_size, err);
> +}
> +EXPORT_SYMBOL_GPL(kst_bio_endio);
> +
> +/*
> + * This callback is invoked by worker thread to process given request.
> + */
> +int kst_data_callback(struct dst_request *req, unsigned int revents)
> +{
> + int err;
> +
> + dprintk("%s: req: %p, num: %d, idx: %d, bio: %p, "
> + "revents: %x, flags: %x.\n",
> + __func__, req, req->num, req->idx, req->bio,
> + revents, req->flags);
> +
> + if (req->flags & DST_REQ_EXPORT_READ)
> + return 1;
> +
> + err = kst_data_process_bio(req);
> +
> + if (revents & (POLLERR | POLLHUP | POLLRDHUP))
> + err = -EPIPE;
> +
> + return err;
> +}
> +EXPORT_SYMBOL_GPL(kst_data_callback);
> +
> +struct dst_request *dst_clone_request(struct dst_request *req, mempool_t *pool)
> +{
> + struct dst_request *new_req;
> +
> + new_req = mempool_alloc(pool, GFP_NOIO);
> + if (!new_req)
> + return NULL;
> +
> + memset(new_req, 0, sizeof(struct dst_request));
> +
> + dprintk("%s: req: %p, new_req: %p.\n", __func__, req, new_req);
> +
> + if (req) {
> + new_req->bio = req->bio;
> + new_req->state = req->state;
> + new_req->node = req->node;
> + new_req->idx = req->idx;
> + new_req->num = req->num;
> + new_req->size = req->size;
> + new_req->orig_size = req->orig_size;
> + new_req->offset = req->offset;
> + new_req->tmp_offset = req->tmp_offset;
> + new_req->tmp_csum = req->tmp_csum;
> + new_req->start = req->start;
> + new_req->flags = req->flags;
> + new_req->bio_endio = req->bio_endio;
> + new_req->priv = req->priv;
> + }
> +
> + return new_req;
> +}
> +EXPORT_SYMBOL_GPL(dst_clone_request);
> +
> +void dst_free_request(struct dst_request *req)
> +{
> + dprintk("%s: free req: %p, pool: %p, bio: %p, state: %p, node: %p.\n",
> + __func__, req, req->node->w->req_pool,
> + req->bio, req->state, req->node);
> + mempool_free(req, req->node->w->req_pool);
> +}
> +EXPORT_SYMBOL_GPL(dst_free_request);
> +
> +/*
> + * This is main data processing function, eventually invoked from block layer.
> + * It tries to complte request, but if it is about to block, it allocates
> + * new request and queues it to main worker to be processed when events allow.
> + */
> +static int kst_data_push(struct dst_request *req)
> +{
> + struct kst_state *st = req->state;
> + struct dst_request *new_req;
> + unsigned int revents;
> + int err, locked = 0;
> +
> + dprintk("%s: start: %llu, size: %llu, bio: %p.\n",
> + __func__, req->start, req->size, req->bio);
> +
> + if (!list_empty(&st->request_list) || (req->flags & DST_REQ_ALWAYS_QUEUE))
> + goto alloc_new_req;
> +
> + if (mutex_trylock(&st->request_lock)) {
> + locked = 1;
> +
> + if (!list_empty(&st->request_list))
> + goto alloc_new_req;
> +
> + revents = st->socket->ops->poll(NULL, st->socket, NULL);
> + if (revents & POLLOUT) {
> + err = kst_data_process_bio(req);
> + if (err < 0)
> + goto out_unlock;
> +
> + if (!req->size)
> + goto out_bio_endio;
> + }
> + }
> +
> +alloc_new_req:
> + err = -ENOMEM;
> + new_req = dst_clone_request(req, req->node->w->req_pool);
> + if (!new_req)
> + goto out_unlock;
> +
> + new_req->callback = &kst_data_callback;
> +
> + if (!locked)
> + mutex_lock(&st->request_lock);
> +
> + locked = 1;
> +
> + err = kst_enqueue_req(st, new_req);
> + if (err)
> + goto out_unlock;
> + mutex_unlock(&st->request_lock);
> +
> + err = 0;
> + goto out;
> +
> +out_bio_endio:
> + req->bio_endio(req, err);
> +out_unlock:
> + if (locked)
> + mutex_unlock(&st->request_lock);
> + locked = 0;
> +
> + if (err) {
> + err = kst_error(st, err);
> + if (!err)
> + goto alloc_new_req;
> + }
> +
> + if (err && printk_ratelimit()) {
> + printk("%s: error [%c], start: %llu, idx: %d, num: %d, "
> + "size: %llu, offset: %u, err: %d.\n",
> + __func__, (bio_rw(req->bio) == WRITE)?'W':'R',
> + req->start, req->idx, req->num, req->size,
> + req->offset, err);
> + }
> +
> +out:
> +
> + kst_wake(st);
> + return err;
> +}
> +
> +/*
> + * Remote node initialization callback.
> + */
> +static int kst_data_init(struct kst_state *st, void *data)
> +{
> + int err;
> +
> + st->socket = data;
> + st->socket->sk->sk_allocation = GFP_NOIO;
> + /*
> + * Why not?
> + */
> + st->socket->sk->sk_sndbuf = st->socket->sk->sk_sndbuf = 1024*1024*10;
> +
> + err = kst_poll_init(st);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +/*
> + * Remote node recovery function - tries to reconnect to given target.
> + */
> +static int kst_data_recovery(struct kst_state *st, int err)
> +{
> + struct socket *sock;
> + struct sockaddr addr;
> + int addrlen;
> + struct dst_request *req;
> +
> + if (err != -ECONNRESET && err != -EPIPE) {
> + dprintk("%s: state %p does not know how "
> + "to recover from error %d.\n",
> + __func__, st, err);
> + return err;
> + }
> +
> + err = sock_create(st->socket->ops->family, st->socket->type,
> + st->socket->sk->sk_protocol, &sock);
> + if (err < 0)
> + goto err_out_exit;
> +
> + sock->sk->sk_sndtimeo = sock->sk->sk_rcvtimeo =
> + msecs_to_jiffies(DST_DEFAULT_TIMEO);
> +
> + err = sock->ops->getname(st->socket, &addr, &addrlen, 2);
> + if (err)
> + goto err_out_destroy;
> +
> + err = sock->ops->connect(sock, &addr, addrlen, 0);
> + if (err)
> + goto err_out_destroy;
> +
> + kst_poll_exit(st);
> + kst_sock_release(st);
> +
> + mutex_lock(&st->request_lock);
> + err = st->ops->init(st, sock);
> + if (!err) {
> + /*
> + * After reconnection is completed all requests
> + * must be resent from the state they were finished previously,
> + * but with new headers.
> + */
> + list_for_each_entry(req, &st->request_list, request_list_entry)
> + req->flags &= ~(DST_REQ_HEADER_SENT | DST_REQ_CHEKSUM_RECV);
> + }
> + mutex_unlock(&st->request_lock);
> + if (err < 0)
> + goto err_out_destroy;
> +
> + kst_wake(st);
> + dprintk("%s: reconnected.\n", __func__);
> +
> + return 0;
> +
> +err_out_destroy:
> + sock_release(sock);
> +err_out_exit:
> + dprintk("%s: revovery failed: st: %p, err: %d.\n", __func__, st, err);
> + return err;
> +}
> +
> +/*
> + * Local exporting node end IO callbacks.
> + */
> +static int kst_export_write_end_io(struct bio *bio, unsigned int size, int err)
> +{
> + dprintk("%s: bio: %p, size: %u, idx: %d, num: %d, err: %d.\n",
> + __func__, bio, bio->bi_size, bio->bi_idx, bio->bi_vcnt, err);
> +
> + if (bio->bi_size)
> + return 1;
> +
> + kst_export_put_bio(bio);
> + return 0;
> +}
> +
> +static int kst_export_read_end_io(struct bio *bio, unsigned int size, int err)
> +{
> + struct dst_request *req = bio->bi_private;
> + struct kst_state *st = req->state;
> + int use_csum = test_bit(DST_NODE_USE_CSUM, &req->node->flags);
> +
> + dprintk("%s: bio: %p, req: %p, size: %u, idx: %d, num: %d, err: %d.\n",
> + __func__, bio, req, bio->bi_size, bio->bi_idx,
> + bio->bi_vcnt, err);
> +
> + if (bio->bi_size)
> + return 1;
> +
> + if (err) {
> + kst_export_put_bio(bio);
> + return 0;
> + }
> +
> + bio->bi_size = req->size = req->orig_size;
> + bio->bi_rw = WRITE;
> + if (use_csum)
> + req->flags &= ~(DST_REQ_HEADER_SENT | DST_REQ_CHEKSUM_RECV);
> +
> + /*
> + * This is a race with kst_data_callback(), which checks
> + * this bit to determine if it can or can not process given
> + * request. This does not harm actually, since subsequent
> + * state wakeup will call it again and thus will pick
> + * given request in time.
> + */
> + req->flags &= ~DST_REQ_EXPORT_READ;
> + kst_wake(st);
> + return 0;
> +}
> +
> +/*
> + * This callback is invoked each time new request from remote
> + * node to given local export node is received.
> + * It allocates new block IO request and queues it for processing.
> + */
> +static int kst_export_ready(struct kst_state *st)
> +{
> + struct dst_remote_request r;
> + struct bio *bio;
> + int err, nr, i;
> + struct dst_request *req;
> + unsigned int revents = st->socket->ops->poll(NULL, st->socket, NULL);
> +
> + if (revents & (POLLERR | POLLHUP)) {
> + err = -EPIPE;
> + goto err_out_exit;
> + }
> +
> + if (!(revents & POLLIN) || !list_empty(&st->request_list))
> + return 0;
> +
> + err = dst_data_recv_header(st->socket, &r, 1);
> + if (err != sizeof(struct dst_remote_request)) {
> + err = -ECONNRESET;
> + goto err_out_exit;
> + }
> +
> + kst_convert_header(&r);
> +
> + dprintk("\n%s: st: %p, cmd: %u, sector: %llu, size: %u, "
> + "csum: %x, offset: %u.\n",
> + __func__, st, r.cmd, r.sector,
> + r.size, r.csum, r.offset);
> +
> + err = -EINVAL;
> + if (r.cmd != DST_READ && r.cmd != DST_WRITE && r.cmd != DST_REMOTE_CFG)
> + goto err_out_exit;
> +
> + if ((s64)(r.sector + to_sector(r.size)) < 0 ||
> + (r.sector + to_sector(r.size)) > st->node->size ||
> + r.offset >= PAGE_SIZE)
> + goto err_out_exit;
> +
> + if (r.cmd == DST_REMOTE_CFG) {
> + r.sector = st->node->size;
> +
> + if (test_bit(DST_NODE_USE_CSUM, &st->node->flags))
> + r.csum = 1;
> +
> + kst_convert_header(&r);
> +
> + err = dst_data_send_header(st->socket, &r);
> + if (err != sizeof(struct dst_remote_request)) {
> + err = -EINVAL;
> + goto err_out_exit;
> + }
> + kst_wake(st);
> + return 0;
> + }
> +
> + nr = DIV_ROUND_UP(r.size, PAGE_SIZE);
> +
> + while (r.size) {
> + int nr_pages = min(BIO_MAX_PAGES, nr);
> + unsigned int size;
> + struct page *page;
> +
> + err = -ENOMEM;
> + req = dst_clone_request(NULL, st->node->w->req_pool);
> + if (!req)
> + goto err_out_exit;
> +
> + bio = bio_alloc(GFP_NOIO, nr_pages);
> + if (!bio)
> + goto err_out_free_req;
> +
> + req->flags = DST_REQ_EXPORT | DST_REQ_HEADER_SENT |
> + DST_REQ_CHEKSUM_RECV;
> + req->bio = bio;
> + req->state = st;
> + req->node = st->node;
> + req->callback = &kst_data_callback;
> + req->bio_endio = &kst_bio_endio;
> +
> + req->tmp_offset = 0;
> + req->tmp_csum = r.csum;
> +
> + /*
> + * Yes, looks a bit weird.
> + * Logic is simple - for local exporting node all operations
> + * are reversed compared to usual nodes, since usual nodes
> + * process remote data and local export node process remote
> + * requests, so that writing data means sending data to
> + * remote node and receiving on the local export one.
> + *
> + * So, to process writing to the exported node we need first
> + * to receive data from the net (i.e. to perform READ
> + * operationin terms of usual node), and then put it to the
> + * storage (WRITE command, so it will be changed before
> + * calling generic_make_request()).
> + *
> + * To process read request from the exported node we need
> + * first to read it from storage (READ command for BIO)
> + * and then send it over the net (perform WRITE operation
> + * in terms of network).
> + */
> + if (r.cmd == DST_WRITE) {
> + req->flags |= DST_REQ_EXPORT_WRITE;
> + bio->bi_end_io = kst_export_write_end_io;
> + } else {
> + req->flags |= DST_REQ_EXPORT_READ;
> + bio->bi_end_io = kst_export_read_end_io;
> + }
> + bio->bi_rw = READ;
> + bio->bi_private = req;
> + bio->bi_sector = r.sector;
> + bio->bi_bdev = st->node->bdev;
> +
> + for (i = 0; i < nr_pages; ++i) {
> + page = alloc_page(GFP_NOIO);
> + if (!page)
> + break;
> +
> + size = min_t(u32, PAGE_SIZE - r.offset, r.size);
> +
> + err = bio_add_page(bio, page, size, 0);
> + dprintk("%s: %d/%d: page: %p, size: %u, "
> + "offset: %u (used zero), err: %d.\n",
> + __func__, i, nr_pages, page, size,
> + r.offset, err);
> + if (err <= 0)
> + break;
> +
> + if (err == size)
> + nr--;
> +
> + r.size -= err;
> + r.sector += to_sector(err);
> +
> + if (!r.size)
> + break;
> + }
> +
> + if (!bio->bi_vcnt) {
> + err = -ENOMEM;
> + goto err_out_put;
> + }
> +
> + req->size = req->orig_size = bio->bi_size;
> + req->start = bio->bi_sector;
> + req->idx = 0;
> + req->num = bio->bi_vcnt;
> +
> + dprintk("%s: submitting: bio: %p, req: %p, start: %llu, "
> + "size: %llu, idx: %d, num: %d, offset: %u, csum: %x.\n",
> + __func__, bio, req, req->start, req->size,
> + req->idx, req->num, req->offset, req->tmp_csum);
> +
> + err = kst_enqueue_req(st, req);
> + if (err)
> + goto err_out_put;
> +
> + if (r.cmd == DST_READ) {
> + generic_make_request(bio);
> + }
> + }
> +
> + kst_wake(st);
> + return 0;
> +
> +err_out_put:
> + bio_put(bio);
> +err_out_free_req:
> + dst_free_request(req);
> +err_out_exit:
> + return err;
> +}
> +
> +static void kst_export_exit(struct kst_state *st)
> +{
> + struct dst_node *n = st->node;
> +
> + kst_common_exit(st);
> + dst_node_put(n);
> +}
> +
> +static struct kst_state_ops kst_data_export_ops = {
> + .init = &kst_data_init,
> + .push = &kst_data_push,
> + .exit = &kst_export_exit,
> + .ready = &kst_export_ready,
> +};
> +
> +/*
> + * This callback is invoked each time listening socket for
> + * given local export node becomes ready.
> + * It creates new state for connected client and queues for processing.
> + */
> +static int kst_listen_ready(struct kst_state *st)
> +{
> + struct socket *newsock;
> + struct saddr addr;
> + struct kst_state *newst;
> + int err;
> + unsigned int revents, permissions = 0;
> + struct dst_secure *s;
> +
> + revents = st->socket->ops->poll(NULL, st->socket, NULL);
> + if (!(revents & POLLIN))
> + return 1;
> +
> + err = sock_create(st->socket->ops->family, st->socket->type,
> + st->socket->sk->sk_protocol, &newsock);
> + if (err)
> + goto err_out_exit;
> +
> + err = st->socket->ops->accept(st->socket, newsock, 0);
> + if (err)
> + goto err_out_put;
> +
> + if (newsock->ops->getname(newsock, (struct sockaddr *)&addr,
> + (int *)&addr.sa_data_len, 2) < 0) {
> + err = -ECONNABORTED;
> + goto err_out_put;
> + }
> +
> + list_for_each_entry(s, &st->request_list, sec_entry) {
> + void *sec_addr, *new_addr;
> +
> + sec_addr = ((void *)&s->sec.addr) + s->sec.check_offset;
> + new_addr = ((void *)&addr) + s->sec.check_offset;
> +
> + if (!memcmp(sec_addr, new_addr,
> + addr.sa_data_len - s->sec.check_offset)) {
> + permissions = s->sec.permissions;
> + break;
> + }
> + }
> +
> + /*
> + * So far only reading and writing are supported.
> + * Block device does not know about anything else,
> + * but as far as I recall, there was a prognosis,
> + * that computer will never require more than 640kb of RAM.
> + */
> + if (permissions == 0) {
> + err = -EPERM;
> + goto err_out_put;
> + }
> +
> + if (st->socket->ops->family == AF_INET) {
> + struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
> + printk(KERN_INFO "%s: Client: %u.%u.%u.%u:%d.\n", __func__,
> + NIPQUAD(sin->sin_addr.s_addr), ntohs(sin->sin_port));
> + } else if (st->socket->ops->family == AF_INET6) {
> + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&addr;
> + printk(KERN_INFO "%s: Client: "
> + "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%d",
> + __func__,
> + NIP6(sin->sin6_addr), ntohs(sin->sin6_port));
> + }
> +
> + dst_node_get(st->node);
> + newst = kst_state_init(st->node, permissions,
> + &kst_data_export_ops, newsock);
> + if (IS_ERR(newst)) {
> + err = PTR_ERR(newst);
> + goto err_out_put;
> + }
> +
> + /*
> + * Negative return value means error, positive - stop this state
> + * processing. Zero allows to check state for pending requests.
> + * Listening socket contains security objects in request list,
> + * since it does not have any requests.
> + */
> + return 1;
> +
> +err_out_put:
> + sock_release(newsock);
> +err_out_exit:
> + return 1;
> +}
> +
> +static int kst_listen_init(struct kst_state *st, void *data)
> +{
> + int err = -ENOMEM, i;
> + struct dst_le_template *tmp = data;
> + struct dst_secure *s;
> +
> + for (i=0; i<tmp->le->secure_attr_num; ++i) {
> + s = kmalloc(sizeof(struct dst_secure), GFP_KERNEL);
> + if (!s)
> + goto err_out_exit;
> +
> + memcpy(&s->sec, tmp->data, sizeof(struct dst_secure_user));
> +
> + list_add_tail(&s->sec_entry, &st->request_list);
> + tmp->data += sizeof(struct dst_secure_user);
> +
> + if (s->sec.addr.sa_family == AF_INET) {
> + struct sockaddr_in *sin =
> + (struct sockaddr_in *)&s->sec.addr;
> + printk(KERN_INFO "%s: Client: %u.%u.%u.%u:%d, "
> + "permissions: %x.\n",
> + __func__, NIPQUAD(sin->sin_addr.s_addr),
> + ntohs(sin->sin_port), s->sec.permissions);
> + } else if (s->sec.addr.sa_family == AF_INET6) {
> + struct sockaddr_in6 *sin =
> + (struct sockaddr_in6 *)&s->sec.addr;
> + printk(KERN_INFO "%s: Client: "
> + "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%d, "
> + "permissions: %x.\n",
> + __func__, NIP6(sin->sin6_addr),
> + ntohs(sin->sin6_port), s->sec.permissions);
> + }
> + }
> +
> + err = kst_sock_create(st, &tmp->le->rctl.addr, tmp->le->rctl.type,
> + tmp->le->rctl.proto, tmp->le->backlog);
> + if (err)
> + goto err_out_exit;
> +
> + err = kst_poll_init(st);
> + if (err)
> + goto err_out_release;
> +
> + return 0;
> +
> +err_out_release:
> + kst_sock_release(st);
> +err_out_exit:
> + kst_listen_flush(st);
> + return err;
> +}
> +
> +/*
> + * Operations for different types of states.
> + * There are three:
> + * data state - created for remote node, when distributed storage connects
> + * to remote node, which contain data.
> + * listen state - created for local export node, when remote distributed
> + * storage's node connects to given node to get/put data.
> + * data export state - created for each client connected to above listen
> + * state.
> + */
> +static struct kst_state_ops kst_listen_ops = {
> + .init = &kst_listen_init,
> + .exit = &kst_listen_exit,
> + .ready = &kst_listen_ready,
> +};
> +static struct kst_state_ops kst_data_ops = {
> + .init = &kst_data_init,
> + .push = &kst_data_push,
> + .exit = &kst_common_exit,
> + .recovery = &kst_data_recovery,
> +};
> +
> +struct kst_state *kst_listener_state_init(struct dst_node *node,
> + struct dst_le_template *tmp)
> +{
> + return kst_state_init(node, DST_PERM_READ | DST_PERM_WRITE,
> + &kst_listen_ops, tmp);
> +}
> +
> +struct kst_state *kst_data_state_init(struct dst_node *node,
> + struct socket *newsock)
> +{
> + return kst_state_init(node, DST_PERM_READ | DST_PERM_WRITE,
> + &kst_data_ops, newsock);
> +}
> +
> +/*
> + * Remove all workers and associated states.
> + */
> +void kst_exit_all(void)
> +{
> + struct kst_worker *w, *n;
> +
> + list_for_each_entry_safe(w, n, &kst_worker_list, entry) {
> + kst_worker_exit(w);
> + }
> +}
>
> -
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
[-- Attachment #2: 0001-patch-fixes.patch --]
[-- Type: text/plain, Size: 924 bytes --]
drivers/block/dst/kst.c | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/drivers/block/dst/kst.c b/drivers/block/dst/kst.c
index 8fa3387..d275bb9 100644
--- a/drivers/block/dst/kst.c
+++ b/drivers/block/dst/kst.c
@@ -1111,8 +1111,17 @@ static int kst_export_read_end_io(struct bio *bio, unsigned int size, int err)
return 0;
}
+ /* FIXME: This is a litle bit strange, but bio_end_io will
+ be called one more time for this bio later from here:
+ ->kst_complete_req
+ ->kst_bio_endio
+ At this moment network layer has already pinned bio's pages
+ and we may sefly release all pages, so let's reuse existing
+ kst_export_write_end_io method instead of writing new one.
+ */
bio->bi_size = req->size = req->orig_size;
bio->bi_rw = WRITE;
+ bio->bi_end_io = kst_export_write_end_io;
if (use_csum)
req->flags &= ~(DST_REQ_HEADER_SENT | DST_REQ_CHEKSUM_RECV);
^ permalink raw reply related
* Re: [RFC] net: napi fix
From: David Miller @ 2007-12-13 20:38 UTC (permalink / raw)
To: shemminger
Cc: gallatin, joonwpark81, auke-jan.h.kok, netdev, linux-kernel,
jgarzik, jesse.brandeburg
In-Reply-To: <4761896B.7070801@linux-foundation.org>
From: Stephen Hemminger <shemminger@linux-foundation.org>
Date: Thu, 13 Dec 2007 11:35:07 -0800
> How about allowing a return value of -1 from napi_poll and letting
> device check itself.
It doesn't avoid the code duplication in the ->poll() fast paths.
I don't care, on the other hand, if crap accumulates in non-critical
slow paths like napi_disable() and dev_close(). That's why I'm
suggesting solutions in that area.
^ permalink raw reply
* Re: [RFC] net: napi fix
From: David Miller @ 2007-12-13 20:37 UTC (permalink / raw)
To: jarkao2
Cc: auke-jan.h.kok, gallatin, joonwpark81, netdev, linux-kernel,
jgarzik, shemminger, jesse.brandeburg
In-Reply-To: <4761930C.3030504@gmail.com>
From: Jarek Poplawski <jarkao2@gmail.com>
Date: Thu, 13 Dec 2007 21:16:12 +0100
> I see in a nearby thread you would prefer to save some work to drivers
> (like this netif_running() check), but I think this all is at the cost
> of flexibility, and there will probably appear new problems, when a
> driver simply can't wait till the next poll (which btw. looks strange
> with all these hotplugging, usb and powersaving).
As someone who has actually had to edit the NAPI support of _EVERY_
single driver in the tree I can tell you that code duplication and
subtle semantic differences are a huge issue.
And when you talk about driver flexibility, it's wise to mention that
this comes at the expense of flexibility in the core implmentation.
For example, if we export the list handling widget into the ->poll()
routines, god help the person who wants to change how the poll list is
managed in net_rx_action() :-/
So we don't want to export datastructure details like that to the
driver.
^ permalink raw reply
* Re: [RFC] net: napi fix
From: Jarek Poplawski @ 2007-12-13 20:16 UTC (permalink / raw)
To: David Miller
Cc: auke-jan.h.kok, gallatin, joonwpark81, netdev, linux-kernel,
jgarzik, shemminger, jesse.brandeburg
In-Reply-To: <20071213.055013.83963139.davem@davemloft.net>
David Miller wrote, On 12/13/2007 02:50 PM:
> From: Jarek Poplawski <jarkao2@gmail.com>
> Date: Thu, 13 Dec 2007 14:49:53 +0100
>
>> As a matter of fact, since it's "unlikely()" in net_rx_action() anyway,
>> I wonder what is the main reason or gain of leaving such a tricky
>> exception, instead of letting drivers to always decide which is the
>> best moment for napi_complete()? (Or maybe even, in such a case, they
>> should call some function with this list_move_tail() if it's so
>> useful?)
>
> It is the only sane way to synchronize the list manipulations.
>
> There has to be a way for ->poll() to tell net_rx_action() two things:
>
> 1) How much work was completed, so we can adjust 'budget'
The 'budget' line would stay where it is. IMHO, it's only about this
list_move_tail(). (Probably also doing netpoll_poll_unlock()
during n->poll() could be considered to let the driver even destroy
napi just after napi_complete() - but it's another subject.)
> 2) Was the NAPI quota exhausted? So that we know that
> net_rx_action() still "owns" the polling context and
> thus can do the list manipulation safely.
>
> And these both need to be encoded into one single return value, thus
> the adopted convention that "work == weight" means that the device has
> not done a NAPI complete.
Of course, with some care and explanations to driver maintainers, like in
this case, this all should probably work like it is. But IMHO it would be
easier to remember and maintain if there are some simple rules with no
exceptions, so here e.g. driver always "owns" (with functions like
napi_schedule(), napi_complete(), and maybe napi_move_tail()), and
net_rx_action() only reads the list and runs these functions?!
I see in a nearby thread you would prefer to save some work to drivers
(like this netif_running() check), but I think this all is at the cost
of flexibility, and there will probably appear new problems, when a
driver simply can't wait till the next poll (which btw. looks strange
with all these hotplugging, usb and powersaving).
Regards,
Jarek P.
^ permalink raw reply
* Re: 2.6.24-rc5-mm1 regression - kernel warning on tcp_fastretrans_alert()
From: Andrew Morton @ 2007-12-13 19:55 UTC (permalink / raw)
To: Kamalesh Babulal; +Cc: linux-kernel, Andy Whitcroft, Balbir Singh, netdev
In-Reply-To: <47614815.8070007@linux.vnet.ibm.com>
On Thu, 13 Dec 2007 20:26:21 +0530 Kamalesh Babulal <kamalesh@linux.vnet.ibm.com> wrote:
> Hi Andrew,
Hi. Please do try to cc netdev@vger.kernel.org on net-related problems.
Doing so will often save multiple hours latency and will optimise away one
entire email (ie: this one).
> Following call trace is seen in 2.6.24-rc5-mm1 kernel also,it was reported
> for 2.6.24-rc4-mm1 kernel http://lkml.org/lkml/2007/12/6/22
>
> ls21b kernel: [ 7530.313408] WARNING: at net/ipv4/tcp_input.c:2533 tcp_fastretrans_alert()
> ls21b kernel: [ 7530.354051] Pid: 0, comm: swapper Not tainted 2.6.24-rc5-mm1 #1
> ls21b kernel: [ 7530.389487]
> ls21b kernel: [ 7530.389488] Call Trace:
> ls21b kernel: [ 7530.413030] <IRQ> [<ffffffff80482374>] tcp_fastretrans_alert+0x127/0xdaf
> ls21b kernel: [ 7530.454295] [<ffffffff804850cd>] tcp_ack+0xf2f/0x10fe
> ls21b kernel: [ 7530.485066] [<ffffffff80488503>] tcp_rcv_established+0x695/0x79a
> ls21b kernel: [ 7530.521542] [<ffffffff8025c46a>] trace_hardirqs_off+0x39/0xdc
> ls21b kernel: [ 7530.556468] [<ffffffff8048eb70>] tcp_v4_do_rcv+0x37/0x3e1
> ls21b kernel: [ 7530.589317] [<ffffffff80491764>] tcp_v4_rcv+0xac7/0xb93
> ls21b kernel: [ 7530.621126] [<ffffffff80472c40>] ip_local_deliver_finish+0x54/0x20f
> ls21b kernel: [ 7530.659168] [<ffffffff80472d20>] ip_local_deliver_finish+0x134/0x20f
> ls21b kernel: [ 7530.697724] [<ffffffff804732cc>] ip_local_deliver+0x72/0x7a
> ls21b kernel: [ 7530.731609] [<ffffffff80472b7c>] ip_rcv_finish+0x3c0/0x430
> ls21b kernel: [ 7530.764977] [<ffffffff8044d9a6>] netif_receive_skb+0x10e/0x44d
> ls21b kernel: [ 7530.800422] [<ffffffff80473223>] ip_rcv+0x326/0x35d
> ls21b kernel: [ 7530.830148] [<ffffffff8044dc77>] netif_receive_skb+0x3df/0x44d
> ls21b kernel: [ 7530.865603] [<ffffffff8814d44a>] :bnx2:bnx2_poll+0x1262/0x14a4
> ls21b kernel: [ 7530.901039] [<ffffffff8034817d>] __next_cpu+0x19/0x28
> ls21b kernel: [ 7530.931805] [<ffffffff802323a1>] find_busiest_group+0x252/0x6da
> ls21b kernel: [ 7530.967768] [<ffffffff8025c46a>] trace_hardirqs_off+0x39/0xdc
> ls21b kernel: [ 7531.002693] [<ffffffff8025c46a>] trace_hardirqs_off+0x39/0xdc
> ls21b kernel: [ 7531.037612] [<ffffffff8025c21f>] check_chain_key+0x9c/0x15f
> ls21b kernel: [ 7531.071501] [<ffffffff8026012b>] __lock_acquire+0xdee/0xf06
> ls21b kernel: [ 7531.105386] [<ffffffff80450476>] net_rx_action+0x75/0x234
> ls21b kernel: [ 7531.138233] [<ffffffff80450476>] net_rx_action+0x75/0x234
> ls21b kernel: [ 7531.171074] [<ffffffff804504ed>] net_rx_action+0xec/0x234
> ls21b kernel: [ 7531.203920] [<ffffffff80243f02>] __do_softirq+0x5f/0xe3
> ls21b kernel: [ 7531.235721] [<ffffffff8020d5cc>] call_softirq+0x1c/0x28
> ls21b kernel: [ 7531.267528] [<ffffffff8020ecdf>] do_softirq+0x45/0x108
> ls21b kernel: [ 7531.298811] [<ffffffff80243ea1>] irq_exit+0x4e/0x50
> ls21b kernel: [ 7531.328540] [<ffffffff8020ef3d>] do_IRQ+0x171/0x194
> ls21b kernel: [ 7531.358267] [<ffffffff8020c8c6>] ret_from_intr+0x0/0xf
> ls21b kernel: [ 7531.389549] <EOI> [<ffffffff8020b1ec>] default_idle+0x58/0x8a
> ls21b kernel: [ 7531.425096] [<ffffffff8020b1ea>] default_idle+0x56/0x8a
> ls21b kernel: [ 7531.456900] [<ffffffff8020b194>] default_idle+0x0/0x8a
> ls21b kernel: [ 7531.488186] [<ffffffff8020b2d3>] cpu_idle+0xb5/0xec
> ls21b kernel: [ 7531.517913] [<ffffffff802226f4>] start_secondary+0x3ca/0x3da
>
That is
if (WARN_ON(!tp->sacked_out && tp->fackets_out))
tp->fackets_out = 0;
^ permalink raw reply
* Re: [RFC] net: napi fix
From: Stephen Hemminger @ 2007-12-13 19:35 UTC (permalink / raw)
To: David Miller
Cc: gallatin, joonwpark81, auke-jan.h.kok, netdev, linux-kernel,
jgarzik, jesse.brandeburg
In-Reply-To: <20071213.110928.26060224.davem@davemloft.net>
David Miller wrote:
> From: Andrew Gallatin <gallatin@myri.com>
> Date: Thu, 13 Dec 2007 14:02:25 -0500
>
>
>> Or perhaps we should just leave things as is.
>>
>
> We should probably add a "disabling" state bit to the
> napi struct flags, this will be set by napi_disable()
> before it loops trying to set the sched bit.
>
> net_rx_action() can then check this.
>
How about allowing a return value of -1 from napi_poll and letting device
check itself.
^ permalink raw reply
* [PATCH] [RFC] New driver "sfc" for Solarstorm SFC4000 controller - 2nd try
From: Robert Stonehouse @ 2007-12-13 19:18 UTC (permalink / raw)
To: netdev; +Cc: linux-net-drivers, spope
This is a resubmission of a new driver for Solarflare network controllers.
The driver supports many types of PHY (10Gbase-T, XFP, CX4) on five
different 10G reference designs and one 1G NIC ref design.
The previous thread was:
"[PATCH] [RFC] New driver "sfc" for Solarstorm SFC4000 controller"
http://marc.info/?l=linux-netdev&m=119583775622559&w=2
Since the 1st patch we have addressed the review comments we received
- removed usage of __LINK_STATE_START as unnecessary with latest NAPI
- cleaned up many checkpatch violations
- used the generic drv_* logging
(macros were kept so that the network device name can be printed
consistently in all messaging but these can be dissolved if necessary)
- Reduced over-use of docbook type comments
- replaced uintN_t with uN types
- merged some small headers to reduce the file count
It is still quite a large driver at ~25k LOC. The main body of the driver
is within efx.c, falcon.c, tx.c and rx.c if this helps direct review effort.
We welcome more review comments and will try and respond to them more
quickly than last time.
The patch (against net-2.6.25) is at:
https://support.solarflare.com/netdev/2/net-2.6.25-sfc-2.2.0026.patch
The new files may also be downloaded as a tarball:
https://support.solarflare.com/netdev/2/net-2.6.25-sfc-2.2.0026.tgz
And for verification there is:
https://support.solarflare.com/netdev/2/MD5SUMS
Regards
--
Rob Stonehouse
^ permalink raw reply
* Re: reading the tcp headers within the write queue
From: David Miller @ 2007-12-13 19:13 UTC (permalink / raw)
To: Gavin.McCullagh; +Cc: netdev
In-Reply-To: <20071213190859.GW7409@nuim.ie>
From: Gavin McCullagh <Gavin.McCullagh@nuim.ie>
Date: Thu, 13 Dec 2007 19:08:59 +0000
> Thought that might be it. I presume there isn't any other residue of the
> tcp options elsewhere, that one could look at when the packet gets
> acknowledged? I'm particularly interested in the timestamp.
Every time we transmit, the timestamp will be different.
We store the jiffies at transmit time in TCP_SKB_CB(skb)->when,
so you can use that. This is the value we use to compute the
timestamp.
^ permalink raw reply
* Re: [RFC] net: napi fix
From: Andrew Gallatin @ 2007-12-13 19:02 UTC (permalink / raw)
To: Stephen Hemminger
Cc: David Miller, joonwpark81, auke-jan.h.kok, netdev, linux-kernel,
jgarzik, jesse.brandeburg
In-Reply-To: <20071213102233.3727e9a0@freepuppy.rosehill>
Stephen Hemminger wrote:
> On Thu, 13 Dec 2007 06:19:38 -0800 (PST)
> David Miller <davem@davemloft.net> wrote:
>
>> From: Andrew Gallatin <gallatin@myri.com>
>> Date: Thu, 13 Dec 2007 09:13:54 -0500
>>
>>> If the netif_running() check is indeed required to make a device break
>>> out of napi polling and respond to an ifconfig down, then I think the
>>> netif_running() check should be moved up into net_rx_action() to avoid
>>> potential for driver complexity and bugs like the ones you found.
>> That, or something like it, definitely sounds reasonable and much
>> better than putting the check into every driver :-)
>> --
>> To unsubscribe from this list: send the line "unsubscribe netdev" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
> It is not possible to do netif_running() check in generic code as currently
> written because of the case of devices where a single NAPI object is
> being used to handle two devices. The association between napi and netdevice
> is M to N. There are cases like niu that have multiple NAPI's and one
> netdevice; and devices like sky2 that can have one NAPI and 2 netdevice's.
Ah, now I see. I forgot that not every device has a 1:1::napi:netdev
relationship.
Could we make an optional *dev_state field in the napi structure.
It would be initialized to __LINK_STATE_START. Devices which have
a 1:1 NAPI:netdevice relationship would set it to &netdev->state.
The generic code would then do a test_bit(__LINK_STATE_START,
napi->dev_state), and 1:1 drivers could remove this check.
M:N drivers would pay for a useless (to them) test_bit, and would
have to provide their own netif_running check to get termination
under heavy load.
Just an idea, perhaps there is a better way which is less hacky.
Or perhaps we should just leave things as is.
Drew
^ permalink raw reply
* Re: [RFC] net: napi fix
From: David Miller @ 2007-12-13 19:09 UTC (permalink / raw)
To: gallatin
Cc: shemminger, joonwpark81, auke-jan.h.kok, netdev, linux-kernel,
jgarzik, jesse.brandeburg
In-Reply-To: <476181C1.3060204@myri.com>
From: Andrew Gallatin <gallatin@myri.com>
Date: Thu, 13 Dec 2007 14:02:25 -0500
> Or perhaps we should just leave things as is.
We should probably add a "disabling" state bit to the
napi struct flags, this will be set by napi_disable()
before it loops trying to set the sched bit.
net_rx_action() can then check this.
^ permalink raw reply
* Re: reading the tcp headers within the write queue
From: Gavin McCullagh @ 2007-12-13 19:08 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20071213.110050.104716141.davem@davemloft.net>
Hi,
thanks for the swift reply.
On Thu, 13 Dec 2007, David Miller wrote:
> > I'm trying to hack together something which will run through the
> > retransmit queue looking at the tcp headers.
>
> The packets in the retransmit queue are headerless, the
> header only gets added to clones of the retransmit queue
> frames during the actual transmit.
Thought that might be it. I presume there isn't any other residue of the
tcp options elsewhere, that one could look at when the packet gets
acknowledged? I'm particularly interested in the timestamp.
> And this question belongs on netdev not linux-net.
Oops, sorry.
Gavin
^ permalink raw reply
* Re: reading the tcp headers within the write queue
From: David Miller @ 2007-12-13 19:00 UTC (permalink / raw)
To: Gavin.McCullagh; +Cc: linux-net, netdev
In-Reply-To: <20071213185626.GV7409@nuim.ie>
From: Gavin McCullagh <Gavin.McCullagh@nuim.ie>
Date: Thu, 13 Dec 2007 18:56:26 +0000
> I'm trying to hack together something which will run through the
> retransmit queue looking at the tcp headers.
The packets in the retransmit queue are headerless, the
header only gets added to clones of the retransmit queue
frames during the actual transmit.
And this question belongs on netdev not linux-net.
^ permalink raw reply
* Re: [kvm-devel] [PATCH resent] virtio_net: Fix stalled inbound trafficon early packets
From: Christian Borntraeger @ 2007-12-13 18:30 UTC (permalink / raw)
To: dor.laor; +Cc: Rusty Russell, kvm-devel, netdev, virtualization
In-Reply-To: <47613277.3060403@qumranet.com>
Am Donnerstag, 13. Dezember 2007 schrieb Dor Laor:
> You're right I got confused somehow.
> So in that case setting the driver status field on open in addition to
> your enable will do the trick.
> On DRIVER_OPEN the host will trigger an interrupt if the queue is not
> empty..
> Thanks,
> Dor
After looking into some other drivers, I prefer my first patch (moving
napi_enable) ;-)
There are some drivers like xen-netfront, b44, which call napi_enable before
the buffers are passed to the hardware. So it seems that moving napi is
also a valid option.
But maybe I can just wait until Rusty returns from vacation (I will leave
next week) so everything might be wonderful when I return ;-)
Rusty, if you decide to apply my patch, there is one downside: The debugging
code in virtio_ring sometimes triggers with a false positive:
try_fill_recv calls vring_kick. Here we do a notify to the host. This might
cause an immediate interrupt, triggering the poll routine before vring_kick
can run END_USE. This is no real problem, as we dont touch the vq after
notify.
Christian, facing 64 guest cpus unconvering all kind of races
^ permalink raw reply
* Re: [RFC] net: napi fix
From: Stephen Hemminger @ 2007-12-13 18:22 UTC (permalink / raw)
To: David Miller
Cc: gallatin, joonwpark81, auke-jan.h.kok, netdev, linux-kernel,
jgarzik, jesse.brandeburg
In-Reply-To: <20071213.061938.86541759.davem@davemloft.net>
On Thu, 13 Dec 2007 06:19:38 -0800 (PST)
David Miller <davem@davemloft.net> wrote:
> From: Andrew Gallatin <gallatin@myri.com>
> Date: Thu, 13 Dec 2007 09:13:54 -0500
>
> > If the netif_running() check is indeed required to make a device break
> > out of napi polling and respond to an ifconfig down, then I think the
> > netif_running() check should be moved up into net_rx_action() to avoid
> > potential for driver complexity and bugs like the ones you found.
>
> That, or something like it, definitely sounds reasonable and much
> better than putting the check into every driver :-)
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
It is not possible to do netif_running() check in generic code as currently
written because of the case of devices where a single NAPI object is
being used to handle two devices. The association between napi and netdevice
is M to N. There are cases like niu that have multiple NAPI's and one
netdevice; and devices like sky2 that can have one NAPI and 2 netdevice's.
The existing pointer from napi to netdevice is only used by netconsole
now. For devices like sky2 it means that netconsole can't work on the the
second port which is a not a big problem. But adding a netif_running()
check would be a big issue.
--
Stephen Hemminger <shemminger@linux-foundation.org>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox