Skip to content

Commit 5454819

Browse files
jmberg-intelaloktiwa
authored andcommitted
wifi: iwlwifi: fw: allocate chained SG tables for dump
[ Upstream commit 7774e39 ] 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]> Signed-off-by: Sasha Levin <[email protected]> (cherry picked from commit f5302f67865fd6b1febf49e9edd21af2f82fbd3e) Signed-off-by: Alok Tiwari <[email protected]>
1 parent 51d8905 commit 5454819

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
@@ -619,41 +619,71 @@ static void iwl_dump_prph(struct iwl_fw_runtime *fwrt,
619619
}
620620

621621
/*
622-
* alloc_sgtable - allocates scallerlist table in the given size,
623-
* fills it with pages and returns it
622+
* alloc_sgtable - allocates (chained) scatterlist in the given size,
623+
* fills it with pages and returns it
624624
* @size: the size (in bytes) of the table
625-
*/
626-
static struct scatterlist *alloc_sgtable(int size)
625+
*/
626+
static struct scatterlist *alloc_sgtable(ssize_t size)
627627
{
628-
int alloc_size, nents, i;
629-
struct page *new_page;
630-
struct scatterlist *iter;
631-
struct scatterlist *table;
628+
struct scatterlist *result = NULL, *prev;
629+
int nents, i, n_prev;
632630

633631
nents = DIV_ROUND_UP(size, PAGE_SIZE);
634-
table = kcalloc(nents, sizeof(*table), GFP_KERNEL);
635-
if (!table)
636-
return NULL;
637-
sg_init_table(table, nents);
638-
iter = table;
639-
for_each_sg(table, iter, sg_nents(table), i) {
640-
new_page = alloc_page(GFP_KERNEL);
641-
if (!new_page) {
642-
/* release all previous allocated pages in the table */
643-
iter = table;
644-
for_each_sg(table, iter, sg_nents(table), i) {
645-
new_page = sg_page(iter);
646-
if (new_page)
647-
__free_page(new_page);
648-
}
649-
kfree(table);
632+
633+
#define N_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(*result))
634+
/*
635+
* We need an additional entry for table chaining,
636+
* this ensures the loop can finish i.e. we can
637+
* fit at least two entries per page (obviously,
638+
* many more really fit.)
639+
*/
640+
BUILD_BUG_ON(N_ENTRIES_PER_PAGE < 2);
641+
642+
while (nents > 0) {
643+
struct scatterlist *new, *iter;
644+
int n_fill, n_alloc;
645+
646+
if (nents <= N_ENTRIES_PER_PAGE) {
647+
/* last needed table */
648+
n_fill = nents;
649+
n_alloc = nents;
650+
nents = 0;
651+
} else {
652+
/* fill a page with entries */
653+
n_alloc = N_ENTRIES_PER_PAGE;
654+
/* reserve one for chaining */
655+
n_fill = n_alloc - 1;
656+
nents -= n_fill;
657+
}
658+
659+
new = kcalloc(n_alloc, sizeof(*new), GFP_KERNEL);
660+
if (!new) {
661+
if (result)
662+
_devcd_free_sgtable(result);
650663
return NULL;
651664
}
652-
alloc_size = min_t(int, size, PAGE_SIZE);
653-
size -= PAGE_SIZE;
654-
sg_set_page(iter, new_page, alloc_size, 0);
665+
sg_init_table(new, n_alloc);
666+
667+
if (!result)
668+
result = new;
669+
else
670+
sg_chain(prev, n_prev, new);
671+
prev = new;
672+
n_prev = n_alloc;
673+
674+
for_each_sg(new, iter, n_fill, i) {
675+
struct page *new_page = alloc_page(GFP_KERNEL);
676+
677+
if (!new_page) {
678+
_devcd_free_sgtable(result);
679+
return NULL;
680+
}
681+
682+
sg_set_page(iter, new_page, PAGE_SIZE, 0);
683+
}
655684
}
656-
return table;
685+
686+
return result;
657687
}
658688

659689
static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt,

0 commit comments

Comments
 (0)