diff options
| author | Ming Lei <ming.lei@canonical.com> | 2012-08-04 00:01:21 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-08-16 16:22:45 -0400 |
| commit | 1f2b79599ee8f5fc82cc73c6c090eb6cdff881d6 (patch) | |
| tree | 6b3fed8d355f708cc6a001e772f07783a300d2cd /drivers/base | |
| parent | 1244691c73b250be522e77ac1a00ad53b601b4c4 (diff) | |
firmware loader: always let firmware_buf own the pages buffer
This patch always let firmware_buf own the pages buffer allocated
inside firmware_data_write, and 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 the current 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 implementation of the following cache_firmware/
uncache_firmware very easy and simple.
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>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base')
| -rw-r--r-- | drivers/base/firmware_class.c | 240 |
1 files changed, 171 insertions, 69 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 5f2076e5d5b1..848ad97e8d79 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include <linux/firmware.h> | 21 | #include <linux/firmware.h> |
| 22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
| 23 | #include <linux/sched.h> | 23 | #include <linux/sched.h> |
| 24 | #include <linux/list.h> | ||
| 24 | 25 | ||
| 25 | MODULE_AUTHOR("Manuel Estrada Sainz"); | 26 | MODULE_AUTHOR("Manuel Estrada Sainz"); |
| 26 | MODULE_DESCRIPTION("Multi purpose firmware loading support"); | 27 | MODULE_DESCRIPTION("Multi purpose firmware loading support"); |
| @@ -85,13 +86,17 @@ static inline long firmware_loading_timeout(void) | |||
| 85 | return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT; | 86 | return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT; |
| 86 | } | 87 | } |
| 87 | 88 | ||
| 88 | /* fw_lock could be moved to 'struct firmware_priv' but since it is just | 89 | struct firmware_cache { |
| 89 | * guarding for corner cases a global lock should be OK */ | 90 | /* firmware_buf instance will be added into the below list */ |
| 90 | static DEFINE_MUTEX(fw_lock); | 91 | spinlock_t lock; |
| 92 | struct list_head head; | ||
| 93 | }; | ||
| 91 | 94 | ||
| 92 | struct firmware_buf { | 95 | struct firmware_buf { |
| 96 | struct kref ref; | ||
| 97 | struct list_head list; | ||
| 93 | struct completion completion; | 98 | struct completion completion; |
| 94 | struct firmware *fw; | 99 | struct firmware_cache *fwc; |
| 95 | unsigned long status; | 100 | unsigned long status; |
| 96 | void *data; | 101 | void *data; |
| 97 | size_t size; | 102 | size_t size; |
| @@ -106,8 +111,94 @@ struct firmware_priv { | |||
| 106 | bool nowait; | 111 | bool nowait; |
| 107 | struct device dev; | 112 | struct device dev; |
| 108 | struct firmware_buf *buf; | 113 | struct firmware_buf *buf; |
| 114 | struct firmware *fw; | ||
| 109 | }; | 115 | }; |
| 110 | 116 | ||
| 117 | #define to_fwbuf(d) container_of(d, struct firmware_buf, ref) | ||
| 118 | |||
| 119 | /* fw_lock could be moved to 'struct firmware_priv' but since it is just | ||
| 120 | * guarding for corner cases a global lock should be OK */ | ||
| 121 | static DEFINE_MUTEX(fw_lock); | ||
| 122 | |||
| 123 | static struct firmware_cache fw_cache; | ||
| 124 | |||
| 125 | static struct firmware_buf *__allocate_fw_buf(const char *fw_name, | ||
| 126 | struct firmware_cache *fwc) | ||
| 127 | { | ||
| 128 | struct firmware_buf *buf; | ||
| 129 | |||
| 130 | buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1 , GFP_ATOMIC); | ||
| 131 | |||
| 132 | if (!buf) | ||
| 133 | return buf; | ||
| 134 | |||
| 135 | kref_init(&buf->ref); | ||
| 136 | strcpy(buf->fw_id, fw_name); | ||
| 137 | buf->fwc = fwc; | ||
| 138 | init_completion(&buf->completion); | ||
| 139 | |||
| 140 | pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); | ||
| 141 | |||
| 142 | return buf; | ||
| 143 | } | ||
| 144 | |||
| 145 | static int fw_lookup_and_allocate_buf(const char *fw_name, | ||
| 146 | struct firmware_cache *fwc, | ||
| 147 | struct firmware_buf **buf) | ||
| 148 | { | ||
| 149 | struct firmware_buf *tmp; | ||
| 150 | |||
| 151 | spin_lock(&fwc->lock); | ||
| 152 | list_for_each_entry(tmp, &fwc->head, list) | ||
| 153 | if (!strcmp(tmp->fw_id, fw_name)) { | ||
| 154 | kref_get(&tmp->ref); | ||
| 155 | spin_unlock(&fwc->lock); | ||
| 156 | *buf = tmp; | ||
| 157 | return 1; | ||
| 158 | } | ||
| 159 | |||
| 160 | tmp = __allocate_fw_buf(fw_name, fwc); | ||
| 161 | if (tmp) | ||
| 162 | list_add(&tmp->list, &fwc->head); | ||
| 163 | spin_unlock(&fwc->lock); | ||
| 164 | |||
| 165 | *buf = tmp; | ||
| 166 | |||
| 167 | return tmp ? 0 : -ENOMEM; | ||
| 168 | } | ||
| 169 | |||
| 170 | static void __fw_free_buf(struct kref *ref) | ||
| 171 | { | ||
| 172 | struct firmware_buf *buf = to_fwbuf(ref); | ||
| 173 | struct firmware_cache *fwc = buf->fwc; | ||
| 174 | int i; | ||
| 175 | |||
| 176 | pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", | ||
| 177 | __func__, buf->fw_id, buf, buf->data, | ||
| 178 | (unsigned int)buf->size); | ||
| 179 | |||
| 180 | spin_lock(&fwc->lock); | ||
| 181 | list_del(&buf->list); | ||
| 182 | spin_unlock(&fwc->lock); | ||
| 183 | |||
| 184 | vunmap(buf->data); | ||
| 185 | for (i = 0; i < buf->nr_pages; i++) | ||
| 186 | __free_page(buf->pages[i]); | ||
| 187 | kfree(buf->pages); | ||
| 188 | kfree(buf); | ||
| 189 | } | ||
| 190 | |||
| 191 | static void fw_free_buf(struct firmware_buf *buf) | ||
| 192 | { | ||
| 193 | kref_put(&buf->ref, __fw_free_buf); | ||
| 194 | } | ||
| 195 | |||
| 196 | static void __init fw_cache_init(void) | ||
| 197 | { | ||
| 198 | spin_lock_init(&fw_cache.lock); | ||
| 199 | INIT_LIST_HEAD(&fw_cache.head); | ||
| 200 | } | ||
| 201 | |||
| 111 | static struct firmware_priv *to_firmware_priv(struct device *dev) | 202 | static struct firmware_priv *to_firmware_priv(struct device *dev) |
| 112 | { | 203 | { |
| 113 | return container_of(dev, struct firmware_priv, dev); | 204 | return container_of(dev, struct firmware_priv, dev); |
| @@ -118,7 +209,7 @@ static void fw_load_abort(struct firmware_priv *fw_priv) | |||
| 118 | struct firmware_buf *buf = fw_priv->buf; | 209 | struct firmware_buf *buf = fw_priv->buf; |
| 119 | 210 | ||
| 120 | set_bit(FW_STATUS_ABORT, &buf->status); | 211 | set_bit(FW_STATUS_ABORT, &buf->status); |
| 121 | complete(&buf->completion); | 212 | complete_all(&buf->completion); |
| 122 | } | 213 | } |
| 123 | 214 | ||
| 124 | static ssize_t firmware_timeout_show(struct class *class, | 215 | static ssize_t firmware_timeout_show(struct class *class, |
| @@ -158,18 +249,6 @@ static struct class_attribute firmware_class_attrs[] = { | |||
| 158 | __ATTR_NULL | 249 | __ATTR_NULL |
| 159 | }; | 250 | }; |
| 160 | 251 | ||
| 161 | static void fw_free_buf(struct firmware_buf *buf) | ||
| 162 | { | ||
| 163 | int i; | ||
| 164 | |||
| 165 | if (!buf) | ||
| 166 | return; | ||
| 167 | |||
| 168 | for (i = 0; i < buf->nr_pages; i++) | ||
| 169 | __free_page(buf->pages[i]); | ||
| 170 | kfree(buf->pages); | ||
| 171 | } | ||
| 172 | |||
| 173 | static void fw_dev_release(struct device *dev) | 252 | static void fw_dev_release(struct device *dev) |
| 174 | { | 253 | { |
| 175 | struct firmware_priv *fw_priv = to_firmware_priv(dev); | 254 | struct firmware_priv *fw_priv = to_firmware_priv(dev); |
| @@ -212,13 +291,8 @@ static ssize_t firmware_loading_show(struct device *dev, | |||
| 212 | /* firmware holds the ownership of pages */ | 291 | /* firmware holds the ownership of pages */ |
| 213 | static void firmware_free_data(const struct firmware *fw) | 292 | static void firmware_free_data(const struct firmware *fw) |
| 214 | { | 293 | { |
| 215 | int i; | 294 | WARN_ON(!fw->priv); |
| 216 | vunmap(fw->data); | 295 | fw_free_buf(fw->priv); |
| 217 | if (fw->pages) { | ||
| 218 | for (i = 0; i < PFN_UP(fw->size); i++) | ||
| 219 | __free_page(fw->pages[i]); | ||
| 220 | kfree(fw->pages); | ||
| 221 | } | ||
| 222 | } | 296 | } |
| 223 | 297 | ||
| 224 | /* Some architectures don't have PAGE_KERNEL_RO */ | 298 | /* Some architectures don't have PAGE_KERNEL_RO */ |
| @@ -269,7 +343,7 @@ static ssize_t firmware_loading_store(struct device *dev, | |||
| 269 | if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) { | 343 | if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) { |
| 270 | set_bit(FW_STATUS_DONE, &fw_buf->status); | 344 | set_bit(FW_STATUS_DONE, &fw_buf->status); |
| 271 | clear_bit(FW_STATUS_LOADING, &fw_buf->status); | 345 | clear_bit(FW_STATUS_LOADING, &fw_buf->status); |
| 272 | complete(&fw_buf->completion); | 346 | complete_all(&fw_buf->completion); |
| 273 | break; | 347 | break; |
| 274 | } | 348 | } |
| 275 | /* fallthrough */ | 349 | /* fallthrough */ |
| @@ -448,7 +522,6 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, | |||
| 448 | struct device *device, bool uevent, bool nowait) | 522 | struct device *device, bool uevent, bool nowait) |
| 449 | { | 523 | { |
| 450 | struct firmware_priv *fw_priv; | 524 | struct firmware_priv *fw_priv; |
| 451 | struct firmware_buf *buf; | ||
| 452 | struct device *f_dev; | 525 | struct device *f_dev; |
| 453 | 526 | ||
| 454 | fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL); | 527 | fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL); |
| @@ -458,21 +531,10 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, | |||
| 458 | goto exit; | 531 | goto exit; |
| 459 | } | 532 | } |
| 460 | 533 | ||
| 461 | buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1, GFP_KERNEL); | ||
| 462 | if (!buf) { | ||
| 463 | dev_err(device, "%s: kmalloc failed\n", __func__); | ||
| 464 | kfree(fw_priv); | ||
| 465 | fw_priv = ERR_PTR(-ENOMEM); | ||
| 466 | goto exit; | ||
| 467 | } | ||
| 468 | |||
| 469 | buf->fw = firmware; | ||
| 470 | fw_priv->buf = buf; | ||
| 471 | fw_priv->nowait = nowait; | 534 | fw_priv->nowait = nowait; |
| 535 | fw_priv->fw = firmware; | ||
| 472 | setup_timer(&fw_priv->timeout, | 536 | setup_timer(&fw_priv->timeout, |
| 473 | firmware_class_timeout, (u_long) fw_priv); | 537 | firmware_class_timeout, (u_long) fw_priv); |
| 474 | strcpy(buf->fw_id, fw_name); | ||
| 475 | init_completion(&buf->completion); | ||
| 476 | 538 | ||
| 477 | f_dev = &fw_priv->dev; | 539 | f_dev = &fw_priv->dev; |
| 478 | 540 | ||
| @@ -484,12 +546,42 @@ exit: | |||
| 484 | return fw_priv; | 546 | return fw_priv; |
| 485 | } | 547 | } |
| 486 | 548 | ||
| 549 | /* one pages buffer is mapped/unmapped only once */ | ||
| 550 | static int fw_map_pages_buf(struct firmware_buf *buf) | ||
| 551 | { | ||
| 552 | buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); | ||
| 553 | if (!buf->data) | ||
| 554 | return -ENOMEM; | ||
| 555 | return 0; | ||
| 556 | } | ||
| 557 | |||
| 558 | /* store the pages buffer info firmware from buf */ | ||
| 559 | static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) | ||
| 560 | { | ||
| 561 | fw->priv = buf; | ||
| 562 | fw->pages = buf->pages; | ||
| 563 | fw->size = buf->size; | ||
| 564 | fw->data = buf->data; | ||
| 565 | |||
| 566 | pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", | ||
| 567 | __func__, buf->fw_id, buf, buf->data, | ||
| 568 | (unsigned int)buf->size); | ||
| 569 | } | ||
| 570 | |||
| 571 | static void _request_firmware_cleanup(const struct firmware **firmware_p) | ||
| 572 | { | ||
| 573 | release_firmware(*firmware_p); | ||
| 574 | *firmware_p = NULL; | ||
| 575 | } | ||
| 576 | |||
| 487 | static struct firmware_priv * | 577 | static struct firmware_priv * |
| 488 | _request_firmware_prepare(const struct firmware **firmware_p, const char *name, | 578 | _request_firmware_prepare(const struct firmware **firmware_p, const char *name, |
| 489 | struct device *device, bool uevent, bool nowait) | 579 | struct device *device, bool uevent, bool nowait) |
| 490 | { | 580 | { |
| 491 | struct firmware *firmware; | 581 | struct firmware *firmware; |
| 492 | struct firmware_priv *fw_priv; | 582 | struct firmware_priv *fw_priv = NULL; |
| 583 | struct firmware_buf *buf; | ||
| 584 | int ret; | ||
| 493 | 585 | ||
| 494 | if (!firmware_p) | 586 | if (!firmware_p) |
| 495 | return ERR_PTR(-EINVAL); | 587 | return ERR_PTR(-EINVAL); |
| @@ -506,35 +598,45 @@ _request_firmware_prepare(const struct firmware **firmware_p, const char *name, | |||
| 506 | return NULL; | 598 | return NULL; |
| 507 | } | 599 | } |
| 508 | 600 | ||
| 509 | fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); | 601 | ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); |
| 510 | if (IS_ERR(fw_priv)) { | 602 | if (!ret) |
| 511 | release_firmware(firmware); | 603 | fw_priv = fw_create_instance(firmware, name, device, |
| 604 | uevent, nowait); | ||
| 605 | |||
| 606 | if (IS_ERR(fw_priv) || ret < 0) { | ||
| 607 | kfree(firmware); | ||
| 512 | *firmware_p = NULL; | 608 | *firmware_p = NULL; |
| 609 | return ERR_PTR(-ENOMEM); | ||
| 610 | } else if (fw_priv) { | ||
| 611 | fw_priv->buf = buf; | ||
| 612 | |||
| 613 | /* | ||
| 614 | * bind with 'buf' now to avoid warning in failure path | ||
| 615 | * of requesting firmware. | ||
| 616 | */ | ||
| 617 | firmware->priv = buf; | ||
| 618 | return fw_priv; | ||
| 513 | } | 619 | } |
| 514 | return fw_priv; | ||
| 515 | } | ||
| 516 | |||
| 517 | static void _request_firmware_cleanup(const struct firmware **firmware_p) | ||
| 518 | { | ||
| 519 | release_firmware(*firmware_p); | ||
| 520 | *firmware_p = NULL; | ||
| 521 | } | ||
| 522 | 620 | ||
| 523 | /* transfer the ownership of pages to firmware */ | 621 | /* share the cached buf, which is inprogessing or completed */ |
| 524 | static int fw_set_page_data(struct firmware_buf *buf) | 622 | check_status: |
| 525 | { | 623 | mutex_lock(&fw_lock); |
| 526 | struct firmware *fw = buf->fw; | 624 | if (test_bit(FW_STATUS_ABORT, &buf->status)) { |
| 527 | 625 | fw_priv = ERR_PTR(-ENOENT); | |
| 528 | buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); | 626 | _request_firmware_cleanup(firmware_p); |
| 529 | if (!buf->data) | 627 | goto exit; |
| 530 | return -ENOMEM; | 628 | } else if (test_bit(FW_STATUS_DONE, &buf->status)) { |
| 531 | 629 | fw_priv = NULL; | |
| 532 | fw->data = buf->data; | 630 | fw_set_page_data(buf, firmware); |
| 533 | fw->pages = buf->pages; | 631 | goto exit; |
| 534 | fw->size = buf->size; | 632 | } |
| 535 | WARN_ON(PFN_UP(fw->size) != buf->nr_pages); | 633 | mutex_unlock(&fw_lock); |
| 634 | wait_for_completion(&buf->completion); | ||
| 635 | goto check_status; | ||
| 536 | 636 | ||
| 537 | return 0; | 637 | exit: |
| 638 | mutex_unlock(&fw_lock); | ||
| 639 | return fw_priv; | ||
| 538 | } | 640 | } |
| 539 | 641 | ||
| 540 | static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | 642 | static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, |
| @@ -585,13 +687,12 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, | |||
| 585 | if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) | 687 | if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) |
| 586 | retval = -ENOENT; | 688 | retval = -ENOENT; |
| 587 | 689 | ||
| 588 | /* transfer pages ownership at the last minute */ | ||
| 589 | if (!retval) | 690 | if (!retval) |
| 590 | retval = fw_set_page_data(buf); | 691 | retval = fw_map_pages_buf(buf); |
| 591 | if (retval) | 692 | |
| 592 | fw_free_buf(buf); /* free untransfered pages buffer */ | 693 | /* pass the pages buffer to driver at the last minute */ |
| 694 | fw_set_page_data(buf, fw_priv->fw); | ||
| 593 | 695 | ||
| 594 | kfree(buf); | ||
| 595 | fw_priv->buf = NULL; | 696 | fw_priv->buf = NULL; |
| 596 | mutex_unlock(&fw_lock); | 697 | mutex_unlock(&fw_lock); |
| 597 | 698 | ||
| @@ -753,6 +854,7 @@ request_firmware_nowait( | |||
| 753 | 854 | ||
| 754 | static int __init firmware_class_init(void) | 855 | static int __init firmware_class_init(void) |
| 755 | { | 856 | { |
| 857 | fw_cache_init(); | ||
| 756 | return class_register(&firmware_class); | 858 | return class_register(&firmware_class); |
| 757 | } | 859 | } |
| 758 | 860 | ||
