Hi Dan,

Am 12.10.2013 um 21:58 schrieb Dan Williams:

> On Sat, 2013-10-12 at 18:02 +0200, Dr. H. Nikolaus Schaller wrote:
>> While upgrading the GTA04 kernel to 3.12-rc4 we came across
>> an issue with libertas/sdio referencing stale memory on ifconfig up
>> when trying to load the firmware (for a second time).
>> 
>> I am not at all sure if the patch is how it should be done and the right
>> location, but it appears to work for us with resetting priv->helper_fw to 
>> NULL
>> before asynchronously loading the firmware again.
> 
> How about we go even simpler?  helper_fw is *only* used inside
> firmware.c anyway, and that's probably where its lifetime should be
> handled.  Same for the main firmware.  I have no idea why the bus code
> is responsible for releasing the firmware anyway, when it originates
> from firmware.c and control returns back there after the firmware loaded
> callback is done.  Does the following patch fix your problem too?

Yes, it works!

I think your approach is much better from software architecture point
of view than our hack.

Thank you,
Nikolaus

> 
> Dan
> 
> ---
> diff --git a/drivers/net/wireless/libertas/firmware.c 
> b/drivers/net/wireless/libertas/firmware.c
> index c0f9e7e..51b92b5 100644
> --- a/drivers/net/wireless/libertas/firmware.c
> +++ b/drivers/net/wireless/libertas/firmware.c
> @@ -49,14 +49,19 @@ static void main_firmware_cb(const struct firmware 
> *firmware, void *context)
>               /* Failed to find firmware: try next table entry */
>               load_next_firmware_from_table(priv);
>               return;
>       }
> 
>       /* Firmware found! */
>       lbs_fw_loaded(priv, 0, priv->helper_fw, firmware);
> +     if (priv->helper_fw) {
> +             release_firmware (priv->helper_fw);
> +             priv->helper_fw = NULL;
> +     }
> +     release_firmware (firmware);
> }
> 
> static void helper_firmware_cb(const struct firmware *firmware, void *context)
> {
>       struct lbs_private *priv = context;
> 
>       if (!firmware) {
> diff --git a/drivers/net/wireless/libertas/if_cs.c 
> b/drivers/net/wireless/libertas/if_cs.c
> index c94dd68..ef8c98e 100644
> --- a/drivers/net/wireless/libertas/if_cs.c
> +++ b/drivers/net/wireless/libertas/if_cs.c
> @@ -750,22 +750,22 @@ static void if_cs_prog_firmware(struct lbs_private 
> *priv, int ret,
>       }
> 
>       /* Load the firmware */
>       ret = if_cs_prog_helper(card, helper);
>       if (ret == 0 && (card->model != MODEL_8305))
>               ret = if_cs_prog_real(card, mainfw);
>       if (ret)
> -             goto out;
> +             return;
> 
>       /* Now actually get the IRQ */
>       ret = request_irq(card->p_dev->irq, if_cs_interrupt,
>               IRQF_SHARED, DRV_NAME, card);
>       if (ret) {
>               pr_err("error in request_irq\n");
> -             goto out;
> +             return;
>       }
> 
>       /*
>        * Clear any interrupt cause that happened while sending
>        * firmware/initializing card
>        */
>       if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK);
> @@ -773,18 +773,14 @@ static void if_cs_prog_firmware(struct lbs_private 
> *priv, int ret,
> 
>       /* And finally bring the card up */
>       priv->fw_ready = 1;
>       if (lbs_start_card(priv) != 0) {
>               pr_err("could not activate card\n");
>               free_irq(card->p_dev->irq, card);
>       }
> -
> -out:
> -     release_firmware(helper);
> -     release_firmware(mainfw);
> }
> 
> 
> /********************************************************************/
> /* Callback functions for libertas.ko                               */
> /********************************************************************/
> 
> diff --git a/drivers/net/wireless/libertas/if_sdio.c 
> b/drivers/net/wireless/libertas/if_sdio.c
> index 4557833..991238a 100644
> --- a/drivers/net/wireless/libertas/if_sdio.c
> +++ b/drivers/net/wireless/libertas/if_sdio.c
> @@ -704,28 +704,24 @@ static void if_sdio_do_prog_firmware(struct lbs_private 
> *priv, int ret,
>       if (ret) {
>               pr_err("failed to find firmware (%d)\n", ret);
>               return;
>       }
> 
>       ret = if_sdio_prog_helper(card, helper);
>       if (ret)
> -             goto out;
> +             return;
> 
>       lbs_deb_sdio("Helper firmware loaded\n");
> 
>       ret = if_sdio_prog_real(card, mainfw);
>       if (ret)
> -             goto out;
> +             return;
> 
>       lbs_deb_sdio("Firmware loaded\n");
>       if_sdio_finish_power_on(card);
> -
> -out:
> -     release_firmware(helper);
> -     release_firmware(mainfw);
> }
> 
> static int if_sdio_prog_firmware(struct if_sdio_card *card)
> {
>       int ret;
>       u16 scratch;
> 
> diff --git a/drivers/net/wireless/libertas/if_spi.c 
> b/drivers/net/wireless/libertas/if_spi.c
> index 4bb6574..87ff0ca 100644
> --- a/drivers/net/wireless/libertas/if_spi.c
> +++ b/drivers/net/wireless/libertas/if_spi.c
> @@ -1090,19 +1090,15 @@ static int if_spi_init_card(struct if_spi_card *card)
>       }
> 
>       err = spu_set_interrupt_mode(card, 0, 1);
>       if (err)
>               goto out;
> 
> out:
> -     release_firmware(helper);
> -     release_firmware(mainfw);
> -
>       lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
> -
>       return err;
> }
> 
> static void if_spi_resume_worker(struct work_struct *work)
> {
>       struct if_spi_card *card;
> 
> diff --git a/drivers/net/wireless/libertas/if_usb.c 
> b/drivers/net/wireless/libertas/if_usb.c
> index 2798077..dff08a2 100644
> --- a/drivers/net/wireless/libertas/if_usb.c
> +++ b/drivers/net/wireless/libertas/if_usb.c
> @@ -840,15 +840,15 @@ static void if_usb_prog_firmware(struct lbs_private 
> *priv, int ret,
>               pr_err("failed to find firmware (%d)\n", ret);
>               goto done;
>       }
> 
>       cardp->fw = fw;
>       if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) {
>               ret = -EINVAL;
> -             goto release_fw;
> +             goto done;
>       }
> 
>       /* Cancel any pending usb business */
>       usb_kill_urb(cardp->rx_urb);
>       usb_kill_urb(cardp->tx_urb);
> 
>       cardp->fwlastblksent = 0;
> @@ -857,15 +857,15 @@ static void if_usb_prog_firmware(struct lbs_private 
> *priv, int ret,
>       cardp->fwfinalblk = 0;
>       cardp->bootcmdresp = 0;
> 
> restart:
>       if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
>               lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
>               ret = -EIO;
> -             goto release_fw;
> +             goto done;
>       }
> 
>       cardp->bootcmdresp = 0;
>       do {
>               int j = 0;
>               i++;
>               if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB);
> @@ -879,22 +879,22 @@ restart:
>       if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) {
>               /* Return to normal operation */
>               ret = -EOPNOTSUPP;
>               usb_kill_urb(cardp->rx_urb);
>               usb_kill_urb(cardp->tx_urb);
>               if (if_usb_submit_rx_urb(cardp) < 0)
>                       ret = -EIO;
> -             goto release_fw;
> +             goto done;
>       } else if (cardp->bootcmdresp <= 0) {
>               if (--reset_count >= 0) {
>                       if_usb_reset_device(cardp);
>                       goto restart;
>               }
>               ret = -EIO;
> -             goto release_fw;
> +             goto done;
>       }
> 
>       i = 0;
> 
>       cardp->totalbytes = 0;
>       cardp->fwlastblksent = 0;
>       cardp->CRC_OK = 1;
> @@ -917,37 +917,34 @@ restart:
>               if (--reset_count >= 0) {
>                       if_usb_reset_device(cardp);
>                       goto restart;
>               }
> 
>               pr_info("FW download failure, time = %d ms\n", i * 100);
>               ret = -EIO;
> -             goto release_fw;
> +             goto done;
>       }
> 
>       cardp->priv->fw_ready = 1;
>       if_usb_submit_rx_urb(cardp);
> 
>       if (lbs_start_card(priv))
> -             goto release_fw;
> +             goto done;
> 
>       if_usb_setup_firmware(priv);
> 
>       /*
>        * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware.
>        */
>       priv->wol_criteria = EHS_REMOVE_WAKEUP;
>       if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL))
>               priv->ehs_remove_supported = false;
> 
> - release_fw:
> -     release_firmware(cardp->fw);
> -     cardp->fw = NULL;
> -
>  done:
> +     cardp->fw = NULL;
>       lbs_deb_leave(LBS_DEB_USB);
> }
> 
> 
> #ifdef CONFIG_PM
> static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
> {
> 
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to