Skip to content

Commit a098ecd

Browse files
bebarinotorvalds
authored andcommitted
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/[email protected] Signed-off-by: Stephen Boyd <[email protected]> Cc: Mimi Zohar <[email protected]> Cc: Vikram Mulukutla <[email protected]> Cc: Mark Brown <[email protected]> Cc: Ming Lei <[email protected]> Cc: Greg Kroah-Hartman <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 0e742e9 commit a098ecd

File tree

4 files changed

+114
-29
lines changed

4 files changed

+114
-29
lines changed

drivers/base/firmware_class.c

Lines changed: 99 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,18 @@ MODULE_LICENSE("GPL");
4646
extern struct builtin_fw __start_builtin_fw[];
4747
extern struct builtin_fw __end_builtin_fw[];
4848

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)
5051
{
5152
struct builtin_fw *b_fw;
5253

5354
for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
5455
if (strcmp(name, b_fw->name) == 0) {
5556
fw->size = b_fw->size;
5657
fw->data = b_fw->data;
58+
59+
if (buf && fw->size <= size)
60+
memcpy(buf, fw->data, fw->size);
5761
return true;
5862
}
5963
}
@@ -74,7 +78,9 @@ static bool fw_is_builtin_firmware(const struct firmware *fw)
7478

7579
#else /* Module case - no builtin firmware support */
7680

77-
static inline bool fw_get_builtin_firmware(struct firmware *fw, const char *name)
81+
static inline bool fw_get_builtin_firmware(struct firmware *fw,
82+
const char *name, void *buf,
83+
size_t size)
7884
{
7985
return false;
8086
}
@@ -144,6 +150,7 @@ struct firmware_buf {
144150
unsigned long status;
145151
void *data;
146152
size_t size;
153+
size_t allocated_size;
147154
#ifdef CONFIG_FW_LOADER_USER_HELPER
148155
bool is_paged_buf;
149156
bool need_uevent;
@@ -179,7 +186,8 @@ static DEFINE_MUTEX(fw_lock);
179186
static struct firmware_cache fw_cache;
180187

181188
static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
182-
struct firmware_cache *fwc)
189+
struct firmware_cache *fwc,
190+
void *dbuf, size_t size)
183191
{
184192
struct firmware_buf *buf;
185193

@@ -195,6 +203,8 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
195203

196204
kref_init(&buf->ref);
197205
buf->fwc = fwc;
206+
buf->data = dbuf;
207+
buf->allocated_size = size;
198208
init_completion(&buf->completion);
199209
#ifdef CONFIG_FW_LOADER_USER_HELPER
200210
INIT_LIST_HEAD(&buf->pending_list);
@@ -218,7 +228,8 @@ static struct firmware_buf *__fw_lookup_buf(const char *fw_name)
218228

219229
static int fw_lookup_and_allocate_buf(const char *fw_name,
220230
struct firmware_cache *fwc,
221-
struct firmware_buf **buf)
231+
struct firmware_buf **buf, void *dbuf,
232+
size_t size)
222233
{
223234
struct firmware_buf *tmp;
224235

@@ -230,7 +241,7 @@ static int fw_lookup_and_allocate_buf(const char *fw_name,
230241
*buf = tmp;
231242
return 1;
232243
}
233-
tmp = __allocate_fw_buf(fw_name, fwc);
244+
tmp = __allocate_fw_buf(fw_name, fwc, dbuf, size);
234245
if (tmp)
235246
list_add(&tmp->list, &fwc->head);
236247
spin_unlock(&fwc->lock);
@@ -262,6 +273,7 @@ static void __fw_free_buf(struct kref *ref)
262273
vfree(buf->pages);
263274
} else
264275
#endif
276+
if (!buf->allocated_size)
265277
vfree(buf->data);
266278
kfree_const(buf->fw_id);
267279
kfree(buf);
@@ -302,13 +314,21 @@ static void fw_finish_direct_load(struct device *device,
302314
mutex_unlock(&fw_lock);
303315
}
304316

305-
static int fw_get_filesystem_firmware(struct device *device,
306-
struct firmware_buf *buf)
317+
static int
318+
fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
307319
{
308320
loff_t size;
309321
int i, len;
310322
int rc = -ENOENT;
311323
char *path;
324+
enum kernel_read_file_id id = READING_FIRMWARE;
325+
size_t msize = INT_MAX;
326+
327+
/* Already populated data member means we're loading into a buffer */
328+
if (buf->data) {
329+
id = READING_FIRMWARE_PREALLOC_BUFFER;
330+
msize = buf->allocated_size;
331+
}
312332

313333
path = __getname();
314334
if (!path)
@@ -327,8 +347,8 @@ static int fw_get_filesystem_firmware(struct device *device,
327347
}
328348

329349
buf->size = 0;
330-
rc = kernel_read_file_from_path(path, &buf->data, &size,
331-
INT_MAX, READING_FIRMWARE);
350+
rc = kernel_read_file_from_path(path, &buf->data, &size, msize,
351+
id);
332352
if (rc) {
333353
if (rc == -ENOENT)
334354
dev_dbg(device, "loading %s failed with error %d\n",
@@ -692,6 +712,15 @@ static ssize_t firmware_loading_store(struct device *dev,
692712

693713
static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
694714

715+
static void firmware_rw_buf(struct firmware_buf *buf, char *buffer,
716+
loff_t offset, size_t count, bool read)
717+
{
718+
if (read)
719+
memcpy(buffer, buf->data + offset, count);
720+
else
721+
memcpy(buf->data + offset, buffer, count);
722+
}
723+
695724
static void firmware_rw(struct firmware_buf *buf, char *buffer,
696725
loff_t offset, size_t count, bool read)
697726
{
@@ -739,7 +768,10 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
739768

740769
ret_count = count;
741770

742-
firmware_rw(buf, buffer, offset, count, true);
771+
if (buf->data)
772+
firmware_rw_buf(buf, buffer, offset, count, true);
773+
else
774+
firmware_rw(buf, buffer, offset, count, true);
743775

744776
out:
745777
mutex_unlock(&fw_lock);
@@ -815,12 +847,21 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
815847
goto out;
816848
}
817849

818-
retval = fw_realloc_buffer(fw_priv, offset + count);
819-
if (retval)
820-
goto out;
850+
if (buf->data) {
851+
if (offset + count > buf->allocated_size) {
852+
retval = -ENOMEM;
853+
goto out;
854+
}
855+
firmware_rw_buf(buf, buffer, offset, count, false);
856+
retval = count;
857+
} else {
858+
retval = fw_realloc_buffer(fw_priv, offset + count);
859+
if (retval)
860+
goto out;
821861

822-
retval = count;
823-
firmware_rw(buf, buffer, offset, count, false);
862+
retval = count;
863+
firmware_rw(buf, buffer, offset, count, false);
864+
}
824865

825866
buf->size = max_t(size_t, offset + count, buf->size);
826867
out:
@@ -890,7 +931,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
890931
struct firmware_buf *buf = fw_priv->buf;
891932

892933
/* fall back on userspace loading */
893-
buf->is_paged_buf = true;
934+
if (!buf->data)
935+
buf->is_paged_buf = true;
894936

895937
dev_set_uevent_suppress(f_dev, true);
896938

@@ -925,7 +967,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
925967

926968
if (is_fw_load_aborted(buf))
927969
retval = -EAGAIN;
928-
else if (!buf->data)
970+
else if (buf->is_paged_buf && !buf->data)
929971
retval = -ENOMEM;
930972

931973
device_del(f_dev);
@@ -1008,7 +1050,7 @@ static int sync_cached_firmware_buf(struct firmware_buf *buf)
10081050
*/
10091051
static int
10101052
_request_firmware_prepare(struct firmware **firmware_p, const char *name,
1011-
struct device *device)
1053+
struct device *device, void *dbuf, size_t size)
10121054
{
10131055
struct firmware *firmware;
10141056
struct firmware_buf *buf;
@@ -1021,12 +1063,12 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
10211063
return -ENOMEM;
10221064
}
10231065

1024-
if (fw_get_builtin_firmware(firmware, name)) {
1066+
if (fw_get_builtin_firmware(firmware, name, dbuf, size)) {
10251067
dev_dbg(device, "using built-in %s\n", name);
10261068
return 0; /* assigned */
10271069
}
10281070

1029-
ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf);
1071+
ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf, dbuf, size);
10301072

10311073
/*
10321074
* bind with 'buf' now to avoid warning in failure path
@@ -1089,7 +1131,8 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device,
10891131
/* called from request_firmware() and request_firmware_work_func() */
10901132
static int
10911133
_request_firmware(const struct firmware **firmware_p, const char *name,
1092-
struct device *device, unsigned int opt_flags)
1134+
struct device *device, void *buf, size_t size,
1135+
unsigned int opt_flags)
10931136
{
10941137
struct firmware *fw = NULL;
10951138
long timeout;
@@ -1103,7 +1146,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
11031146
goto out;
11041147
}
11051148

1106-
ret = _request_firmware_prepare(&fw, name, device);
1149+
ret = _request_firmware_prepare(&fw, name, device, buf, size);
11071150
if (ret <= 0) /* error or already assigned */
11081151
goto out;
11091152

@@ -1182,7 +1225,7 @@ request_firmware(const struct firmware **firmware_p, const char *name,
11821225

11831226
/* Need to pin this module until return */
11841227
__module_get(THIS_MODULE);
1185-
ret = _request_firmware(firmware_p, name, device,
1228+
ret = _request_firmware(firmware_p, name, device, NULL, 0,
11861229
FW_OPT_UEVENT | FW_OPT_FALLBACK);
11871230
module_put(THIS_MODULE);
11881231
return ret;
@@ -1206,13 +1249,43 @@ int request_firmware_direct(const struct firmware **firmware_p,
12061249
int ret;
12071250

12081251
__module_get(THIS_MODULE);
1209-
ret = _request_firmware(firmware_p, name, device,
1252+
ret = _request_firmware(firmware_p, name, device, NULL, 0,
12101253
FW_OPT_UEVENT | FW_OPT_NO_WARN);
12111254
module_put(THIS_MODULE);
12121255
return ret;
12131256
}
12141257
EXPORT_SYMBOL_GPL(request_firmware_direct);
12151258

1259+
/**
1260+
* request_firmware_into_buf - load firmware into a previously allocated buffer
1261+
* @firmware_p: pointer to firmware image
1262+
* @name: name of firmware file
1263+
* @device: device for which firmware is being loaded and DMA region allocated
1264+
* @buf: address of buffer to load firmware into
1265+
* @size: size of buffer
1266+
*
1267+
* This function works pretty much like request_firmware(), but it doesn't
1268+
* allocate a buffer to hold the firmware data. Instead, the firmware
1269+
* is loaded directly into the buffer pointed to by @buf and the @firmware_p
1270+
* data member is pointed at @buf.
1271+
*
1272+
* This function doesn't cache firmware either.
1273+
*/
1274+
int
1275+
request_firmware_into_buf(const struct firmware **firmware_p, const char *name,
1276+
struct device *device, void *buf, size_t size)
1277+
{
1278+
int ret;
1279+
1280+
__module_get(THIS_MODULE);
1281+
ret = _request_firmware(firmware_p, name, device, buf, size,
1282+
FW_OPT_UEVENT | FW_OPT_FALLBACK |
1283+
FW_OPT_NOCACHE);
1284+
module_put(THIS_MODULE);
1285+
return ret;
1286+
}
1287+
EXPORT_SYMBOL(request_firmware_into_buf);
1288+
12161289
/**
12171290
* release_firmware: - release the resource associated with a firmware image
12181291
* @fw: firmware resource to release
@@ -1245,7 +1318,7 @@ static void request_firmware_work_func(struct work_struct *work)
12451318

12461319
fw_work = container_of(work, struct firmware_work, work);
12471320

1248-
_request_firmware(&fw, fw_work->name, fw_work->device,
1321+
_request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
12491322
fw_work->opt_flags);
12501323
fw_work->cont(fw, fw_work->context);
12511324
put_device(fw_work->device); /* taken in request_firmware_nowait() */
@@ -1378,7 +1451,7 @@ static int uncache_firmware(const char *fw_name)
13781451

13791452
pr_debug("%s: %s\n", __func__, fw_name);
13801453

1381-
if (fw_get_builtin_firmware(&fw, fw_name))
1454+
if (fw_get_builtin_firmware(&fw, fw_name, NULL, 0))
13821455
return 0;
13831456

13841457
buf = fw_lookup_buf(fw_name);

fs/exec.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,8 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size,
866866
goto out;
867867
}
868868

869-
*buf = vmalloc(i_size);
869+
if (id != READING_FIRMWARE_PREALLOC_BUFFER)
870+
*buf = vmalloc(i_size);
870871
if (!*buf) {
871872
ret = -ENOMEM;
872873
goto out;
@@ -897,8 +898,10 @@ int kernel_read_file(struct file *file, void **buf, loff_t *size,
897898

898899
out_free:
899900
if (ret < 0) {
900-
vfree(*buf);
901-
*buf = NULL;
901+
if (id != READING_FIRMWARE_PREALLOC_BUFFER) {
902+
vfree(*buf);
903+
*buf = NULL;
904+
}
902905
}
903906

904907
out:

include/linux/firmware.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ int request_firmware_nowait(
4747
void (*cont)(const struct firmware *fw, void *context));
4848
int request_firmware_direct(const struct firmware **fw, const char *name,
4949
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);
5052

5153
void release_firmware(const struct firmware *fw);
5254
#else
@@ -75,5 +77,11 @@ static inline int request_firmware_direct(const struct firmware **fw,
7577
return -EINVAL;
7678
}
7779

80+
static inline int request_firmware_into_buf(const struct firmware **firmware_p,
81+
const char *name, struct device *device, void *buf, size_t size)
82+
{
83+
return -EINVAL;
84+
}
85+
7886
#endif
7987
#endif

include/linux/fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2652,6 +2652,7 @@ extern int do_pipe_flags(int *, int);
26522652
#define __kernel_read_file_id(id) \
26532653
id(UNKNOWN, unknown) \
26542654
id(FIRMWARE, firmware) \
2655+
id(FIRMWARE_PREALLOC_BUFFER, firmware) \
26552656
id(MODULE, kernel-module) \
26562657
id(KEXEC_IMAGE, kexec-image) \
26572658
id(KEXEC_INITRAMFS, kexec-initramfs) \

0 commit comments

Comments
 (0)