aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/firmware_class.c
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@infradead.org>2009-04-10 01:04:07 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-05-15 07:51:29 -0400
commit6e03a201bbe8137487f340d26aa662110e324b20 (patch)
tree5a2c61945ea2fad720e7a18b061dd31303a28523 /drivers/base/firmware_class.c
parent091bf7624d1c90cec9e578a18529f615213ff847 (diff)
firmware: speed up request_firmware(), v3
Rather than calling vmalloc() repeatedly to grow the firmware image as we receive data from userspace, just allocate and fill individual pages. Then vmap() the whole lot in one go when we're done. A quick test with a 337KiB iwlagn firmware shows the time taken for request_firmware() going from ~32ms to ~5ms after I apply this patch. [v2: define PAGE_KERNEL_RO as PAGE_KERNEL where necessary, use min_t()] [v3: kunmap() takes the struct page *, not the virtual address] Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> Tested-by: Sachin Sant <sachinp@in.ibm.com>
Diffstat (limited to 'drivers/base/firmware_class.c')
-rw-r--r--drivers/base/firmware_class.c129
1 files changed, 103 insertions, 26 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index d3a59c688fe4..8a267c427629 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -17,7 +17,7 @@
17#include <linux/bitops.h> 17#include <linux/bitops.h>
18#include <linux/mutex.h> 18#include <linux/mutex.h>
19#include <linux/kthread.h> 19#include <linux/kthread.h>
20 20#include <linux/highmem.h>
21#include <linux/firmware.h> 21#include <linux/firmware.h>
22#include "base.h" 22#include "base.h"
23 23
@@ -45,7 +45,10 @@ struct firmware_priv {
45 struct bin_attribute attr_data; 45 struct bin_attribute attr_data;
46 struct firmware *fw; 46 struct firmware *fw;
47 unsigned long status; 47 unsigned long status;
48 int alloc_size; 48 struct page **pages;
49 int nr_pages;
50 int page_array_size;
51 const char *vdata;
49 struct timer_list timeout; 52 struct timer_list timeout;
50}; 53};
51 54
@@ -122,6 +125,10 @@ static ssize_t firmware_loading_show(struct device *dev,
122 return sprintf(buf, "%d\n", loading); 125 return sprintf(buf, "%d\n", loading);
123} 126}
124 127
128/* Some architectures don't have PAGE_KERNEL_RO */
129#ifndef PAGE_KERNEL_RO
130#define PAGE_KERNEL_RO PAGE_KERNEL
131#endif
125/** 132/**
126 * firmware_loading_store - set value in the 'loading' control file 133 * firmware_loading_store - set value in the 'loading' control file
127 * @dev: device pointer 134 * @dev: device pointer
@@ -141,6 +148,7 @@ static ssize_t firmware_loading_store(struct device *dev,
141{ 148{
142 struct firmware_priv *fw_priv = dev_get_drvdata(dev); 149 struct firmware_priv *fw_priv = dev_get_drvdata(dev);
143 int loading = simple_strtol(buf, NULL, 10); 150 int loading = simple_strtol(buf, NULL, 10);
151 int i;
144 152
145 switch (loading) { 153 switch (loading) {
146 case 1: 154 case 1:
@@ -151,13 +159,30 @@ static ssize_t firmware_loading_store(struct device *dev,
151 } 159 }
152 vfree(fw_priv->fw->data); 160 vfree(fw_priv->fw->data);
153 fw_priv->fw->data = NULL; 161 fw_priv->fw->data = NULL;
162 for (i = 0; i < fw_priv->nr_pages; i++)
163 __free_page(fw_priv->pages[i]);
164 kfree(fw_priv->pages);
165 fw_priv->pages = NULL;
166 fw_priv->page_array_size = 0;
167 fw_priv->nr_pages = 0;
154 fw_priv->fw->size = 0; 168 fw_priv->fw->size = 0;
155 fw_priv->alloc_size = 0;
156 set_bit(FW_STATUS_LOADING, &fw_priv->status); 169 set_bit(FW_STATUS_LOADING, &fw_priv->status);
157 mutex_unlock(&fw_lock); 170 mutex_unlock(&fw_lock);
158 break; 171 break;
159 case 0: 172 case 0:
160 if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { 173 if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
174 vfree(fw_priv->fw->data);
175 fw_priv->fw->data = vmap(fw_priv->pages,
176 fw_priv->nr_pages,
177 0, PAGE_KERNEL_RO);
178 if (!fw_priv->fw->data) {
179 dev_err(dev, "%s: vmap() failed\n", __func__);
180 goto err;
181 }
182 /* Pages will be freed by vfree() */
183 fw_priv->pages = NULL;
184 fw_priv->page_array_size = 0;
185 fw_priv->nr_pages = 0;
161 complete(&fw_priv->completion); 186 complete(&fw_priv->completion);
162 clear_bit(FW_STATUS_LOADING, &fw_priv->status); 187 clear_bit(FW_STATUS_LOADING, &fw_priv->status);
163 break; 188 break;
@@ -167,6 +192,7 @@ static ssize_t firmware_loading_store(struct device *dev,
167 dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading); 192 dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
168 /* fallthrough */ 193 /* fallthrough */
169 case -1: 194 case -1:
195 err:
170 fw_load_abort(fw_priv); 196 fw_load_abort(fw_priv);
171 break; 197 break;
172 } 198 }
@@ -191,8 +217,28 @@ firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr,
191 ret_count = -ENODEV; 217 ret_count = -ENODEV;
192 goto out; 218 goto out;
193 } 219 }
194 ret_count = memory_read_from_buffer(buffer, count, &offset, 220 if (offset > fw->size)
195 fw->data, fw->size); 221 return 0;
222 if (count > fw->size - offset)
223 count = fw->size - offset;
224
225 ret_count = count;
226
227 while (count) {
228 void *page_data;
229 int page_nr = offset >> PAGE_SHIFT;
230 int page_ofs = offset & (PAGE_SIZE-1);
231 int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
232
233 page_data = kmap(fw_priv->pages[page_nr]);
234
235 memcpy(buffer, page_data + page_ofs, page_cnt);
236
237 kunmap(fw_priv->pages[page_nr]);
238 buffer += page_cnt;
239 offset += page_cnt;
240 count -= page_cnt;
241 }
196out: 242out:
197 mutex_unlock(&fw_lock); 243 mutex_unlock(&fw_lock);
198 return ret_count; 244 return ret_count;
@@ -201,27 +247,39 @@ out:
201static int 247static int
202fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) 248fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
203{ 249{
204 u8 *new_data; 250 int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
205 int new_size = fw_priv->alloc_size; 251
252 /* If the array of pages is too small, grow it... */
253 if (fw_priv->page_array_size < pages_needed) {
254 int new_array_size = max(pages_needed,
255 fw_priv->page_array_size * 2);
256 struct page **new_pages;
257
258 new_pages = kmalloc(new_array_size * sizeof(void *),
259 GFP_KERNEL);
260 if (!new_pages) {
261 fw_load_abort(fw_priv);
262 return -ENOMEM;
263 }
264 memcpy(new_pages, fw_priv->pages,
265 fw_priv->page_array_size * sizeof(void *));
266 memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
267 (new_array_size - fw_priv->page_array_size));
268 kfree(fw_priv->pages);
269 fw_priv->pages = new_pages;
270 fw_priv->page_array_size = new_array_size;
271 }
206 272
207 if (min_size <= fw_priv->alloc_size) 273 while (fw_priv->nr_pages < pages_needed) {
208 return 0; 274 fw_priv->pages[fw_priv->nr_pages] =
275 alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
209 276
210 new_size = ALIGN(min_size, PAGE_SIZE); 277 if (!fw_priv->pages[fw_priv->nr_pages]) {
211 new_data = vmalloc(new_size); 278 fw_load_abort(fw_priv);
212 if (!new_data) { 279 return -ENOMEM;
213 printk(KERN_ERR "%s: unable to alloc buffer\n", __func__); 280 }
214 /* Make sure that we don't keep incomplete data */ 281 fw_priv->nr_pages++;
215 fw_load_abort(fw_priv);
216 return -ENOMEM;
217 }
218 fw_priv->alloc_size = new_size;
219 if (fw_priv->fw->data) {
220 memcpy(new_data, fw_priv->fw->data, fw_priv->fw->size);
221 vfree(fw_priv->fw->data);
222 } 282 }
223 fw_priv->fw->data = new_data;
224 BUG_ON(min_size > fw_priv->alloc_size);
225 return 0; 283 return 0;
226} 284}
227 285
@@ -258,10 +316,25 @@ firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr,
258 if (retval) 316 if (retval)
259 goto out; 317 goto out;
260 318
261 memcpy((u8 *)fw->data + offset, buffer, count);
262
263 fw->size = max_t(size_t, offset + count, fw->size);
264 retval = count; 319 retval = count;
320
321 while (count) {
322 void *page_data;
323 int page_nr = offset >> PAGE_SHIFT;
324 int page_ofs = offset & (PAGE_SIZE - 1);
325 int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
326
327 page_data = kmap(fw_priv->pages[page_nr]);
328
329 memcpy(page_data + page_ofs, buffer, page_cnt);
330
331 kunmap(fw_priv->pages[page_nr]);
332 buffer += page_cnt;
333 offset += page_cnt;
334 count -= page_cnt;
335 }
336
337 fw->size = max_t(size_t, offset, fw->size);
265out: 338out:
266 mutex_unlock(&fw_lock); 339 mutex_unlock(&fw_lock);
267 return retval; 340 return retval;
@@ -277,7 +350,11 @@ static struct bin_attribute firmware_attr_data_tmpl = {
277static void fw_dev_release(struct device *dev) 350static void fw_dev_release(struct device *dev)
278{ 351{
279 struct firmware_priv *fw_priv = dev_get_drvdata(dev); 352 struct firmware_priv *fw_priv = dev_get_drvdata(dev);
353 int i;
280 354
355 for (i = 0; i < fw_priv->nr_pages; i++)
356 __free_page(fw_priv->pages[i]);
357 kfree(fw_priv->pages);
281 kfree(fw_priv); 358 kfree(fw_priv);
282 kfree(dev); 359 kfree(dev);
283 360