orinoco: reduce stack usage in firmware download path
authorAndrey Borzenkov <arvidjaar@newmail.ru>
Fri, 10 Oct 2008 17:26:30 +0000 (21:26 +0400)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 22 Oct 2008 22:09:32 +0000 (18:09 -0400)
orinoco_dl_firmware and symbol_dl_mage allocate large local
variables (1K); at least orinoco fails with panic or hung
kernel if 4K stacks is enabled.

Allocate large buffers dynamically at run time.

Tested-By: Andrey Borzenkov <arvidjaar@mail.ru> for Agere case
Signed-off-by: Andrey Borzenkov < arvidjaar@mail.ru>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/orinoco.c

index 5090477..e0512e4 100644 (file)
@@ -433,7 +433,7 @@ struct fw_info {
 const static struct fw_info orinoco_fw[] = {
        { "", "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 },
        { "", "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 },
-       { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", "", 0x00003100, 0x100 }
+       { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", "", 0x00003100, 512 }
 };
 
 /* Structure used to access fields in FW
@@ -458,7 +458,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
                    int ap)
 {
        /* Plug Data Area (PDA) */
-       __le16 pda[512] = { 0 };
+       __le16 *pda;
 
        hermes_t *hw = &priv->hw;
        const struct firmware *fw_entry;
@@ -467,7 +467,11 @@ orinoco_dl_firmware(struct orinoco_private *priv,
        const unsigned char *end;
        const char *firmware;
        struct net_device *dev = priv->ndev;
-       int err;
+       int err = 0;
+
+       pda = kzalloc(fw->pda_size, GFP_KERNEL);
+       if (!pda)
+               return -ENOMEM;
 
        if (ap)
                firmware = fw->ap_fw;
@@ -478,17 +482,17 @@ orinoco_dl_firmware(struct orinoco_private *priv,
               dev->name, firmware);
 
        /* Read current plug data */
-       err = hermes_read_pda(hw, pda, fw->pda_addr,
-                             min_t(u16, fw->pda_size, sizeof(pda)), 0);
+       err = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 0);
        printk(KERN_DEBUG "%s: Read PDA returned %d\n", dev->name, err);
        if (err)
-               return err;
+               goto free;
 
        err = request_firmware(&fw_entry, firmware, priv->dev);
        if (err) {
                printk(KERN_ERR "%s: Cannot find firmware %s\n",
                       dev->name, firmware);
-               return -ENOENT;
+               err = -ENOENT;
+               goto free;
        }
 
        hdr = (const struct orinoco_fw_header *) fw_entry->data;
@@ -532,6 +536,9 @@ orinoco_dl_firmware(struct orinoco_private *priv,
 
 abort:
        release_firmware(fw_entry);
+
+free:
+       kfree(pda);
        return err;
 }
 
@@ -549,12 +556,12 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
                int secondary)
 {
        hermes_t *hw = &priv->hw;
-       int ret;
+       int ret = 0;
        const unsigned char *ptr;
        const unsigned char *first_block;
 
        /* Plug Data Area (PDA) */
-       __le16 pda[256];
+       __le16 *pda = NULL;
 
        /* Binary block begins after the 0x1A marker */
        ptr = image;
@@ -563,28 +570,33 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
 
        /* Read the PDA from EEPROM */
        if (secondary) {
-               ret = hermes_read_pda(hw, pda, fw->pda_addr, sizeof(pda), 1);
+               pda = kzalloc(fw->pda_size, GFP_KERNEL);
+               if (!pda)
+                       return -ENOMEM;
+
+               ret = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 1);
                if (ret)
-                       return ret;
+                       goto free;
        }
 
        /* Stop the firmware, so that it can be safely rewritten */
        if (priv->stop_fw) {
                ret = priv->stop_fw(priv, 1);
                if (ret)
-                       return ret;
+                       goto free;
        }
 
        /* Program the adapter with new firmware */
        ret = hermes_program(hw, first_block, end);
        if (ret)
-               return ret;
+               goto free;
 
        /* Write the PDA to the adapter */
        if (secondary) {
                size_t len = hermes_blocks_length(first_block);
                ptr = first_block + len;
                ret = hermes_apply_pda(hw, ptr, pda);
+               kfree(pda);
                if (ret)
                        return ret;
        }
@@ -608,6 +620,10 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
                return -ENODEV;
 
        return 0;
+
+free:
+       kfree(pda);
+       return ret;
 }