Fixing the Netgear WPN311 system freezes in Ubuntu

We had this long-time problem with the Netgear WPN311 wireless cards which seemed to go and come back intermittently with each kernel revision: if we used intensively the wireless, the computer would freeze. As in totally frozen (you couldn't even go back to a console and restart the X or something from there).

With Ubuntu 9.10 and the 2.6.31 kernel, the problem seemed semi-stable and only appeared if we used packet-crazy software such as bittorrent at high speeds, but at least we could connect to internet. In Ubuntu 10.4 things went worse: the wireless card would freeze the computer almost every 20 minutes, and even worse, without any pattern such as "using a lot the internet" or "using bittorrent"!

After I upgraded my computer hardware-wise I felt it was the time to risk it and upgrade it software-wise too.

So after carefully considering all the bug reports about the ath5k driver, I dared to update the system.

And as expected, a mere 15-20 minutes after the system had been updated and restarted, it froze.

None of the solutions out there seemed to make much sense, except for one which suggested a patch to the ath5k driver. This is how I applied the solution to the latest version of Ubuntu:

Download the source for the latest kernel:


sudo apt-get install linux-source-2.6.32

It ends up downloading a .bz2 file into /usr/src. To uncompress it:


cd /usr/src
sudo tar -xvjf linux-source-2.6.32.tar
cd linux-source-2.6.32

The patch expects the files to be in drivers/net/wireless/ but it seems they have been moved around since then. Now they are in drivers/net/wireless/ath/ath5k (note the extra /ath/).

I took a look at the files, and noticed that the patch hadn't been applied to the kernel, even if the patch was almost two years old already.

So I manually applied the patch to the files (it was easy, just around 10 lines of code). Here's how it should be done, green lines are new lines (additions), red lines are deleted things:

base.c


diff --git a/home/sole/Desktop/linux-source-2.6.32/drivers/net/wireless/ath/ath5k/base.c b/driver
index 6313788..db2c785 100644
--- a/home/sole/Desktop/linux-source-2.6.32/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -551,6 +551,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
        sc->opmode = NL80211_IFTYPE_STATION;
        sc->bintval = 1000;
        mutex_init(&sc->lock);
+       spin_lock_init(&sc->restlock);
        spin_lock_init(&sc->rxbuflock);
        spin_lock_init(&sc->txbuflock);
        spin_lock_init(&sc->block);
@@ -2727,6 +2728,7 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
 {
        struct ath5k_hw *ah = sc->ah;
        int ret;
+       unsigned long flags;

        ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "resetting\n");

@@ -2738,7 +2740,10 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
                sc->curchan = chan;
                sc->curband = &sc->sbands[chan->band];
        }
+       spin_lock_irqsave(&sc->restlock, flags);
        ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL);
+       spin_unlock_irqrestore(&sc->restlock, flags);
+
        if (ret) {
                ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret);
                goto err;

base.h


diff --git a/home/sole/Desktop/linux-source-2.6.32/drivers/net/wireless/ath/ath5k/base.h b/drivers/net
index a28c42f..a0cbf61 100644
--- a/home/sole/Desktop/linux-source-2.6.32/drivers/net/wireless/ath/ath5k/base.h
+++ b/drivers/net/wireless/ath/ath5k/base.h
@@ -162,6 +162,7 @@ struct ath5k_softc {
                                led_on;         /* pin setting for LED on */

        struct tasklet_struct   restq;          /* reset tasklet */
+       spinlock_t              restlock;       /* protects reset state */

        unsigned int            rxbufsize;      /* rx size based on mtu */
        struct list_head        rxbuf;          /* receive buffer */

And phy.c


diff --git a/home/sole/Desktop/linux-source-2.6.32/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/
index 9d67647..112e8e2 100644
--- a/home/sole/Desktop/linux-source-2.6.32/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -578,8 +578,9 @@ int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct ieee80211_channel *channel,
         * ah->ah_rf_banks based on ah->ah_rf_banks_size
         * we set above */
        if (ah->ah_rf_banks == NULL) {
-               ah->ah_rf_banks = kmalloc(sizeof(u32) * ah->ah_rf_banks_size,
-                                                               GFP_KERNEL);
+               //ah->ah_rf_banks = kmalloc(sizeof(u32) * ah->ah_rf_banks_size,
+               //                                              GFP_KERNEL);
+               ah->ah_rf_banks = kmalloc(sizeof(u32) * ah->ah_rf_banks_size, GFP_ATOMIC);
                if (ah->ah_rf_banks == NULL) {
                        ATH5K_ERR(ah->ah_sc, "out of memory\n");
                        return -ENOMEM;

Then this:


sudo make oldconfig
sudo make

and I let the computer compiling the kernel while I went to sleep, but I forgot to deactivate the wireless and it got frozen again, without completing the compilation.

Next morning, after cursing myself for having forgotten to deactivate the wireless, I rebooted again, but using the previous kernel (selected it from GRUB), just to be safe, and once the wireless had been deactivated, I tried to recompile again. This time it stopped in the middle because there's an error in one Makefile (I seriously wonder how the Ubuntu engineers manage to compile the kernel if it fails like this for them!)

Anyway, I manually applied this patch as well, and restarted the compilation again.

Once it finished, I got the desired new driver module (drivers/net/wireless/ath/ath5k/ath5k.ko) which I wanted to use instead of the existing module. So I backed it up and copied the new one over the old one:


cp /lib/modules/2.6.32-23-generic/kernel/drivers/net/wireless/ath/ath5k/ath5k.ko ~/Desktop
sudo cp /usr/src/linux-source-2.6.32/drivers/net/wireless/ath/ath5k/ath5k.ko /lib/modules/2.6.32-23-generic/kernel/drivers/net/wireless/ath/ath5k/ath5k.ko

and restarted the computer to use the latest kernel (and the new updated module)

I have tested it using bittorrent, amule, youtube, downloading packages from synaptic, all at the same time. So far I haven't experienced any freeze, and the net speed is normal (if there's a slowdown, I can't appreciate it), so I would say the patch fixes this annoying bug.

I wonder why the fix has not been applied to the kernel (after two years!!) but I presume that maybe these cards are not as popular as maybe other wireless chipsets and as such they get less coverage/reports than other bugs. In any case, Bob Copeland (the author of the patch) is my personal hero for at least this month.

Bob, THANKS A LOT FOR THE PATCH!!!