diff options
| author | Ming Lei <ming.lei@canonical.com> | 2012-10-09 00:01:03 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-10-22 11:37:17 -0400 |
| commit | 746058f4304343507e48d39f80d7a3b0d8550e3a (patch) | |
| tree | c372c4a60a4a5b00b408de1c09f27bba077f9fc0 | |
| parent | 253c9240ee09fd2a05d315ea44ac037a893d8981 (diff) | |
firmware loader: let direct loading back on 'firmware_buf'
Firstly 'firmware_buf' is introduced to make all loading requests
to share one firmware kernel buffer, so firmware_buf should
be used in direct loading for saving memory and speedup firmware
loading.
Secondly, the commit below
abb139e75c2cdbb955e840d6331cb5863e409d0e(firmware:teach
the kernel to load firmware files directly from the filesystem)
introduces direct loading for fixing udev regression, but it
bypasses the firmware cache meachnism, so this patch enables
caching firmware for direct loading case since it is still needed
to solve drivers' dependency during system resume.
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/base/firmware_class.c | 171 |
1 files changed, 100 insertions, 71 deletions
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index f2882511a9c1..a095d84ddfd9 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c | |||
| @@ -36,68 +36,6 @@ MODULE_AUTHOR("Manuel Estrada Sainz"); | |||
| 36 | MODULE_DESCRIPTION("Multi purpose firmware loading support"); | 36 | MODULE_DESCRIPTION("Multi purpose firmware loading support"); |
| 37 | MODULE_LICENSE("GPL"); | 37 | MODULE_LICENSE("GPL"); |
| 38 | 38 | ||
| 39 | static const char *fw_path[] = { | ||
| 40 | "/lib/firmware/updates/" UTS_RELEASE, | ||
| 41 | "/lib/firmware/updates", | ||
| 42 | "/lib/firmware/" UTS_RELEASE, | ||
| 43 | "/lib/firmware" | ||
| 44 | }; | ||
| 45 | |||
| 46 | /* Don't inline this: 'struct kstat' is biggish */ | ||
| 47 | static noinline long fw_file_size(struct file *file) | ||
| 48 | { | ||
| 49 | struct kstat st; | ||
| 50 | if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st)) | ||
| 51 | return -1; | ||
| 52 | if (!S_ISREG(st.mode)) | ||
| 53 | return -1; | ||
| 54 | if (st.size != (long)st.size) | ||
| 55 | return -1; | ||
| 56 | return st.size; | ||
| 57 | } | ||
| 58 | |||
| 59 | static bool fw_read_file_contents(struct file *file, struct firmware *fw) | ||
| 60 | { | ||
| 61 | long size; | ||
| 62 | char *buf; | ||
| 63 | |||
| 64 | size = fw_file_size(file); | ||
| 65 | if (size < 0) | ||
| 66 | return false; | ||
| 67 | buf = vmalloc(size); | ||
| 68 | if (!buf) | ||
| 69 | return false; | ||
| 70 | if (kernel_read(file, 0, buf, size) != size) { | ||
| 71 | vfree(buf); | ||
| 72 | return false; | ||
| 73 | } | ||
| 74 | fw->data = buf; | ||
| 75 | fw->size = size; | ||
| 76 | return true; | ||
| 77 | } | ||
| 78 | |||
| 79 | static bool fw_get_filesystem_firmware(struct firmware *fw, const char *name) | ||
| 80 | { | ||
| 81 | int i; | ||
| 82 | bool success = false; | ||
| 83 | char *path = __getname(); | ||
| 84 | |||
| 85 | for (i = 0; i < ARRAY_SIZE(fw_path); i++) { | ||
| 86 | struct file *file; | ||
| 87 | snprintf(path, PATH_MAX, "%s/%s", fw_path[i], name); | ||
| 88 | |||
| 89 | file = filp_open(path, O_RDONLY, 0); | ||
| 90 | if (IS_ERR(file)) | ||
| 91 | continue; | ||
| 92 | success = fw_read_file_contents(file, fw); | ||
| 93 | fput(file); | ||
| 94 | if (success) | ||
| 95 | break; | ||
| 96 | } | ||
| 97 | __putname(path); | ||
| 98 | return success; | ||
| 99 | } | ||
| 100 | |||
| 101 | /* Builtin firmware support */ | 39 | /* Builtin firmware support */ |
| 102 | 40 | ||
| 103 | #ifdef CONFIG_FW_LOADER | 41 | #ifdef CONFIG_FW_LOADER |
| @@ -150,6 +88,11 @@ enum { | |||
| 150 | FW_STATUS_ABORT, | 88 | FW_STATUS_ABORT, |
| 151 | }; | 89 | }; |
| 152 | 90 | ||
| 91 | enum fw_buf_fmt { | ||
| 92 | VMALLOC_BUF, /* used in direct loading */ | ||
| 93 | PAGE_BUF, /* used in loading via userspace */ | ||
| 94 | }; | ||
| 95 | |||
| 153 | static int loading_timeout = 60; /* In seconds */ | 96 | static int loading_timeout = 60; /* In seconds */ |
| 154 | 97 | ||
| 155 | static inline long firmware_loading_timeout(void) | 98 | static inline long firmware_loading_timeout(void) |
| @@ -187,6 +130,7 @@ struct firmware_buf { | |||
| 187 | struct completion completion; | 130 | struct completion completion; |
| 188 | struct firmware_cache *fwc; | 131 | struct firmware_cache *fwc; |
| 189 | unsigned long status; | 132 | unsigned long status; |
| 133 | enum fw_buf_fmt fmt; | ||
| 190 | void *data; | 134 | void *data; |
| 191 | size_t size; | 135 | size_t size; |
| 192 | struct page **pages; | 136 | struct page **pages; |
| @@ -240,6 +184,7 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, | |||
| 240 | strcpy(buf->fw_id, fw_name); | 184 | strcpy(buf->fw_id, fw_name); |
| 241 | buf->fwc = fwc; | 185 | buf->fwc = fwc; |
| 242 | init_completion(&buf->completion); | 186 | init_completion(&buf->completion); |
| 187 | buf->fmt = VMALLOC_BUF; | ||
| 243 | 188 | ||
| 244 | pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); | 189 | pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); |
| 245 | 190 | ||
| @@ -307,10 +252,14 @@ static void __fw_free_buf(struct kref *ref) | |||
| 307 | list_del(&buf->list); | 252 | list_del(&buf->list); |
| 308 | spin_unlock(&fwc->lock); | 253 | spin_unlock(&fwc->lock); |
| 309 | 254 | ||
| 310 | vunmap(buf->data); | 255 | |
| 311 | for (i = 0; i < buf->nr_pages; i++) | 256 | if (buf->fmt == PAGE_BUF) { |
| 312 | __free_page(buf->pages[i]); | 257 | vunmap(buf->data); |
| 313 | kfree(buf->pages); | 258 | for (i = 0; i < buf->nr_pages; i++) |
| 259 | __free_page(buf->pages[i]); | ||
| 260 | kfree(buf->pages); | ||
| 261 | } else | ||
| 262 | vfree(buf->data); | ||
| 314 | kfree(buf); | 263 | kfree(buf); |
| 315 | } | 264 | } |
| 316 | 265 | ||
| @@ -319,6 +268,69 @@ static void fw_free_buf(struct firmware_buf *buf) | |||
| 319 | kref_put(&buf->ref, __fw_free_buf); | 268 | kref_put(&buf->ref, __fw_free_buf); |
| 320 | } | 269 | } |
| 321 | 270 | ||
| 271 | /* direct firmware loading support */ | ||
| 272 | static const char *fw_path[] = { | ||
| 273 | "/lib/firmware/updates/" UTS_RELEASE, | ||
| 274 | "/lib/firmware/updates", | ||
| 275 | "/lib/firmware/" UTS_RELEASE, | ||
| 276 | "/lib/firmware" | ||
| 277 | }; | ||
| 278 | |||
| 279 | /* Don't inline this: 'struct kstat' is biggish */ | ||
| 280 | static noinline long fw_file_size(struct file *file) | ||
| 281 | { | ||
| 282 | struct kstat st; | ||
| 283 | if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st)) | ||
| 284 | return -1; | ||
| 285 | if (!S_ISREG(st.mode)) | ||
| 286 | return -1; | ||
| 287 | if (st.size != (long)st.size) | ||
| 288 | return -1; | ||
| 289 | return st.size; | ||
| 290 | } | ||
| 291 | |||
| 292 | static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf) | ||
| 293 | { | ||
| 294 | long size; | ||
| 295 | char *buf; | ||
| 296 | |||
| 297 | size = fw_file_size(file); | ||
| 298 | if (size < 0) | ||
| 299 | return false; | ||
| 300 | buf = vmalloc(size); | ||
| 301 | if (!buf) | ||
| 302 | return false; | ||
| 303 | if (kernel_read(file, 0, buf, size) != size) { | ||
| 304 | vfree(buf); | ||
| 305 | return false; | ||
| 306 | } | ||
| 307 | fw_buf->data = buf; | ||
| 308 | fw_buf->size = size; | ||
| 309 | return true; | ||
| 310 | } | ||
| 311 | |||
| 312 | static bool fw_get_filesystem_firmware(struct firmware_buf *buf) | ||
| 313 | { | ||
| 314 | int i; | ||
| 315 | bool success = false; | ||
| 316 | char *path = __getname(); | ||
| 317 | |||
| 318 | for (i = 0; i < ARRAY_SIZE(fw_path); i++) { | ||
| 319 | struct file *file; | ||
| 320 | snprintf(path, PATH_MAX, "%s/%s", fw_path[i], buf->fw_id); | ||
| 321 | |||
| 322 | file = filp_open(path, O_RDONLY, 0); | ||
| 323 | if (IS_ERR(file)) | ||
| 324 | continue; | ||
| 325 | success = fw_read_file_contents(file, buf); | ||
| 326 | fput(file); | ||
| 327 | if (success) | ||
| 328 | break; | ||
| 329 | } | ||
| 330 | __putname(path); | ||
| 331 | return success; | ||
| 332 | } | ||
| 333 | |||
| 322 | static struct firmware_priv *to_firmware_priv(struct device *dev) | 334 | static struct firmware_priv *to_firmware_priv(struct device *dev) |
