Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: support loading into a pre-allocated buffer

Some systems are memory constrained but they need to load very large
firmwares. The firmware subsystem allows drivers to request this
firmware be loaded from the filesystem, but this requires that the
entire firmware be loaded into kernel memory first before it's provided
to the driver. This can lead to a situation where we map the firmware
twice, once to load the firmware into kernel memory and once to copy the
firmware into the final resting place.

This creates needless memory pressure and delays loading because we have
to copy from kernel memory to somewhere else. Let's add a
request_firmware_into_buf() API that allows drivers to request firmware
be loaded directly into a pre-allocated buffer. This skips the
intermediate step of allocating a buffer in kernel memory to hold the
firmware image while it's read from the filesystem. It also requires
that drivers know how much memory they'll require before requesting the
firmware and negates any benefits of firmware caching because the
firmware layer doesn't manage the buffer lifetime.

For a 16MB buffer, about half the time is spent performing a memcpy from
the buffer to the final resting place. I see loading times go from
0.081171 seconds to 0.047696 seconds after applying this patch. Plus
the vmalloc pressure is reduced.

This is based on a patch from Vikram Mulukutla on codeaurora.org:
https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.18/commit/drivers/base/firmware_class.c?h=rel/msm-3.18&id=0a328c5f6cd999f5c591f172216835636f39bcb5

Link: http://lkml.kernel.org/r/20160607164741.31849-4-stephen.boyd@linaro.org
Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
Cc: Mimi Zohar <zohar@linux.vnet.ibm.com>
Cc: Vikram Mulukutla <markivx@codeaurora.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Ming Lei <ming.lei@canonical.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Stephen Boyd and committed by
Linus Torvalds
a098ecd2 0e742e92

+114 -29
+99 -26
drivers/base/firmware_class.c
··· 46 46 extern struct builtin_fw __start_builtin_fw[]; 47 47 extern struct builtin_fw __end_builtin_fw[]; 48 48 49 - static bool fw_get_builtin_firmware(struct firmware *fw, const char *name) 49 + static bool fw_get_builtin_firmware(struct firmware *fw, const char *name, 50 + void *buf, size_t size) 50 51 { 51 52 struct builtin_fw *b_fw; 52 53 ··· 55 54 if (strcmp(name, b_fw->name) == 0) { 56 55 fw->size = b_fw->size; 57 56 fw->data = b_fw->data; 57 + 58 + if (buf && fw->size <= size) 59 + memcpy(buf, fw->data, fw->size); 58 60 return true; 59 61 } 60 62 } ··· 78 74 79 75 #else /* Module case - no builtin firmware support */ 80 76 81 - static inline bool fw_get_builtin_firmware(struct firmware *fw, const char *name) 77 + static inline bool fw_get_builtin_firmware(struct firmware *fw, 78 + const char *name, void *buf, 79 + size_t size) 82 80 { 83 81 return false; 84 82 } ··· 150 144 unsigned long status; 151 145 void *data; 152 146 size_t size; 147 + size_t allocated_size; 153 148 #ifdef CONFIG_FW_LOADER_USER_HELPER 154 149 bool is_paged_buf; 155 150 bool need_uevent; ··· 186 179 static struct firmware_cache fw_cache; 187 180 188 181 static struct firmware_buf *__allocate_fw_buf(const char *fw_name, 189 - struct firmware_cache *fwc) 182 + struct firmware_cache *fwc, 183 + void *dbuf, size_t size) 190 184 { 191 185 struct firmware_buf *buf; 192 186 ··· 203 195 204 196 kref_init(&buf->ref); 205 197 buf->fwc = fwc; 198 + buf->data = dbuf; 199 + buf->allocated_size = size; 206 200 init_completion(&buf->completion); 207 201 #ifdef CONFIG_FW_LOADER_USER_HELPER 208 202 INIT_LIST_HEAD(&buf->pending_list); ··· 228 218 229 219 static int fw_lookup_and_allocate_buf(const char *fw_name, 230 220 struct firmware_cache *fwc, 231 - struct firmware_buf **buf) 221 + struct firmware_buf **buf, void *dbuf, 222 + size_t size) 232 223 { 233 224 struct firmware_buf *tmp; 234 225 ··· 241 230 *buf = tmp; 242 231 return 1; 243 232 } 244 - tmp = __allocate_fw_buf(fw_name, fwc); 233 + tmp = __allocate_fw_buf(fw_name, fwc, dbuf, size); 245 234 if (tmp) 246 235 list_add(&tmp->list, &fwc->head); 247 236 spin_unlock(&fwc->lock); ··· 273 262 vfree(buf->pages); 274 263 } else 275 264 #endif 265 + if (!buf->allocated_size) 276 266 vfree(buf->data); 277 267 kfree_const(buf->fw_id); 278 268 kfree(buf); ··· 314 302 mutex_unlock(&fw_lock); 315 303 } 316 304 317 - static int fw_get_filesystem_firmware(struct device *device, 318 - struct firmware_buf *buf) 305 + static int 306 + fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) 319 307 { 320 308 loff_t size; 321 309 int i, len; 322 310 int rc = -ENOENT; 323 311 char *path; 312 + enum kernel_read_file_id id = READING_FIRMWARE; 313 + size_t msize = INT_MAX; 314 + 315 + /* Already populated data member means we're loading into a buffer */ 316 + if (buf->data) { 317 + id = READING_FIRMWARE_PREALLOC_BUFFER; 318 + msize = buf->allocated_size; 319 + } 324 320 325 321 path = __getname(); 326 322 if (!path) ··· 347 327 } 348 328 349 329 buf->size = 0; 350 - rc = kernel_read_file_from_path(path, &buf->data, &size, 351 - INT_MAX, READING_FIRMWARE); 330 + rc = kernel_read_file_from_path(path, &buf->data, &size, msize, 331 + id); 352 332 if (rc) { 353 333 if (rc == -ENOENT) 354 334 dev_dbg(device, "loading %s failed with error %d\n", ··· 712 692 713 693 static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); 714 694 695 + static void firmware_rw_buf(struct firmware_buf *buf, char *buffer, 696 + loff_t offset, size_t count, bool read) 697 + { 698 + if (read) 699 + memcpy(buffer, buf->data + offset, count); 700 + else 701 + memcpy(buf->data + offset, buffer, count); 702 + } 703 + 715 704 static void firmware_rw(struct firmware_buf *buf, char *buffer, 716 705 loff_t offset, size_t count, bool read) 717 706 { ··· 768 739 769 740 ret_count = count; 770 741 771 - firmware_rw(buf, buffer, offset, count, true); 742 + if (buf->data) 743 + firmware_rw_buf(buf, buffer, offset, count, true); 744 + else 745 + firmware_rw(buf, buffer, offset, count, true); 772 746 773 747 out: 774 748 mutex_unlock(&fw_lock); ··· 847 815 goto out; 848 816 } 849 817 850 - retval = fw_realloc_buffer(fw_priv, offset + count); 851 - if (retval) 852 - goto out; 818 + if (buf->data) { 819 + if (offset + count > buf->allocated_size) { 820 + retval = -ENOMEM; 821 + goto out; 822 + } 823 + firmware_rw_buf(buf, buffer, offset, count, false); 824 + retval = count; 825 + } else { 826 + retval = fw_realloc_buffer(fw_priv, offset + count); 827 + if (retval) 828 + goto out; 853 829 854 - retval = count; 855 - firmware_rw(buf, buffer, offset, count, false); 830 + retval = count; 831 + firmware_rw(buf, buffer, offset, count, false); 832 + } 856 833 857 834 buf->size = max_t(size_t, offset + count, buf->size); 858 835 out: ··· 931 890 struct firmware_buf *buf = fw_priv->buf; 932 891 933 892 /* fall back on userspace loading */ 934 - buf->is_paged_buf = true; 893 + if (!buf->data) 894 + buf->is_paged_buf = true; 935 895 936 896 dev_set_uevent_suppress(f_dev, true); 937 897 ··· 967 925 968 926 if (is_fw_load_aborted(buf)) 969 927 retval = -EAGAIN; 970 - else if (!buf->data) 928 + else if (buf->is_paged_buf && !buf->data) 971 929 retval = -ENOMEM; 972 930 973 931 device_del(f_dev); ··· 1050 1008 */ 1051 1009 static int 1052 1010 _request_firmware_prepare(struct firmware **firmware_p, const char *name, 1053 - struct device *device) 1011 + struct device *device, void *dbuf, size_t size) 1054 1012 { 1055 1013 struct firmware *firmware; 1056 1014 struct firmware_buf *buf; ··· 1063 1021 return -ENOMEM; 1064 1022 } 1065 1023 1066 - if (fw_get_builtin_firmware(firmware, name)) { 1024 + if (fw_get_builtin_firmware(firmware, name, dbuf, size)) { 1067 1025 dev_dbg(device, "using built-in %s\n", name); 1068 1026 return 0; /* assigned */ 1069 1027 } 1070 1028 1071 - ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); 1029 + ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf, dbuf, size); 1072 1030 1073 1031 /* 1074 1032 * bind with 'buf' now to avoid warning in failure path ··· 1131 1089 /* called from request_firmware() and request_firmware_work_func() */ 1132 1090 static int 1133 1091 _request_firmware(const struct firmware **firmware_p, const char *name, 1134 - struct device *device, unsigned int opt_flags) 1092 + struct device *device, void *buf, size_t size, 1093 + unsigned int opt_flags) 1135 1094 { 1136 1095 struct firmware *fw = NULL; 1137 1096 long timeout; ··· 1146 1103 goto out; 1147 1104 } 1148 1105 1149 - ret = _request_firmware_prepare(&fw, name, device); 1106 + ret = _request_firmware_prepare(&fw, name, device, buf, size); 1150 1107 if (ret <= 0) /* error or already assigned */ 1151 1108 goto out; 1152 1109 ··· 1225 1182 1226 1183 /* Need to pin this module until return */ 1227 1184 __module_get(THIS_MODULE); 1228 - ret = _request_firmware(firmware_p, name, device, 1185 + ret = _request_firmware(firmware_p, name, device, NULL, 0, 1229 1186 FW_OPT_UEVENT | FW_OPT_FALLBACK); 1230 1187 module_put(THIS_MODULE); 1231 1188 return ret; ··· 1249 1206 int ret; 1250 1207 1251 1208 __module_get(THIS_MODULE); 1252 - ret = _request_firmware(firmware_p, name, device, 1209 + ret = _request_firmware(firmware_p, name, device, NULL, 0, 1253 1210 FW_OPT_UEVENT | FW_OPT_NO_WARN); 1254 1211 module_put(THIS_MODULE); 1255 1212 return ret; 1256 1213 } 1257 1214 EXPORT_SYMBOL_GPL(request_firmware_direct); 1215 + 1216 + /** 1217 + * request_firmware_into_buf - load firmware into a previously allocated buffer 1218 + * @firmware_p: pointer to firmware image 1219 + * @name: name of firmware file 1220 + * @device: device for which firmware is being loaded and DMA region allocated 1221 + * @buf: address of buffer to load firmware into 1222 + * @size: size of buffer 1223 + * 1224 + * This function works pretty much like request_firmware(), but it doesn't 1225 + * allocate a buffer to hold the firmware data. Instead, the firmware 1226 + * is loaded directly into the buffer pointed to by @buf and the @firmware_p 1227 + * data member is pointed at @buf. 1228 + * 1229 + * This function doesn't cache firmware either. 1230 + */ 1231 + int 1232 + request_firmware_into_buf(const struct firmware **firmware_p, const char *name, 1233 + struct device *device, void *buf, size_t size) 1234 + { 1235 + int ret; 1236 + 1237 + __module_get(THIS_MODULE); 1238 + ret = _request_firmware(firmware_p, name, device, buf, size, 1239 + FW_OPT_UEVENT | FW_OPT_FALLBACK | 1240 + FW_OPT_NOCACHE); 1241 + module_put(THIS_MODULE); 1242 + return ret; 1243 + } 1244 + EXPORT_SYMBOL(request_firmware_into_buf); 1258 1245 1259 1246 /** 1260 1247 * release_firmware: - release the resource associated with a firmware image ··· 1318 1245 1319 1246 fw_work = container_of(work, struct firmware_work, work); 1320 1247 1321 - _request_firmware(&fw, fw_work->name, fw_work->device, 1248 + _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 1322 1249 fw_work->opt_flags); 1323 1250 fw_work->cont(fw, fw_work->context); 1324 1251 put_device(fw_work->device); /* taken in request_firmware_nowait() */ ··· 1451 1378 1452 1379 pr_debug("%s: %s\n", __func__, fw_name); 1453 1380 1454 - if (fw_get_builtin_firmware(&fw, fw_name)) 1381 + if (fw_get_builtin_firmware(&fw, fw_name, NULL, 0)) 1455 1382 return 0; 1456 1383 1457 1384 buf = fw_lookup_buf(fw_name);
+6 -3
fs/exec.c
··· 866 866 goto out; 867 867 } 868 868 869 - *buf = vmalloc(i_size); 869 + if (id != READING_FIRMWARE_PREALLOC_BUFFER) 870 + *buf = vmalloc(i_size); 870 871 if (!*buf) { 871 872 ret = -ENOMEM; 872 873 goto out; ··· 898 897 899 898 out_free: 900 899 if (ret < 0) { 901 - vfree(*buf); 902 - *buf = NULL; 900 + if (id != READING_FIRMWARE_PREALLOC_BUFFER) { 901 + vfree(*buf); 902 + *buf = NULL; 903 + } 903 904 } 904 905 905 906 out:
+8
include/linux/firmware.h
··· 47 47 void (*cont)(const struct firmware *fw, void *context)); 48 48 int request_firmware_direct(const struct firmware **fw, const char *name, 49 49 struct device *device); 50 + int request_firmware_into_buf(const struct firmware **firmware_p, 51 + const char *name, struct device *device, void *buf, size_t size); 50 52 51 53 void release_firmware(const struct firmware *fw); 52 54 #else ··· 73 71 static inline int request_firmware_direct(const struct firmware **fw, 74 72 const char *name, 75 73 struct device *device) 74 + { 75 + return -EINVAL; 76 + } 77 + 78 + static inline int request_firmware_into_buf(const struct firmware **firmware_p, 79 + const char *name, struct device *device, void *buf, size_t size) 76 80 { 77 81 return -EINVAL; 78 82 }
+1
include/linux/fs.h
··· 2652 2652 #define __kernel_read_file_id(id) \ 2653 2653 id(UNKNOWN, unknown) \ 2654 2654 id(FIRMWARE, firmware) \ 2655 + id(FIRMWARE_PREALLOC_BUFFER, firmware) \ 2655 2656 id(MODULE, kernel-module) \ 2656 2657 id(KEXEC_IMAGE, kexec-image) \ 2657 2658 id(KEXEC_INITRAMFS, kexec-initramfs) \