aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMing Lei <ming.lei@canonical.com>2012-08-04 00:01:16 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-08-16 16:13:18 -0400
commit65710cb6ea315b3ef76a8a3da7be99afcf58d2bb (patch)
tree40043563aca5fa81fbc1f04f16ea887b6b372212
parent3cd52ab68b7f17eddbff46c1f8e5a105cd901f8e (diff)
firmware loader: simplify pages ownership transfer
This patch doesn't transfer ownership of pages' buffer to the instance of firmware until the firmware loading is completed, which will simplify firmware_loading_store a lot, so help to introduce the following cache_firmware and uncache_firmware mechanism during system suspend-resume cycle. In fact, this patch fixes one bug: if writing data into firmware loader device is bypassed between writting 1 and 0 to 'loading', OOPS will be triggered without the patch. Also handle the vmap failure case, and add some comments to make code more readable. 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>
-rw-r--r--drivers/base/firmware_class.c62
1 files changed, 39 insertions, 23 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 803cfc1597a9..1cbefcfd15f7 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -93,6 +93,8 @@ struct firmware_priv {
93 struct completion completion; 93 struct completion completion;
94 struct firmware *fw; 94 struct firmware *fw;
95 unsigned long status; 95 unsigned long status;
96 void *data;
97 size_t size;
96 struct page **pages; 98 struct page **pages;
97 int nr_pages; 99 int nr_pages;
98 int page_array_size; 100 int page_array_size;
@@ -156,9 +158,11 @@ static void fw_dev_release(struct device *dev)
156 struct firmware_priv *fw_priv = to_firmware_priv(dev); 158 struct firmware_priv *fw_priv = to_firmware_priv(dev);
157 int i; 159 int i;
158 160
161 /* free untransfered pages buffer */
159 for (i = 0; i < fw_priv->nr_pages; i++) 162 for (i = 0; i < fw_priv->nr_pages; i++)
160 __free_page(fw_priv->pages[i]); 163 __free_page(fw_priv->pages[i]);
161 kfree(fw_priv->pages); 164 kfree(fw_priv->pages);
165
162 kfree(fw_priv); 166 kfree(fw_priv);
163 167
164 module_put(THIS_MODULE); 168 module_put(THIS_MODULE);
@@ -194,6 +198,7 @@ static ssize_t firmware_loading_show(struct device *dev,
194 return sprintf(buf, "%d\n", loading); 198 return sprintf(buf, "%d\n", loading);
195} 199}
196 200
201/* firmware holds the ownership of pages */
197static void firmware_free_data(const struct firmware *fw) 202static void firmware_free_data(const struct firmware *fw)
198{ 203{
199 int i; 204 int i;
@@ -237,9 +242,7 @@ static ssize_t firmware_loading_store(struct device *dev,
237 242
238 switch (loading) { 243 switch (loading) {
239 case 1: 244 case 1:
240 firmware_free_data(fw_priv->fw); 245 /* discarding any previous partial load */
241 memset(fw_priv->fw, 0, sizeof(struct firmware));
242 /* If the pages are not owned by 'struct firmware' */
243 for (i = 0; i < fw_priv->nr_pages; i++) 246 for (i = 0; i < fw_priv->nr_pages; i++)
244 __free_page(fw_priv->pages[i]); 247 __free_page(fw_priv->pages[i]);
245 kfree(fw_priv->pages); 248 kfree(fw_priv->pages);
@@ -250,20 +253,6 @@ static ssize_t firmware_loading_store(struct device *dev,
250 break; 253 break;
251 case 0: 254 case 0:
252 if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { 255 if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
253 vunmap(fw_priv->fw->data);
254 fw_priv->fw->data = vmap(fw_priv->pages,
255 fw_priv->nr_pages,
256 0, PAGE_KERNEL_RO);
257 if (!fw_priv->fw->data) {
258 dev_err(dev, "%s: vmap() failed\n", __func__);
259 goto err;
260 }
261 /* Pages are now owned by 'struct firmware' */
262 fw_priv->fw->pages = fw_priv->pages;
263 fw_priv->pages = NULL;
264
265 fw_priv->page_array_size = 0;
266 fw_priv->nr_pages = 0;
267 complete(&fw_priv->completion); 256 complete(&fw_priv->completion);
268 clear_bit(FW_STATUS_LOADING, &fw_priv->status); 257 clear_bit(FW_STATUS_LOADING, &fw_priv->status);
269 break; 258 break;
@@ -273,7 +262,6 @@ static ssize_t firmware_loading_store(struct device *dev,
273 dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); 262 dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
274 /* fallthrough */ 263 /* fallthrough */
275 case -1: 264 case -1:
276 err:
277 fw_load_abort(fw_priv); 265 fw_load_abort(fw_priv);
278 break; 266 break;
279 } 267 }
@@ -299,12 +287,12 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
299 ret_count = -ENODEV; 287 ret_count = -ENODEV;
300 goto out; 288 goto out;
301 } 289 }
302 if (offset > fw->size) { 290 if (offset > fw_priv->size) {
303 ret_count = 0; 291 ret_count = 0;
304 goto out; 292 goto out;
305 } 293 }
306 if (count > fw->size - offset) 294 if (count > fw_priv->size - offset)
307 count = fw->size - offset; 295 count = fw_priv->size - offset;
308 296
309 ret_count = count; 297 ret_count = count;
310 298
@@ -396,6 +384,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
396 retval = -ENODEV; 384 retval = -ENODEV;
397 goto out; 385 goto out;
398 } 386 }
387
399 retval = fw_realloc_buffer(fw_priv, offset + count); 388 retval = fw_realloc_buffer(fw_priv, offset + count);
400 if (retval) 389 if (retval)
401 goto out; 390 goto out;
@@ -418,7 +407,7 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
418 count -= page_cnt; 407 count -= page_cnt;
419 } 408 }
420 409
421 fw->size = max_t(size_t, offset, fw->size); 410 fw_priv->size = max_t(size_t, offset, fw_priv->size);
422out: 411out:
423 mutex_unlock(&fw_lock); 412 mutex_unlock(&fw_lock);
424 return retval; 413 return retval;
@@ -504,6 +493,29 @@ static void _request_firmware_cleanup(const struct firmware **firmware_p)
504 *firmware_p = NULL; 493 *firmware_p = NULL;
505} 494}
506 495
496/* transfer the ownership of pages to firmware */
497static int fw_set_page_data(struct firmware_priv *fw_priv)
498{
499 struct firmware *fw = fw_priv->fw;
500
501 fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages,
502 0, PAGE_KERNEL_RO);
503 if (!fw_priv->data)
504 return -ENOMEM;
505
506 fw->data = fw_priv->data;
507 fw->pages = fw_priv->pages;
508 fw->size = fw_priv->size;
509
510 WARN_ON(PFN_UP(fw->size) != fw_priv->nr_pages);
511
512 fw_priv->nr_pages = 0;
513 fw_priv->pages = NULL;
514 fw_priv->data = NULL;
515
516 return 0;
517}
518
507static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, 519static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
508 long timeout) 520 long timeout)
509{ 521{
@@ -549,8 +561,12 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
549 del_timer_sync(&fw_priv->timeout); 561 del_timer_sync(&fw_priv->timeout);
550 562
551 mutex_lock(&fw_lock); 563 mutex_lock(&fw_lock);
552 if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) 564 if (!fw_priv->size || test_bit(FW_STATUS_ABORT, &fw_priv->status))
553 retval = -ENOENT; 565 retval = -ENOENT;
566
567 /* transfer pages ownership at the last minute */
568 if (!retval)
569 retval = fw_set_page_data(fw_priv);
554 fw_priv->fw = NULL; 570 fw_priv->fw = NULL;
555 mutex_unlock(&fw_lock); 571 mutex_unlock(&fw_lock);
556 572