Skip to content

Commit 7774e39

Browse files
committed
wifi: iwlwifi: fw: allocate chained SG tables for dump
The firmware dumps can be pretty big, and since we use single pages for each SG table entry, even the table itself may end up being an order-5 allocation. Build chained tables so that we need not allocate a higher-order table here. This could be improved and cleaned up, e.g. by using the SG pool code or simply kvmalloc(), but all of that would require also updating the devcoredump first since that frees it all, so we need to be more careful. SG pool might also run against the CONFIG_ARCH_NO_SG_CHAIN limitation, which is irrelevant here. Also use _devcd_free_sgtable() for the error paths now, much simpler especially since it's in two places now. Signed-off-by: Johannes Berg <[email protected]> Signed-off-by: Miri Korenblit <[email protected]> Link: https://patch.msgid.link/20250209143303.697c7a465ac9.Iea982df46b5c075bfb77ade36f187d99a70c63db@changeid Signed-off-by: Johannes Berg <[email protected]>
1 parent 646262c commit 7774e39

File tree

1 file changed

+58
-28
lines changed
  • drivers/net/wireless/intel/iwlwifi/fw

1 file changed

+58
-28
lines changed

drivers/net/wireless/intel/iwlwifi/fw/dbg.c

Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -558,41 +558,71 @@ static void iwl_dump_prph(struct iwl_fw_runtime *fwrt,
558558
}
559559

560560
/*
561-
* alloc_sgtable - allocates scallerlist table in the given size,
562-
* fills it with pages and returns it
561+
* alloc_sgtable - allocates (chained) scatterlist in the given size,
562+
* fills it with pages and returns it
563563
* @size: the size (in bytes) of the table
564-
*/
565-
static struct scatterlist *alloc_sgtable(int size)
564+
*/
565+
static struct scatterlist *alloc_sgtable(ssize_t size)
566566
{
567-
int alloc_size, nents, i;
568-
struct page *new_page;
569-
struct scatterlist *iter;
570-
struct scatterlist *table;
567+
struct scatterlist *result = NULL, *prev;
568+
int nents, i, n_prev;
571569

572570
nents = DIV_ROUND_UP(size, PAGE_SIZE);
573-
table = kcalloc(nents, sizeof(*table), GFP_KERNEL);
574-
if (!table)
575-
return NULL;
576-
sg_init_table(table, nents);
577-
iter = table;
578-
for_each_sg(table, iter, sg_nents(table), i) {
579-
new_page = alloc_page(GFP_KERNEL);
580-
if (!new_page) {
581-
/* release all previous allocated pages in the table */
582-
iter = table;
583-
for_each_sg(table, iter, sg_nents(table), i) {
584-
new_page = sg_page(iter);
585-
if (new_page)
586-
__free_page(new_page);
587-
}
588-
kfree(table);
571+
572+
#define N_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(*result))
573+
/*
574+
* We need an additional entry for table chaining,
575+
* this ensures the loop can finish i.e. we can
576+
* fit at least two entries per page (obviously,
577+
* many more really fit.)
578+
*/
579+
BUILD_BUG_ON(N_ENTRIES_PER_PAGE < 2);
580+
581+
while (nents > 0) {
582+
struct scatterlist *new, *iter;
583+
int n_fill, n_alloc;
584+
585+
if (nents <= N_ENTRIES_PER_PAGE) {
586+
/* last needed table */
587+
n_fill = nents;
588+
n_alloc = nents;
589+
nents = 0;
590+
} else {
591+
/* fill a page with entries */
592+
n_alloc = N_ENTRIES_PER_PAGE;
593+
/* reserve one for chaining */
594+
n_fill = n_alloc - 1;
595+
nents -= n_fill;
596+
}
597+
598+
new = kcalloc(n_alloc, sizeof(*new), GFP_KERNEL);
599+
if (!new) {
600+
if (result)
601+
_devcd_free_sgtable(result);
589602
return NULL;
590603
}
591-
alloc_size = min_t(int, size, PAGE_SIZE);
592-
size -= PAGE_SIZE;
593-
sg_set_page(iter, new_page, alloc_size, 0);
604+
sg_init_table(new, n_alloc);
605+
606+
if (!result)
607+
result = new;
608+
else
609+
sg_chain(prev, n_prev, new);
610+
prev = new;
611+
n_prev = n_alloc;
612+
613+
for_each_sg(new, iter, n_fill, i) {
614+
struct page *new_page = alloc_page(GFP_KERNEL);
615+
616+
if (!new_page) {
617+
_devcd_free_sgtable(result);
618+
return NULL;
619+
}
620+
621+
sg_set_page(iter, new_page, PAGE_SIZE, 0);
622+
}
594623
}
595-
return table;
624+
625+
return result;
596626
}
597627

598628
static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt,

0 commit comments

Comments
 (0)