mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Borislav Petkov <bp@amd64.org>
To: Ming Lei <ming.lei@canonical.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	"Rafael J. Wysocki" <rjw@sisk.pl>,
	linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH 06/13] driver core: firmware loader: always let firmware_buf own the pages buffer
Date: Wed, 25 Jul 2012 16:37:50 +0200	[thread overview]
Message-ID: <20120725143750.GE11152@aftab.osrc.amd.com> (raw)
In-Reply-To: <1343149213-10160-7-git-send-email-ming.lei@canonical.com>

On Wed, Jul 25, 2012 at 01:00:06AM +0800, Ming Lei wrote:
> This patch always let firmware_buf own the pages buffer allocated
> inside firmware_data_write, also add all instances of firmware_buf
> into the firmware cache global list. Also introduce one private field
> in 'struct firmware', so release_firmware will see the instance of
> firmware_buf associated with one firmware instance, then just 'free'
> the instance of firmware_buf.
> 
> The firmware_buf instance represents one pages buffer for one
> firmware image, so lots of firmware loading requests can share
> the same firmware_buf instance if they request the same firmware
> image file.
> 
> This patch will make introducing cache_firmware/uncache_firmware
> easily.
> 
> In fact, the patch improves request_formware/release_firmware:
> 
>         - only request userspace to write firmware image once if
> 	several devices share one same firmware image and its drivers
> 	call request_firmware concurrently.
> 
> Signed-off-by: Ming Lei <ming.lei@canonical.com>
> ---
>  drivers/base/firmware_class.c |  222 ++++++++++++++++++++++++++++-------------
>  include/linux/firmware.h      |    3 +
>  2 files changed, 157 insertions(+), 68 deletions(-)
> 
> diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
> index 986d9df..225898e 100644
> --- a/drivers/base/firmware_class.c
> +++ b/drivers/base/firmware_class.c
> @@ -21,6 +21,7 @@
>  #include <linux/firmware.h>
>  #include <linux/slab.h>
>  #include <linux/sched.h>
> +#include <linux/list.h>
>  
>  MODULE_AUTHOR("Manuel Estrada Sainz");
>  MODULE_DESCRIPTION("Multi purpose firmware loading support");
> @@ -85,13 +86,18 @@ static inline long firmware_loading_timeout(void)
>  	return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT;
>  }
>  
> -/* fw_lock could be moved to 'struct firmware_priv' but since it is just
> - * guarding for corner cases a global lock should be OK */
> -static DEFINE_MUTEX(fw_lock);
> +struct firmware_cache {
> +
> +	/* firmware_buf instance will be added into the below list */
> +	spinlock_t lock;
> +	struct list_head head;
> +};
>  
>  struct firmware_buf {
> +	struct kref ref;
> +	struct list_head list;
>  	struct completion completion;
> -	struct firmware *fw;
> +	struct firmware_cache *fwc;
>  	unsigned long status;
>  	void *data;
>  	size_t size;
> @@ -106,8 +112,93 @@ struct firmware_priv {
>  	bool nowait;
>  	struct device dev;
>  	struct firmware_buf *buf;
> +	struct firmware *fw;
>  };
>  
> +#define to_fwbuf(d) container_of(d, struct firmware_buf, ref)
> +
> +/* fw_lock could be moved to 'struct firmware_priv' but since it is just
> + * guarding for corner cases a global lock should be OK */
> +static DEFINE_MUTEX(fw_lock);
> +
> +static struct firmware_cache fw_cache;
> +
> +static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
> +		struct firmware_cache *fwc)

Please indent like so:

static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
					      struct firmware_cache *fwc)

for better readability.

> +{
> +	struct firmware_buf *buf;
> +
> +	buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1 , GFP_ATOMIC);
> +
> +	if (!buf)
> +		return buf;
> +
> +	kref_init(&buf->ref);
> +	strcpy(buf->fw_id, fw_name);
> +	buf->fwc = fwc;
> +	init_completion(&buf->completion);
> +
> +	pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf);
> +
> +	return buf;
> +}
> +
> +static int fw_lookup_and_alloate_buf(const char *fw_name,
> +		struct firmware_cache *fwc,
> +		struct firmware_buf **buf)

Ditto.

> +{
> +	struct firmware_buf *tmp;
> +
> +	spin_lock(&fwc->lock);
> +	list_for_each_entry(tmp, &fwc->head, list)
> +		if (!strcmp(tmp->fw_id, fw_name)) {
> +			kref_get(&tmp->ref);
> +			spin_unlock(&fwc->lock);
> +			*buf = tmp;
> +			return 1;
> +		}
> +
> +	tmp = __allocate_fw_buf(fw_name, fwc);
> +	if (tmp)
> +		list_add(&tmp->list, &fwc->head);
> +	spin_unlock(&fwc->lock);
> +
> +	*buf = tmp;
> +
> +	return tmp ? 0 : -1;
> +}
> +
> +static void __fw_free_buf(struct kref *ref)
> +{
> +	struct firmware_buf *buf = to_fwbuf(ref);
> +	struct firmware_cache *fwc = buf->fwc;
> +	int i;
> +
> +	pr_debug("%s: fw-%s buf=%p nr_pages=%d\n",
> +			__func__, buf->fw_id, buf, buf->nr_pages);

Arg alignment:

	pr_debug("%s: fw-%s buf=%p nr_pages=%d\n",
		 __func__, buf->fw_id, buf, buf->nr_pages);

> +	spin_lock(&fwc->lock);
> +	list_del(&buf->list);
> +	spin_unlock(&fwc->lock);
> +
> +	vunmap(buf->data);
> +	for (i = 0; i < buf->nr_pages; i++)
> +		__free_page(buf->pages[i]);
> +	kfree(buf->pages);
> +	kfree(buf);
> +}
> +
> +static void fw_free_buf(struct firmware_buf *buf)
> +{
> +	kref_put(&buf->ref, __fw_free_buf);
> +}
> +
> +static void fw_cache_init(void)
> +{
> +	spin_lock_init(&fw_cache.lock);
> +	INIT_LIST_HEAD(&fw_cache.head);
> +}
> +
>  static struct firmware_priv *to_firmware_priv(struct device *dev)
>  {
>  	return container_of(dev, struct firmware_priv, dev);
> @@ -118,7 +209,7 @@ static void fw_load_abort(struct firmware_priv *fw_priv)
>  	struct firmware_buf *buf = fw_priv->buf;
>  
>  	set_bit(FW_STATUS_ABORT, &buf->status);
> -	complete(&buf->completion);
> +	complete_all(&buf->completion);
>  }
>  
>  static ssize_t firmware_timeout_show(struct class *class,
> @@ -158,23 +249,10 @@ static struct class_attribute firmware_class_attrs[] = {
>  	__ATTR_NULL
>  };
>  
> -static void fw_free_buf(struct firmware_buf *buf)
> -{
> -	int i;
> -
> -	if (!buf)
> -		return;
> -
> -	for (i = 0; i < buf->nr_pages; i++)
> -		__free_page(buf->pages[i]);
> -	kfree(buf->pages);
> -}
> -
>  static void fw_dev_release(struct device *dev)
>  {
>  	struct firmware_priv *fw_priv = to_firmware_priv(dev);
>  
> -	kfree(fw_priv->buf);
>  	kfree(fw_priv);
>  
>  	module_put(THIS_MODULE);
> @@ -213,13 +291,8 @@ static ssize_t firmware_loading_show(struct device *dev,
>  /* firmware holds the ownership of pages */
>  static void firmware_free_data(const struct firmware *fw)
>  {
> -	int i;
> -	vunmap(fw->data);
> -	if (fw->pages) {
> -		for (i = 0; i < PFN_UP(fw->size); i++)
> -			__free_page(fw->pages[i]);
> -		kfree(fw->pages);
> -	}
> +	WARN_ON(!fw->priv);
> +	fw_free_buf(fw->priv);

Why the WARN_ON?

Maybe rather:

	if (fw->priv)
		fw_free_buf(fw->priv);

?

>  /* Some architectures don't have PAGE_KERNEL_RO */
> @@ -270,7 +343,7 @@ static ssize_t firmware_loading_store(struct device *dev,
>  		if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
>  			set_bit(FW_STATUS_DONE, &fw_buf->status);
>  			clear_bit(FW_STATUS_LOADING, &fw_buf->status);
> -			complete(&fw_buf->completion);
> +			complete_all(&fw_buf->completion);
>  			break;
>  		}
>  		/* fallthrough */
> @@ -446,10 +519,10 @@ static void firmware_class_timeout(u_long data)
>  
>  static struct firmware_priv *
>  fw_create_instance(struct firmware *firmware, const char *fw_name,
> -		   struct device *device, bool uevent, bool nowait)
> +			struct device *device, bool uevent, bool nowait)
> +

Wrong alignment and superfluous newline.

>  {
>  	struct firmware_priv *fw_priv;
> -	struct firmware_buf *buf;
>  	struct device *f_dev;
>  
>  	fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL);
> @@ -459,21 +532,10 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
>  		goto exit;
>  	}
>  
> -	buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1, GFP_KERNEL);
> -	if (!buf) {
> -		dev_err(device, "%s: kmalloc failed\n", __func__);
> -		kfree(fw_priv);
> -		fw_priv = ERR_PTR(-ENOMEM);
> -		goto exit;
> -	}
> -
> -	buf->fw = firmware;
> -	fw_priv->buf = buf;
>  	fw_priv->nowait = nowait;
> +	fw_priv->fw = firmware;
>  	setup_timer(&fw_priv->timeout,
>  		    firmware_class_timeout, (u_long) fw_priv);
> -	strcpy(buf->fw_id, fw_name);
> -	init_completion(&buf->completion);
>  
>  	f_dev = &fw_priv->dev;
>  
> @@ -485,12 +547,31 @@ exit:
>  	return fw_priv;
>  }
>  
> +/* store the pages buffer info firmware from buf */
> +static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw)
> +{
> +	buf->data = vmap(buf->pages, buf->nr_pages,
> +				0, PAGE_KERNEL_RO);
> +	fw->data = buf->data;
> +	fw->pages = buf->pages;
> +	fw->size = buf->size;
> +	fw->priv = buf;
> +}
> +
> +static void _request_firmware_cleanup(const struct firmware **firmware_p)
> +{
> +	release_firmware(*firmware_p);
> +	*firmware_p = NULL;
> +}
> +
>  static struct firmware_priv *
>  _request_firmware_prepare(const struct firmware **firmware_p, const char *name,
>  			  struct device *device, bool uevent, bool nowait)
>  {
>  	struct firmware *firmware;
> -	struct firmware_priv *fw_priv;
> +	struct firmware_priv *fw_priv = NULL;
> +	struct firmware_buf *buf;
> +	int ret;
>  
>  	if (!firmware_p)
>  		return ERR_PTR(-EINVAL);
> @@ -507,32 +588,40 @@ _request_firmware_prepare(const struct firmware **firmware_p, const char *name,
>  		return NULL;
>  	}
>  
> -	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
> -	if (IS_ERR(fw_priv)) {
> -		release_firmware(firmware);
> -		*firmware_p = NULL;
> -	}
> -	return fw_priv;
> -}
> +	ret = fw_lookup_and_alloate_buf(name, &fw_cache, &buf);
>  
> -static void _request_firmware_cleanup(const struct firmware **firmware_p)
> -{
> -	release_firmware(*firmware_p);
> -	*firmware_p = NULL;
> -}

Superfluous newline here.

> +	if (!ret)
> +		fw_priv = fw_create_instance(firmware, name, device,
> +				uevent, nowait);
>  
> -/* transfer the ownership of pages to firmware */
> -static void fw_set_page_data(struct firmware_buf *buf)
> -{
> -	struct firmware *fw = buf->fw;
> +	if (IS_ERR(fw_priv) || ret == -1) {
> +		kfree(firmware);
> +		*firmware_p = NULL;
> +		return ERR_PTR(-ENOMEM);
> +	} else if (fw_priv) {
> +		fw_priv->buf = buf;
> +		return fw_priv;
> +	}
>  
> -	buf->data = vmap(buf->pages, buf->nr_pages,
> -				0, PAGE_KERNEL_RO);
> -	fw->data = buf->data;
> -	fw->pages = buf->pages;
> -	fw->size = buf->size;
> +	/* share the cached buf, which is inprogessing or completed */

					in progress

> +check_status:

One space in front of the label name.

> +	mutex_lock(&fw_lock);
> +	if (test_bit(FW_STATUS_ABORT, &buf->status)) {
> +		fw_priv = ERR_PTR(-ENOENT);
> +		_request_firmware_cleanup(firmware_p);
> +		goto exit;
> +	} else if (test_bit(FW_STATUS_DONE, &buf->status)) {
> +		fw_priv = NULL;
> +		fw_set_page_data(buf, firmware);
> +		goto exit;
> +	}
> +	mutex_unlock(&fw_lock);
> +	wait_for_completion(&buf->completion);
> +	goto check_status;

Better yet, this is a label with a reverse-jump to it. This can probably
be simplified with a while loop:

	while (true) {
		if (test_bit(... ) {
			...;
			break;
		} else if (test_bit(... ) {
			...;
			break;
		}
		mutex_unlock(...);
		wait_for_completion(...);
	}

and this way you don't need the exit label either.

>  
> -	WARN_ON(PFN_UP(fw->size) != buf->nr_pages);
> +exit:
> +	mutex_unlock(&fw_lock);
> +	return fw_priv;
>  }

[ … ]

Thanks.

-- 
Regards/Gruss,
Boris.

Advanced Micro Devices GmbH
Einsteinring 24, 85609 Dornach
GM: Alberto Bozzo
Reg: Dornach, Landkreis Muenchen
HRB Nr. 43632 WEEE Registernr: 129 19551

  parent reply	other threads:[~2012-07-25 14:38 UTC|newest]

Thread overview: 62+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-07-24 17:00 [RFC PATCH 00/13] firmware loader: introduce cache/uncache firmware Ming Lei
2012-07-24 17:00 ` [RFC PATCH 01/13] driver core: firmware loader: simplify pages ownership transfer Ming Lei
2012-07-24 18:10   ` Borislav Petkov
2012-07-25  2:49     ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 02/13] driver core: firmware loader: fix races during loading firmware Ming Lei
2012-07-24 17:00 ` [RFC PATCH 03/13] driver core: firmware loader: remove unnecessary wmb() Ming Lei
2012-07-24 17:00 ` [RFC PATCH 04/13] driver core: firmware loader: fix creation failure of fw loader device Ming Lei
2012-07-24 17:00 ` [RFC PATCH 05/13] driver core: firmware loader: introduce firmware_buf Ming Lei
2012-07-25 13:59   ` Borislav Petkov
2012-07-26  2:51     ` Ming Lei
2012-07-26 10:08       ` Borislav Petkov
2012-07-24 17:00 ` [RFC PATCH 06/13] driver core: firmware loader: always let firmware_buf own the pages buffer Ming Lei
2012-07-25  7:55   ` Stephen Boyd
2012-07-25 14:37   ` Borislav Petkov [this message]
2012-08-03  8:34     ` Ming Lei
2012-07-25 16:02   ` Borislav Petkov
2012-07-25 16:13   ` Borislav Petkov
2012-07-24 17:00 ` [RFC PATCH 07/13] driver core: firmware loader: introduce cache_firmware and uncache_firmware Ming Lei
2012-07-25  7:54   ` Stephen Boyd
2012-07-26  2:34     ` Ming Lei
2012-07-25 15:52   ` Borislav Petkov
2012-07-26  2:40     ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 08/13] driver core: firmware loader: fix device lifetime Ming Lei
2012-07-25 16:04   ` Borislav Petkov
2012-07-26  2:59     ` Ming Lei
2012-07-26 12:20       ` Borislav Petkov
2012-07-26 15:44         ` Ming Lei
2012-07-26 17:46           ` Borislav Petkov
2012-07-27  1:30             ` Ming Lei
2012-07-27 10:32               ` Borislav Petkov
2012-07-28 14:04                 ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 09/13] driver core: firmware loader: store firmware name into devres list Ming Lei
2012-07-25 16:15   ` Borislav Petkov
2012-07-26 15:15     ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 10/13] driver core: devres: introduce devres_for_each_res Ming Lei
2012-07-25 16:25   ` Borislav Petkov
2012-07-26 16:51     ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 11/13] driver core: firmware: introduce devices_cache/uncache_firmwares Ming Lei
2012-07-25 16:52   ` Borislav Petkov
2012-07-26 15:36     ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 12/13] driver core: firmware loader: use small timeout for cache device firmware Ming Lei
2012-07-26 12:36   ` Borislav Petkov
2012-07-26 15:48     ` Ming Lei
2012-07-26 17:54       ` Borislav Petkov
2012-07-27  1:54         ` Ming Lei
2012-07-27 10:35           ` Borislav Petkov
2012-07-28 13:58             ` Ming Lei
2012-07-24 17:00 ` [RFC PATCH 13/13] driver core: firmware loader: cache devices firmware during suspend/resume cycle Ming Lei
2012-07-26 12:43   ` Borislav Petkov
2012-07-26 15:49     ` Ming Lei
2012-07-24 17:08 ` [RFC PATCH 00/13] firmware loader: introduce cache/uncache firmware Ming Lei
2012-07-24 17:16 ` Linus Torvalds
2012-07-24 17:47   ` Ming Lei
2012-07-24 17:53     ` Linus Torvalds
2012-07-24 17:54       ` Linus Torvalds
2012-07-25 12:35       ` Ming Lei
2012-07-25 12:43         ` Oliver Neukum
2012-07-25 12:50           ` Ming Lei
2012-07-25 12:59             ` Ming Lei
2012-07-25 17:23         ` Linus Torvalds
2012-07-25 19:02           ` Rafael J. Wysocki
2012-07-26  2:29           ` Ming Lei

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120725143750.GE11152@aftab.osrc.amd.com \
    --to=bp@amd64.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ming.lei@canonical.com \
    --cc=rjw@sisk.pl \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®