On Thu, 2016-07-28 at 09:55 +0200, Daniel Wagner wrote:
> From: Daniel Wagner <daniel.wag...@bmw-carit.de>
> 
> Factor out the firmware loading synchronization code in order to
> allow
> drivers to reuse it. This also documents more clearly what is
> happening. This is especial useful the drivers which will be
> converted
> afterwards. Not everyone has to come with yet another way to handle
> it.

It's somewhat odd to me that the structure is "firmware_stat" but most
of the functions are "fw_loading_*".  That seems inconsistent for a
structure that is (currently) only used by these functions.

I would personally do either:

a) "struct fw_load_status" and "fw_load_*()"

or

b) "struct firmware_load_stat" and "firmware_load_*()"

I'd also change the function names from "loading" -> "load", similar to
how userland has read(2), not reading(2).

Dan

> We use swait instead completion. complete() would only wake up one
> waiter, so complete_all() is used. complete_all() wakes max
> MAX_UINT/2
> waiters which is plenty in all cases. Though withh swait we just wait
> until the exptected status shows up and wake any waiter.
> 
> Signed-off-by: Daniel Wagner <daniel.wag...@bmw-carit.de>
> ---
>  drivers/base/firmware_class.c | 112 +++++++++++++++++++-------------
> ----------
>  include/linux/firmware.h      |  71 ++++++++++++++++++++++++++
>  2 files changed, 122 insertions(+), 61 deletions(-)
> 
> diff --git a/drivers/base/firmware_class.c
> b/drivers/base/firmware_class.c
> index 773fc30..bf1ca70 100644
> --- a/drivers/base/firmware_class.c
> +++ b/drivers/base/firmware_class.c
> @@ -30,6 +30,7 @@
>  #include <linux/syscore_ops.h>
>  #include <linux/reboot.h>
>  #include <linux/security.h>
> +#include <linux/swait.h>
>  
>  #include <generated/utsrelease.h>
>  
> @@ -85,11 +86,36 @@ static inline bool fw_is_builtin_firmware(const
> struct firmware *fw)
>  }
>  #endif
>  
> -enum {
> -     FW_STATUS_LOADING,
> -     FW_STATUS_DONE,
> -     FW_STATUS_ABORT,
> -};
> +
> +#if defined(CONFIG_FW_LOADER) || (defined(CONFIG_FW_LOADER_MODULE)
> && defined(MODULE))
> +
> +static inline bool is_fw_sync_done(unsigned long status)
> +{
> +     return status == FW_STATUS_LOADED || status ==
> FW_STATUS_ABORT;
> +}
> +
> +int __firmware_stat_wait(struct firmware_stat *fwst,
> +                             long timeout)
> +{
> +     int err;
> +     err = swait_event_interruptible_timeout(fwst->wq,
> +                             is_fw_sync_done(READ_ONCE(fwst-
> >status)),
> +                             timeout);
> +     if (err == 0 && fwst->status == FW_STATUS_ABORT)
> +             return -ENOENT;
> +
> +     return err;
> +}
> +EXPORT_SYMBOL(__firmware_stat_wait);
> +
> +void __firmware_stat_set(struct firmware_stat *fwst, unsigned long
> status)
> +{
> +     WRITE_ONCE(fwst->status, status);
> +     swake_up(&fwst->wq);
> +}
> +EXPORT_SYMBOL(__firmware_stat_set);
> +
> +#endif
>  
>  static int loading_timeout = 60;     /* In seconds */
>  
> @@ -138,9 +164,8 @@ struct firmware_cache {
>  struct firmware_buf {
>       struct kref ref;
>       struct list_head list;
> -     struct completion completion;
> +     struct firmware_stat fwst;
>       struct firmware_cache *fwc;
> -     unsigned long status;
>       void *data;
>       size_t size;
>  #ifdef CONFIG_FW_LOADER_USER_HELPER
> @@ -194,7 +219,7 @@ static struct firmware_buf
> *__allocate_fw_buf(const char *fw_name,
>  
>       kref_init(&buf->ref);
>       buf->fwc = fwc;
> -     init_completion(&buf->completion);
> +     firmware_stat_init(&buf->fwst);
>  #ifdef CONFIG_FW_LOADER_USER_HELPER
>       INIT_LIST_HEAD(&buf->pending_list);
>  #endif
> @@ -292,15 +317,6 @@ static const char * const fw_path[] = {
>  module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
>  MODULE_PARM_DESC(path, "customized firmware image search path with a
> higher priority than default path");
>  
> -static void fw_finish_direct_load(struct device *device,
> -                               struct firmware_buf *buf)
> -{
> -     mutex_lock(&fw_lock);
> -     set_bit(FW_STATUS_DONE, &buf->status);
> -     complete_all(&buf->completion);
> -     mutex_unlock(&fw_lock);
> -}
> -
>  static int fw_get_filesystem_firmware(struct device *device,
>                                      struct firmware_buf *buf)
>  {
> @@ -339,7 +355,7 @@ static int fw_get_filesystem_firmware(struct
> device *device,
>               }
>               dev_dbg(device, "direct-loading %s\n", buf->fw_id);
>               buf->size = size;
> -             fw_finish_direct_load(device, buf);
> +             fw_loading_done(buf->fwst);
>               break;
>       }
>       __putname(path);
> @@ -457,12 +473,11 @@ static void __fw_load_abort(struct firmware_buf
> *buf)
>        * There is a small window in which user can write to
> 'loading'
>        * between loading done and disappearance of 'loading'
>        */
> -     if (test_bit(FW_STATUS_DONE, &buf->status))
> +     if (is_fw_loading_done(buf->fwst))
>               return;
>  
>       list_del_init(&buf->pending_list);
> -     set_bit(FW_STATUS_ABORT, &buf->status);
> -     complete_all(&buf->completion);
> +     fw_loading_abort(buf->fwst);
>  }
>  
>  static void fw_load_abort(struct firmware_priv *fw_priv)
> @@ -475,9 +490,6 @@ static void fw_load_abort(struct firmware_priv
> *fw_priv)
>       fw_priv->buf = NULL;
>  }
>  
> -#define is_fw_load_aborted(buf)      \
> -     test_bit(FW_STATUS_ABORT, &(buf)->status)
> -
>  static LIST_HEAD(pending_fw_head);
>  
>  /* reboot notifier for avoid deadlock with usermode_lock */
> @@ -577,7 +589,7 @@ static ssize_t firmware_loading_show(struct
> device *dev,
>  
>       mutex_lock(&fw_lock);
>       if (fw_priv->buf)
> -             loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf-
> >status);
> +             loading = is_fw_loading(fw_priv->buf->fwst);
>       mutex_unlock(&fw_lock);
>  
>       return sprintf(buf, "%d\n", loading);
> @@ -632,23 +644,20 @@ static ssize_t firmware_loading_store(struct
> device *dev,
>       switch (loading) {
>       case 1:
>               /* discarding any previous partial load */
> -             if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) {
> +             if (!is_fw_loading_done(fw_buf->fwst)) {
>                       for (i = 0; i < fw_buf->nr_pages; i++)
>                               __free_page(fw_buf->pages[i]);
>                       vfree(fw_buf->pages);
>                       fw_buf->pages = NULL;
>                       fw_buf->page_array_size = 0;
>                       fw_buf->nr_pages = 0;
> -                     set_bit(FW_STATUS_LOADING, &fw_buf->status);
> +                     fw_loading_start(fw_buf->fwst);
>               }
>               break;
>       case 0:
> -             if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
> +             if (is_fw_loading(fw_buf->fwst)) {
>                       int rc;
>  
> -                     set_bit(FW_STATUS_DONE, &fw_buf->status);
> -                     clear_bit(FW_STATUS_LOADING, &fw_buf-
> >status);
> -
>                       /*
>                        * Several loading requests may be pending
> on
>                        * one same firmware buf, so let all
> requests
> @@ -670,10 +679,11 @@ static ssize_t firmware_loading_store(struct
> device *dev,
>                        */
>                       list_del_init(&fw_buf->pending_list);
>                       if (rc) {
> -                             set_bit(FW_STATUS_ABORT, &fw_buf-
> >status);
> +                             fw_loading_abort(fw_buf->fwst);
>                               written = rc;
> +                     } else {
> +                             fw_loading_done(fw_buf->fwst);
>                       }
> -                     complete_all(&fw_buf->completion);
>                       break;
>               }
>               /* fallthrough */
> @@ -681,7 +691,7 @@ static ssize_t firmware_loading_store(struct
> device *dev,
>               dev_err(dev, "%s: unexpected value (%d)\n",
> __func__, loading);
>               /* fallthrough */
>       case -1:
> -             fw_load_abort(fw_priv);
> +             fw_loading_abort(fw_buf->fwst);
>               break;
>       }
>  out:
> @@ -702,7 +712,7 @@ static ssize_t firmware_data_read(struct file
> *filp, struct kobject *kobj,
>  
>       mutex_lock(&fw_lock);
>       buf = fw_priv->buf;
> -     if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
> +     if (!buf || is_fw_loading_done(buf->fwst)) {
>               ret_count = -ENODEV;
>               goto out;
>       }
> @@ -799,7 +809,7 @@ static ssize_t firmware_data_write(struct file
> *filp, struct kobject *kobj,
>  
>       mutex_lock(&fw_lock);
>       buf = fw_priv->buf;
> -     if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
> +     if (!buf || is_fw_loading_done(buf->fwst)) {
>               retval = -ENODEV;
>               goto out;
>       }
> @@ -917,8 +927,7 @@ static int _request_firmware_load(struct
> firmware_priv *fw_priv,
>               timeout = MAX_JIFFY_OFFSET;
>       }
>  
> -     retval = wait_for_completion_interruptible_timeout(&buf-
> >completion,
> -                     timeout);
> +     retval = fw_loading_wait_timeout(buf->fwst, timeout);
>       if (retval == -ERESTARTSYS || !retval) {
>               mutex_lock(&fw_lock);
>               fw_load_abort(fw_priv);
> @@ -927,7 +936,7 @@ static int _request_firmware_load(struct
> firmware_priv *fw_priv,
>               retval = 0;
>       }
>  
> -     if (is_fw_load_aborted(buf))
> +     if (is_fw_loading_aborted(buf->fwst))
>               retval = -EAGAIN;
>       else if (!buf->data)
>               retval = -ENOMEM;
> @@ -986,26 +995,6 @@ static inline void
> kill_requests_without_uevent(void) { }
>  
>  #endif /* CONFIG_FW_LOADER_USER_HELPER */
>  
> -
> -/* wait until the shared firmware_buf becomes ready (or error) */
> -static int sync_cached_firmware_buf(struct firmware_buf *buf)
> -{
> -     int ret = 0;
> -
> -     mutex_lock(&fw_lock);
> -     while (!test_bit(FW_STATUS_DONE, &buf->status)) {
> -             if (is_fw_load_aborted(buf)) {
> -                     ret = -ENOENT;
> -                     break;
> -             }
> -             mutex_unlock(&fw_lock);
> -             ret = wait_for_completion_interruptible(&buf-
> >completion);
> -             mutex_lock(&fw_lock);
> -     }
> -     mutex_unlock(&fw_lock);
> -     return ret;
> -}
> -
>  /* prepare firmware and firmware_buf structs;
>   * return 0 if a firmware is already assigned, 1 if need to load
> one,
>   * or a negative error code
> @@ -1039,7 +1028,8 @@ _request_firmware_prepare(struct firmware
> **firmware_p, const char *name,
>       firmware->priv = buf;
>  
>       if (ret > 0) {
> -             ret = sync_cached_firmware_buf(buf);
> +             ret = fw_loading_wait_timeout(buf->fwst,
> +                                           firmware_loading_timeo
> ut());
>               if (!ret) {
>                       fw_set_page_data(buf, firmware);
>                       return 0; /* assigned */
> @@ -1057,7 +1047,7 @@ static int assign_firmware_buf(struct firmware
> *fw, struct device *device,
>       struct firmware_buf *buf = fw->priv;
>  
>       mutex_lock(&fw_lock);
> -     if (!buf->size || is_fw_load_aborted(buf)) {
> +     if (!buf->size || is_fw_loading_aborted(buf->fwst)) {
>               mutex_unlock(&fw_lock);
>               return -ENOENT;
>       }
> diff --git a/include/linux/firmware.h b/include/linux/firmware.h
> index 5c41c5e..f584160 100644
> --- a/include/linux/firmware.h
> +++ b/include/linux/firmware.h
> @@ -4,10 +4,17 @@
>  #include <linux/types.h>
>  #include <linux/compiler.h>
>  #include <linux/gfp.h>
> +#include <linux/swait.h>
>  
>  #define FW_ACTION_NOHOTPLUG 0
>  #define FW_ACTION_HOTPLUG 1
>  
> +enum {
> +     FW_STATUS_LOADING,
> +     FW_STATUS_LOADED,
> +     FW_STATUS_ABORT,
> +};
> +
>  struct firmware {
>       size_t size;
>       const u8 *data;
> @@ -17,6 +24,11 @@ struct firmware {
>       void *priv;
>  };
>  
> +struct firmware_stat {
> +     unsigned long status;
> +     struct swait_queue_head wq;
> +};
> +
>  struct module;
>  struct device;
>  
> @@ -49,6 +61,36 @@ int request_firmware_direct(const struct firmware
> **fw, const char *name,
>                           struct device *device);
>  
>  void release_firmware(const struct firmware *fw);
> +
> +static inline void firmware_stat_init(struct firmware_stat *fwst)
> +{
> +     init_swait_queue_head(&fwst->wq);
> +}
> +
> +static inline unsigned long __firmware_stat_get(struct firmware_stat
> *fwst)
> +{
> +     return READ_ONCE(fwst->status);
> +}
> +void __firmware_stat_set(struct firmware_stat *fwst, unsigned long
> status);
> +int __firmware_stat_wait(struct firmware_stat *fwst, long timeout);
> +
> +#define fw_loading_start(fwst)                                       
> \
> +     __firmware_stat_set(&fwst, FW_STATUS_LOADING)
> +#define fw_loading_done(fwst)                                        
> \
> +     __firmware_stat_set(&fwst, FW_STATUS_LOADED)
> +#define fw_loading_abort(fwst)                                       
> \
> +     __firmware_stat_set(&fwst, FW_STATUS_ABORT)
> +#define fw_loading_wait(fwst)                                        
> \
> +     __firmware_stat_wait(&fwst, 0)
> +#define fw_loading_wait_timeout(fwst, timeout)                       
> \
> +     __firmware_stat_wait(&fwst, timeout)
> +#define is_fw_loading(fwst)                                  \
> +     (__firmware_stat_get(&fwst) == FW_STATUS_LOADING)
> +#define is_fw_loading_done(fwst)                             \
> +     (__firmware_stat_get(&fwst) == FW_STATUS_LOADED)
> +#define is_fw_loading_aborted(fwst)                          \
> +     (__firmware_stat_get(&fwst) == FW_STATUS_ABORT)
> +
>  #else
>  static inline int request_firmware(const struct firmware **fw,
>                                  const char *name,
> @@ -75,5 +117,34 @@ static inline int request_firmware_direct(const
> struct firmware **fw,
>       return -EINVAL;
>  }
>  
> +static inline void firmware_stat_init(struct firmware_stat *fwst)
> +{
> +}
> +
> +static inline unsigned long __firmware_stat_get(struct firmware_stat
> *fwst)
> +{
> +     return -EINVAL;
> +}
> +
> +static inline void __firmware_stat_set(struct firmware_stat *fwst,
> +                                    unsigned long status)
> +{
> +}
> +
> +static inline int __firmware_stat_wait(struct firmware_stat *fwst,
> +                                    long timeout)
> +{
> +     return -EINVAL;
> +}
> +
> +#define fw_loading_start(fwst)
> +#define fw_loading_done(fwst)
> +#define fw_loading_abort(fwst)
> +#define fw_loading_wait(fwst)
> +#define fw_loading_wait_timeout(fwst, timeout)
> +#define is_fw_loading(fwst)          0
> +#define is_fw_loading_done(fwst)     0
> +#define is_fw_loading_aborted(fwst)  0
> +
>  #endif
>  #endif
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to