From 9b4b3f8441004eccf45a412370840188ae3a44ff Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:45 +0100 Subject: [PATCH 001/155] jfs: Convert metapage_read_folio to use folio APIs Use bio_add_folio_nofail() as we just allocated the bio and know it cannot fail. Other than that, this is a 1:1 conversion from page APIs to folio APIs. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 961569c11159..8266c43ec728 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -266,14 +266,14 @@ static void last_read_complete(struct page *page) static void metapage_read_end_io(struct bio *bio) { - struct page *page = bio->bi_private; + struct folio *folio = bio->bi_private; if (bio->bi_status) { printk(KERN_ERR "metapage_read_end_io: I/O error\n"); - SetPageError(page); + folio_set_error(folio); } - dec_io(page, last_read_complete); + dec_io(&folio->page, last_read_complete); bio_put(bio); } @@ -469,20 +469,18 @@ err_out: static int metapage_read_folio(struct file *fp, struct folio *folio) { - struct page *page = &folio->page; - struct inode *inode = page->mapping->host; + struct inode *inode = folio->mapping->host; struct bio *bio = NULL; int block_offset; - int blocks_per_page = i_blocks_per_page(inode, page); + int blocks_per_page = i_blocks_per_folio(inode, folio); sector_t page_start; /* address of page in fs blocks */ sector_t pblock; int xlen; unsigned int len; int offset; - BUG_ON(!PageLocked(page)); - page_start = (sector_t)page->index << - (PAGE_SHIFT - inode->i_blkbits); + BUG_ON(!folio_test_locked(folio)); + page_start = folio_pos(folio) >> inode->i_blkbits; block_offset = 0; while (block_offset < blocks_per_page) { @@ -490,9 +488,9 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) pblock = metapage_get_blocks(inode, page_start + block_offset, &xlen); if (pblock) { - if (!PagePrivate(page)) - insert_metapage(page, NULL); - inc_io(page); + if (!folio->private) + insert_metapage(&folio->page, NULL); + inc_io(&folio->page); if (bio) submit_bio(bio); @@ -501,11 +499,10 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) bio->bi_iter.bi_sector = pblock << (inode->i_blkbits - 9); bio->bi_end_io = metapage_read_end_io; - bio->bi_private = page; + bio->bi_private = folio; len = xlen << inode->i_blkbits; offset = block_offset << inode->i_blkbits; - if (bio_add_page(bio, page, len, offset) < len) - goto add_failed; + bio_add_folio_nofail(bio, folio, len, offset); block_offset += xlen; } else block_offset++; @@ -513,15 +510,9 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) if (bio) submit_bio(bio); else - unlock_page(page); + folio_unlock(folio); return 0; - -add_failed: - printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n"); - bio_put(bio); - dec_io(page, last_read_complete); - return -EIO; } static bool metapage_release_folio(struct folio *folio, gfp_t gfp_mask) From 35474d52c6056976e675e9130d755cdb749ded5a Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:46 +0100 Subject: [PATCH 002/155] jfs: Convert metapage_writepage to metapage_write_folio Implement writepages rather than writepage by using write_cache_pages() to call metapage_write_folio(). Use bio_add_folio_nofail() as we know we just allocated the bio. Replace the call to SetPageError (which is never checked) with a call to mapping_set_error (which ... might be checked somewhere?) Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 75 +++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 8266c43ec728..beecc9ad656e 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -4,6 +4,7 @@ * Portions Copyright (C) Christoph Hellwig, 2001-2002 */ +#include #include #include #include @@ -321,23 +322,25 @@ static void last_write_complete(struct page *page) static void metapage_write_end_io(struct bio *bio) { - struct page *page = bio->bi_private; + struct folio *folio = bio->bi_private; - BUG_ON(!PagePrivate(page)); + BUG_ON(!folio->private); if (bio->bi_status) { + int err = blk_status_to_errno(bio->bi_status); printk(KERN_ERR "metapage_write_end_io: I/O error\n"); - SetPageError(page); + mapping_set_error(folio->mapping, err); } - dec_io(page, last_write_complete); + dec_io(&folio->page, last_write_complete); bio_put(bio); } -static int metapage_writepage(struct page *page, struct writeback_control *wbc) +static int metapage_write_folio(struct folio *folio, + struct writeback_control *wbc, void *unused) { struct bio *bio = NULL; int block_offset; /* block offset of mp within page */ - struct inode *inode = page->mapping->host; + struct inode *inode = folio->mapping->host; int blocks_per_mp = JFS_SBI(inode->i_sb)->nbperpage; int len; int xlen; @@ -353,14 +356,13 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) int offset; int bad_blocks = 0; - page_start = (sector_t)page->index << - (PAGE_SHIFT - inode->i_blkbits); - BUG_ON(!PageLocked(page)); - BUG_ON(PageWriteback(page)); - set_page_writeback(page); + page_start = folio_pos(folio) >> inode->i_blkbits; + BUG_ON(!folio_test_locked(folio)); + BUG_ON(folio_test_writeback(folio)); + folio_start_writeback(folio); for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(page, offset); + mp = page_to_mp(&folio->page, offset); if (!mp || !test_bit(META_dirty, &mp->flag)) continue; @@ -389,22 +391,20 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) continue; } /* Not contiguous */ - if (bio_add_page(bio, page, bio_bytes, bio_offset) < - bio_bytes) - goto add_failed; + bio_add_folio_nofail(bio, folio, bio_bytes, bio_offset); /* * Increment counter before submitting i/o to keep * count from hitting zero before we're through */ - inc_io(page); + inc_io(&folio->page); if (!bio->bi_iter.bi_size) goto dump_bio; submit_bio(bio); nr_underway++; bio = NULL; } else - inc_io(page); - xlen = (PAGE_SIZE - offset) >> inode->i_blkbits; + inc_io(&folio->page); + xlen = (folio_size(folio) - offset) >> inode->i_blkbits; pblock = metapage_get_blocks(inode, lblock, &xlen); if (!pblock) { printk(KERN_ERR "JFS: metapage_get_blocks failed\n"); @@ -420,7 +420,7 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) bio = bio_alloc(inode->i_sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOFS); bio->bi_iter.bi_sector = pblock << (inode->i_blkbits - 9); bio->bi_end_io = metapage_write_end_io; - bio->bi_private = page; + bio->bi_private = folio; /* Don't call bio_add_page yet, we may add to this vec */ bio_offset = offset; @@ -430,8 +430,7 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) next_block = lblock + len; } if (bio) { - if (bio_add_page(bio, page, bio_bytes, bio_offset) < bio_bytes) - goto add_failed; + bio_add_folio_nofail(bio, folio, bio_bytes, bio_offset); if (!bio->bi_iter.bi_size) goto dump_bio; @@ -439,34 +438,42 @@ static int metapage_writepage(struct page *page, struct writeback_control *wbc) nr_underway++; } if (redirty) - redirty_page_for_writepage(wbc, page); + folio_redirty_for_writepage(wbc, folio); - unlock_page(page); + folio_unlock(folio); if (bad_blocks) goto err_out; if (nr_underway == 0) - end_page_writeback(page); + folio_end_writeback(folio); return 0; -add_failed: - /* We should never reach here, since we're only adding one vec */ - printk(KERN_ERR "JFS: bio_add_page failed unexpectedly\n"); - goto skip; dump_bio: print_hex_dump(KERN_ERR, "JFS: dump of bio: ", DUMP_PREFIX_ADDRESS, 16, 4, bio, sizeof(*bio), 0); -skip: bio_put(bio); - unlock_page(page); - dec_io(page, last_write_complete); + folio_unlock(folio); + dec_io(&folio->page, last_write_complete); err_out: while (bad_blocks--) - dec_io(page, last_write_complete); + dec_io(&folio->page, last_write_complete); return -EIO; } +static int metapage_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct blk_plug plug; + int err; + + blk_start_plug(&plug); + err = write_cache_pages(mapping, wbc, metapage_write_folio, NULL); + blk_finish_plug(&plug); + + return err; +} + static int metapage_read_folio(struct file *fp, struct folio *folio) { struct inode *inode = folio->mapping->host; @@ -556,7 +563,7 @@ static void metapage_invalidate_folio(struct folio *folio, size_t offset, const struct address_space_operations jfs_metapage_aops = { .read_folio = metapage_read_folio, - .writepage = metapage_writepage, + .writepages = metapage_writepages, .release_folio = metapage_release_folio, .invalidate_folio = metapage_invalidate_folio, .dirty_folio = filemap_dirty_folio, @@ -698,7 +705,7 @@ static int metapage_write_one(struct page *page) if (folio_clear_dirty_for_io(folio)) { folio_get(folio); - ret = metapage_writepage(page, &wbc); + ret = metapage_write_folio(folio, &wbc, NULL); if (ret == 0) folio_wait_writeback(folio); folio_put(folio); From 2dcd9630d988509aa87de2ce7826a697f8ce58ea Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:47 +0100 Subject: [PATCH 003/155] jfs: Convert __get_metapage to use a folio Remove four hidden calls to compound_head(). Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index beecc9ad656e..4ef85e264f51 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -577,7 +577,7 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, int l2bsize; struct address_space *mapping; struct metapage *mp = NULL; - struct page *page; + struct folio *folio; unsigned long page_index; unsigned long page_offset; @@ -608,22 +608,22 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, } if (new && (PSIZE == PAGE_SIZE)) { - page = grab_cache_page(mapping, page_index); - if (!page) { - jfs_err("grab_cache_page failed!"); + folio = filemap_grab_folio(mapping, page_index); + if (IS_ERR(folio)) { + jfs_err("filemap_grab_folio failed!"); return NULL; } - SetPageUptodate(page); + folio_mark_uptodate(folio); } else { - page = read_mapping_page(mapping, page_index, NULL); - if (IS_ERR(page)) { + folio = read_mapping_folio(mapping, page_index, NULL); + if (IS_ERR(folio)) { jfs_err("read_mapping_page failed!"); return NULL; } - lock_page(page); + folio_lock(folio); } - mp = page_to_mp(page, page_offset); + mp = page_to_mp(&folio->page, page_offset); if (mp) { if (mp->logical_size != size) { jfs_error(inode->i_sb, @@ -649,16 +649,16 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, mp = alloc_metapage(GFP_NOFS); if (!mp) goto unlock; - mp->page = page; + mp->page = &folio->page; mp->sb = inode->i_sb; mp->flag = 0; mp->xflag = COMMIT_PAGE; mp->count = 1; mp->nohomeok = 0; mp->logical_size = size; - mp->data = page_address(page) + page_offset; + mp->data = folio_address(folio) + page_offset; mp->index = lblock; - if (unlikely(insert_metapage(page, mp))) { + if (unlikely(insert_metapage(&folio->page, mp))) { free_metapage(mp); goto unlock; } @@ -670,12 +670,12 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, memset(mp->data, 0, PSIZE); } - unlock_page(page); + folio_unlock(folio); jfs_info("__get_metapage: returning = 0x%p data = 0x%p", mp, mp->data); return mp; unlock: - unlock_page(page); + folio_unlock(folio); return NULL; } From 9346476d211611f3c0d512cb6e942ab76f5376d8 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:48 +0100 Subject: [PATCH 004/155] jfs: Convert insert_metapage() to take a folio Both of its callers now have a folio, so convert this function. Use folio_attach_private() instead of manually setting folio->private. This also gets the expected refcount of the folio correct. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 4ef85e264f51..6fa7023f5bc9 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -87,25 +87,23 @@ static inline struct metapage *page_to_mp(struct page *page, int offset) return mp_anchor(page)->mp[offset >> L2PSIZE]; } -static inline int insert_metapage(struct page *page, struct metapage *mp) +static inline int insert_metapage(struct folio *folio, struct metapage *mp) { struct meta_anchor *a; int index; int l2mp_blocks; /* log2 blocks per metapage */ - if (PagePrivate(page)) - a = mp_anchor(page); - else { + a = folio->private; + if (!a) { a = kzalloc(sizeof(struct meta_anchor), GFP_NOFS); if (!a) return -ENOMEM; - set_page_private(page, (unsigned long)a); - SetPagePrivate(page); - kmap(page); + folio_attach_private(folio, a); + kmap(&folio->page); } if (mp) { - l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits; + l2mp_blocks = L2PSIZE - folio->mapping->host->i_blkbits; index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1); a->mp_count++; a->mp[index] = mp; @@ -127,8 +125,7 @@ static inline void remove_metapage(struct page *page, struct metapage *mp) a->mp[index] = NULL; if (--a->mp_count == 0) { kfree(a); - set_page_private(page, 0); - ClearPagePrivate(page); + detach_page_private(page); kunmap(page); } } @@ -150,20 +147,18 @@ static inline struct metapage *page_to_mp(struct page *page, int offset) return PagePrivate(page) ? (struct metapage *)page_private(page) : NULL; } -static inline int insert_metapage(struct page *page, struct metapage *mp) +static inline int insert_metapage(struct folio *folio, struct metapage *mp) { if (mp) { - set_page_private(page, (unsigned long)mp); - SetPagePrivate(page); - kmap(page); + folio_attach_private(folio, mp); + kmap(&folio->page); } return 0; } static inline void remove_metapage(struct page *page, struct metapage *mp) { - set_page_private(page, 0); - ClearPagePrivate(page); + detach_page_private(page); kunmap(page); } @@ -496,7 +491,7 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) &xlen); if (pblock) { if (!folio->private) - insert_metapage(&folio->page, NULL); + insert_metapage(folio, NULL); inc_io(&folio->page); if (bio) submit_bio(bio); @@ -658,7 +653,7 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, mp->logical_size = size; mp->data = folio_address(folio) + page_offset; mp->index = lblock; - if (unlikely(insert_metapage(&folio->page, mp))) { + if (unlikely(insert_metapage(folio, mp))) { free_metapage(mp); goto unlock; } From 40e1bd195b0ee3880699bb0ae1897e9f9116aa29 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:49 +0100 Subject: [PATCH 005/155] jfs; Convert release_metapage to use a folio Convert mp->page to a folio and remove 7 hidden calls to compound_head(). Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 6fa7023f5bc9..4515dc1ac40e 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -749,37 +749,35 @@ void put_metapage(struct metapage *mp) void release_metapage(struct metapage * mp) { - struct page *page = mp->page; + struct folio *folio = page_folio(mp->page); jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag); - BUG_ON(!page); - - lock_page(page); + folio_lock(folio); unlock_metapage(mp); assert(mp->count); if (--mp->count || mp->nohomeok) { - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); return; } if (test_bit(META_dirty, &mp->flag)) { - set_page_dirty(page); + folio_mark_dirty(folio); if (test_bit(META_sync, &mp->flag)) { clear_bit(META_sync, &mp->flag); - if (metapage_write_one(page)) + if (metapage_write_one(&folio->page)) jfs_error(mp->sb, "metapage_write_one() failed\n"); - lock_page(page); + folio_lock(folio); } } else if (mp->lsn) /* discard_metapage doesn't remove it */ remove_from_logsync(mp); /* Try to keep metapages from using up too much memory */ - drop_metapage(page, mp); + drop_metapage(&folio->page, mp); - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); } void __invalidate_metapages(struct inode *ip, s64 addr, int len) From dd23bf310384d8b48985b41640e3f0bc2b5226f9 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:50 +0100 Subject: [PATCH 006/155] jfs: Convert drop_metapage and remove_metapage to take a folio All callers now have a folio, so pass it in instead of the page. Removes a couple of calls to compound_head(). Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 4515dc1ac40e..9fc52c27b0ce 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -112,10 +112,10 @@ static inline int insert_metapage(struct folio *folio, struct metapage *mp) return 0; } -static inline void remove_metapage(struct page *page, struct metapage *mp) +static inline void remove_metapage(struct folio *folio, struct metapage *mp) { - struct meta_anchor *a = mp_anchor(page); - int l2mp_blocks = L2PSIZE - page->mapping->host->i_blkbits; + struct meta_anchor *a = folio->private; + int l2mp_blocks = L2PSIZE - folio->mapping->host->i_blkbits; int index; index = (mp->index >> l2mp_blocks) & (MPS_PER_PAGE - 1); @@ -125,8 +125,8 @@ static inline void remove_metapage(struct page *page, struct metapage *mp) a->mp[index] = NULL; if (--a->mp_count == 0) { kfree(a); - detach_page_private(page); - kunmap(page); + folio_detach_private(folio); + kunmap(&folio->page); } } @@ -156,10 +156,10 @@ static inline int insert_metapage(struct folio *folio, struct metapage *mp) return 0; } -static inline void remove_metapage(struct page *page, struct metapage *mp) +static inline void remove_metapage(struct folio *folio, struct metapage *mp) { - detach_page_private(page); - kunmap(page); + folio_detach_private(folio); + kunmap(&folio->page); } #define inc_io(page) do {} while(0) @@ -214,12 +214,12 @@ void metapage_exit(void) kmem_cache_destroy(metapage_cache); } -static inline void drop_metapage(struct page *page, struct metapage *mp) +static inline void drop_metapage(struct folio *folio, struct metapage *mp) { if (mp->count || mp->nohomeok || test_bit(META_dirty, &mp->flag) || test_bit(META_io, &mp->flag)) return; - remove_metapage(page, mp); + remove_metapage(folio, mp); INCREMENT(mpStat.pagefree); free_metapage(mp); } @@ -539,7 +539,7 @@ static bool metapage_release_folio(struct folio *folio, gfp_t gfp_mask) } if (mp->lsn) remove_from_logsync(mp); - remove_metapage(&folio->page, mp); + remove_metapage(folio, mp); INCREMENT(mpStat.pagefree); free_metapage(mp); } @@ -774,7 +774,7 @@ void release_metapage(struct metapage * mp) remove_from_logsync(mp); /* Try to keep metapages from using up too much memory */ - drop_metapage(&folio->page, mp); + drop_metapage(folio, mp); folio_unlock(folio); folio_put(folio); From 1f0dc610da985673dd5fc4243f20751eb2df66a7 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:51 +0100 Subject: [PATCH 007/155] jfs: Convert dec_io to take a folio This means also converting the two handlers to take a folio. Saves four calls to compound_head(). Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 9fc52c27b0ce..dd540df0a617 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -135,10 +135,12 @@ static inline void inc_io(struct page *page) atomic_inc(&mp_anchor(page)->io_count); } -static inline void dec_io(struct page *page, void (*handler) (struct page *)) +static inline void dec_io(struct folio *folio, void (*handler) (struct folio *)) { - if (atomic_dec_and_test(&mp_anchor(page)->io_count)) - handler(page); + struct meta_anchor *anchor = folio->private; + + if (atomic_dec_and_test(&anchor->io_count)) + handler(folio); } #else @@ -163,7 +165,7 @@ static inline void remove_metapage(struct folio *folio, struct metapage *mp) } #define inc_io(page) do {} while(0) -#define dec_io(page, handler) handler(page) +#define dec_io(folio, handler) handler(folio) #endif @@ -253,11 +255,11 @@ static sector_t metapage_get_blocks(struct inode *inode, sector_t lblock, return lblock; } -static void last_read_complete(struct page *page) +static void last_read_complete(struct folio *folio) { - if (!PageError(page)) - SetPageUptodate(page); - unlock_page(page); + if (!folio_test_error(folio)) + folio_mark_uptodate(folio); + folio_unlock(folio); } static void metapage_read_end_io(struct bio *bio) @@ -269,7 +271,7 @@ static void metapage_read_end_io(struct bio *bio) folio_set_error(folio); } - dec_io(&folio->page, last_read_complete); + dec_io(folio, last_read_complete); bio_put(bio); } @@ -295,13 +297,13 @@ static void remove_from_logsync(struct metapage *mp) LOGSYNC_UNLOCK(log, flags); } -static void last_write_complete(struct page *page) +static void last_write_complete(struct folio *folio) { struct metapage *mp; unsigned int offset; for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(page, offset); + mp = page_to_mp(&folio->page, offset); if (mp && test_bit(META_io, &mp->flag)) { if (mp->lsn) remove_from_logsync(mp); @@ -312,7 +314,7 @@ static void last_write_complete(struct page *page) * safe unless I have the page locked */ } - end_page_writeback(page); + folio_end_writeback(folio); } static void metapage_write_end_io(struct bio *bio) @@ -326,7 +328,7 @@ static void metapage_write_end_io(struct bio *bio) printk(KERN_ERR "metapage_write_end_io: I/O error\n"); mapping_set_error(folio->mapping, err); } - dec_io(&folio->page, last_write_complete); + dec_io(folio, last_write_complete); bio_put(bio); } @@ -449,10 +451,10 @@ dump_bio: 4, bio, sizeof(*bio), 0); bio_put(bio); folio_unlock(folio); - dec_io(&folio->page, last_write_complete); + dec_io(folio, last_write_complete); err_out: while (bad_blocks--) - dec_io(&folio->page, last_write_complete); + dec_io(folio, last_write_complete); return -EIO; } From f86a3a182483f2c3ec4d83b8d972da8f74882a42 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:52 +0100 Subject: [PATCH 008/155] jfs; Convert __invalidate_metapages to use a folio Retrieve a folio from the page cache instead of a page. Saves a couple of calls to compound_head(). Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index dd540df0a617..90a284d3bef7 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -791,7 +791,6 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len) struct address_space *mapping = JFS_SBI(ip->i_sb)->direct_inode->i_mapping; struct metapage *mp; - struct page *page; unsigned int offset; /* @@ -800,11 +799,12 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len) */ for (lblock = addr & ~(BlocksPerPage - 1); lblock < addr + len; lblock += BlocksPerPage) { - page = find_lock_page(mapping, lblock >> l2BlocksPerPage); - if (!page) + struct folio *folio = filemap_lock_folio(mapping, + lblock >> l2BlocksPerPage); + if (IS_ERR(folio)) continue; for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(page, offset); + mp = page_to_mp(&folio->page, offset); if (!mp) continue; if (mp->index < addr) @@ -817,8 +817,8 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len) if (mp->lsn) remove_from_logsync(mp); } - unlock_page(page); - put_page(page); + folio_unlock(folio); + folio_put(folio); } } From 501bb988774b38cfe41783aed53d8890c87f1b1e Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:53 +0100 Subject: [PATCH 009/155] jfs: Convert page_to_mp to folio_to_mp Access folio->private directly instead of testing the page private flag. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 90a284d3bef7..67d5d417fe01 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -80,11 +80,13 @@ struct meta_anchor { }; #define mp_anchor(page) ((struct meta_anchor *)page_private(page)) -static inline struct metapage *page_to_mp(struct page *page, int offset) +static inline struct metapage *folio_to_mp(struct folio *folio, int offset) { - if (!PagePrivate(page)) + struct meta_anchor *anchor = folio->private; + + if (!anchor) return NULL; - return mp_anchor(page)->mp[offset >> L2PSIZE]; + return anchor->mp[offset >> L2PSIZE]; } static inline int insert_metapage(struct folio *folio, struct metapage *mp) @@ -144,9 +146,9 @@ static inline void dec_io(struct folio *folio, void (*handler) (struct folio *)) } #else -static inline struct metapage *page_to_mp(struct page *page, int offset) +static inline struct metapage *folio_to_mp(struct folio *folio, int offset) { - return PagePrivate(page) ? (struct metapage *)page_private(page) : NULL; + return folio->private; } static inline int insert_metapage(struct folio *folio, struct metapage *mp) @@ -303,7 +305,7 @@ static void last_write_complete(struct folio *folio) unsigned int offset; for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(&folio->page, offset); + mp = folio_to_mp(folio, offset); if (mp && test_bit(META_io, &mp->flag)) { if (mp->lsn) remove_from_logsync(mp); @@ -359,7 +361,7 @@ static int metapage_write_folio(struct folio *folio, folio_start_writeback(folio); for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(&folio->page, offset); + mp = folio_to_mp(folio, offset); if (!mp || !test_bit(META_dirty, &mp->flag)) continue; @@ -526,7 +528,7 @@ static bool metapage_release_folio(struct folio *folio, gfp_t gfp_mask) int offset; for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(&folio->page, offset); + mp = folio_to_mp(folio, offset); if (!mp) continue; @@ -620,7 +622,7 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, folio_lock(folio); } - mp = page_to_mp(&folio->page, page_offset); + mp = folio_to_mp(folio, page_offset); if (mp) { if (mp->logical_size != size) { jfs_error(inode->i_sb, @@ -804,7 +806,7 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len) if (IS_ERR(folio)) continue; for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { - mp = page_to_mp(&folio->page, offset); + mp = folio_to_mp(folio, offset); if (!mp) continue; if (mp->index < addr) From d9c36002d015e49df77cc5dc733f09beaa32765e Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:54 +0100 Subject: [PATCH 010/155] jfs: Convert inc_io to take a folio All their callers now have a folio, so pass it in. Remove mp_anchor() as inc_io() was the last user. No savings here, just cleaning up some remnants. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 67d5d417fe01..f03e217ec1cb 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -78,7 +78,6 @@ struct meta_anchor { atomic_t io_count; struct metapage *mp[MPS_PER_PAGE]; }; -#define mp_anchor(page) ((struct meta_anchor *)page_private(page)) static inline struct metapage *folio_to_mp(struct folio *folio, int offset) { @@ -132,9 +131,11 @@ static inline void remove_metapage(struct folio *folio, struct metapage *mp) } } -static inline void inc_io(struct page *page) +static inline void inc_io(struct folio *folio) { - atomic_inc(&mp_anchor(page)->io_count); + struct meta_anchor *anchor = folio->private; + + atomic_inc(&anchor->io_count); } static inline void dec_io(struct folio *folio, void (*handler) (struct folio *)) @@ -166,7 +167,7 @@ static inline void remove_metapage(struct folio *folio, struct metapage *mp) kunmap(&folio->page); } -#define inc_io(page) do {} while(0) +#define inc_io(folio) do {} while(0) #define dec_io(folio, handler) handler(folio) #endif @@ -395,14 +396,14 @@ static int metapage_write_folio(struct folio *folio, * Increment counter before submitting i/o to keep * count from hitting zero before we're through */ - inc_io(&folio->page); + inc_io(folio); if (!bio->bi_iter.bi_size) goto dump_bio; submit_bio(bio); nr_underway++; bio = NULL; } else - inc_io(&folio->page); + inc_io(folio); xlen = (folio_size(folio) - offset) >> inode->i_blkbits; pblock = metapage_get_blocks(inode, lblock, &xlen); if (!pblock) { @@ -496,7 +497,7 @@ static int metapage_read_folio(struct file *fp, struct folio *folio) if (pblock) { if (!folio->private) insert_metapage(folio, NULL); - inc_io(&folio->page); + inc_io(folio); if (bio) submit_bio(bio); From 1252ad136e1680284c6313156d81d39bc38354c6 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:55 +0100 Subject: [PATCH 011/155] jfs: Convert force_metapage to use a folio Convert the mp->page to a folio and operate on it. That lets us convert metapage_write_one() to take a folio. Replaces five calls to compound_head() with one. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index f03e217ec1cb..c88a7bc3f736 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -689,9 +689,8 @@ void grab_metapage(struct metapage * mp) unlock_page(mp->page); } -static int metapage_write_one(struct page *page) +static int metapage_write_one(struct folio *folio) { - struct folio *folio = page_folio(page); struct address_space *mapping = folio->mapping; struct writeback_control wbc = { .sync_mode = WB_SYNC_ALL, @@ -720,17 +719,17 @@ static int metapage_write_one(struct page *page) void force_metapage(struct metapage *mp) { - struct page *page = mp->page; + struct folio *folio = page_folio(mp->page); jfs_info("force_metapage: mp = 0x%p", mp); set_bit(META_forcewrite, &mp->flag); clear_bit(META_sync, &mp->flag); - get_page(page); - lock_page(page); - set_page_dirty(page); - if (metapage_write_one(page)) + folio_get(folio); + folio_lock(folio); + folio_mark_dirty(folio); + if (metapage_write_one(folio)) jfs_error(mp->sb, "metapage_write_one() failed\n"); clear_bit(META_forcewrite, &mp->flag); - put_page(page); + folio_put(folio); } void hold_metapage(struct metapage *mp) @@ -771,7 +770,7 @@ void release_metapage(struct metapage * mp) folio_mark_dirty(folio); if (test_bit(META_sync, &mp->flag)) { clear_bit(META_sync, &mp->flag); - if (metapage_write_one(&folio->page)) + if (metapage_write_one(folio)) jfs_error(mp->sb, "metapage_write_one() failed\n"); folio_lock(folio); } From ad6c19e5f9323a2e6c6d1316e53f71f305a6a60f Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:56 +0100 Subject: [PATCH 012/155] jfs: Change metapage->page to metapage->folio Convert all the users to operate on a folio. Saves sixteen calls to compound_head(). We still use sizeof(struct page) in print_hex_dump, otherwise it will go into the second and third pages of the folio which won't exist for jfs folios (since they are not large). This needs a better solution, but finding it can be postponed. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_logmgr.c | 2 +- fs/jfs/jfs_metapage.c | 26 +++++++++++++------------- fs/jfs/jfs_metapage.h | 16 ++++++++-------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c index 9609349e92e5..270808b6219b 100644 --- a/fs/jfs/jfs_logmgr.c +++ b/fs/jfs/jfs_logmgr.c @@ -1600,7 +1600,7 @@ void jfs_flush_journal(struct jfs_log *log, int wait) mp, sizeof(struct metapage), 0); print_hex_dump(KERN_ERR, "page: ", DUMP_PREFIX_ADDRESS, 16, - sizeof(long), mp->page, + sizeof(long), mp->folio, sizeof(struct page), 0); } else print_hex_dump(KERN_ERR, "tblock:", diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index c88a7bc3f736..19854bd8dfea 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -47,9 +47,9 @@ static inline void __lock_metapage(struct metapage *mp) do { set_current_state(TASK_UNINTERRUPTIBLE); if (metapage_locked(mp)) { - unlock_page(mp->page); + folio_unlock(mp->folio); io_schedule(); - lock_page(mp->page); + folio_lock(mp->folio); } } while (trylock_metapage(mp)); __set_current_state(TASK_RUNNING); @@ -57,7 +57,7 @@ static inline void __lock_metapage(struct metapage *mp) } /* - * Must have mp->page locked + * Must have mp->folio locked */ static inline void lock_metapage(struct metapage *mp) { @@ -649,7 +649,7 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, mp = alloc_metapage(GFP_NOFS); if (!mp) goto unlock; - mp->page = &folio->page; + mp->folio = folio; mp->sb = inode->i_sb; mp->flag = 0; mp->xflag = COMMIT_PAGE; @@ -682,11 +682,11 @@ unlock: void grab_metapage(struct metapage * mp) { jfs_info("grab_metapage: mp = 0x%p", mp); - get_page(mp->page); - lock_page(mp->page); + folio_get(mp->folio); + folio_lock(mp->folio); mp->count++; lock_metapage(mp); - unlock_page(mp->page); + folio_unlock(mp->folio); } static int metapage_write_one(struct folio *folio) @@ -719,7 +719,7 @@ static int metapage_write_one(struct folio *folio) void force_metapage(struct metapage *mp) { - struct folio *folio = page_folio(mp->page); + struct folio *folio = mp->folio; jfs_info("force_metapage: mp = 0x%p", mp); set_bit(META_forcewrite, &mp->flag); clear_bit(META_sync, &mp->flag); @@ -734,26 +734,26 @@ void force_metapage(struct metapage *mp) void hold_metapage(struct metapage *mp) { - lock_page(mp->page); + folio_lock(mp->folio); } void put_metapage(struct metapage *mp) { if (mp->count || mp->nohomeok) { /* Someone else will release this */ - unlock_page(mp->page); + folio_unlock(mp->folio); return; } - get_page(mp->page); + folio_get(mp->folio); mp->count++; lock_metapage(mp); - unlock_page(mp->page); + folio_unlock(mp->folio); release_metapage(mp); } void release_metapage(struct metapage * mp) { - struct folio *folio = page_folio(mp->page); + struct folio *folio = mp->folio; jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag); folio_lock(folio); diff --git a/fs/jfs/jfs_metapage.h b/fs/jfs/jfs_metapage.h index 4179f9df4deb..2e5015c2705b 100644 --- a/fs/jfs/jfs_metapage.h +++ b/fs/jfs/jfs_metapage.h @@ -24,7 +24,7 @@ struct metapage { wait_queue_head_t wait; /* implementation */ - struct page *page; + struct folio *folio; struct super_block *sb; unsigned int logical_size; @@ -90,14 +90,14 @@ static inline void discard_metapage(struct metapage *mp) static inline void metapage_nohomeok(struct metapage *mp) { - struct page *page = mp->page; - lock_page(page); + struct folio *folio = mp->folio; + folio_lock(folio); if (!mp->nohomeok++) { mark_metapage_dirty(mp); - get_page(page); - wait_on_page_writeback(page); + folio_get(folio); + folio_wait_writeback(folio); } - unlock_page(page); + folio_unlock(folio); } /* @@ -107,7 +107,7 @@ static inline void metapage_nohomeok(struct metapage *mp) static inline void metapage_wait_for_io(struct metapage *mp) { if (test_bit(META_io, &mp->flag)) - wait_on_page_writeback(mp->page); + folio_wait_writeback(mp->folio); } /* @@ -116,7 +116,7 @@ static inline void metapage_wait_for_io(struct metapage *mp) static inline void _metapage_homeok(struct metapage *mp) { if (!--mp->nohomeok) - put_page(mp->page); + folio_put(mp->folio); } static inline void metapage_homeok(struct metapage *mp) From 3fefd9b594aa6be9f120733f5bb57b07d47871a9 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Wed, 17 Apr 2024 18:56:57 +0100 Subject: [PATCH 013/155] fs: Remove i_blocks_per_page The last caller has been converted to i_blocks_per_folio() so we can remove this wrapper. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- include/linux/pagemap.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 3d69589c00a4..63f2f3602a7f 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -1536,10 +1536,4 @@ unsigned int i_blocks_per_folio(struct inode *inode, struct folio *folio) { return folio_size(folio) >> inode->i_blkbits; } - -static inline -unsigned int i_blocks_per_page(struct inode *inode, struct page *page) -{ - return i_blocks_per_folio(inode, page_folio(page)); -} #endif /* _LINUX_PAGEMAP_H */ From ee6817e72d4e690b65ce0f77f406ed1a1ac5b5c2 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Sat, 20 Apr 2024 03:50:00 +0100 Subject: [PATCH 014/155] jfs: Remove use of folio error flag Store the blk_status per folio (if we can have multiple metapages per folio) instead of setting the folio error flag. This will allow us to reclaim a precious folio flag shortly. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_metapage.c | 47 +++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 19854bd8dfea..df575a873ec6 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -76,6 +76,7 @@ static mempool_t *metapage_mempool; struct meta_anchor { int mp_count; atomic_t io_count; + blk_status_t status; struct metapage *mp[MPS_PER_PAGE]; }; @@ -138,12 +139,16 @@ static inline void inc_io(struct folio *folio) atomic_inc(&anchor->io_count); } -static inline void dec_io(struct folio *folio, void (*handler) (struct folio *)) +static inline void dec_io(struct folio *folio, blk_status_t status, + void (*handler)(struct folio *, blk_status_t)) { struct meta_anchor *anchor = folio->private; + if (anchor->status == BLK_STS_OK) + anchor->status = status; + if (atomic_dec_and_test(&anchor->io_count)) - handler(folio); + handler(folio, anchor->status); } #else @@ -168,7 +173,7 @@ static inline void remove_metapage(struct folio *folio, struct metapage *mp) } #define inc_io(folio) do {} while(0) -#define dec_io(folio, handler) handler(folio) +#define dec_io(folio, status, handler) handler(folio, status) #endif @@ -258,23 +263,20 @@ static sector_t metapage_get_blocks(struct inode *inode, sector_t lblock, return lblock; } -static void last_read_complete(struct folio *folio) +static void last_read_complete(struct folio *folio, blk_status_t status) { - if (!folio_test_error(folio)) - folio_mark_uptodate(folio); - folio_unlock(folio); + if (status) + printk(KERN_ERR "Read error %d at %#llx\n", status, + folio_pos(folio)); + + folio_end_read(folio, status == 0); } static void metapage_read_end_io(struct bio *bio) { struct folio *folio = bio->bi_private; - if (bio->bi_status) { - printk(KERN_ERR "metapage_read_end_io: I/O error\n"); - folio_set_error(folio); - } - - dec_io(folio, last_read_complete); + dec_io(folio, bio->bi_status, last_read_complete); bio_put(bio); } @@ -300,11 +302,17 @@ static void remove_from_logsync(struct metapage *mp) LOGSYNC_UNLOCK(log, flags); } -static void last_write_complete(struct folio *folio) +static void last_write_complete(struct folio *folio, blk_status_t status) { struct metapage *mp; unsigned int offset; + if (status) { + int err = blk_status_to_errno(status); + printk(KERN_ERR "metapage_write_end_io: I/O error\n"); + mapping_set_error(folio->mapping, err); + } + for (offset = 0; offset < PAGE_SIZE; offset += PSIZE) { mp = folio_to_mp(folio, offset); if (mp && test_bit(META_io, &mp->flag)) { @@ -326,12 +334,7 @@ static void metapage_write_end_io(struct bio *bio) BUG_ON(!folio->private); - if (bio->bi_status) { - int err = blk_status_to_errno(bio->bi_status); - printk(KERN_ERR "metapage_write_end_io: I/O error\n"); - mapping_set_error(folio->mapping, err); - } - dec_io(folio, last_write_complete); + dec_io(folio, bio->bi_status, last_write_complete); bio_put(bio); } @@ -454,10 +457,10 @@ dump_bio: 4, bio, sizeof(*bio), 0); bio_put(bio); folio_unlock(folio); - dec_io(folio, last_write_complete); + dec_io(folio, BLK_STS_OK, last_write_complete); err_out: while (bad_blocks--) - dec_io(folio, last_write_complete); + dec_io(folio, BLK_STS_OK, last_write_complete); return -EIO; } From 9dbd9962cfe56d210be5232349851420b5f9c8f6 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 20 May 2024 13:27:24 +0200 Subject: [PATCH 015/155] remoteproc: mediatek: Zero out only remaining bytes of IPI buffer In scp_ipi_handler(), instead of zeroing out the entire shared buffer, which may be as large as 600 bytes, overwrite it with the received data, then zero out only the remaining bytes. Signed-off-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20240520112724.139945-1-angelogioacchino.delregno@collabora.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/mtk_scp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index b8498772dba1..b885a9a041e4 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -117,8 +117,8 @@ static void scp_ipi_handler(struct mtk_scp *scp) return; } - memset(scp->share_buf, 0, scp_sizes->ipi_share_buffer_size); memcpy_fromio(scp->share_buf, &rcv_obj->share_buf, len); + memset(&scp->share_buf[len], 0, scp_sizes->ipi_share_buffer_size - len); handler(scp->share_buf, len, ipi_desc[id].priv); scp_ipi_unlock(scp, id); From f720fa010d03bbb336cf8dcb949e939b5cfba610 Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Fri, 17 May 2024 18:56:54 +0200 Subject: [PATCH 016/155] rpmsg: char: Fix rpmsg_eptdev structure documentation Add missing @ tags for some rpmsg_eptdev structure parameters. This fixes warning messages on build: drivers/rpmsg/rpmsg_char.c:75: warning: Function parameter or struct member 'remote_flow_restricted' not described in 'rpmsg_eptdev' drivers/rpmsg/rpmsg_char.c:75: warning: Function parameter or struct member 'remote_flow_updated' not described in 'rpmsg_eptdev' Fixes: 5550201c0fe2 ("rpmsg: char: Add RPMSG GET/SET FLOWCONTROL IOCTL support") Signed-off-by: Arnaud Pouliquen Link: https://lore.kernel.org/r/20240517165654.427746-1-arnaud.pouliquen@foss.st.com Signed-off-by: Mathieu Poirier --- drivers/rpmsg/rpmsg_char.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index d7a342510902..3ec02751bc35 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -52,8 +52,8 @@ static DEFINE_IDA(rpmsg_minor_ida); * @readq: wait object for incoming queue * @default_ept: set to channel default endpoint if the default endpoint should be re-used * on device open to prevent endpoint address update. - * remote_flow_restricted: to indicate if the remote has requested for flow to be limited - * remote_flow_updated: to indicate if the flow control has been requested + * @remote_flow_restricted: to indicate if the remote has requested for flow to be limited + * @remote_flow_updated: to indicate if the flow control has been requested */ struct rpmsg_eptdev { struct device dev; From c3281abea67c9c0dc6219bbc41d1feae05a16da3 Mon Sep 17 00:00:00 2001 From: Gwenael Treuveur Date: Tue, 21 May 2024 18:23:16 +0200 Subject: [PATCH 017/155] remoteproc: stm32_rproc: Fix mailbox interrupts queuing Manage interrupt coming from coprocessor also when state is ATTACHED. Fixes: 35bdafda40cc ("remoteproc: stm32_rproc: Add mutex protection for workqueue") Cc: stable@vger.kernel.org Signed-off-by: Gwenael Treuveur Acked-by: Arnaud Pouliquen Link: https://lore.kernel.org/r/20240521162316.156259-1-gwenael.treuveur@foss.st.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/stm32_rproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 88623df7d0c3..8c7f7950b80e 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -294,7 +294,7 @@ static void stm32_rproc_mb_vq_work(struct work_struct *work) mutex_lock(&rproc->lock); - if (rproc->state != RPROC_RUNNING) + if (rproc->state != RPROC_RUNNING && rproc->state != RPROC_ATTACHED) goto unlock_mutex; if (rproc_vq_interrupt(rproc, mb->vq_id) == IRQ_NONE) From 7c327d56597d8de1680cf24e956b704270d3d84a Mon Sep 17 00:00:00 2001 From: Richard Maina Date: Wed, 29 May 2024 11:09:55 -0700 Subject: [PATCH 018/155] hwspinlock: Introduce hwspin_lock_bust() When a remoteproc crashes or goes down unexpectedly this can result in a state where locks held by the remoteproc will remain locked possibly resulting in deadlock. This new API hwspin_lock_bust() allows hwspinlock implementers to define a bust operation for freeing previously acquired hwspinlocks after verifying ownership of the acquired lock. Signed-off-by: Richard Maina Reviewed-by: Bjorn Andersson Signed-off-by: Chris Lew Link: https://lore.kernel.org/r/20240529-hwspinlock-bust-v3-1-c8b924ffa5a2@quicinc.com Signed-off-by: Bjorn Andersson --- Documentation/locking/hwspinlock.rst | 11 ++++++++++ drivers/hwspinlock/hwspinlock_core.c | 28 ++++++++++++++++++++++++ drivers/hwspinlock/hwspinlock_internal.h | 3 +++ include/linux/hwspinlock.h | 6 +++++ 4 files changed, 48 insertions(+) diff --git a/Documentation/locking/hwspinlock.rst b/Documentation/locking/hwspinlock.rst index 6f03713b7003..2ffaa3cbd63f 100644 --- a/Documentation/locking/hwspinlock.rst +++ b/Documentation/locking/hwspinlock.rst @@ -85,6 +85,17 @@ is already free). Should be called from a process context (might sleep). +:: + + int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id); + +After verifying the owner of the hwspinlock, release a previously acquired +hwspinlock; returns 0 on success, or an appropriate error code on failure +(e.g. -EOPNOTSUPP if the bust operation is not defined for the specific +hwspinlock). + +Should be called from a process context (might sleep). + :: int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int timeout); diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index 0c0a932c00f3..6505261e6068 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -305,6 +305,34 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) } EXPORT_SYMBOL_GPL(__hwspin_unlock); +/** + * hwspin_lock_bust() - bust a specific hwspinlock + * @hwlock: a previously-acquired hwspinlock which we want to bust + * @id: identifier of the remote lock holder, if applicable + * + * This function will bust a hwspinlock that was previously acquired as + * long as the current owner of the lock matches the id given by the caller. + * + * Context: Process context. + * + * Returns: 0 on success, or -EINVAL if the hwspinlock does not exist, or + * the bust operation fails, and -EOPNOTSUPP if the bust operation is not + * defined for the hwspinlock. + */ +int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id) +{ + if (WARN_ON(!hwlock)) + return -EINVAL; + + if (!hwlock->bank->ops->bust) { + pr_err("bust operation not defined\n"); + return -EOPNOTSUPP; + } + + return hwlock->bank->ops->bust(hwlock, id); +} +EXPORT_SYMBOL_GPL(hwspin_lock_bust); + /** * of_hwspin_lock_simple_xlate - translate hwlock_spec to return a lock id * @hwlock_spec: hwlock specifier as found in the device tree diff --git a/drivers/hwspinlock/hwspinlock_internal.h b/drivers/hwspinlock/hwspinlock_internal.h index 29892767bb7a..f298fc0ee5ad 100644 --- a/drivers/hwspinlock/hwspinlock_internal.h +++ b/drivers/hwspinlock/hwspinlock_internal.h @@ -21,6 +21,8 @@ struct hwspinlock_device; * @trylock: make a single attempt to take the lock. returns 0 on * failure and true on success. may _not_ sleep. * @unlock: release the lock. always succeed. may _not_ sleep. + * @bust: optional, platform-specific bust handler, called by hwspinlock + * core to bust a specific lock. * @relax: optional, platform-specific relax handler, called by hwspinlock * core while spinning on a lock, between two successive * invocations of @trylock. may _not_ sleep. @@ -28,6 +30,7 @@ struct hwspinlock_device; struct hwspinlock_ops { int (*trylock)(struct hwspinlock *lock); void (*unlock)(struct hwspinlock *lock); + int (*bust)(struct hwspinlock *lock, unsigned int id); void (*relax)(struct hwspinlock *lock); }; diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h index bfe7c1f1ac6d..f0231dbc4777 100644 --- a/include/linux/hwspinlock.h +++ b/include/linux/hwspinlock.h @@ -68,6 +68,7 @@ int __hwspin_lock_timeout(struct hwspinlock *, unsigned int, int, int __hwspin_trylock(struct hwspinlock *, int, unsigned long *); void __hwspin_unlock(struct hwspinlock *, int, unsigned long *); int of_hwspin_lock_get_id_byname(struct device_node *np, const char *name); +int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id); int devm_hwspin_lock_free(struct device *dev, struct hwspinlock *hwlock); struct hwspinlock *devm_hwspin_lock_request(struct device *dev); struct hwspinlock *devm_hwspin_lock_request_specific(struct device *dev, @@ -127,6 +128,11 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) { } +static inline int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id) +{ + return 0; +} + static inline int of_hwspin_lock_get_id(struct device_node *np, int index) { return 0; From 73100deb59c3892e280234fcc0171a5376c71788 Mon Sep 17 00:00:00 2001 From: Richard Maina Date: Wed, 29 May 2024 11:09:56 -0700 Subject: [PATCH 019/155] hwspinlock: qcom: implement bust operation Implement a new operation qcom_hwspinlock_bust() which can be invoked to bust any locks that are in use when a remoteproc is stopped or crashed. Signed-off-by: Richard Maina Reviewed-by: Bjorn Andersson Signed-off-by: Chris Lew Link: https://lore.kernel.org/r/20240529-hwspinlock-bust-v3-2-c8b924ffa5a2@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/hwspinlock/qcom_hwspinlock.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c index 814dfe8697bf..0390979fd765 100644 --- a/drivers/hwspinlock/qcom_hwspinlock.c +++ b/drivers/hwspinlock/qcom_hwspinlock.c @@ -64,9 +64,34 @@ static void qcom_hwspinlock_unlock(struct hwspinlock *lock) pr_err("%s: failed to unlock spinlock\n", __func__); } +static int qcom_hwspinlock_bust(struct hwspinlock *lock, unsigned int id) +{ + struct regmap_field *field = lock->priv; + u32 owner; + int ret; + + ret = regmap_field_read(field, &owner); + if (ret) { + dev_err(lock->bank->dev, "unable to query spinlock owner\n"); + return ret; + } + + if (owner != id) + return 0; + + ret = regmap_field_write(field, 0); + if (ret) { + dev_err(lock->bank->dev, "failed to bust spinlock\n"); + return ret; + } + + return 0; +} + static const struct hwspinlock_ops qcom_hwspinlock_ops = { .trylock = qcom_hwspinlock_trylock, .unlock = qcom_hwspinlock_unlock, + .bust = qcom_hwspinlock_bust, }; static const struct regmap_config sfpb_mutex_config = { From 2e3f0d693875db698891ffe89a18121bda5b95b8 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Wed, 29 May 2024 11:09:57 -0700 Subject: [PATCH 020/155] soc: qcom: smem: Add qcom_smem_bust_hwspin_lock_by_host() Add qcom_smem_bust_hwspin_lock_by_host to enable remoteproc to bust the hwspin_lock owned by smem. In the event the remoteproc crashes unexpectedly, the remoteproc driver can invoke this API to try and bust the hwspin_lock and release the lock if still held by the remoteproc device. Signed-off-by: Chris Lew Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20240529-hwspinlock-bust-v3-3-c8b924ffa5a2@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smem.c | 26 ++++++++++++++++++++++++++ include/linux/soc/qcom/smem.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 7191fa0c087f..50039e983eba 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -359,6 +359,32 @@ static struct qcom_smem *__smem; /* Timeout (ms) for the trylock of remote spinlocks */ #define HWSPINLOCK_TIMEOUT 1000 +/* The qcom hwspinlock id is always plus one from the smem host id */ +#define SMEM_HOST_ID_TO_HWSPINLOCK_ID(__x) ((__x) + 1) + +/** + * qcom_smem_bust_hwspin_lock_by_host() - bust the smem hwspinlock for a host + * @host: remote processor id + * + * Busts the hwspin_lock for the given smem host id. This helper is intended + * for remoteproc drivers that manage remoteprocs with an equivalent smem + * driver instance in the remote firmware. Drivers can force a release of the + * smem hwspin_lock if the rproc unexpectedly goes into a bad state. + * + * Context: Process context. + * + * Returns: 0 on success, otherwise negative errno. + */ +int qcom_smem_bust_hwspin_lock_by_host(unsigned int host) +{ + /* This function is for remote procs, so ignore SMEM_HOST_APPS */ + if (host == SMEM_HOST_APPS || host >= SMEM_HOST_COUNT) + return -EINVAL; + + return hwspin_lock_bust(__smem->hwlock, SMEM_HOST_ID_TO_HWSPINLOCK_ID(host)); +} +EXPORT_SYMBOL_GPL(qcom_smem_bust_hwspin_lock_by_host); + /** * qcom_smem_is_available() - Check if SMEM is available * diff --git a/include/linux/soc/qcom/smem.h b/include/linux/soc/qcom/smem.h index a36a3b9d4929..03187bc95851 100644 --- a/include/linux/soc/qcom/smem.h +++ b/include/linux/soc/qcom/smem.h @@ -14,4 +14,6 @@ phys_addr_t qcom_smem_virt_to_phys(void *p); int qcom_smem_get_soc_id(u32 *id); +int qcom_smem_bust_hwspin_lock_by_host(unsigned int host); + #endif From 568b13b65078e2b557ccf47674a354cecd1db641 Mon Sep 17 00:00:00 2001 From: Richard Maina Date: Wed, 29 May 2024 11:09:58 -0700 Subject: [PATCH 021/155] remoteproc: qcom_q6v5_pas: Add hwspinlock bust on stop When remoteproc goes down unexpectedly this results in a state where any acquired hwspinlocks will remain locked possibly resulting in deadlock. In order to ensure all locks are freed we include a call to qcom_smem_bust_hwspin_lock_by_host() during remoteproc shutdown. For qcom_q6v5_pas remoteprocs, each remoteproc has an assigned smem host_id. Remoteproc can pass this id to smem to try and bust the lock on remoteproc stop. This edge case only occurs with q6v5_pas watchdog crashes. The error fatal case has handling to clear the hwspinlock before the error fatal interrupt is triggered. Signed-off-by: Richard Maina Reviewed-by: Bjorn Andersson Signed-off-by: Chris Lew Link: https://lore.kernel.org/r/20240529-hwspinlock-bust-v3-4-c8b924ffa5a2@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pas.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 54d8005d40a3..8458bcfe9e19 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -52,6 +52,7 @@ struct adsp_data { const char *ssr_name; const char *sysmon_name; int ssctl_id; + unsigned int smem_host_id; int region_assign_idx; int region_assign_count; @@ -81,6 +82,7 @@ struct qcom_adsp { int lite_pas_id; unsigned int minidump_id; int crash_reason_smem; + unsigned int smem_host_id; bool decrypt_shutdown; const char *info_name; @@ -399,6 +401,9 @@ static int adsp_stop(struct rproc *rproc) if (handover) qcom_pas_handover(&adsp->q6v5); + if (adsp->smem_host_id) + ret = qcom_smem_bust_hwspin_lock_by_host(adsp->smem_host_id); + return ret; } @@ -727,6 +732,7 @@ static int adsp_probe(struct platform_device *pdev) adsp->pas_id = desc->pas_id; adsp->lite_pas_id = desc->lite_pas_id; adsp->info_name = desc->sysmon_name; + adsp->smem_host_id = desc->smem_host_id; adsp->decrypt_shutdown = desc->decrypt_shutdown; adsp->region_assign_idx = desc->region_assign_idx; adsp->region_assign_count = min_t(int, MAX_ASSIGN_COUNT, desc->region_assign_count); @@ -1196,6 +1202,7 @@ static const struct adsp_data sm8550_adsp_resource = { .ssr_name = "lpass", .sysmon_name = "adsp", .ssctl_id = 0x14, + .smem_host_id = 2, }; static const struct adsp_data sm8550_cdsp_resource = { @@ -1216,6 +1223,7 @@ static const struct adsp_data sm8550_cdsp_resource = { .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, + .smem_host_id = 5, }; static const struct adsp_data sm8550_mpss_resource = { @@ -1236,6 +1244,7 @@ static const struct adsp_data sm8550_mpss_resource = { .ssr_name = "mpss", .sysmon_name = "modem", .ssctl_id = 0x12, + .smem_host_id = 1, .region_assign_idx = 2, .region_assign_count = 1, .region_assign_vmid = QCOM_SCM_VMID_MSS_MSA, @@ -1275,6 +1284,7 @@ static const struct adsp_data sm8650_cdsp_resource = { .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, + .smem_host_id = 5, .region_assign_idx = 2, .region_assign_count = 1, .region_assign_shared = true, @@ -1299,6 +1309,7 @@ static const struct adsp_data sm8650_mpss_resource = { .ssr_name = "mpss", .sysmon_name = "modem", .ssctl_id = 0x12, + .smem_host_id = 1, .region_assign_idx = 2, .region_assign_count = 3, .region_assign_vmid = QCOM_SCM_VMID_MSS_MSA, From 430e29787fd013500c5907cdf3f7371cc97079ed Mon Sep 17 00:00:00 2001 From: Hari Nagalla Date: Tue, 4 Jun 2024 12:14:50 -0500 Subject: [PATCH 022/155] dt-bindings: remoteproc: k3-dsp: Correct optional sram properties for AM62A SoCs The C7xv-dsp on AM62A have 32KB L1 I-cache and a 64KB L1 D-cache. It does not have an addressable l1dram . So, remove this optional sram property from the bindings to fix device tree build warnings. Signed-off-by: Hari Nagalla Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240604171450.2455-1-hnagalla@ti.com Signed-off-by: Mathieu Poirier --- .../bindings/remoteproc/ti,k3-dsp-rproc.yaml | 89 +++++++++++-------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/Documentation/devicetree/bindings/remoteproc/ti,k3-dsp-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,k3-dsp-rproc.yaml index 9768db8663eb..b51bb863d759 100644 --- a/Documentation/devicetree/bindings/remoteproc/ti,k3-dsp-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/ti,k3-dsp-rproc.yaml @@ -25,9 +25,6 @@ description: | host processor (Arm CorePac) to perform the device management of the remote processor and to communicate with the remote processor. -allOf: - - $ref: /schemas/arm/keystone/ti,k3-sci-common.yaml# - properties: compatible: enum: @@ -89,41 +86,57 @@ properties: should be defined as per the generic bindings in, Documentation/devicetree/bindings/sram/sram.yaml -if: - properties: - compatible: - enum: - - ti,j721e-c66-dsp -then: - properties: - reg: - items: - - description: Address and Size of the L2 SRAM internal memory region - - description: Address and Size of the L1 PRAM internal memory region - - description: Address and Size of the L1 DRAM internal memory region - reg-names: - items: - - const: l2sram - - const: l1pram - - const: l1dram -else: - if: - properties: - compatible: - enum: - - ti,am62a-c7xv-dsp - - ti,j721e-c71-dsp - - ti,j721s2-c71-dsp - then: - properties: - reg: - items: - - description: Address and Size of the L2 SRAM internal memory region - - description: Address and Size of the L1 DRAM internal memory region - reg-names: - items: - - const: l2sram - - const: l1dram +allOf: + - if: + properties: + compatible: + enum: + - ti,j721e-c66-dsp + then: + properties: + reg: + items: + - description: Address and Size of the L2 SRAM internal memory region + - description: Address and Size of the L1 PRAM internal memory region + - description: Address and Size of the L1 DRAM internal memory region + reg-names: + items: + - const: l2sram + - const: l1pram + - const: l1dram + + - if: + properties: + compatible: + enum: + - ti,j721e-c71-dsp + - ti,j721s2-c71-dsp + then: + properties: + reg: + items: + - description: Address and Size of the L2 SRAM internal memory region + - description: Address and Size of the L1 DRAM internal memory region + reg-names: + items: + - const: l2sram + - const: l1dram + + - if: + properties: + compatible: + enum: + - ti,am62a-c7xv-dsp + then: + properties: + reg: + items: + - description: Address and Size of the L2 SRAM internal memory region + reg-names: + items: + - const: l2sram + + - $ref: /schemas/arm/keystone/ti,k3-sci-common.yaml# required: - compatible From 36862eab884d997e182aad9163cc6ca953614a68 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Tue, 4 Jun 2024 18:53:44 -0700 Subject: [PATCH 023/155] rpmsg: char: add missing MODULE_DESCRIPTION() macro make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/rpmsg/rpmsg_char.o Add the missing invocation of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Link: https://lore.kernel.org/r/20240604-md-drivers-rpmsg_char-v1-1-675453267fc6@quicinc.com Signed-off-by: Mathieu Poirier --- drivers/rpmsg/rpmsg_char.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index 3ec02751bc35..eec7642d2686 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -566,4 +566,5 @@ static void rpmsg_chrdev_exit(void) module_exit(rpmsg_chrdev_exit); MODULE_ALIAS("rpmsg:rpmsg_chrdev"); +MODULE_DESCRIPTION("RPMSG device interface"); MODULE_LICENSE("GPL v2"); From 2fa26ca8b786888673689ccc9da6094150939982 Mon Sep 17 00:00:00 2001 From: Aleksandr Mishin Date: Thu, 6 Jun 2024 10:52:04 +0300 Subject: [PATCH 024/155] remoteproc: imx_rproc: Skip over memory region when node value is NULL In imx_rproc_addr_init() "nph = of_count_phandle_with_args()" just counts number of phandles. But phandles may be empty. So of_parse_phandle() in the parsing loop (0 < a < nph) may return NULL which is later dereferenced. Adjust this issue by adding NULL-return check. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: a0ff4aa6f010 ("remoteproc: imx_rproc: add a NXP/Freescale imx_rproc driver") Signed-off-by: Aleksandr Mishin Reviewed-by: Peng Fan Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20240606075204.12354-1-amishin@t-argos.ru [Fixed title to fit within the prescribed 70-75 charcters] Signed-off-by: Mathieu Poirier --- drivers/remoteproc/imx_rproc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 5a3fb902acc9..39eacd90af14 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -726,6 +726,8 @@ static int imx_rproc_addr_init(struct imx_rproc *priv, struct resource res; node = of_parse_phandle(np, "memory-region", a); + if (!node) + continue; /* Not map vdevbuffer, vdevring region */ if (!strncmp(node->name, "vdev", strlen("vdev"))) { of_node_put(node); From fc01008c92f40015aeeced94750855a7111b6929 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 21 May 2024 14:23:17 +0800 Subject: [PATCH 025/155] f2fs: fix to do sanity check on F2FS_INLINE_DATA flag in inode during GC syzbot reports a f2fs bug as below: ------------[ cut here ]------------ kernel BUG at fs/f2fs/inline.c:258! CPU: 1 PID: 34 Comm: kworker/u8:2 Not tainted 6.9.0-rc6-syzkaller-00012-g9e4bc4bcae01 #0 RIP: 0010:f2fs_write_inline_data+0x781/0x790 fs/f2fs/inline.c:258 Call Trace: f2fs_write_single_data_page+0xb65/0x1d60 fs/f2fs/data.c:2834 f2fs_write_cache_pages fs/f2fs/data.c:3133 [inline] __f2fs_write_data_pages fs/f2fs/data.c:3288 [inline] f2fs_write_data_pages+0x1efe/0x3a90 fs/f2fs/data.c:3315 do_writepages+0x35b/0x870 mm/page-writeback.c:2612 __writeback_single_inode+0x165/0x10b0 fs/fs-writeback.c:1650 writeback_sb_inodes+0x905/0x1260 fs/fs-writeback.c:1941 wb_writeback+0x457/0xce0 fs/fs-writeback.c:2117 wb_do_writeback fs/fs-writeback.c:2264 [inline] wb_workfn+0x410/0x1090 fs/fs-writeback.c:2304 process_one_work kernel/workqueue.c:3254 [inline] process_scheduled_works+0xa12/0x17c0 kernel/workqueue.c:3335 worker_thread+0x86d/0xd70 kernel/workqueue.c:3416 kthread+0x2f2/0x390 kernel/kthread.c:388 ret_from_fork+0x4d/0x80 arch/x86/kernel/process.c:147 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244 The root cause is: inline_data inode can be fuzzed, so that there may be valid blkaddr in its direct node, once f2fs triggers background GC to migrate the block, it will hit f2fs_bug_on() during dirty page writeback. Let's add sanity check on F2FS_INLINE_DATA flag in inode during GC, so that, it can forbid migrating inline_data inode's data block for fixing. Reported-by: syzbot+848062ba19c8782ca5c8@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-f2fs-devel/000000000000d103ce06174d7ec3@google.com Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 6066c6eecf41..20e2f989013b 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1563,6 +1563,16 @@ next_step: continue; } + if (f2fs_has_inline_data(inode)) { + iput(inode); + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_err_ratelimited(sbi, + "inode %lx has both inline_data flag and " + "data block, nid=%u, ofs_in_node=%u", + inode->i_ino, dni.nid, ofs_in_node); + continue; + } + err = f2fs_gc_pinned_control(inode, gc_type, segno); if (err == -EAGAIN) { iput(inode); From c240c87bcd44a1a2375fc8ef8c645d1f1fe76466 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 21 May 2024 14:23:18 +0800 Subject: [PATCH 026/155] f2fs: fix to do sanity check on blocks for inline_data inode inode can be fuzzed, so it can has F2FS_INLINE_DATA flag and valid i_blocks/i_nid value, this patch supports to do extra sanity check to detect such corrupted state. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- fs/f2fs/inline.c | 20 +++++++++++++++++++- fs/f2fs/inode.c | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1974b6aff397..f463961b497c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4149,7 +4149,7 @@ extern struct kmem_cache *f2fs_inode_entry_slab; * inline.c */ bool f2fs_may_inline_data(struct inode *inode); -bool f2fs_sanity_check_inline_data(struct inode *inode); +bool f2fs_sanity_check_inline_data(struct inode *inode, struct page *ipage); bool f2fs_may_inline_dentry(struct inode *inode); void f2fs_do_read_inline_data(struct folio *folio, struct page *ipage); void f2fs_truncate_inline_inode(struct inode *inode, diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 7638d0d7b7ee..0203c3baabb6 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -33,11 +33,29 @@ bool f2fs_may_inline_data(struct inode *inode) return !f2fs_post_read_required(inode); } -bool f2fs_sanity_check_inline_data(struct inode *inode) +static bool inode_has_blocks(struct inode *inode, struct page *ipage) +{ + struct f2fs_inode *ri = F2FS_INODE(ipage); + int i; + + if (F2FS_HAS_BLOCKS(inode)) + return true; + + for (i = 0; i < DEF_NIDS_PER_INODE; i++) { + if (ri->i_nid[i]) + return true; + } + return false; +} + +bool f2fs_sanity_check_inline_data(struct inode *inode, struct page *ipage) { if (!f2fs_has_inline_data(inode)) return false; + if (inode_has_blocks(inode, ipage)) + return false; + if (!support_inline_data(inode)) return true; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 005dde72aff3..33b2778d5452 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -344,7 +344,7 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) } } - if (f2fs_sanity_check_inline_data(inode)) { + if (f2fs_sanity_check_inline_data(inode, node_page)) { f2fs_warn(sbi, "%s: inode (ino=%lx, mode=%u) should not have inline_data, run fsck to fix", __func__, inode->i_ino, inode->i_mode); return false; From cc260b66c4cd21a370b39f73c9034a420baf8f79 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 15 May 2024 16:12:33 +0800 Subject: [PATCH 027/155] f2fs: add support for FS_IOC_GETFSSYSFSPATH FS_IOC_GETFSSYSFSPATH ioctl expects sysfs sub-path of a filesystem, the format can be "$FSTYP/$SYSFS_IDENTIFIER" under /sys/fs, it can helps to standardizes exporting sysfs datas across filesystems. This patch wires up FS_IOC_GETFSSYSFSPATH for f2fs, it will output "f2fs/". Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 1f1b3647a998..4a1bc8f40f9a 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -4481,6 +4481,7 @@ try_onemore: sb->s_flags = (sb->s_flags & ~SB_POSIXACL) | (test_opt(sbi, POSIX_ACL) ? SB_POSIXACL : 0); super_set_uuid(sb, (void *) raw_super->uuid, sizeof(raw_super->uuid)); + super_set_sysfs_name_bdev(sb); sb->s_iflags |= SB_I_CGROUPWB; /* init f2fs-specific super block info */ From 76da333f4b935af65c9465277032a1e24405375c Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Thu, 30 May 2024 18:01:58 +0800 Subject: [PATCH 028/155] f2fs: alloc new section if curseg is not the first seg in its zone If curseg is not the first segment in its zone, the zone is not empty. A new section should be allocated and avoid resetting the old zone. Reviewed-by: Chao Yu Signed-off-by: Sheng Yong Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a0ce3d080f80..c4d951ed54ff 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -5187,7 +5187,8 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) } /* Allocate a new section if it's not new. */ - if (cs->next_blkoff) { + if (cs->next_blkoff || + cs->segno != GET_SEG_FROM_SEC(sbi, GET_ZONE_FROM_SEC(sbi, cs_section))) { unsigned int old_segno = cs->segno, old_blkoff = cs->next_blkoff; f2fs_allocate_new_section(sbi, type, true); From 6924c8b6fdf96db24edf8392c2dd00f79eeb8e4b Mon Sep 17 00:00:00 2001 From: Zhiguo Niu Date: Mon, 27 May 2024 19:12:37 +0800 Subject: [PATCH 029/155] f2fs: fix to remove redundant SBI_NEED_FSCK flag set Subsequent f2fs_stop_checkpoint will set cp_err, so this SBI_NEED_FSCK flag set action is invalid. Signed-off-by: Zhiguo Niu Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 20e2f989013b..ef667fec9a12 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1752,7 +1752,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, if (type != GET_SUM_TYPE((&sum->footer))) { f2fs_err(sbi, "Inconsistent segment (%u) type [%d, %d] in SSA and SIT", segno, type, GET_SUM_TYPE((&sum->footer))); - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_SUMMARY); goto skip; From 5c8764f8679e659c5cb295af7d32279002d13735 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 23 May 2024 21:29:48 +0800 Subject: [PATCH 030/155] f2fs: fix to force buffered IO on inline_data inode It will return all zero data when DIO reading from inline_data inode, it is because f2fs_iomap_begin() assign iomap->type w/ IOMAP_HOLE incorrectly for this case. We can let iomap framework handle inline data via assigning iomap->type and iomap->inline_data correctly, however, it will be a little bit complicated when handling race case in between direct IO and buffered IO. So, let's force to use buffered IO to fix this issue. Cc: stable@vger.kernel.org Reported-by: Barry Song Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 5c0b281a70f3..0cbaeb97c7f5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -825,6 +825,8 @@ static bool f2fs_force_buffered_io(struct inode *inode, int rw) return true; if (f2fs_compressed_file(inode)) return true; + if (f2fs_has_inline_data(inode)) + return true; /* disallow direct IO if any of devices has unaligned blksize */ if (f2fs_is_multi_device(sbi) && !sbi->aligned_blksize) From 21327a042dd94bc73181d7300e688699cb1f467e Mon Sep 17 00:00:00 2001 From: Zhiguo Niu Date: Wed, 29 May 2024 17:47:00 +0800 Subject: [PATCH 031/155] f2fs: fix to avoid use SSR allocate when do defragment SSR allocate mode will be used when doing file defragment if ATGC is working at the same time, that is because set_page_private_gcing may make CURSEG_ALL_DATA_ATGC segment type got in f2fs_allocate_data_block when defragment page is writeback, which may cause file fragmentation is worse. A file with 2 fragmentations is changed as following after defragment: ----------------file info------------------- sensorsdata : -------------------------------------------- dev [254:48] ino [0x 3029 : 12329] mode [0x 81b0 : 33200] nlink [0x 1 : 1] uid [0x 27e6 : 10214] gid [0x 27e6 : 10214] size [0x 242000 : 2367488] blksize [0x 1000 : 4096] blocks [0x 1210 : 4624] -------------------------------------------- file_pos start_blk end_blk blks 0 11361121 11361207 87 356352 11361215 11361216 2 364544 11361218 11361218 1 368640 11361220 11361221 2 376832 11361224 11361225 2 385024 11361227 11361238 12 434176 11361240 11361252 13 487424 11361254 11361254 1 491520 11361271 11361279 9 528384 3681794 3681795 2 536576 3681797 3681797 1 540672 3681799 3681799 1 544768 3681803 3681803 1 548864 3681805 3681805 1 552960 3681807 3681807 1 557056 3681809 3681809 1 Signed-off-by: Zhiguo Niu Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c4d951ed54ff..6e8a4b332ad5 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3483,7 +3483,8 @@ static int __get_segment_type_6(struct f2fs_io_info *fio) if (page_private_gcing(fio->page)) { if (fio->sbi->am.atgc_enabled && (fio->io_type == FS_DATA_IO) && - (fio->sbi->gc_mode != GC_URGENT_HIGH)) + (fio->sbi->gc_mode != GC_URGENT_HIGH) && + !is_inode_flag_set(inode, FI_OPU_WRITE)) return CURSEG_ALL_DATA_ATGC; else return CURSEG_COLD_DATA; From 192b8fb8d1c8ca3c87366ebbef599fa80bb626b8 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 4 Jun 2024 15:56:36 +0800 Subject: [PATCH 032/155] f2fs: fix to don't dirty inode for readonly filesystem syzbot reports f2fs bug as below: kernel BUG at fs/f2fs/inode.c:933! RIP: 0010:f2fs_evict_inode+0x1576/0x1590 fs/f2fs/inode.c:933 Call Trace: evict+0x2a4/0x620 fs/inode.c:664 dispose_list fs/inode.c:697 [inline] evict_inodes+0x5f8/0x690 fs/inode.c:747 generic_shutdown_super+0x9d/0x2c0 fs/super.c:675 kill_block_super+0x44/0x90 fs/super.c:1667 kill_f2fs_super+0x303/0x3b0 fs/f2fs/super.c:4894 deactivate_locked_super+0xc1/0x130 fs/super.c:484 cleanup_mnt+0x426/0x4c0 fs/namespace.c:1256 task_work_run+0x24a/0x300 kernel/task_work.c:180 ptrace_notify+0x2cd/0x380 kernel/signal.c:2399 ptrace_report_syscall include/linux/ptrace.h:411 [inline] ptrace_report_syscall_exit include/linux/ptrace.h:473 [inline] syscall_exit_work kernel/entry/common.c:251 [inline] syscall_exit_to_user_mode_prepare kernel/entry/common.c:278 [inline] __syscall_exit_to_user_mode_work kernel/entry/common.c:283 [inline] syscall_exit_to_user_mode+0x15c/0x280 kernel/entry/common.c:296 do_syscall_64+0x50/0x110 arch/x86/entry/common.c:88 entry_SYSCALL_64_after_hwframe+0x63/0x6b The root cause is: - do_sys_open - f2fs_lookup - __f2fs_find_entry - f2fs_i_depth_write - f2fs_mark_inode_dirty_sync - f2fs_dirty_inode - set_inode_flag(inode, FI_DIRTY_INODE) - umount - kill_f2fs_super - kill_block_super - generic_shutdown_super - sync_filesystem : sb is readonly, skip sync_filesystem() - evict_inodes - iput - f2fs_evict_inode - f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE)) : trigger kernel panic When we try to repair i_current_depth in readonly filesystem, let's skip dirty inode to avoid panic in later f2fs_evict_inode(). Cc: stable@vger.kernel.org Reported-by: syzbot+31e4659a3fe953aec2f4@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-f2fs-devel/000000000000e890bc0609a55cff@google.com Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 33b2778d5452..ffbcf3de2bdb 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -29,6 +29,9 @@ void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync) if (is_inode_flag_set(inode, FI_NEW_INODE)) return; + if (f2fs_readonly(F2FS_I_SB(inode)->sb)) + return; + if (f2fs_inode_dirtied(inode, sync)) return; From 270b09313b4e16cdfd01d8371023a1661dd1112e Mon Sep 17 00:00:00 2001 From: Zhiguo Niu Date: Mon, 3 Jun 2024 19:35:26 +0800 Subject: [PATCH 033/155] f2fs: use new ioprio Macro to get ckpt thread ioprio level IOPRIO_PRIO_DATA in the new kernel version includes level and hint, So Macro IOPRIO_PRIO_LEVEL is more accurate to get ckpt thread ioprio data/level, and it is also consisten with the way setting ckpt thread ioprio by IOPRIO_PRIO_VALUE(class, data/level). Besides, change variable name from "data" to "level" for more readable. Signed-off-by: Zhiguo Niu Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/sysfs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 09d3ecfaa4f1..fee7ee45ceaa 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -340,13 +340,13 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a, if (!strcmp(a->attr.name, "ckpt_thread_ioprio")) { struct ckpt_req_control *cprc = &sbi->cprc_info; int class = IOPRIO_PRIO_CLASS(cprc->ckpt_thread_ioprio); - int data = IOPRIO_PRIO_DATA(cprc->ckpt_thread_ioprio); + int level = IOPRIO_PRIO_LEVEL(cprc->ckpt_thread_ioprio); if (class != IOPRIO_CLASS_RT && class != IOPRIO_CLASS_BE) return -EINVAL; return sysfs_emit(buf, "%s,%d\n", - class == IOPRIO_CLASS_RT ? "rt" : "be", data); + class == IOPRIO_CLASS_RT ? "rt" : "be", level); } #ifdef CONFIG_F2FS_FS_COMPRESSION @@ -450,7 +450,7 @@ out: const char *name = strim((char *)buf); struct ckpt_req_control *cprc = &sbi->cprc_info; int class; - long data; + long level; int ret; if (!strncmp(name, "rt,", 3)) @@ -461,13 +461,13 @@ out: return -EINVAL; name += 3; - ret = kstrtol(name, 10, &data); + ret = kstrtol(name, 10, &level); if (ret) return ret; - if (data >= IOPRIO_NR_LEVELS || data < 0) + if (level >= IOPRIO_NR_LEVELS || level < 0) return -EINVAL; - cprc->ckpt_thread_ioprio = IOPRIO_PRIO_VALUE(class, data); + cprc->ckpt_thread_ioprio = IOPRIO_PRIO_VALUE(class, level); if (test_opt(sbi, MERGE_CHECKPOINT)) { ret = set_task_ioprio(cprc->f2fs_issue_ckpt, cprc->ckpt_thread_ioprio); From a8eb3de28e7a365690c61161e7a07a4fc7c60bbf Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 3 Jun 2024 09:07:45 +0800 Subject: [PATCH 034/155] f2fs: fix return value of f2fs_convert_inline_inode() If device is readonly, make f2fs_convert_inline_inode() return EROFS instead of zero, otherwise it may trigger panic during writeback of inline inode's dirty page as below: f2fs_write_single_data_page+0xbb6/0x1e90 fs/f2fs/data.c:2888 f2fs_write_cache_pages fs/f2fs/data.c:3187 [inline] __f2fs_write_data_pages fs/f2fs/data.c:3342 [inline] f2fs_write_data_pages+0x1efe/0x3a90 fs/f2fs/data.c:3369 do_writepages+0x359/0x870 mm/page-writeback.c:2634 filemap_fdatawrite_wbc+0x125/0x180 mm/filemap.c:397 __filemap_fdatawrite_range mm/filemap.c:430 [inline] file_write_and_wait_range+0x1aa/0x290 mm/filemap.c:788 f2fs_do_sync_file+0x68a/0x1ae0 fs/f2fs/file.c:276 generic_write_sync include/linux/fs.h:2806 [inline] f2fs_file_write_iter+0x7bd/0x24e0 fs/f2fs/file.c:4977 call_write_iter include/linux/fs.h:2114 [inline] new_sync_write fs/read_write.c:497 [inline] vfs_write+0xa72/0xc90 fs/read_write.c:590 ksys_write+0x1a0/0x2c0 fs/read_write.c:643 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xf5/0x240 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f Cc: stable@vger.kernel.org Reported-by: syzbot+848062ba19c8782ca5c8@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-f2fs-devel/000000000000d103ce06174d7ec3@google.com Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inline.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 0203c3baabb6..1fba5728be70 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -221,8 +221,10 @@ int f2fs_convert_inline_inode(struct inode *inode) struct page *ipage, *page; int err = 0; - if (!f2fs_has_inline_data(inode) || - f2fs_hw_is_readonly(sbi) || f2fs_readonly(sbi->sb)) + if (f2fs_hw_is_readonly(sbi) || f2fs_readonly(sbi->sb)) + return -EROFS; + + if (!f2fs_has_inline_data(inode)) return 0; err = f2fs_dquot_initialize(inode); From d7409b05a64f212735f0d33f5f1602051a886eab Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 31 May 2024 10:00:32 +0800 Subject: [PATCH 035/155] f2fs: fix to cover read extent cache access with lock syzbot reports a f2fs bug as below: BUG: KASAN: slab-use-after-free in sanity_check_extent_cache+0x370/0x410 fs/f2fs/extent_cache.c:46 Read of size 4 at addr ffff8880739ab220 by task syz-executor200/5097 CPU: 0 PID: 5097 Comm: syz-executor200 Not tainted 6.9.0-rc6-syzkaller #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/27/2024 Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0x241/0x360 lib/dump_stack.c:114 print_address_description mm/kasan/report.c:377 [inline] print_report+0x169/0x550 mm/kasan/report.c:488 kasan_report+0x143/0x180 mm/kasan/report.c:601 sanity_check_extent_cache+0x370/0x410 fs/f2fs/extent_cache.c:46 do_read_inode fs/f2fs/inode.c:509 [inline] f2fs_iget+0x33e1/0x46e0 fs/f2fs/inode.c:560 f2fs_nfs_get_inode+0x74/0x100 fs/f2fs/super.c:3237 generic_fh_to_dentry+0x9f/0xf0 fs/libfs.c:1413 exportfs_decode_fh_raw+0x152/0x5f0 fs/exportfs/expfs.c:444 exportfs_decode_fh+0x3c/0x80 fs/exportfs/expfs.c:584 do_handle_to_path fs/fhandle.c:155 [inline] handle_to_path fs/fhandle.c:210 [inline] do_handle_open+0x495/0x650 fs/fhandle.c:226 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xf5/0x240 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f We missed to cover sanity_check_extent_cache() w/ extent cache lock, so, below race case may happen, result in use after free issue. - f2fs_iget - do_read_inode - f2fs_init_read_extent_tree : add largest extent entry in to cache - shrink - f2fs_shrink_read_extent_tree - __shrink_extent_tree - __detach_extent_node : drop largest extent entry - sanity_check_extent_cache : access et->largest w/o lock let's refactor sanity_check_extent_cache() to avoid extent cache access and call it before f2fs_init_read_extent_tree() to fix this issue. Reported-by: syzbot+74ebe2104433e9dc610d@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-f2fs-devel/00000000000009beea061740a531@google.com Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/extent_cache.c | 50 +++++++++++++++++------------------------- fs/f2fs/f2fs.h | 2 +- fs/f2fs/inode.c | 10 ++++----- 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c index 48048fa36427..fd1fc06359ee 100644 --- a/fs/f2fs/extent_cache.c +++ b/fs/f2fs/extent_cache.c @@ -19,33 +19,23 @@ #include "node.h" #include -bool sanity_check_extent_cache(struct inode *inode) +bool sanity_check_extent_cache(struct inode *inode, struct page *ipage) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct f2fs_inode_info *fi = F2FS_I(inode); - struct extent_tree *et = fi->extent_tree[EX_READ]; - struct extent_info *ei; + struct f2fs_extent *i_ext = &F2FS_INODE(ipage)->i_ext; + struct extent_info ei; - if (!et) + get_read_extent_info(&ei, i_ext); + + if (!ei.len) return true; - ei = &et->largest; - if (!ei->len) - return true; - - /* Let's drop, if checkpoint got corrupted. */ - if (is_set_ckpt_flags(sbi, CP_ERROR_FLAG)) { - ei->len = 0; - et->largest_updated = true; - return true; - } - - if (!f2fs_is_valid_blkaddr(sbi, ei->blk, DATA_GENERIC_ENHANCE) || - !f2fs_is_valid_blkaddr(sbi, ei->blk + ei->len - 1, + if (!f2fs_is_valid_blkaddr(sbi, ei.blk, DATA_GENERIC_ENHANCE) || + !f2fs_is_valid_blkaddr(sbi, ei.blk + ei.len - 1, DATA_GENERIC_ENHANCE)) { f2fs_warn(sbi, "%s: inode (ino=%lx) extent info [%u, %u, %u] is incorrect, run fsck to fix", __func__, inode->i_ino, - ei->blk, ei->fofs, ei->len); + ei.blk, ei.fofs, ei.len); return false; } return true; @@ -394,24 +384,22 @@ void f2fs_init_read_extent_tree(struct inode *inode, struct page *ipage) if (!__may_extent_tree(inode, EX_READ)) { /* drop largest read extent */ - if (i_ext && i_ext->len) { + if (i_ext->len) { f2fs_wait_on_page_writeback(ipage, NODE, true, true); i_ext->len = 0; set_page_dirty(ipage); } - goto out; + set_inode_flag(inode, FI_NO_EXTENT); + return; } et = __grab_extent_tree(inode, EX_READ); - if (!i_ext || !i_ext->len) - goto out; - get_read_extent_info(&ei, i_ext); write_lock(&et->lock); - if (atomic_read(&et->node_cnt)) - goto unlock_out; + if (atomic_read(&et->node_cnt) || !ei.len) + goto skip; en = __attach_extent_node(sbi, et, &ei, NULL, &et->root.rb_root.rb_node, true); @@ -423,11 +411,13 @@ void f2fs_init_read_extent_tree(struct inode *inode, struct page *ipage) list_add_tail(&en->list, &eti->extent_list); spin_unlock(&eti->extent_lock); } -unlock_out: +skip: + /* Let's drop, if checkpoint got corrupted. */ + if (f2fs_cp_error(sbi)) { + et->largest.len = 0; + et->largest_updated = true; + } write_unlock(&et->lock); -out: - if (!F2FS_I(inode)->extent_tree[EX_READ]) - set_inode_flag(inode, FI_NO_EXTENT); } void f2fs_init_age_extent_tree(struct inode *inode) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f463961b497c..86d4f7b19881 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4190,7 +4190,7 @@ void f2fs_leave_shrinker(struct f2fs_sb_info *sbi); /* * extent_cache.c */ -bool sanity_check_extent_cache(struct inode *inode); +bool sanity_check_extent_cache(struct inode *inode, struct page *ipage); void f2fs_init_extent_tree(struct inode *inode); void f2fs_drop_extent_tree(struct inode *inode); void f2fs_destroy_extent_node(struct inode *inode); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index ffbcf3de2bdb..9290b396e11c 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -511,16 +511,16 @@ static int do_read_inode(struct inode *inode) init_idisk_time(inode); - /* Need all the flag bits */ - f2fs_init_read_extent_tree(inode, node_page); - f2fs_init_age_extent_tree(inode); - - if (!sanity_check_extent_cache(inode)) { + if (!sanity_check_extent_cache(inode, node_page)) { f2fs_put_page(node_page, 1); f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE); return -EFSCORRUPTED; } + /* Need all the flag bits */ + f2fs_init_read_extent_tree(inode, node_page); + f2fs_init_age_extent_tree(inode); + f2fs_put_page(node_page, 1); stat_inc_inline_xattr(inode); From 298b1e4182d657c3e388adcc29477904e9600ed5 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 29 May 2024 18:01:03 +0800 Subject: [PATCH 036/155] f2fs: fix to truncate preallocated blocks in f2fs_file_open() chenyuwen reports a f2fs bug as below: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000011 fscrypt_set_bio_crypt_ctx+0x78/0x1e8 f2fs_grab_read_bio+0x78/0x208 f2fs_submit_page_read+0x44/0x154 f2fs_get_read_data_page+0x288/0x5f4 f2fs_get_lock_data_page+0x60/0x190 truncate_partial_data_page+0x108/0x4fc f2fs_do_truncate_blocks+0x344/0x5f0 f2fs_truncate_blocks+0x6c/0x134 f2fs_truncate+0xd8/0x200 f2fs_iget+0x20c/0x5ac do_garbage_collect+0x5d0/0xf6c f2fs_gc+0x22c/0x6a4 f2fs_disable_checkpoint+0xc8/0x310 f2fs_fill_super+0x14bc/0x1764 mount_bdev+0x1b4/0x21c f2fs_mount+0x20/0x30 legacy_get_tree+0x50/0xbc vfs_get_tree+0x5c/0x1b0 do_new_mount+0x298/0x4cc path_mount+0x33c/0x5fc __arm64_sys_mount+0xcc/0x15c invoke_syscall+0x60/0x150 el0_svc_common+0xb8/0xf8 do_el0_svc+0x28/0xa0 el0_svc+0x24/0x84 el0t_64_sync_handler+0x88/0xec It is because inode.i_crypt_info is not initialized during below path: - mount - f2fs_fill_super - f2fs_disable_checkpoint - f2fs_gc - f2fs_iget - f2fs_truncate So, let's relocate truncation of preallocated blocks to f2fs_file_open(), after fscrypt_file_open(). Fixes: d4dd19ec1ea0 ("f2fs: do not expose unwritten blocks to user by DIO") Reported-by: chenyuwen Closes: https://lore.kernel.org/linux-kernel/20240517085327.1188515-1-yuwen.chen@xjmz.com Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/file.c | 42 +++++++++++++++++++++++++++++++++++++++++- fs/f2fs/inode.c | 8 -------- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 86d4f7b19881..9688df332147 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -803,6 +803,7 @@ enum { FI_COW_FILE, /* indicate COW file */ FI_ATOMIC_COMMITTED, /* indicate atomic commit completed except disk sync */ FI_ATOMIC_REPLACE, /* indicate atomic replace */ + FI_OPENED_FILE, /* indicate file has been opened */ FI_MAX, /* max flag, never be used */ }; diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 0cbaeb97c7f5..c50213da474d 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -554,6 +554,42 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) return 0; } +static int finish_preallocate_blocks(struct inode *inode) +{ + int ret; + + inode_lock(inode); + if (is_inode_flag_set(inode, FI_OPENED_FILE)) { + inode_unlock(inode); + return 0; + } + + if (!file_should_truncate(inode)) { + set_inode_flag(inode, FI_OPENED_FILE); + inode_unlock(inode); + return 0; + } + + f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + filemap_invalidate_lock(inode->i_mapping); + + truncate_setsize(inode, i_size_read(inode)); + ret = f2fs_truncate(inode); + + filemap_invalidate_unlock(inode->i_mapping); + f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + + if (!ret) + set_inode_flag(inode, FI_OPENED_FILE); + + inode_unlock(inode); + if (ret) + return ret; + + file_dont_truncate(inode); + return 0; +} + static int f2fs_file_open(struct inode *inode, struct file *filp) { int err = fscrypt_file_open(inode, filp); @@ -571,7 +607,11 @@ static int f2fs_file_open(struct inode *inode, struct file *filp) filp->f_mode |= FMODE_NOWAIT; filp->f_mode |= FMODE_CAN_ODIRECT; - return dquot_file_open(inode, filp); + err = dquot_file_open(inode, filp); + if (err) + return err; + + return finish_preallocate_blocks(inode); } void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 9290b396e11c..dbfebbddf675 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -613,14 +613,6 @@ make_now: } f2fs_set_inode_flags(inode); - if (file_should_truncate(inode) && - !is_sbi_flag_set(sbi, SBI_POR_DOING)) { - ret = f2fs_truncate(inode); - if (ret) - goto bad_inode; - file_dont_truncate(inode); - } - unlock_new_inode(inode); trace_f2fs_iget(inode); return inode; From 6159855954ba16a8b3f6a01c305f938ea7e88f11 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Mon, 10 Jun 2024 10:17:16 -0500 Subject: [PATCH 037/155] remoteproc: omap: Use devm_rproc_alloc() helper Use the device lifecycle managed allocation function. This helps prevent mistakes like freeing out of order in cleanup functions and forgetting to free on error paths. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240610151721.189472-1-afd@ti.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/omap_remoteproc.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 8f50ab80e56f..e91e01658380 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -1305,8 +1305,8 @@ static int omap_rproc_probe(struct platform_device *pdev) return ret; } - rproc = rproc_alloc(&pdev->dev, dev_name(&pdev->dev), &omap_rproc_ops, - firmware, sizeof(*oproc)); + rproc = devm_rproc_alloc(&pdev->dev, dev_name(&pdev->dev), &omap_rproc_ops, + firmware, sizeof(*oproc)); if (!rproc) return -ENOMEM; @@ -1318,15 +1318,15 @@ static int omap_rproc_probe(struct platform_device *pdev) ret = omap_rproc_of_get_internal_memories(pdev, rproc); if (ret) - goto free_rproc; + return ret; ret = omap_rproc_get_boot_data(pdev, rproc); if (ret) - goto free_rproc; + return ret; ret = omap_rproc_of_get_timers(pdev, rproc); if (ret) - goto free_rproc; + return ret; init_completion(&oproc->pm_comp); oproc->autosuspend_delay = DEFAULT_AUTOSUSPEND_DELAY; @@ -1337,10 +1337,8 @@ static int omap_rproc_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(&pdev->dev, oproc->autosuspend_delay); oproc->fck = devm_clk_get(&pdev->dev, 0); - if (IS_ERR(oproc->fck)) { - ret = PTR_ERR(oproc->fck); - goto free_rproc; - } + if (IS_ERR(oproc->fck)) + return PTR_ERR(oproc->fck); ret = of_reserved_mem_device_init(&pdev->dev); if (ret) { @@ -1359,8 +1357,7 @@ static int omap_rproc_probe(struct platform_device *pdev) release_mem: of_reserved_mem_device_release(&pdev->dev); -free_rproc: - rproc_free(rproc); + return ret; } @@ -1369,7 +1366,6 @@ static void omap_rproc_remove(struct platform_device *pdev) struct rproc *rproc = platform_get_drvdata(pdev); rproc_del(rproc); - rproc_free(rproc); of_reserved_mem_device_release(&pdev->dev); } From 5efcc364fc63750fee93e5791130a1658b5061e8 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Mon, 10 Jun 2024 10:17:17 -0500 Subject: [PATCH 038/155] remoteproc: omap: Use devm action to release reserved memory This helps prevent mistakes like freeing out of order in cleanup functions and forgetting to free on error paths. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240610151721.189472-2-afd@ti.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/omap_remoteproc.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index e91e01658380..df46be84658f 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -1277,6 +1277,13 @@ static int omap_rproc_of_get_timers(struct platform_device *pdev, return 0; } +static void omap_rproc_mem_release(void *data) +{ + struct device *dev = data; + + of_reserved_mem_device_release(dev); +} + static int omap_rproc_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1346,19 +1353,17 @@ static int omap_rproc_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "Typically this should be provided,\n"); dev_warn(&pdev->dev, "only omit if you know what you are doing.\n"); } + ret = devm_add_action_or_reset(&pdev->dev, omap_rproc_mem_release, &pdev->dev); + if (ret) + return ret; platform_set_drvdata(pdev, rproc); ret = rproc_add(rproc); if (ret) - goto release_mem; + return ret; return 0; - -release_mem: - of_reserved_mem_device_release(&pdev->dev); - - return ret; } static void omap_rproc_remove(struct platform_device *pdev) @@ -1366,7 +1371,6 @@ static void omap_rproc_remove(struct platform_device *pdev) struct rproc *rproc = platform_get_drvdata(pdev); rproc_del(rproc); - of_reserved_mem_device_release(&pdev->dev); } static const struct dev_pm_ops omap_rproc_pm_ops = { From 8d3739dbba3ba35c43984d762e7cdcca031a8fcf Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Mon, 10 Jun 2024 10:17:18 -0500 Subject: [PATCH 039/155] remoteproc: omap: Use devm_rproc_add() helper Use the device lifecycle managed add function. This helps prevent mistakes like deleting out of order in cleanup functions and forgetting to delete on error paths. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240610151721.189472-3-afd@ti.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/omap_remoteproc.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index df46be84658f..9ae2e831456d 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -1359,20 +1359,13 @@ static int omap_rproc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rproc); - ret = rproc_add(rproc); + ret = devm_rproc_add(&pdev->dev, rproc); if (ret) return ret; return 0; } -static void omap_rproc_remove(struct platform_device *pdev) -{ - struct rproc *rproc = platform_get_drvdata(pdev); - - rproc_del(rproc); -} - static const struct dev_pm_ops omap_rproc_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(omap_rproc_suspend, omap_rproc_resume) SET_RUNTIME_PM_OPS(omap_rproc_runtime_suspend, @@ -1381,7 +1374,6 @@ static const struct dev_pm_ops omap_rproc_pm_ops = { static struct platform_driver omap_rproc_driver = { .probe = omap_rproc_probe, - .remove_new = omap_rproc_remove, .driver = { .name = "omap-rproc", .pm = &omap_rproc_pm_ops, From dce68a49be26abf52712e0ee452a45fa01ab4624 Mon Sep 17 00:00:00 2001 From: Aleksandr Mishin Date: Wed, 12 Jun 2024 16:17:14 +0300 Subject: [PATCH 040/155] remoteproc: imx_rproc: Fix refcount mistake in imx_rproc_addr_init In imx_rproc_addr_init() strcmp() is performed over the node after the of_node_put() is performed over it. Fix this error by moving of_node_put() calls. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 5e4c1243071d ("remoteproc: imx_rproc: support remote cores booted before Linux Kernel") Cc: stable@vger.kernel.org Signed-off-by: Aleksandr Mishin Link: https://lore.kernel.org/r/20240612131714.12907-1-amishin@t-argos.ru Signed-off-by: Mathieu Poirier --- drivers/remoteproc/imx_rproc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 39eacd90af14..144c8e9a642e 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -734,25 +734,29 @@ static int imx_rproc_addr_init(struct imx_rproc *priv, continue; } err = of_address_to_resource(node, 0, &res); - of_node_put(node); if (err) { dev_err(dev, "unable to resolve memory region\n"); + of_node_put(node); return err; } - if (b >= IMX_RPROC_MEM_MAX) + if (b >= IMX_RPROC_MEM_MAX) { + of_node_put(node); break; + } /* Not use resource version, because we might share region */ priv->mem[b].cpu_addr = devm_ioremap_wc(&pdev->dev, res.start, resource_size(&res)); if (!priv->mem[b].cpu_addr) { dev_err(dev, "failed to remap %pr\n", &res); + of_node_put(node); return -ENOMEM; } priv->mem[b].sys_addr = res.start; priv->mem[b].size = resource_size(&res); if (!strcmp(node->name, "rsc-table")) priv->rsc_table = priv->mem[b].cpu_addr; + of_node_put(node); b++; } From 78aaf42921b759f39bedcf4e6aa56151fba64465 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 10 Jun 2024 11:17:21 -0400 Subject: [PATCH 041/155] dt-bindings: remoteproc: imx_rproc: Add minItems for power-domain "fsl,imx8qxp-cm4" and "fsl,imx8qm-cm4" need minimum 2 power domains. Other platform doesn't require 'power-domain'. Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20240610151721.274424-1-Frank.Li@nxp.com Reviewed-by: Rob Herring (Arm) Signed-off-by: Mathieu Poirier --- .../bindings/remoteproc/fsl,imx-rproc.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml index df36e29d974c..57d75acb0b5e 100644 --- a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml @@ -59,6 +59,7 @@ properties: maxItems: 32 power-domains: + minItems: 2 maxItems: 8 fsl,auto-boot: @@ -99,6 +100,20 @@ allOf: properties: fsl,iomuxc-gpr: false + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8qxp-cm4 + - fsl,imx8qm-cm4 + then: + required: + - power-domains + else: + properties: + power-domains: false + additionalProperties: false examples: From 6efc3a05e6132812edf7eee0eb040eb88af34203 Mon Sep 17 00:00:00 2001 From: Zhiguo Niu Date: Fri, 7 Jun 2024 17:00:30 +0800 Subject: [PATCH 042/155] f2fs: enable atgc dynamically if conditions are met Now atgc can only be enabled when umounted->mounted device even related conditions have reached. If the device has not be umounted->mounted for a long time, atgc can not work. So enable atgc dynamically when atgc_age_threshold is less than elapsed_time and ATGC mount option is on. Signed-off-by: Chao Yu Signed-off-by: Zhiguo Niu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 1 + fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 27 ++++++++++++++++++++++++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 55d444bec5c0..7cfe4e01dd7e 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1718,6 +1718,7 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) } f2fs_restore_inmem_curseg(sbi); + f2fs_reinit_atgc_curseg(sbi); stat_inc_cp_count(sbi); stop: unblock_operations(sbi); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9688df332147..8d385a1c75ad 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3693,6 +3693,7 @@ void f2fs_release_discard_addrs(struct f2fs_sb_info *sbi); int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra); bool f2fs_segment_has_free_slot(struct f2fs_sb_info *sbi, int segno); int f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi); +int f2fs_reinit_atgc_curseg(struct f2fs_sb_info *sbi); void f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi); void f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi); int f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type, diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6e8a4b332ad5..362cfb550408 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2931,12 +2931,12 @@ static int get_atssr_segment(struct f2fs_sb_info *sbi, int type, return ret; } -static int __f2fs_init_atgc_curseg(struct f2fs_sb_info *sbi) +static int __f2fs_init_atgc_curseg(struct f2fs_sb_info *sbi, bool force) { struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_ALL_DATA_ATGC); int ret = 0; - if (!sbi->am.atgc_enabled) + if (!sbi->am.atgc_enabled && !force) return 0; f2fs_down_read(&SM_I(sbi)->curseg_lock); @@ -2953,9 +2953,30 @@ static int __f2fs_init_atgc_curseg(struct f2fs_sb_info *sbi) f2fs_up_read(&SM_I(sbi)->curseg_lock); return ret; } + int f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi) { - return __f2fs_init_atgc_curseg(sbi); + return __f2fs_init_atgc_curseg(sbi, false); +} + +int f2fs_reinit_atgc_curseg(struct f2fs_sb_info *sbi) +{ + int ret; + + if (!test_opt(sbi, ATGC)) + return 0; + if (sbi->am.atgc_enabled) + return 0; + if (le64_to_cpu(F2FS_CKPT(sbi)->elapsed_time) < + sbi->am.age_threshold) + return 0; + + ret = __f2fs_init_atgc_curseg(sbi, true); + if (!ret) { + sbi->am.atgc_enabled = true; + f2fs_info(sbi, "reenabled age threshold GC"); + } + return ret; } static void __f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi, int type) From 6aeb084fa0b1ada0290df08c5230e41881aef783 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 12 Jun 2024 18:21:56 +0000 Subject: [PATCH 043/155] f2fs: clean up set REQ_RAHEAD given rac Let's set REQ_RAHEAD per rac by single source. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/compress.c | 2 +- fs/f2fs/data.c | 17 +++++++++++------ fs/f2fs/f2fs.h | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 1ef82a546391..990b93689b46 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -1100,7 +1100,7 @@ retry: struct bio *bio = NULL; ret = f2fs_read_multi_pages(cc, &bio, cc->cluster_size, - &last_block_in_bio, false, true); + &last_block_in_bio, NULL, true); f2fs_put_rpages(cc); f2fs_destroy_compress_ctx(cc, true); if (ret) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b9b0debc6b3d..b6dcb3bcaef7 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2067,12 +2067,17 @@ static inline loff_t f2fs_readpage_limit(struct inode *inode) return i_size_read(inode); } +static inline blk_opf_t f2fs_ra_op_flags(struct readahead_control *rac) +{ + return rac ? REQ_RAHEAD : 0; +} + static int f2fs_read_single_page(struct inode *inode, struct folio *folio, unsigned nr_pages, struct f2fs_map_blocks *map, struct bio **bio_ret, sector_t *last_block_in_bio, - bool is_readahead) + struct readahead_control *rac) { struct bio *bio = *bio_ret; const unsigned blocksize = blks_to_bytes(inode, 1); @@ -2148,7 +2153,7 @@ submit_and_realloc: } if (bio == NULL) { bio = f2fs_grab_read_bio(inode, block_nr, nr_pages, - is_readahead ? REQ_RAHEAD : 0, index, + f2fs_ra_op_flags(rac), index, false); if (IS_ERR(bio)) { ret = PTR_ERR(bio); @@ -2178,7 +2183,7 @@ out: #ifdef CONFIG_F2FS_FS_COMPRESSION int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret, unsigned nr_pages, sector_t *last_block_in_bio, - bool is_readahead, bool for_write) + struct readahead_control *rac, bool for_write) { struct dnode_of_data dn; struct inode *inode = cc->inode; @@ -2301,7 +2306,7 @@ submit_and_realloc: if (!bio) { bio = f2fs_grab_read_bio(inode, blkaddr, nr_pages, - is_readahead ? REQ_RAHEAD : 0, + f2fs_ra_op_flags(rac), page->index, for_write); if (IS_ERR(bio)) { ret = PTR_ERR(bio); @@ -2399,7 +2404,7 @@ static int f2fs_mpage_readpages(struct inode *inode, ret = f2fs_read_multi_pages(&cc, &bio, max_nr_pages, &last_block_in_bio, - rac != NULL, false); + rac, false); f2fs_destroy_compress_ctx(&cc, false); if (ret) goto set_error_page; @@ -2449,7 +2454,7 @@ next_page: ret = f2fs_read_multi_pages(&cc, &bio, max_nr_pages, &last_block_in_bio, - rac != NULL, false); + rac, false); f2fs_destroy_compress_ctx(&cc, false); } } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 8d385a1c75ad..f7ee6c5e371e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4298,7 +4298,7 @@ void f2fs_update_read_extent_tree_range_compressed(struct inode *inode, unsigned int llen, unsigned int c_len); int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret, unsigned nr_pages, sector_t *last_block_in_bio, - bool is_readahead, bool for_write); + struct readahead_control *rac, bool for_write); struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc); void f2fs_decompress_end_io(struct decompress_io_ctx *dic, bool failed, bool in_task); From 1efb7c8fd8bf6b9d6f10dd2a9e0fde9e7a650ab4 Mon Sep 17 00:00:00 2001 From: Zhiguo Niu Date: Thu, 13 Jun 2024 17:35:33 +0800 Subject: [PATCH 044/155] f2fs: fix to use mnt_{want,drop}_write_file replace file_{start,end}_wrtie mnt_{want,drop}_write_file is more suitable than file_{start,end}_wrtie and also is consistent with other ioctl operations. Signed-off-by: Zhiguo Niu Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index c50213da474d..e4a7cff00796 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -3935,7 +3935,9 @@ static int f2fs_sec_trim_file(struct file *filp, unsigned long arg) IS_ENCRYPTED(inode) && f2fs_is_multi_device(sbi))) return -EOPNOTSUPP; - file_start_write(filp); + ret = mnt_want_write_file(filp); + if (ret) + return ret; inode_lock(inode); if (f2fs_is_atomic_file(inode) || f2fs_compressed_file(inode) || @@ -4061,7 +4063,7 @@ out: f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); err: inode_unlock(inode); - file_end_write(filp); + mnt_drop_write_file(filp); return ret; } @@ -4115,7 +4117,9 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg) option.algorithm >= COMPRESS_MAX) return -EINVAL; - file_start_write(filp); + ret = mnt_want_write_file(filp); + if (ret) + return ret; inode_lock(inode); f2fs_down_write(&F2FS_I(inode)->i_sem); @@ -4154,7 +4158,7 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg) out: f2fs_up_write(&F2FS_I(inode)->i_sem); inode_unlock(inode); - file_end_write(filp); + mnt_drop_write_file(filp); return ret; } @@ -4211,7 +4215,9 @@ static int f2fs_ioc_decompress_file(struct file *filp) f2fs_balance_fs(sbi, true); - file_start_write(filp); + ret = mnt_want_write_file(filp); + if (ret) + return ret; inode_lock(inode); if (!f2fs_is_compress_backend_ready(inode)) { @@ -4266,7 +4272,7 @@ static int f2fs_ioc_decompress_file(struct file *filp) f2fs_update_time(sbi, REQ_TIME); out: inode_unlock(inode); - file_end_write(filp); + mnt_drop_write_file(filp); return ret; } @@ -4288,7 +4294,9 @@ static int f2fs_ioc_compress_file(struct file *filp) f2fs_balance_fs(sbi, true); - file_start_write(filp); + ret = mnt_want_write_file(filp); + if (ret) + return ret; inode_lock(inode); if (!f2fs_is_compress_backend_ready(inode)) { @@ -4344,7 +4352,7 @@ static int f2fs_ioc_compress_file(struct file *filp) f2fs_update_time(sbi, REQ_TIME); out: inode_unlock(inode); - file_end_write(filp); + mnt_drop_write_file(filp); return ret; } From 8cb1f4080dd91c6e6b01dbea013a3f42341cb6a1 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 18 Jun 2024 02:15:38 +0000 Subject: [PATCH 045/155] f2fs: assign CURSEG_ALL_DATA_ATGC if blkaddr is valid mkdir /mnt/test/comp f2fs_io setflags compression /mnt/test/comp dd if=/dev/zero of=/mnt/test/comp/testfile bs=16k count=1 truncate --size 13 /mnt/test/comp/testfile In the above scenario, we can get a BUG_ON. kernel BUG at fs/f2fs/segment.c:3589! Call Trace: do_write_page+0x78/0x390 [f2fs] f2fs_outplace_write_data+0x62/0xb0 [f2fs] f2fs_do_write_data_page+0x275/0x740 [f2fs] f2fs_write_single_data_page+0x1dc/0x8f0 [f2fs] f2fs_write_multi_pages+0x1e5/0xae0 [f2fs] f2fs_write_cache_pages+0xab1/0xc60 [f2fs] f2fs_write_data_pages+0x2d8/0x330 [f2fs] do_writepages+0xcf/0x270 __writeback_single_inode+0x44/0x350 writeback_sb_inodes+0x242/0x530 __writeback_inodes_wb+0x54/0xf0 wb_writeback+0x192/0x310 wb_workfn+0x30d/0x400 The reason is we gave CURSEG_ALL_DATA_ATGC to COMPR_ADDR where the page was set the gcing flag by set_cluster_dirty(). Cc: stable@vger.kernel.org Fixes: 4961acdd65c9 ("f2fs: fix to tag gcing flag on page during block migration") Reviewed-by: Chao Yu Tested-by: Will McVicker Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 362cfb550408..4db1add43e36 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3505,6 +3505,7 @@ static int __get_segment_type_6(struct f2fs_io_info *fio) if (fio->sbi->am.atgc_enabled && (fio->io_type == FS_DATA_IO) && (fio->sbi->gc_mode != GC_URGENT_HIGH) && + __is_valid_data_blkaddr(fio->old_blkaddr) && !is_inode_flag_set(inode, FI_OPU_WRITE)) return CURSEG_ALL_DATA_ATGC; else From 388a2a0640e16a8887f0d47dab207f344fbdb913 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 6 Jun 2024 17:55:51 +0800 Subject: [PATCH 046/155] f2fs: remove redundant sanity check in sanity_check_inode() Commit f240d3aaf5a1 ("f2fs: do more sanity check on inode") missed to remove redundant sanity check on flexible_inline_xattr flag, fix it. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index dbfebbddf675..7a3e2458b2d9 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -313,10 +313,6 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) if (!sanity_check_compress_inode(inode, ri)) return false; } - } else if (f2fs_sb_has_flexible_inline_xattr(sbi)) { - f2fs_warn(sbi, "%s: corrupted inode ino=%lx, run fsck to fix.", - __func__, inode->i_ino); - return false; } if (!f2fs_sb_has_extra_attr(sbi)) { From 5b9f51b200dcb2c3924ecbff324fa52f1faa84d3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 22 Jun 2024 01:03:44 +0300 Subject: [PATCH 047/155] remoteproc: qcom: enable in-kernel PD mapper Request in-kernel protection domain mapper to be started before starting Qualcomm DSP and release it once DSP is stopped. Once all DSPs are stopped, the PD mapper will be stopped too. Reviewed-by: Chris Lew Tested-by: Steev Klimaszewski Tested-by: Neil Armstrong # on SM8550-QRD Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20240622-qcom-pd-mapper-v9-5-a84ee3591c8e@linaro.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_common.c | 87 +++++++++++++++++++++++++++++ drivers/remoteproc/qcom_common.h | 10 ++++ drivers/remoteproc/qcom_q6v5_adsp.c | 3 + drivers/remoteproc/qcom_q6v5_mss.c | 3 + drivers/remoteproc/qcom_q6v5_pas.c | 3 + drivers/remoteproc/qcom_q6v5_wcss.c | 3 + 6 files changed, 109 insertions(+) diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index 03e5f5d533eb..8c8688f99f0a 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev) #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev) #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev) +#define to_pdm_subdev(d) container_of(d, struct qcom_rproc_pdm, subdev) #define MAX_NUM_OF_SS 10 #define MAX_REGION_NAME_LENGTH 16 @@ -519,5 +521,90 @@ void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr) } EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev); +static void pdm_dev_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + kfree(adev); +} + +static int pdm_notify_prepare(struct rproc_subdev *subdev) +{ + struct qcom_rproc_pdm *pdm = to_pdm_subdev(subdev); + struct auxiliary_device *adev; + int ret; + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) + return -ENOMEM; + + adev->dev.parent = pdm->dev; + adev->dev.release = pdm_dev_release; + adev->name = "pd-mapper"; + adev->id = pdm->index; + + ret = auxiliary_device_init(adev); + if (ret) { + kfree(adev); + return ret; + } + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + pdm->adev = adev; + + return 0; +} + + +static void pdm_notify_unprepare(struct rproc_subdev *subdev) +{ + struct qcom_rproc_pdm *pdm = to_pdm_subdev(subdev); + + if (!pdm->adev) + return; + + auxiliary_device_delete(pdm->adev); + auxiliary_device_uninit(pdm->adev); + pdm->adev = NULL; +} + +/** + * qcom_add_pdm_subdev() - register PD Mapper subdevice + * @rproc: rproc handle + * @pdm: PDM subdevice handle + * + * Register @pdm so that Protection Device mapper service is started when the + * DSP is started too. + */ +void qcom_add_pdm_subdev(struct rproc *rproc, struct qcom_rproc_pdm *pdm) +{ + pdm->dev = &rproc->dev; + pdm->index = rproc->index; + + pdm->subdev.prepare = pdm_notify_prepare; + pdm->subdev.unprepare = pdm_notify_unprepare; + + rproc_add_subdev(rproc, &pdm->subdev); +} +EXPORT_SYMBOL_GPL(qcom_add_pdm_subdev); + +/** + * qcom_remove_pdm_subdev() - remove PD Mapper subdevice + * @rproc: rproc handle + * @pdm: PDM subdevice handle + * + * Remove the PD Mapper subdevice. + */ +void qcom_remove_pdm_subdev(struct rproc *rproc, struct qcom_rproc_pdm *pdm) +{ + rproc_remove_subdev(rproc, &pdm->subdev); +} +EXPORT_SYMBOL_GPL(qcom_remove_pdm_subdev); + MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h index 9ef4449052a9..b07fbaa091a0 100644 --- a/drivers/remoteproc/qcom_common.h +++ b/drivers/remoteproc/qcom_common.h @@ -34,6 +34,13 @@ struct qcom_rproc_ssr { struct qcom_ssr_subsystem *info; }; +struct qcom_rproc_pdm { + struct rproc_subdev subdev; + struct device *dev; + int index; + struct auxiliary_device *adev; +}; + void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, void (*rproc_dumpfn_t)(struct rproc *rproc, struct rproc_dump_segment *segment, void *dest, size_t offset, @@ -52,6 +59,9 @@ void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, const char *ssr_name); void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr); +void qcom_add_pdm_subdev(struct rproc *rproc, struct qcom_rproc_pdm *pdm); +void qcom_remove_pdm_subdev(struct rproc *rproc, struct qcom_rproc_pdm *pdm); + #if IS_ENABLED(CONFIG_QCOM_SYSMON) struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, const char *name, diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 1d24c9b656a8..572dcb0f055b 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -112,6 +112,7 @@ struct qcom_adsp { struct dev_pm_domain_list *pd_list; struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_pdm pdm_subdev; struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; @@ -726,6 +727,7 @@ static int adsp_probe(struct platform_device *pdev) goto disable_pm; qcom_add_glink_subdev(rproc, &adsp->glink_subdev, desc->ssr_name); + qcom_add_pdm_subdev(rproc, &adsp->pdm_subdev); qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, @@ -755,6 +757,7 @@ static void adsp_remove(struct platform_device *pdev) qcom_q6v5_deinit(&adsp->q6v5); qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); + qcom_remove_pdm_subdev(adsp->rproc, &adsp->pdm_subdev); qcom_remove_sysmon_subdev(adsp->sysmon); qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); qcom_rproc_pds_detach(adsp); diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 1779fc890e10..2a42215ce8e0 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -228,6 +228,7 @@ struct q6v5 { struct qcom_rproc_glink glink_subdev; struct qcom_rproc_subdev smd_subdev; + struct qcom_rproc_pdm pdm_subdev; struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; struct platform_device *bam_dmux; @@ -2102,6 +2103,7 @@ static int q6v5_probe(struct platform_device *pdev) qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); qcom_add_glink_subdev(rproc, &qproc->glink_subdev, "mpss"); qcom_add_smd_subdev(rproc, &qproc->smd_subdev); + qcom_add_pdm_subdev(rproc, &qproc->pdm_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); if (IS_ERR(qproc->sysmon)) { @@ -2143,6 +2145,7 @@ static void q6v5_remove(struct platform_device *pdev) qcom_q6v5_deinit(&qproc->q6v5); qcom_remove_sysmon_subdev(qproc->sysmon); qcom_remove_ssr_subdev(rproc, &qproc->ssr_subdev); + qcom_remove_pdm_subdev(rproc, &qproc->pdm_subdev); qcom_remove_smd_subdev(rproc, &qproc->smd_subdev); qcom_remove_glink_subdev(rproc, &qproc->glink_subdev); diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 8458bcfe9e19..88e7b84f223c 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -111,6 +111,7 @@ struct qcom_adsp { struct qcom_rproc_glink glink_subdev; struct qcom_rproc_subdev smd_subdev; + struct qcom_rproc_pdm pdm_subdev; struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; @@ -777,6 +778,7 @@ static int adsp_probe(struct platform_device *pdev) qcom_add_glink_subdev(rproc, &adsp->glink_subdev, desc->ssr_name); qcom_add_smd_subdev(rproc, &adsp->smd_subdev); + qcom_add_pdm_subdev(rproc, &adsp->pdm_subdev); adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, desc->ssctl_id); @@ -811,6 +813,7 @@ static void adsp_remove(struct platform_device *pdev) qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); qcom_remove_sysmon_subdev(adsp->sysmon); qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); + qcom_remove_pdm_subdev(adsp->rproc, &adsp->pdm_subdev); qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); adsp_pds_detach(adsp, adsp->proxy_pds, adsp->proxy_pd_count); device_init_wakeup(adsp->dev, false); diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c index 94f68c919ee6..e913dabae992 100644 --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -148,6 +148,7 @@ struct q6v5_wcss { bool requires_force_stop; struct qcom_rproc_glink glink_subdev; + struct qcom_rproc_pdm pdm_subdev; struct qcom_rproc_ssr ssr_subdev; }; @@ -1052,6 +1053,7 @@ static int q6v5_wcss_probe(struct platform_device *pdev) return ret; qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss"); + qcom_add_pdm_subdev(rproc, &wcss->pdm_subdev); qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss"); if (desc->ssctl_id) @@ -1074,6 +1076,7 @@ static void q6v5_wcss_remove(struct platform_device *pdev) struct q6v5_wcss *wcss = rproc->priv; qcom_q6v5_deinit(&wcss->q6v5); + qcom_remove_pdm_subdev(rproc, &wcss->pdm_subdev); rproc_del(rproc); } From ce6dede912f064a855acf6f04a04cbb2c25b8c8c Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Thu, 11 Apr 2024 20:05:28 +0800 Subject: [PATCH 048/155] jfs: fix null ptr deref in dtInsertEntry [syzbot reported] general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] PREEMPT SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] CPU: 0 PID: 5061 Comm: syz-executor404 Not tainted 6.8.0-syzkaller-08951-gfe46a7dd189e #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/27/2024 RIP: 0010:dtInsertEntry+0xd0c/0x1780 fs/jfs/jfs_dtree.c:3713 ... [Analyze] In dtInsertEntry(), when the pointer h has the same value as p, after writing name in UniStrncpy_to_le(), p->header.flag will be cleared. This will cause the previously true judgment "p->header.flag & BT-LEAF" to change to no after writing the name operation, this leads to entering an incorrect branch and accessing the uninitialized object ih when judging this condition for the second time. [Fix] After got the page, check freelist first, if freelist == 0 then exit dtInsert() and return -EINVAL. Reported-by: syzbot+bba84aef3a26fb93deb9@syzkaller.appspotmail.com Signed-off-by: Edward Adam Davis Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_dtree.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c index 031d8f570f58..5d3127ca68a4 100644 --- a/fs/jfs/jfs_dtree.c +++ b/fs/jfs/jfs_dtree.c @@ -834,6 +834,8 @@ int dtInsert(tid_t tid, struct inode *ip, * the full page. */ DT_GETSEARCH(ip, btstack->top, bn, mp, p, index); + if (p->header.freelist == 0) + return -EINVAL; /* * insert entry for new key From f73f969b2eb39ad8056f6c7f3a295fa2f85e313a Mon Sep 17 00:00:00 2001 From: Jeongjun Park Date: Thu, 30 May 2024 22:28:09 +0900 Subject: [PATCH 049/155] jfs: Fix array-index-out-of-bounds in diFree Reported-by: syzbot+241c815bda521982cb49@syzkaller.appspotmail.com Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Jeongjun Park Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_imap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c index 2ec35889ad24..1407feccbc2d 100644 --- a/fs/jfs/jfs_imap.c +++ b/fs/jfs/jfs_imap.c @@ -290,7 +290,7 @@ int diSync(struct inode *ipimap) int diRead(struct inode *ip) { struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb); - int iagno, ino, extno, rc; + int iagno, ino, extno, rc, agno; struct inode *ipimap; struct dinode *dp; struct iag *iagp; @@ -339,8 +339,11 @@ int diRead(struct inode *ip) /* get the ag for the iag */ agstart = le64_to_cpu(iagp->agstart); + agno = BLKTOAG(agstart, JFS_SBI(ip->i_sb)); release_metapage(mp); + if (agno >= MAXAG || agno < 0) + return -EIO; rel_inode = (ino & (INOSPERPAGE - 1)); pageno = blkno >> sbi->l2nbperpage; From 7063b80268e2593e58bee8a8d709c2f3ff93e2f2 Mon Sep 17 00:00:00 2001 From: Pei Li Date: Tue, 25 Jun 2024 09:42:05 -0700 Subject: [PATCH 050/155] jfs: Fix shift-out-of-bounds in dbDiscardAG When searching for the next smaller log2 block, BLKSTOL2() returned 0, causing shift exponent -1 to be negative. This patch fixes the issue by exiting the loop directly when negative shift is found. Reported-by: syzbot+61be3359d2ee3467e7e4@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=61be3359d2ee3467e7e4 Signed-off-by: Pei Li Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_dmap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c index cb3cda1390ad..5713994328cb 100644 --- a/fs/jfs/jfs_dmap.c +++ b/fs/jfs/jfs_dmap.c @@ -1626,6 +1626,8 @@ s64 dbDiscardAG(struct inode *ip, int agno, s64 minlen) } else if (rc == -ENOSPC) { /* search for next smaller log2 block */ l2nb = BLKSTOL2(nblocks) - 1; + if (unlikely(l2nb < 0)) + break; nblocks = 1LL << l2nb; } else { /* Trim any already allocated blocks */ From d0fa70aca54c8643248e89061da23752506ec0d4 Mon Sep 17 00:00:00 2001 From: lei lu Date: Wed, 29 May 2024 02:30:40 +0800 Subject: [PATCH 051/155] jfs: don't walk off the end of ealist Add a check before visiting the members of ea to make sure each ea stays within the ealist. Signed-off-by: lei lu Signed-off-by: Dave Kleikamp --- fs/jfs/xattr.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index 0fb7afac298e..ab9b85ce7ff7 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -795,7 +795,7 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, size_t buf_size) { struct jfs_ea_list *ealist; - struct jfs_ea *ea; + struct jfs_ea *ea, *ealist_end; struct ea_buffer ea_buf; int xattr_size; ssize_t size; @@ -815,9 +815,16 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, goto not_found; ealist = (struct jfs_ea_list *) ea_buf.xattr; + ealist_end = END_EALIST(ealist); /* Find the named attribute */ - for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) + for (ea = FIRST_EA(ealist); ea < ealist_end; ea = NEXT_EA(ea)) { + if (unlikely(ea + 1 > ealist_end) || + unlikely(NEXT_EA(ea) > ealist_end)) { + size = -EUCLEAN; + goto release; + } + if ((namelen == ea->namelen) && memcmp(name, ea->name, namelen) == 0) { /* Found it */ @@ -832,6 +839,7 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, memcpy(data, value, size); goto release; } + } not_found: size = -ENODATA; release: @@ -859,7 +867,7 @@ ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) ssize_t size = 0; int xattr_size; struct jfs_ea_list *ealist; - struct jfs_ea *ea; + struct jfs_ea *ea, *ealist_end; struct ea_buffer ea_buf; down_read(&JFS_IP(inode)->xattr_sem); @@ -874,9 +882,16 @@ ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size) goto release; ealist = (struct jfs_ea_list *) ea_buf.xattr; + ealist_end = END_EALIST(ealist); /* compute required size of list */ - for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) { + for (ea = FIRST_EA(ealist); ea < ealist_end; ea = NEXT_EA(ea)) { + if (unlikely(ea + 1 > ealist_end) || + unlikely(NEXT_EA(ea) > ealist_end)) { + size = -EUCLEAN; + goto release; + } + if (can_list(ea)) size += name_size(ea) + 1; } From 67ca3f98070ffdf308b91e08a477fcb1e9684ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcolas=20F=2E=20R=2E=20A=2E=20Prado?= Date: Thu, 27 Jun 2024 17:20:55 -0400 Subject: [PATCH 052/155] remoteproc: mediatek: Don't attempt to remap l1tcm memory if missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code doesn't check whether platform_get_resource_byname() succeeded to get the l1tcm memory, which is optional, before attempting to map it. This results in the following error message when it is missing: mtk-scp 10500000.scp: error -EINVAL: invalid resource (null) Add a check so that the remapping is only attempted if the memory region exists. This also allows to simplify the logic handling failure to remap, since a failure then is always a failure. Fixes: ca23ecfdbd44 ("remoteproc/mediatek: support L1TCM") Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: Tzung-Bi Shih Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20240627-scp-invalid-resource-l1tcm-v1-1-7d221e6c495a@collabora.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/mtk_scp.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index b885a9a041e4..b17757900cd7 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -1344,14 +1344,12 @@ static int scp_probe(struct platform_device *pdev) /* l1tcm is an optional memory region */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "l1tcm"); - scp_cluster->l1tcm_base = devm_ioremap_resource(dev, res); - if (IS_ERR(scp_cluster->l1tcm_base)) { - ret = PTR_ERR(scp_cluster->l1tcm_base); - if (ret != -EINVAL) - return dev_err_probe(dev, ret, "Failed to map l1tcm memory\n"); + if (res) { + scp_cluster->l1tcm_base = devm_ioremap_resource(dev, res); + if (IS_ERR(scp_cluster->l1tcm_base)) + return dev_err_probe(dev, PTR_ERR(scp_cluster->l1tcm_base), + "Failed to map l1tcm memory\n"); - scp_cluster->l1tcm_base = NULL; - } else { scp_cluster->l1tcm_size = resource_size(res); scp_cluster->l1tcm_phys = res->start; } From a8631f6d6344d976096b1efafdb2fbb3111bd790 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 21 Jun 2024 17:00:55 +0200 Subject: [PATCH 053/155] remoteproc: k3-r5: Fix IPC-only mode detection ret variable was used to test reset status, get from reset_control_status() call. But this variable was overwritten by ti_sci_proc_get_status() a few lines bellow. And as ti_sci_proc_get_status() returns 0 or a negative value (in this latter case, followed by a return), the expression !ret was always true, Clearly, this was not what was intended: In the comment above it's said that "requires both local and module resets to be deasserted"; if reset_control_status() returns 0 it means that the reset line is deasserted. So, it's pretty clear that the return value of reset_control_status() was intended to be used instead of ti_sci_proc_get_status() return value. This could lead in an incorrect IPC-only mode detection if reset line is asserted (so reset_control_status() return > 0) and c_state != 0 and halted == 0. In this case, the old code would have detected an IPC-only mode instead of a mismatched mode. Fixes: 1168af40b1ad ("remoteproc: k3-r5: Add support for IPC-only mode for all R5Fs") Signed-off-by: Richard Genoud Reviewed-by: Hari Nagalla Link: https://lore.kernel.org/r/20240621150058.319524-2-richard.genoud@bootlin.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/ti_k3_r5_remoteproc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index 50e486bcfa10..39a47540c590 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -1144,6 +1144,7 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) u32 atcm_enable, btcm_enable, loczrama; struct k3_r5_core *core0; enum cluster_mode mode = cluster->mode; + int reset_ctrl_status; int ret; core0 = list_first_entry(&cluster->cores, struct k3_r5_core, elem); @@ -1160,11 +1161,11 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) r_state, c_state); } - ret = reset_control_status(core->reset); - if (ret < 0) { + reset_ctrl_status = reset_control_status(core->reset); + if (reset_ctrl_status < 0) { dev_err(cdev, "failed to get initial local reset status, ret = %d\n", - ret); - return ret; + reset_ctrl_status); + return reset_ctrl_status; } /* @@ -1199,7 +1200,7 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) * irrelevant if module reset is asserted (POR value has local reset * deasserted), and is deemed as remoteproc mode */ - if (c_state && !ret && !halted) { + if (c_state && !reset_ctrl_status && !halted) { dev_info(cdev, "configured R5F for IPC-only mode\n"); kproc->rproc->state = RPROC_DETACHED; ret = 1; @@ -1217,7 +1218,7 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) ret = 0; } else { dev_err(cdev, "mismatched mode: local_reset = %s, module_reset = %s, core_state = %s\n", - !ret ? "deasserted" : "asserted", + !reset_ctrl_status ? "deasserted" : "asserted", c_state ? "deasserted" : "asserted", halted ? "halted" : "unhalted"); ret = -EINVAL; From e9b854382c8df07385c778bd8b25c03442bfa9e2 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 26 Jun 2024 22:12:38 +0300 Subject: [PATCH 054/155] remoteproc: qcom: select AUXILIARY_BUS The QCOM_PD_MAPPER implementation made Qualcomm remoteproc drivers use auxiliary bus for the pd-mapper subdevice. Add necessary dependency. Reported-by: Mark Brown Fixes: 5b9f51b200dc ("remoteproc: qcom: enable in-kernel PD mapper") Signed-off-by: Dmitry Baryshkov Reviewed-by: Chris Lew Link: https://lore.kernel.org/r/20240626-qcom-pd-mapper-fix-deps-v1-2-644678dc4663@linaro.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48845dc8fa85..dda2ada215b7 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -166,6 +166,7 @@ config QCOM_PIL_INFO config QCOM_RPROC_COMMON tristate + select AUXILIARY_BUS config QCOM_Q6V5_COMMON tristate From bca4b02ef92e0652d07e39c1f16f40aee800dafe Mon Sep 17 00:00:00 2001 From: Tanmay Shah Date: Thu, 27 Jun 2024 10:29:38 -0700 Subject: [PATCH 055/155] remoteproc: xlnx: Add attach detach support It is possible that remote processor is already running before linux boot or remoteproc platform driver probe. Implement required remoteproc framework ops to provide resource table address and connect or disconnect with remote processor in such case. Signed-off-by: Tanmay Shah Link: https://lore.kernel.org/r/20240627172936.227439-1-tanmay.shah@amd.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/xlnx_r5_remoteproc.c | 151 ++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 84243d1dff9f..596f3ffb8935 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -25,6 +25,10 @@ /* RX mailbox client buffer max length */ #define MBOX_CLIENT_BUF_MAX (IPI_BUF_LEN_MAX + \ sizeof(struct zynqmp_ipi_message)) + +#define RSC_TBL_XLNX_MAGIC ((uint32_t)'x' << 24 | (uint32_t)'a' << 16 | \ + (uint32_t)'m' << 8 | (uint32_t)'p') + /* * settings for RPU cluster mode which * reflects possible values of xlnx,cluster-mode dt-property @@ -73,6 +77,26 @@ struct mbox_info { struct mbox_chan *rx_chan; }; +/** + * struct rsc_tbl_data + * + * Platform specific data structure used to sync resource table address. + * It's important to maintain order and size of each field on remote side. + * + * @version: version of data structure + * @magic_num: 32-bit magic number. + * @comp_magic_num: complement of above magic number + * @rsc_tbl_size: resource table size + * @rsc_tbl: resource table address + */ +struct rsc_tbl_data { + const int version; + const u32 magic_num; + const u32 comp_magic_num; + const u32 rsc_tbl_size; + const uintptr_t rsc_tbl; +} __packed; + /* * Hardcoded TCM bank values. This will stay in driver to maintain backward * compatibility with device-tree that does not have TCM information. @@ -95,20 +119,24 @@ static const struct mem_bank_data zynqmp_tcm_banks_lockstep[] = { /** * struct zynqmp_r5_core * + * @rsc_tbl_va: resource table virtual address * @dev: device of RPU instance * @np: device node of RPU instance * @tcm_bank_count: number TCM banks accessible to this RPU * @tcm_banks: array of each TCM bank data * @rproc: rproc handle + * @rsc_tbl_size: resource table size retrieved from remote * @pm_domain_id: RPU CPU power domain id * @ipi: pointer to mailbox information */ struct zynqmp_r5_core { + void __iomem *rsc_tbl_va; struct device *dev; struct device_node *np; int tcm_bank_count; struct mem_bank_data **tcm_banks; struct rproc *rproc; + u32 rsc_tbl_size; u32 pm_domain_id; struct mbox_info *ipi; }; @@ -557,6 +585,14 @@ static int add_tcm_banks(struct rproc *rproc) dev_dbg(dev, "TCM carveout %s addr=%llx, da=0x%x, size=0x%lx", bank_name, bank_addr, da, bank_size); + /* + * In DETACHED state firmware is already running so no need to + * request add TCM registers. However, request TCM PD node to let + * platform management firmware know that TCM is in use. + */ + if (rproc->state == RPROC_DETACHED) + continue; + rproc_mem = rproc_mem_entry_init(dev, NULL, bank_addr, bank_size, da, tcm_mem_map, tcm_mem_unmap, @@ -662,6 +698,107 @@ static int zynqmp_r5_rproc_unprepare(struct rproc *rproc) return 0; } +static struct resource_table *zynqmp_r5_get_loaded_rsc_table(struct rproc *rproc, + size_t *size) +{ + struct zynqmp_r5_core *r5_core; + + r5_core = rproc->priv; + + *size = r5_core->rsc_tbl_size; + + return (struct resource_table *)r5_core->rsc_tbl_va; +} + +static int zynqmp_r5_get_rsc_table_va(struct zynqmp_r5_core *r5_core) +{ + struct resource_table *rsc_tbl_addr; + struct device *dev = r5_core->dev; + struct rsc_tbl_data *rsc_data_va; + struct resource res_mem; + struct device_node *np; + int ret; + + /* + * It is expected from remote processor firmware to provide resource + * table address via struct rsc_tbl_data data structure. + * Start address of first entry under "memory-region" property list + * contains that data structure which holds resource table address, size + * and some magic number to validate correct resource table entry. + */ + np = of_parse_phandle(r5_core->np, "memory-region", 0); + if (!np) { + dev_err(dev, "failed to get memory region dev node\n"); + return -EINVAL; + } + + ret = of_address_to_resource(np, 0, &res_mem); + of_node_put(np); + if (ret) { + dev_err(dev, "failed to get memory-region resource addr\n"); + return -EINVAL; + } + + rsc_data_va = (struct rsc_tbl_data *)ioremap_wc(res_mem.start, + sizeof(struct rsc_tbl_data)); + if (!rsc_data_va) { + dev_err(dev, "failed to map resource table data address\n"); + return -EIO; + } + + /* + * If RSC_TBL_XLNX_MAGIC number and its complement isn't found then + * do not consider resource table address valid and don't attach + */ + if (rsc_data_va->magic_num != RSC_TBL_XLNX_MAGIC || + rsc_data_va->comp_magic_num != ~RSC_TBL_XLNX_MAGIC) { + dev_dbg(dev, "invalid magic number, won't attach\n"); + return -EINVAL; + } + + r5_core->rsc_tbl_va = ioremap_wc(rsc_data_va->rsc_tbl, + rsc_data_va->rsc_tbl_size); + if (!r5_core->rsc_tbl_va) { + dev_err(dev, "failed to get resource table va\n"); + return -EINVAL; + } + + rsc_tbl_addr = (struct resource_table *)r5_core->rsc_tbl_va; + + /* + * As of now resource table version 1 is expected. Don't fail to attach + * but warn users about it. + */ + if (rsc_tbl_addr->ver != 1) + dev_warn(dev, "unexpected resource table version %d\n", + rsc_tbl_addr->ver); + + r5_core->rsc_tbl_size = rsc_data_va->rsc_tbl_size; + + iounmap((void __iomem *)rsc_data_va); + + return 0; +} + +static int zynqmp_r5_attach(struct rproc *rproc) +{ + dev_dbg(&rproc->dev, "rproc %d attached\n", rproc->index); + + return 0; +} + +static int zynqmp_r5_detach(struct rproc *rproc) +{ + /* + * Generate last notification to remote after clearing virtio flag. + * Remote can avoid polling on virtio reset flag if kick is generated + * during detach by host and check virtio reset flag on kick interrupt. + */ + zynqmp_r5_rproc_kick(rproc, 0); + + return 0; +} + static const struct rproc_ops zynqmp_r5_rproc_ops = { .prepare = zynqmp_r5_rproc_prepare, .unprepare = zynqmp_r5_rproc_unprepare, @@ -673,6 +810,9 @@ static const struct rproc_ops zynqmp_r5_rproc_ops = { .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, .kick = zynqmp_r5_rproc_kick, + .get_loaded_rsc_table = zynqmp_r5_get_loaded_rsc_table, + .attach = zynqmp_r5_attach, + .detach = zynqmp_r5_detach, }; /** @@ -723,6 +863,16 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) goto free_rproc; } + /* + * If firmware is already available in the memory then move rproc state + * to DETACHED. Firmware can be preloaded via debugger or by any other + * agent (processors) in the system. + * If firmware isn't available in the memory and resource table isn't + * found, then rproc state remains OFFLINE. + */ + if (!zynqmp_r5_get_rsc_table_va(r5_core)) + r5_rproc->state = RPROC_DETACHED; + r5_core->rproc = r5_rproc; return r5_core; @@ -1134,6 +1284,7 @@ static void zynqmp_r5_cluster_exit(void *data) for (i = 0; i < cluster->core_count; i++) { r5_core = cluster->r5_cores[i]; zynqmp_r5_free_mbox(r5_core->ipi); + iounmap(r5_core->rsc_tbl_va); of_reserved_mem_device_release(r5_core->dev); put_device(r5_core->dev); rproc_del(r5_core->rproc); From 9dfd8d92f7b19ba8eaf5391380a923396f8a9dd0 Mon Sep 17 00:00:00 2001 From: Garrett Giordano Date: Wed, 26 Jun 2024 12:14:38 -0700 Subject: [PATCH 056/155] remoteproc: k3-dsp: Fix log levels where appropriate Driver was logging information as errors. Changed dev_err to dev_dbg where appropriate. Signed-off-by: Garrett Giordano Acked-by: Andrew Davis Link: https://lore.kernel.org/r/20240626191438.490524-1-ggiordano@phytec.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/ti_k3_dsp_remoteproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/remoteproc/ti_k3_dsp_remoteproc.c b/drivers/remoteproc/ti_k3_dsp_remoteproc.c index 3555b535b168..a22d41689a7d 100644 --- a/drivers/remoteproc/ti_k3_dsp_remoteproc.c +++ b/drivers/remoteproc/ti_k3_dsp_remoteproc.c @@ -327,7 +327,7 @@ static int k3_dsp_rproc_start(struct rproc *rproc) goto put_mbox; } - dev_err(dev, "booting DSP core using boot addr = 0x%x\n", boot_addr); + dev_dbg(dev, "booting DSP core using boot addr = 0x%x\n", boot_addr); ret = ti_sci_proc_set_config(kproc->tsp, boot_addr, 0, 0); if (ret) goto put_mbox; From 19cb6058620620e68f1a9aed99393be5c3629db4 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Wed, 3 Jul 2024 11:44:09 +0800 Subject: [PATCH 057/155] remoteproc: mediatek: Increase MT8188/MT8195 SCP core0 DRAM size The current DRAM size is insufficient for the HEVC feature, which requires more memory for proper functionality. This change ensures the feature has the necessary resources. Signed-off-by: Jason Chen Reviewed-by: AngeloGioacchino Del Regno Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20240703034409.698-1-Jason-ch.Chen@mediatek.com Signed-off-by: Mathieu Poirier --- drivers/remoteproc/mtk_scp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index b17757900cd7..e744c07507ee 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -1388,7 +1388,7 @@ static const struct mtk_scp_sizes_data default_scp_sizes = { }; static const struct mtk_scp_sizes_data mt8188_scp_sizes = { - .max_dram_size = 0x500000, + .max_dram_size = 0x800000, .ipi_share_buffer_size = 600, }; @@ -1397,6 +1397,11 @@ static const struct mtk_scp_sizes_data mt8188_scp_c1_sizes = { .ipi_share_buffer_size = 600, }; +static const struct mtk_scp_sizes_data mt8195_scp_sizes = { + .max_dram_size = 0x800000, + .ipi_share_buffer_size = 288, +}; + static const struct mtk_scp_of_data mt8183_of_data = { .scp_clk_get = mt8183_scp_clk_get, .scp_before_load = mt8183_scp_before_load, @@ -1474,7 +1479,7 @@ static const struct mtk_scp_of_data mt8195_of_data = { .scp_da_to_va = mt8192_scp_da_to_va, .host_to_scp_reg = MT8192_GIPC_IN_SET, .host_to_scp_int_bit = MT8192_HOST_IPC_INT_BIT, - .scp_sizes = &default_scp_sizes, + .scp_sizes = &mt8195_scp_sizes, }; static const struct mtk_scp_of_data mt8195_of_data_c1 = { From 8c409989678e92e4a737e7cd2bb04f3efb81071a Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Mon, 8 Jul 2024 20:04:07 +0800 Subject: [PATCH 058/155] f2fs: fix start segno of large section get_ckpt_valid_blocks() checks valid ckpt blocks in current section. It counts all vblocks from the first to the last segment in the large section. However, START_SEGNO() is used to get the first segno in an SIT block. This patch fixes that to get the correct start segno. Fixes: 61461fc921b7 ("f2fs: fix to avoid touching checkpointed data in get_victim()") Signed-off-by: Sheng Yong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index e1c0f418aa11..bfc01a521cb9 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -347,7 +347,8 @@ static inline unsigned int get_ckpt_valid_blocks(struct f2fs_sb_info *sbi, unsigned int segno, bool use_section) { if (use_section && __is_large_section(sbi)) { - unsigned int start_segno = START_SEGNO(segno); + unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); + unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno); unsigned int blocks = 0; int i; From c82bc1ab2a8a5e73d9728e80c4c2ed87e8921a38 Mon Sep 17 00:00:00 2001 From: Daejun Park Date: Thu, 4 Jul 2024 10:01:21 +0900 Subject: [PATCH 059/155] f2fs: fix null reference error when checking end of zone This patch fixes a potentially null pointer being accessed by is_end_zone_blkaddr() that checks the last block of a zone when f2fs is mounted as a single device. Fixes: e067dc3c6b9c ("f2fs: maintain six open zones for zoned devices") Signed-off-by: Daejun Park Reviewed-by: Chao Yu Reviewed-by: Daeho Jeong Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index b6dcb3bcaef7..1aa7eefa659c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -925,6 +925,7 @@ alloc_new: #ifdef CONFIG_BLK_DEV_ZONED static bool is_end_zone_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr) { + struct block_device *bdev = sbi->sb->s_bdev; int devi = 0; if (f2fs_is_multi_device(sbi)) { @@ -935,8 +936,9 @@ static bool is_end_zone_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr) return false; } blkaddr -= FDEV(devi).start_blk; + bdev = FDEV(devi).bdev; } - return bdev_is_zoned(FDEV(devi).bdev) && + return bdev_is_zoned(bdev) && f2fs_blkz_is_seq(sbi, devi, blkaddr) && (blkaddr % sbi->blocks_per_blkz == sbi->blocks_per_blkz - 1); } From 54f43a10fa257ad4af02a1d157fefef6ebcfa7dc Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Fri, 28 Jun 2024 23:38:17 -0500 Subject: [PATCH 060/155] f2fs: remove unreachable lazytime mount option parsing The lazytime/nolazytime options are now handled in the VFS, and are never seen in filesystem parsers, so remove handling of these options from f2fs. Note: when lazytime support was added in 6d94c74ab85f it made lazytime the default in default_options() - as a result, lazytime cannot be disabled (because Opt_nolazytime is never seen in f2fs parsing). If lazytime is desired to be configurable, and default off is OK, default_options() could be updated to stop setting it by default and allow mount option control. Signed-off-by: Eric Sandeen Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 4a1bc8f40f9a..28335b5a7af6 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -151,8 +151,6 @@ enum { Opt_mode, Opt_fault_injection, Opt_fault_type, - Opt_lazytime, - Opt_nolazytime, Opt_quota, Opt_noquota, Opt_usrquota, @@ -229,8 +227,6 @@ static match_table_t f2fs_tokens = { {Opt_mode, "mode=%s"}, {Opt_fault_injection, "fault_injection=%u"}, {Opt_fault_type, "fault_type=%u"}, - {Opt_lazytime, "lazytime"}, - {Opt_nolazytime, "nolazytime"}, {Opt_quota, "quota"}, {Opt_noquota, "noquota"}, {Opt_usrquota, "usrquota"}, @@ -918,12 +914,6 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) f2fs_info(sbi, "fault_type options not supported"); break; #endif - case Opt_lazytime: - sb->s_flags |= SB_LAZYTIME; - break; - case Opt_nolazytime: - sb->s_flags &= ~SB_LAZYTIME; - break; #ifdef CONFIG_QUOTA case Opt_quota: case Opt_usrquota: From f06c0f82e38bbda7264d6ef3c90045ad2810e0f3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 25 Jun 2024 10:32:39 +0800 Subject: [PATCH 061/155] f2fs: fix to update user block counts in block_operations() Commit 59c9081bc86e ("f2fs: allow write page cache when writting cp") allows write() to write data to page cache during checkpoint, so block count fields like .total_valid_block_count, .alloc_valid_block_count and .rf_node_block_count may encounter race condition as below: CP Thread A - write_checkpoint - block_operations - f2fs_down_write(&sbi->node_change) - __prepare_cp_block : ckpt->valid_block_count = .total_valid_block_count - f2fs_up_write(&sbi->node_change) - write - f2fs_preallocate_blocks - f2fs_map_blocks(,F2FS_GET_BLOCK_PRE_AIO) - f2fs_map_lock - f2fs_down_read(&sbi->node_change) - f2fs_reserve_new_blocks - inc_valid_block_count : percpu_counter_add(&sbi->alloc_valid_block_count, count) : sbi->total_valid_block_count += count - f2fs_up_read(&sbi->node_change) - do_checkpoint : sbi->last_valid_block_count = sbi->total_valid_block_count : percpu_counter_set(&sbi->alloc_valid_block_count, 0) : percpu_counter_set(&sbi->rf_node_block_count, 0) - fsync - need_do_checkpoint - f2fs_space_for_roll_forward : alloc_valid_block_count was reset to zero, so, it may missed last data during checkpoint Let's change to update .total_valid_block_count, .alloc_valid_block_count and .rf_node_block_count in block_operations(), then their access can be protected by .node_change and .cp_rwsem lock, so that it can avoid above race condition. Fixes: 59c9081bc86e ("f2fs: allow write page cache when writting cp") Cc: Yunlei He Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 7cfe4e01dd7e..bdd96329dddd 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1186,6 +1186,11 @@ static void __prepare_cp_block(struct f2fs_sb_info *sbi) ckpt->valid_node_count = cpu_to_le32(valid_node_count(sbi)); ckpt->valid_inode_count = cpu_to_le32(valid_inode_count(sbi)); ckpt->next_free_nid = cpu_to_le32(last_nid); + + /* update user_block_counts */ + sbi->last_valid_block_count = sbi->total_valid_block_count; + percpu_counter_set(&sbi->alloc_valid_block_count, 0); + percpu_counter_set(&sbi->rf_node_block_count, 0); } static bool __need_flush_quota(struct f2fs_sb_info *sbi) @@ -1575,11 +1580,6 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) start_blk += NR_CURSEG_NODE_TYPE; } - /* update user_block_counts */ - sbi->last_valid_block_count = sbi->total_valid_block_count; - percpu_counter_set(&sbi->alloc_valid_block_count, 0); - percpu_counter_set(&sbi->rf_node_block_count, 0); - /* Here, we have one bio having CP pack except cp pack 2 page */ f2fs_sync_meta_pages(sbi, META, LONG_MAX, FS_CP_META_IO); /* Wait for all dirty meta pages to be submitted for IO */ From e3a19972a49f61775e5e11ac43a663f28cdfb8b2 Mon Sep 17 00:00:00 2001 From: Sheng Yong Date: Wed, 10 Jul 2024 09:26:59 +0800 Subject: [PATCH 062/155] f2fs: only fragment segment in the same section When new_curseg() is allocating a new segment, if mode=fragment:xxx is switched on in large section scenario, __get_next_segno() will select the next segno randomly in the range of [0, maxsegno] in order to fragment segments. If the candidate segno is free, get_new_segment() will use it directly as the new segment. However, if the section of the candidate is not empty, and some other segments have already been used, and have a different type (e.g NODE) with the candidate (e.g DATA), GC will complain inconsistent segment type later. This could be reproduced by the following steps: dd if=/dev/zero of=test.img bs=1M count=10240 mkfs.f2fs -s 128 test.img mount -t f2fs test.img /mnt -o mode=fragment:block echo 1 > /sys/fs/f2fs/loop0/max_fragment_chunk echo 512 > /sys/fs/f2fs/loop0/max_fragment_hole dd if=/dev/zero of=/mnt/testfile bs=4K count=100 umount /mnt F2FS-fs (loop0): Inconsistent segment (4377) type [0, 1] in SSA and SIT In order to allow simulating segment fragmentation in large section scenario, this patch reduces the candidate range: * if curseg is the last segment in the section, return curseg->segno to make get_new_segment() itself find the next free segment. * if curseg is in the middle of the section, select candicate randomly in the range of [curseg + 1, last_seg_in_the_same_section] to keep type consistent. Reviewed-by: Chao Yu Signed-off-by: Sheng Yong Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 4db1add43e36..64b11d88a21c 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2784,11 +2784,19 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type) unsigned short seg_type = curseg->seg_type; sanity_check_seg_type(sbi, seg_type); - if (f2fs_need_rand_seg(sbi)) - return get_random_u32_below(MAIN_SECS(sbi) * SEGS_PER_SEC(sbi)); + if (__is_large_section(sbi)) { + if (f2fs_need_rand_seg(sbi)) { + unsigned int hint = GET_SEC_FROM_SEG(sbi, curseg->segno); - if (__is_large_section(sbi)) + if (GET_SEC_FROM_SEG(sbi, curseg->segno + 1) != hint) + return curseg->segno; + return get_random_u32_inclusive(curseg->segno + 1, + GET_SEG_FROM_SEC(sbi, hint + 1) - 1); + } return curseg->segno; + } else if (f2fs_need_rand_seg(sbi)) { + return get_random_u32_below(MAIN_SECS(sbi) * SEGS_PER_SEC(sbi)); + } /* inmem log may not locate on any segment after mount */ if (!curseg->inited) From b40a2b00370931b0c50148681dd7364573e52e6b Mon Sep 17 00:00:00 2001 From: Sunmin Jeong Date: Wed, 10 Jul 2024 20:51:17 +0900 Subject: [PATCH 063/155] f2fs: use meta inode for GC of atomic file The page cache of the atomic file keeps new data pages which will be stored in the COW file. It can also keep old data pages when GCing the atomic file. In this case, new data can be overwritten by old data if a GC thread sets the old data page as dirty after new data page was evicted. Also, since all writes to the atomic file are redirected to COW inodes, GC for the atomic file is not working well as below. f2fs_gc(gc_type=FG_GC) - select A as a victim segment do_garbage_collect - iget atomic file's inode for block B move_data_page f2fs_do_write_data_page - use dn of cow inode - set fio->old_blkaddr from cow inode - seg_freed is 0 since block B is still valid - goto gc_more and A is selected as victim again To solve the problem, let's separate GC writes and updates in the atomic file by using the meta inode for GC writes. Fixes: 3db1de0e582c ("f2fs: change the current atomic write way") Cc: stable@vger.kernel.org #v5.19+ Reviewed-by: Sungjong Seo Reviewed-by: Yeongjin Gil Signed-off-by: Sunmin Jeong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 4 ++-- fs/f2fs/f2fs.h | 7 ++++++- fs/f2fs/gc.c | 6 +++--- fs/f2fs/segment.c | 6 +++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 1aa7eefa659c..1e4937af50f3 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2695,7 +2695,7 @@ got_it: } /* wait for GCed page writeback via META_MAPPING */ - if (fio->post_read) + if (fio->meta_gc) f2fs_wait_on_block_writeback(inode, fio->old_blkaddr); /* @@ -2790,7 +2790,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted, .submitted = 0, .compr_blocks = compr_blocks, .need_lock = compr_blocks ? LOCK_DONE : LOCK_RETRY, - .post_read = f2fs_post_read_required(inode) ? 1 : 0, + .meta_gc = f2fs_meta_inode_gc_required(inode) ? 1 : 0, .io_type = io_type, .io_wbc = wbc, .bio = bio, diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f7ee6c5e371e..796ae11c0fa3 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1211,7 +1211,7 @@ struct f2fs_io_info { unsigned int in_list:1; /* indicate fio is in io_list */ unsigned int is_por:1; /* indicate IO is from recovery or not */ unsigned int encrypted:1; /* indicate file is encrypted */ - unsigned int post_read:1; /* require post read */ + unsigned int meta_gc:1; /* require meta inode GC */ enum iostat_type io_type; /* io type */ struct writeback_control *io_wbc; /* writeback control */ struct bio **bio; /* bio for ipu */ @@ -4263,6 +4263,11 @@ static inline bool f2fs_post_read_required(struct inode *inode) f2fs_compressed_file(inode); } +static inline bool f2fs_meta_inode_gc_required(struct inode *inode) +{ + return f2fs_post_read_required(inode) || f2fs_is_atomic_file(inode); +} + /* * compress.c */ diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index ef667fec9a12..cb3006551ab5 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1589,7 +1589,7 @@ next_step: start_bidx = f2fs_start_bidx_of_node(nofs, inode) + ofs_in_node; - if (f2fs_post_read_required(inode)) { + if (f2fs_meta_inode_gc_required(inode)) { int err = ra_data_block(inode, start_bidx); f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); @@ -1640,7 +1640,7 @@ next_step: start_bidx = f2fs_start_bidx_of_node(nofs, inode) + ofs_in_node; - if (f2fs_post_read_required(inode)) + if (f2fs_meta_inode_gc_required(inode)) err = move_data_block(inode, start_bidx, gc_type, segno, off); else @@ -1648,7 +1648,7 @@ next_step: segno, off); if (!err && (gc_type == FG_GC || - f2fs_post_read_required(inode))) + f2fs_meta_inode_gc_required(inode))) submitted++; if (locked) { diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 64b11d88a21c..78c3198a6308 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -3859,7 +3859,7 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio) goto drop_bio; } - if (fio->post_read) + if (fio->meta_gc) f2fs_truncate_meta_inode_pages(sbi, fio->new_blkaddr, 1); stat_inc_inplace_blocks(fio->sbi); @@ -4029,7 +4029,7 @@ void f2fs_wait_on_block_writeback(struct inode *inode, block_t blkaddr) struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct page *cpage; - if (!f2fs_post_read_required(inode)) + if (!f2fs_meta_inode_gc_required(inode)) return; if (!__is_valid_data_blkaddr(blkaddr)) @@ -4048,7 +4048,7 @@ void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); block_t i; - if (!f2fs_post_read_required(inode)) + if (!f2fs_meta_inode_gc_required(inode)) return; for (i = 0; i < len; i++) From f18d0076933689775fe7faeeb10ee93ff01be6ab Mon Sep 17 00:00:00 2001 From: Sunmin Jeong Date: Wed, 10 Jul 2024 20:51:43 +0900 Subject: [PATCH 064/155] f2fs: use meta inode for GC of COW file In case of the COW file, new updates and GC writes are already separated to page caches of the atomic file and COW file. As some cases that use the meta inode for GC, there are some race issues between a foreground thread and GC thread. To handle them, we need to take care when to invalidate and wait writeback of GC pages in COW files as the case of using the meta inode. Also, a pointer from the COW inode to the original inode is required to check the state of original pages. For the former, we can solve the problem by using the meta inode for GC of COW files. Then let's get a page from the original inode in move_data_block when GCing the COW file to avoid race condition. Fixes: 3db1de0e582c ("f2fs: change the current atomic write way") Cc: stable@vger.kernel.org #v5.19+ Reviewed-by: Sungjong Seo Reviewed-by: Yeongjin Gil Signed-off-by: Sunmin Jeong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 2 +- fs/f2fs/f2fs.h | 13 +++++++++++-- fs/f2fs/file.c | 3 +++ fs/f2fs/gc.c | 7 +++++-- fs/f2fs/inline.c | 2 +- fs/f2fs/inode.c | 3 ++- 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 1e4937af50f3..6457e5bca9c9 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2608,7 +2608,7 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio) return true; if (IS_NOQUOTA(inode)) return true; - if (f2fs_is_atomic_file(inode)) + if (f2fs_used_in_atomic_write(inode)) return true; /* rewrite low ratio compress data w/ OPU mode to avoid fragmentation */ if (f2fs_compressed_file(inode) && diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 796ae11c0fa3..4a8621e4a33a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -843,7 +843,11 @@ struct f2fs_inode_info { struct task_struct *atomic_write_task; /* store atomic write task */ struct extent_tree *extent_tree[NR_EXTENT_CACHES]; /* cached extent_tree entry */ - struct inode *cow_inode; /* copy-on-write inode for atomic write */ + union { + struct inode *cow_inode; /* copy-on-write inode for atomic write */ + struct inode *atomic_inode; + /* point to atomic_inode, available only for cow_inode */ + }; /* avoid racing between foreground op and gc */ struct f2fs_rwsem i_gc_rwsem[2]; @@ -4263,9 +4267,14 @@ static inline bool f2fs_post_read_required(struct inode *inode) f2fs_compressed_file(inode); } +static inline bool f2fs_used_in_atomic_write(struct inode *inode) +{ + return f2fs_is_atomic_file(inode) || f2fs_is_cow_file(inode); +} + static inline bool f2fs_meta_inode_gc_required(struct inode *inode) { - return f2fs_post_read_required(inode) || f2fs_is_atomic_file(inode); + return f2fs_post_read_required(inode) || f2fs_used_in_atomic_write(inode); } /* diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index e4a7cff00796..547e7ec32b1f 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2183,6 +2183,9 @@ static int f2fs_ioc_start_atomic_write(struct file *filp, bool truncate) set_inode_flag(fi->cow_inode, FI_COW_FILE); clear_inode_flag(fi->cow_inode, FI_INLINE_DATA); + + /* Set the COW inode's atomic_inode to the atomic inode */ + F2FS_I(fi->cow_inode)->atomic_inode = inode; } else { /* Reuse the already created COW inode */ ret = f2fs_do_truncate_blocks(fi->cow_inode, 0, true); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index cb3006551ab5..724bbcb447d3 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1171,7 +1171,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, static int ra_data_block(struct inode *inode, pgoff_t index) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - struct address_space *mapping = inode->i_mapping; + struct address_space *mapping = f2fs_is_cow_file(inode) ? + F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; struct dnode_of_data dn; struct page *page; struct f2fs_io_info fio = { @@ -1260,6 +1261,8 @@ put_page: static int move_data_block(struct inode *inode, block_t bidx, int gc_type, unsigned int segno, int off) { + struct address_space *mapping = f2fs_is_cow_file(inode) ? + F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping; struct f2fs_io_info fio = { .sbi = F2FS_I_SB(inode), .ino = inode->i_ino, @@ -1282,7 +1285,7 @@ static int move_data_block(struct inode *inode, block_t bidx, CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA; /* do not read out */ - page = f2fs_grab_cache_page(inode->i_mapping, bidx, false); + page = f2fs_grab_cache_page(mapping, bidx, false); if (!page) return -ENOMEM; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 1fba5728be70..cca7d448e55c 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -16,7 +16,7 @@ static bool support_inline_data(struct inode *inode) { - if (f2fs_is_atomic_file(inode)) + if (f2fs_used_in_atomic_write(inode)) return false; if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) return false; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 7a3e2458b2d9..18dea43e694b 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -804,8 +804,9 @@ void f2fs_evict_inode(struct inode *inode) f2fs_abort_atomic_write(inode, true); - if (fi->cow_inode) { + if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) { clear_inode_flag(fi->cow_inode, FI_COW_FILE); + F2FS_I(fi->cow_inode)->atomic_inode = NULL; iput(fi->cow_inode); fi->cow_inode = NULL; } From 7309871c03be22bee843f6961dcce183f04d48a9 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 25 Jun 2024 11:16:02 +0800 Subject: [PATCH 065/155] f2fs: clean up F2FS_I() Use temporary variable instead of F2FS_I() for cleanup. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 32 ++++++++++------------- fs/f2fs/file.c | 64 ++++++++++++++++++++++++---------------------- fs/f2fs/inode.c | 54 +++++++++++++++++--------------------- fs/f2fs/namei.c | 20 ++++++++------- fs/f2fs/recovery.c | 11 ++++---- 5 files changed, 88 insertions(+), 93 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4a8621e4a33a..5e414c8ed334 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4403,22 +4403,18 @@ static inline int set_compress_context(struct inode *inode) { #ifdef CONFIG_F2FS_FS_COMPRESSION struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_inode_info *fi = F2FS_I(inode); - F2FS_I(inode)->i_compress_algorithm = - F2FS_OPTION(sbi).compress_algorithm; - F2FS_I(inode)->i_log_cluster_size = - F2FS_OPTION(sbi).compress_log_size; - F2FS_I(inode)->i_compress_flag = - F2FS_OPTION(sbi).compress_chksum ? - BIT(COMPRESS_CHKSUM) : 0; - F2FS_I(inode)->i_cluster_size = - BIT(F2FS_I(inode)->i_log_cluster_size); - if ((F2FS_I(inode)->i_compress_algorithm == COMPRESS_LZ4 || - F2FS_I(inode)->i_compress_algorithm == COMPRESS_ZSTD) && + fi->i_compress_algorithm = F2FS_OPTION(sbi).compress_algorithm; + fi->i_log_cluster_size = F2FS_OPTION(sbi).compress_log_size; + fi->i_compress_flag = F2FS_OPTION(sbi).compress_chksum ? + BIT(COMPRESS_CHKSUM) : 0; + fi->i_cluster_size = BIT(fi->i_log_cluster_size); + if ((fi->i_compress_algorithm == COMPRESS_LZ4 || + fi->i_compress_algorithm == COMPRESS_ZSTD) && F2FS_OPTION(sbi).compress_level) - F2FS_I(inode)->i_compress_level = - F2FS_OPTION(sbi).compress_level; - F2FS_I(inode)->i_flags |= F2FS_COMPR_FL; + fi->i_compress_level = F2FS_OPTION(sbi).compress_level; + fi->i_flags |= F2FS_COMPR_FL; set_inode_flag(inode, FI_COMPRESSED_FILE); stat_inc_compr_inode(inode); inc_compr_inode_stat(inode); @@ -4433,15 +4429,15 @@ static inline bool f2fs_disable_compressed_file(struct inode *inode) { struct f2fs_inode_info *fi = F2FS_I(inode); - f2fs_down_write(&F2FS_I(inode)->i_sem); + f2fs_down_write(&fi->i_sem); if (!f2fs_compressed_file(inode)) { - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); return true; } if (f2fs_is_mmap_file(inode) || (S_ISREG(inode->i_mode) && F2FS_HAS_BLOCKS(inode))) { - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); return false; } @@ -4450,7 +4446,7 @@ static inline bool f2fs_disable_compressed_file(struct inode *inode) clear_inode_flag(inode, FI_COMPRESSED_FILE); f2fs_mark_inode_dirty_sync(inode, true); - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); return true; } diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 547e7ec32b1f..effbaa606a4e 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -981,6 +981,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { struct inode *inode = d_inode(dentry); + struct f2fs_inode_info *fi = F2FS_I(inode); int err; if (unlikely(f2fs_cp_error(F2FS_I_SB(inode)))) @@ -999,7 +1000,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, return -EOPNOTSUPP; if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED) && !IS_ALIGNED(attr->ia_size, - F2FS_BLK_TO_BYTES(F2FS_I(inode)->i_cluster_size))) + F2FS_BLK_TO_BYTES(fi->i_cluster_size))) return -EINVAL; } @@ -1053,7 +1054,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, return err; } - f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_down_write(&fi->i_gc_rwsem[WRITE]); filemap_invalidate_lock(inode->i_mapping); truncate_setsize(inode, attr->ia_size); @@ -1065,14 +1066,14 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, * larger than i_size. */ filemap_invalidate_unlock(inode->i_mapping); - f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_up_write(&fi->i_gc_rwsem[WRITE]); if (err) return err; - spin_lock(&F2FS_I(inode)->i_size_lock); + spin_lock(&fi->i_size_lock); inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); - F2FS_I(inode)->last_disk_size = i_size_read(inode); - spin_unlock(&F2FS_I(inode)->i_size_lock); + fi->last_disk_size = i_size_read(inode); + spin_unlock(&fi->i_size_lock); } __setattr_copy(idmap, inode, attr); @@ -1082,7 +1083,7 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, if (is_inode_flag_set(inode, FI_ACL_MODE)) { if (!err) - inode->i_mode = F2FS_I(inode)->i_acl_mode; + inode->i_mode = fi->i_acl_mode; clear_inode_flag(inode, FI_ACL_MODE); } } @@ -1990,15 +1991,15 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask) if (err) return err; - f2fs_down_write(&F2FS_I(inode)->i_sem); + f2fs_down_write(&fi->i_sem); if (!f2fs_may_compress(inode) || (S_ISREG(inode->i_mode) && F2FS_HAS_BLOCKS(inode))) { - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); return -EINVAL; } err = set_compress_context(inode); - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); if (err) return err; @@ -3588,6 +3589,7 @@ next: static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); + struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); pgoff_t page_idx = 0, last_idx; unsigned int released_blocks = 0; @@ -3625,7 +3627,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) if (ret) goto out; - if (!atomic_read(&F2FS_I(inode)->i_compr_blocks)) { + if (!atomic_read(&fi->i_compr_blocks)) { ret = -EPERM; goto out; } @@ -3634,7 +3636,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) inode_set_ctime_current(inode); f2fs_mark_inode_dirty_sync(inode, true); - f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_down_write(&fi->i_gc_rwsem[WRITE]); filemap_invalidate_lock(inode->i_mapping); last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); @@ -3660,7 +3662,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) end_offset = ADDRS_PER_PAGE(dn.node_page, inode); count = min(end_offset - dn.ofs_in_node, last_idx - page_idx); - count = round_up(count, F2FS_I(inode)->i_cluster_size); + count = round_up(count, fi->i_cluster_size); ret = release_compress_blocks(&dn, count); @@ -3676,7 +3678,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg) } filemap_invalidate_unlock(inode->i_mapping); - f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_up_write(&fi->i_gc_rwsem[WRITE]); out: if (released_blocks) f2fs_update_time(sbi, REQ_TIME); @@ -3687,14 +3689,14 @@ out: if (ret >= 0) { ret = put_user(released_blocks, (u64 __user *)arg); } else if (released_blocks && - atomic_read(&F2FS_I(inode)->i_compr_blocks)) { + atomic_read(&fi->i_compr_blocks)) { set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: partial blocks were released i_ino=%lx " "iblocks=%llu, released=%u, compr_blocks=%u, " "run fsck to fix.", __func__, inode->i_ino, inode->i_blocks, released_blocks, - atomic_read(&F2FS_I(inode)->i_compr_blocks)); + atomic_read(&fi->i_compr_blocks)); } return ret; @@ -3783,6 +3785,7 @@ next: static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); + struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); pgoff_t page_idx = 0, last_idx; unsigned int reserved_blocks = 0; @@ -3808,10 +3811,10 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) goto unlock_inode; } - if (atomic_read(&F2FS_I(inode)->i_compr_blocks)) + if (atomic_read(&fi->i_compr_blocks)) goto unlock_inode; - f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_down_write(&fi->i_gc_rwsem[WRITE]); filemap_invalidate_lock(inode->i_mapping); last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); @@ -3837,7 +3840,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) end_offset = ADDRS_PER_PAGE(dn.node_page, inode); count = min(end_offset - dn.ofs_in_node, last_idx - page_idx); - count = round_up(count, F2FS_I(inode)->i_cluster_size); + count = round_up(count, fi->i_cluster_size); ret = reserve_compress_blocks(&dn, count, &reserved_blocks); @@ -3852,7 +3855,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg) } filemap_invalidate_unlock(inode->i_mapping); - f2fs_up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]); + f2fs_up_write(&fi->i_gc_rwsem[WRITE]); if (!ret) { clear_inode_flag(inode, FI_COMPRESS_RELEASED); @@ -3868,14 +3871,14 @@ unlock_inode: if (!ret) { ret = put_user(reserved_blocks, (u64 __user *)arg); } else if (reserved_blocks && - atomic_read(&F2FS_I(inode)->i_compr_blocks)) { + atomic_read(&fi->i_compr_blocks)) { set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: partial blocks were reserved i_ino=%lx " "iblocks=%llu, reserved=%u, compr_blocks=%u, " "run fsck to fix.", __func__, inode->i_ino, inode->i_blocks, reserved_blocks, - atomic_read(&F2FS_I(inode)->i_compr_blocks)); + atomic_read(&fi->i_compr_blocks)); } return ret; @@ -4101,6 +4104,7 @@ static int f2fs_ioc_get_compress_option(struct file *filp, unsigned long arg) static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); + struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_comp_option option; int ret = 0; @@ -4141,25 +4145,25 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg) goto out; } - F2FS_I(inode)->i_compress_algorithm = option.algorithm; - F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size; - F2FS_I(inode)->i_cluster_size = BIT(option.log_cluster_size); + fi->i_compress_algorithm = option.algorithm; + fi->i_log_cluster_size = option.log_cluster_size; + fi->i_cluster_size = BIT(option.log_cluster_size); /* Set default level */ - if (F2FS_I(inode)->i_compress_algorithm == COMPRESS_ZSTD) - F2FS_I(inode)->i_compress_level = F2FS_ZSTD_DEFAULT_CLEVEL; + if (fi->i_compress_algorithm == COMPRESS_ZSTD) + fi->i_compress_level = F2FS_ZSTD_DEFAULT_CLEVEL; else - F2FS_I(inode)->i_compress_level = 0; + fi->i_compress_level = 0; /* Adjust mount option level */ if (option.algorithm == F2FS_OPTION(sbi).compress_algorithm && F2FS_OPTION(sbi).compress_level) - F2FS_I(inode)->i_compress_level = F2FS_OPTION(sbi).compress_level; + fi->i_compress_level = F2FS_OPTION(sbi).compress_level; f2fs_mark_inode_dirty_sync(inode, true); if (!f2fs_is_compress_backend_ready(inode)) f2fs_warn(sbi, "compression algorithm is successfully set, " "but current kernel doesn't support this algorithm."); out: - f2fs_up_write(&F2FS_I(inode)->i_sem); + f2fs_up_write(&fi->i_sem); inode_unlock(inode); mnt_drop_write_file(filp); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 18dea43e694b..aef57172014f 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -636,8 +636,9 @@ retry: void f2fs_update_inode(struct inode *inode, struct page *node_page) { + struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode *ri; - struct extent_tree *et = F2FS_I(inode)->extent_tree[EX_READ]; + struct extent_tree *et = fi->extent_tree[EX_READ]; f2fs_wait_on_page_writeback(node_page, NODE, true, true); set_page_dirty(node_page); @@ -647,7 +648,7 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page) ri = F2FS_INODE(node_page); ri->i_mode = cpu_to_le16(inode->i_mode); - ri->i_advise = F2FS_I(inode)->i_advise; + ri->i_advise = fi->i_advise; ri->i_uid = cpu_to_le32(i_uid_read(inode)); ri->i_gid = cpu_to_le32(i_gid_read(inode)); ri->i_links = cpu_to_le32(inode->i_nlink); @@ -673,58 +674,49 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page) ri->i_ctime_nsec = cpu_to_le32(inode_get_ctime_nsec(inode)); ri->i_mtime_nsec = cpu_to_le32(inode_get_mtime_nsec(inode)); if (S_ISDIR(inode->i_mode)) - ri->i_current_depth = - cpu_to_le32(F2FS_I(inode)->i_current_depth); + ri->i_current_depth = cpu_to_le32(fi->i_current_depth); else if (S_ISREG(inode->i_mode)) - ri->i_gc_failures = cpu_to_le16(F2FS_I(inode)->i_gc_failures); - ri->i_xattr_nid = cpu_to_le32(F2FS_I(inode)->i_xattr_nid); - ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags); - ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino); + ri->i_gc_failures = cpu_to_le16(fi->i_gc_failures); + ri->i_xattr_nid = cpu_to_le32(fi->i_xattr_nid); + ri->i_flags = cpu_to_le32(fi->i_flags); + ri->i_pino = cpu_to_le32(fi->i_pino); ri->i_generation = cpu_to_le32(inode->i_generation); - ri->i_dir_level = F2FS_I(inode)->i_dir_level; + ri->i_dir_level = fi->i_dir_level; if (f2fs_has_extra_attr(inode)) { - ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize); + ri->i_extra_isize = cpu_to_le16(fi->i_extra_isize); if (f2fs_sb_has_flexible_inline_xattr(F2FS_I_SB(inode))) ri->i_inline_xattr_size = - cpu_to_le16(F2FS_I(inode)->i_inline_xattr_size); + cpu_to_le16(fi->i_inline_xattr_size); if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)) && - F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, - i_projid)) { + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_projid)) { projid_t i_projid; - i_projid = from_kprojid(&init_user_ns, - F2FS_I(inode)->i_projid); + i_projid = from_kprojid(&init_user_ns, fi->i_projid); ri->i_projid = cpu_to_le32(i_projid); } if (f2fs_sb_has_inode_crtime(F2FS_I_SB(inode)) && - F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, - i_crtime)) { - ri->i_crtime = - cpu_to_le64(F2FS_I(inode)->i_crtime.tv_sec); - ri->i_crtime_nsec = - cpu_to_le32(F2FS_I(inode)->i_crtime.tv_nsec); + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_crtime)) { + ri->i_crtime = cpu_to_le64(fi->i_crtime.tv_sec); + ri->i_crtime_nsec = cpu_to_le32(fi->i_crtime.tv_nsec); } if (f2fs_sb_has_compression(F2FS_I_SB(inode)) && - F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, + F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_compress_flag)) { unsigned short compress_flag; - ri->i_compr_blocks = - cpu_to_le64(atomic_read( - &F2FS_I(inode)->i_compr_blocks)); - ri->i_compress_algorithm = - F2FS_I(inode)->i_compress_algorithm; - compress_flag = F2FS_I(inode)->i_compress_flag | - F2FS_I(inode)->i_compress_level << + ri->i_compr_blocks = cpu_to_le64( + atomic_read(&fi->i_compr_blocks)); + ri->i_compress_algorithm = fi->i_compress_algorithm; + compress_flag = fi->i_compress_flag | + fi->i_compress_level << COMPRESS_LEVEL_OFFSET; ri->i_compress_flag = cpu_to_le16(compress_flag); - ri->i_log_cluster_size = - F2FS_I(inode)->i_log_cluster_size; + ri->i_log_cluster_size = fi->i_log_cluster_size; } } diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index e54f8c08bda8..3d9d2b710796 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -221,6 +221,7 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *idmap, const char *name) { struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct f2fs_inode_info *fi; nid_t ino; struct inode *inode; bool nid_free = false; @@ -241,14 +242,15 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *idmap, inode_init_owner(idmap, inode, dir, mode); + fi = F2FS_I(inode); inode->i_ino = ino; inode->i_blocks = 0; simple_inode_init_ts(inode); - F2FS_I(inode)->i_crtime = inode_get_mtime(inode); + fi->i_crtime = inode_get_mtime(inode); inode->i_generation = get_random_u32(); if (S_ISDIR(inode->i_mode)) - F2FS_I(inode)->i_current_depth = 1; + fi->i_current_depth = 1; err = insert_inode_locked(inode); if (err) { @@ -258,9 +260,9 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *idmap, if (f2fs_sb_has_project_quota(sbi) && (F2FS_I(dir)->i_flags & F2FS_PROJINHERIT_FL)) - F2FS_I(inode)->i_projid = F2FS_I(dir)->i_projid; + fi->i_projid = F2FS_I(dir)->i_projid; else - F2FS_I(inode)->i_projid = make_kprojid(&init_user_ns, + fi->i_projid = make_kprojid(&init_user_ns, F2FS_DEF_PROJID); err = fscrypt_prepare_new_inode(dir, inode, &encrypt); @@ -278,7 +280,7 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *idmap, if (f2fs_sb_has_extra_attr(sbi)) { set_inode_flag(inode, FI_EXTRA_ATTR); - F2FS_I(inode)->i_extra_isize = F2FS_TOTAL_EXTRA_ATTR_SIZE; + fi->i_extra_isize = F2FS_TOTAL_EXTRA_ATTR_SIZE; } if (test_opt(sbi, INLINE_XATTR)) @@ -296,15 +298,15 @@ static struct inode *f2fs_new_inode(struct mnt_idmap *idmap, f2fs_has_inline_dentry(inode)) { xattr_size = DEFAULT_INLINE_XATTR_ADDRS; } - F2FS_I(inode)->i_inline_xattr_size = xattr_size; + fi->i_inline_xattr_size = xattr_size; - F2FS_I(inode)->i_flags = + fi->i_flags = f2fs_mask_flags(mode, F2FS_I(dir)->i_flags & F2FS_FL_INHERITED); if (S_ISDIR(inode->i_mode)) - F2FS_I(inode)->i_flags |= F2FS_INDEX_FL; + fi->i_flags |= F2FS_INDEX_FL; - if (F2FS_I(inode)->i_flags & F2FS_PROJINHERIT_FL) + if (fi->i_flags & F2FS_PROJINHERIT_FL) set_inode_flag(inode, FI_PROJ_INHERIT); /* Check compression first. */ diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 496aee53c38a..c74ffdeedc4c 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -287,6 +287,7 @@ static void recover_inline_flags(struct inode *inode, struct f2fs_inode *ri) static int recover_inode(struct inode *inode, struct page *page) { struct f2fs_inode *raw = F2FS_INODE(page); + struct f2fs_inode_info *fi = F2FS_I(inode); char *name; int err; @@ -309,12 +310,12 @@ static int recover_inode(struct inode *inode, struct page *page) i_projid = (projid_t)le32_to_cpu(raw->i_projid); kprojid = make_kprojid(&init_user_ns, i_projid); - if (!projid_eq(kprojid, F2FS_I(inode)->i_projid)) { + if (!projid_eq(kprojid, fi->i_projid)) { err = f2fs_transfer_project_quota(inode, kprojid); if (err) return err; - F2FS_I(inode)->i_projid = kprojid; + fi->i_projid = kprojid; } } } @@ -327,10 +328,10 @@ static int recover_inode(struct inode *inode, struct page *page) inode_set_mtime(inode, le64_to_cpu(raw->i_mtime), le32_to_cpu(raw->i_mtime_nsec)); - F2FS_I(inode)->i_advise = raw->i_advise; - F2FS_I(inode)->i_flags = le32_to_cpu(raw->i_flags); + fi->i_advise = raw->i_advise; + fi->i_flags = le32_to_cpu(raw->i_flags); f2fs_set_inode_flags(inode); - F2FS_I(inode)->i_gc_failures = le16_to_cpu(raw->i_gc_failures); + fi->i_gc_failures = le16_to_cpu(raw->i_gc_failures); recover_inline_flags(inode, raw); From bed6b0317441d82c32506750ccd868d83850e6f4 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 25 Jun 2024 11:16:03 +0800 Subject: [PATCH 066/155] f2fs: clean up addrs_per_{inode,block}() Introduce a new help addrs_per_page() to wrap common code from addrs_per_inode() and addrs_per_block() for cleanup. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 20 +++++++------------- include/linux/f2fs_fs.h | 7 +++---- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5e414c8ed334..3948c181997f 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -3227,21 +3227,15 @@ static inline bool f2fs_need_compress_data(struct inode *inode) return false; } -static inline unsigned int addrs_per_inode(struct inode *inode) +static inline unsigned int addrs_per_page(struct inode *inode, + bool is_inode) { - unsigned int addrs = CUR_ADDRS_PER_INODE(inode) - - get_inline_xattr_addrs(inode); + unsigned int addrs = is_inode ? (CUR_ADDRS_PER_INODE(inode) - + get_inline_xattr_addrs(inode)) : DEF_ADDRS_PER_BLOCK; - if (!f2fs_compressed_file(inode)) - return addrs; - return ALIGN_DOWN(addrs, F2FS_I(inode)->i_cluster_size); -} - -static inline unsigned int addrs_per_block(struct inode *inode) -{ - if (!f2fs_compressed_file(inode)) - return DEF_ADDRS_PER_BLOCK; - return ALIGN_DOWN(DEF_ADDRS_PER_BLOCK, F2FS_I(inode)->i_cluster_size); + if (f2fs_compressed_file(inode)) + return ALIGN_DOWN(addrs, F2FS_I(inode)->i_cluster_size); + return addrs; } static inline void *inline_xattr_addr(struct inode *inode, struct page *page) diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 41d1d71c36ff..01bee2b289c2 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -259,15 +259,14 @@ struct node_footer { #define CUR_ADDRS_PER_INODE(inode) (DEF_ADDRS_PER_INODE - \ get_extra_isize(inode)) #define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */ -#define ADDRS_PER_INODE(inode) addrs_per_inode(inode) +#define ADDRS_PER_INODE(inode) addrs_per_page(inode, true) /* Address Pointers in a Direct Block */ #define DEF_ADDRS_PER_BLOCK ((F2FS_BLKSIZE - sizeof(struct node_footer)) / sizeof(__le32)) -#define ADDRS_PER_BLOCK(inode) addrs_per_block(inode) +#define ADDRS_PER_BLOCK(inode) addrs_per_page(inode, false) /* Node IDs in an Indirect Block */ #define NIDS_PER_BLOCK ((F2FS_BLKSIZE - sizeof(struct node_footer)) / sizeof(__le32)) -#define ADDRS_PER_PAGE(page, inode) \ - (IS_INODE(page) ? ADDRS_PER_INODE(inode) : ADDRS_PER_BLOCK(inode)) +#define ADDRS_PER_PAGE(page, inode) (addrs_per_page(inode, IS_INODE(page))) #define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1) #define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2) From a71ac6c956ee8aac289c7c9dd12dfe1ee3447006 Mon Sep 17 00:00:00 2001 From: Oscar Salvador Date: Sat, 18 May 2024 13:58:07 +0200 Subject: [PATCH 067/155] sh: Drop support for memory hotplug and memory hotremove Support for memory hotplug was restricted to 64-bit platforms in 7ec58a2b941e ("mm/memory_hotplug: restrict CONFIG_MEMORY_HOTPLUG to 64 bit") while sh is a pure 32-bit platform since the removal of sh5 support. Thus, drop support for memory hotplug and the associated memory hotremove on this platform. Signed-off-by: Oscar Salvador Reviewed-by: John Paul Adrian Glaubitz Link: https://lore.kernel.org/r/20240518115808.8888-2-osalvador@suse.de Signed-off-by: John Paul Adrian Glaubitz --- arch/sh/Kconfig | 2 -- arch/sh/mm/Kconfig | 4 ---- arch/sh/mm/init.c | 28 ---------------------------- 3 files changed, 34 deletions(-) diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 5e6a3ead51fb..1aa3c4a0c5b2 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -3,8 +3,6 @@ config SUPERH def_bool y select ARCH_32BIT_OFF_T select ARCH_HAS_CPU_CACHE_ALIASING - select ARCH_ENABLE_MEMORY_HOTPLUG if SPARSEMEM && MMU - select ARCH_ENABLE_MEMORY_HOTREMOVE if SPARSEMEM && MMU select ARCH_HAVE_NMI_SAFE_CMPXCHG if (GUSA_RB || CPU_SH4A) select ARCH_HAS_BINFMT_FLAT if !MMU select ARCH_HAS_CPU_FINALIZE_INIT diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig index f32a1963ff0c..1862411665ab 100644 --- a/arch/sh/mm/Kconfig +++ b/arch/sh/mm/Kconfig @@ -144,10 +144,6 @@ config ARCH_SPARSEMEM_DEFAULT config ARCH_SELECT_MEMORY_MODEL def_bool y -config ARCH_MEMORY_PROBE - def_bool y - depends on MEMORY_HOTPLUG - config IOREMAP_FIXED def_bool y depends on X2TLB diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index bf1b54055316..d1fe90b2f5ff 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -395,31 +395,3 @@ void __init mem_init(void) mem_init_done = 1; } - -#ifdef CONFIG_MEMORY_HOTPLUG -int arch_add_memory(int nid, u64 start, u64 size, - struct mhp_params *params) -{ - unsigned long start_pfn = PFN_DOWN(start); - unsigned long nr_pages = size >> PAGE_SHIFT; - int ret; - - if (WARN_ON_ONCE(params->pgprot.pgprot != PAGE_KERNEL.pgprot)) - return -EINVAL; - - /* We only have ZONE_NORMAL, so this is easy.. */ - ret = __add_pages(nid, start_pfn, nr_pages, params); - if (unlikely(ret)) - printk("%s: Failed, __add_pages() == %d\n", __func__, ret); - - return ret; -} - -void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) -{ - unsigned long start_pfn = PFN_DOWN(start); - unsigned long nr_pages = size >> PAGE_SHIFT; - - __remove_pages(start_pfn, nr_pages, altmap); -} -#endif /* CONFIG_MEMORY_HOTPLUG */ From 6887a9ace00413c9538261b13e07b22cc13cf099 Mon Sep 17 00:00:00 2001 From: Oscar Salvador Date: Sat, 18 May 2024 13:58:08 +0200 Subject: [PATCH 068/155] sh: config: Drop CONFIG_MEMORY_{HOTPLUG,HOTREMOVE} Signed-off-by: Oscar Salvador Reviewed-by: John Paul Adrian Glaubitz Link: https://lore.kernel.org/r/20240518115808.8888-3-osalvador@suse.de Signed-off-by: John Paul Adrian Glaubitz --- arch/sh/configs/apsh4ad0a_defconfig | 2 -- arch/sh/configs/sdk7786_defconfig | 2 -- arch/sh/configs/shx3_defconfig | 2 -- 3 files changed, 6 deletions(-) diff --git a/arch/sh/configs/apsh4ad0a_defconfig b/arch/sh/configs/apsh4ad0a_defconfig index 05d21d91f41d..137573610ec4 100644 --- a/arch/sh/configs/apsh4ad0a_defconfig +++ b/arch/sh/configs/apsh4ad0a_defconfig @@ -24,8 +24,6 @@ CONFIG_BFQ_GROUP_IOSCHED=y CONFIG_CPU_SUBTYPE_SH7786=y CONFIG_MEMORY_SIZE=0x10000000 CONFIG_HUGETLB_PAGE_SIZE_1MB=y -CONFIG_MEMORY_HOTPLUG=y -CONFIG_MEMORY_HOTREMOVE=y CONFIG_KSM=y CONFIG_SH_STORE_QUEUES=y CONFIG_SH_APSH4AD0A=y diff --git a/arch/sh/configs/sdk7786_defconfig b/arch/sh/configs/sdk7786_defconfig index 7b427c17fbfe..07894f13441e 100644 --- a/arch/sh/configs/sdk7786_defconfig +++ b/arch/sh/configs/sdk7786_defconfig @@ -43,8 +43,6 @@ CONFIG_MEMORY_SIZE=0x20000000 CONFIG_PMB=y CONFIG_NUMA=y CONFIG_HUGETLB_PAGE_SIZE_64MB=y -CONFIG_MEMORY_HOTPLUG=y -CONFIG_MEMORY_HOTREMOVE=y CONFIG_KSM=y CONFIG_SH_STORE_QUEUES=y CONFIG_SPECULATIVE_EXECUTION=y diff --git a/arch/sh/configs/shx3_defconfig b/arch/sh/configs/shx3_defconfig index aa353dff7f19..9a0df5ea3866 100644 --- a/arch/sh/configs/shx3_defconfig +++ b/arch/sh/configs/shx3_defconfig @@ -31,8 +31,6 @@ CONFIG_CPU_SUBTYPE_SHX3=y CONFIG_MEMORY_START=0x0c000000 CONFIG_NUMA=y CONFIG_PAGE_SIZE_64KB=y -CONFIG_MEMORY_HOTPLUG=y -CONFIG_MEMORY_HOTREMOVE=y CONFIG_SH_STORE_QUEUES=y CONFIG_SH_X3PROTO=y CONFIG_NO_HZ=y From fb3f7f0f156a111da7d43f6ffe52b48bac5b7159 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 31 May 2024 21:28:25 +0900 Subject: [PATCH 069/155] initramfs: shorten cmd_initfs in usr/Makefile Avoid repetition of long variables. No functional change intended. Signed-off-by: Masahiro Yamada Reviewed-by: Nathan Chancellor --- usr/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/Makefile b/usr/Makefile index 132ef7e96e6d..f1779496bca7 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -62,8 +62,8 @@ $(deps_initramfs): ; quiet_cmd_initfs = GEN $@ cmd_initfs = \ $(CONFIG_SHELL) $< -o $@ -l $(obj)/.initramfs_data.cpio.d \ - $(if $(CONFIG_INITRAMFS_ROOT_UID), -u $(CONFIG_INITRAMFS_ROOT_UID)) \ - $(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) \ + $(addprefix -u , $(CONFIG_INITRAMFS_ROOT_UID)) \ + $(addprefix -g , $(CONFIG_INITRAMFS_ROOT_GID)) \ $(if $(KBUILD_BUILD_TIMESTAMP), -d "$(KBUILD_BUILD_TIMESTAMP)") \ $(ramfs-input) From 060e05c3b422f7ed7112e14f5039870ecdd449cc Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 2 Jun 2024 03:20:39 +0900 Subject: [PATCH 070/155] kconfig: qconf: remove initial call to conf_changed() If any CONFIG option is changed while loading the .config file, conf_read() calls conf_set_changed(true) and then the conf_changed() callback. With conf_read() moved after window initialization, the explicit conf_changed() call can be removed. Signed-off-by: Masahiro Yamada --- scripts/kconfig/qconf.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index c6c42c0f4e5d..e62e862ea283 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -1397,8 +1397,6 @@ ConfigMainWindow::ConfigMainWindow(void) conf_set_changed_callback(conf_changed); - // Set saveAction's initial state - conf_changed(); configname = xstrdup(conf_get_configname()); QAction *saveAsAction = new QAction("Save &As...", this); @@ -1904,7 +1902,6 @@ int main(int ac, char** av) conf_parse(name); fixup_rootmenu(&rootmenu); - conf_read(NULL); //zconfdump(stdout); configApp = new QApplication(ac, av); @@ -1916,6 +1913,9 @@ int main(int ac, char** av) //zconfdump(stdout); configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit())); configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings())); + + conf_read(NULL); + v->show(); configApp->exec(); From 300bf53ecb3673bf41db5dad2afcd77b8de3a662 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 2 Jun 2024 03:20:41 +0900 Subject: [PATCH 071/155] kconfig: gconf: remove unnecessary forward declarations These are defined before their call sites. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index e04dbafd3add..3dc459d9840c 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -65,8 +65,6 @@ static void display_list(void); static void display_tree(struct menu *menu); static void display_tree_part(void); static void update_tree(struct menu *src, GtkTreeIter * dst); -static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row); -static gchar **fill_row(struct menu *menu); static void conf_changed(void); static void replace_button_icon(GladeXML *xml, GdkDrawable *window, From 0b62fe46d77878645d117e70b9f135d7c9fcab47 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 2 Jun 2024 03:20:42 +0900 Subject: [PATCH 072/155] kconfig: gconf: move conf_changed() definition up Define conf_changed() before its call site to remove the forward declaration. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3dc459d9840c..2bf74aee5eff 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -65,7 +65,6 @@ static void display_list(void); static void display_tree(struct menu *menu); static void display_tree_part(void); static void update_tree(struct menu *src, GtkTreeIter * dst); -static void conf_changed(void); static void replace_button_icon(GladeXML *xml, GdkDrawable *window, GtkStyle *style, gchar *btn_name, gchar **xpm) @@ -85,6 +84,13 @@ static void replace_button_icon(GladeXML *xml, GdkDrawable *window, gtk_tool_button_set_icon_widget(button, image); } +static void conf_changed(void) +{ + bool changed = conf_get_changed(); + gtk_widget_set_sensitive(save_btn, changed); + gtk_widget_set_sensitive(save_menu_item, changed); +} + /* Main Window Initialization */ static void init_main_window(const gchar *glade_file) { @@ -1445,10 +1451,3 @@ int main(int ac, char *av[]) return 0; } - -static void conf_changed(void) -{ - bool changed = conf_get_changed(); - gtk_widget_set_sensitive(save_btn, changed); - gtk_widget_set_sensitive(save_menu_item, changed); -} From 03638aaa7995c07376f2e51ac2640ccd25b4ba75 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 2 Jun 2024 03:20:43 +0900 Subject: [PATCH 073/155] kconfig: pass new conf_changed value to the callback Commit ee06a3ef7e3c ("kconfig: Update config changed flag before calling callback") pointed out that conf_updated flag must be updated _before_ calling the callback, which needs to know the new value. Given that, it makes sense to directly pass the new value to the callback. Signed-off-by: Masahiro Yamada --- scripts/kconfig/confdata.c | 10 ++++------ scripts/kconfig/gconf.c | 7 +++---- scripts/kconfig/lkc_proto.h | 2 +- scripts/kconfig/qconf.cc | 4 ++-- scripts/kconfig/qconf.h | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 85b53069ba7a..946185506380 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -1141,16 +1141,14 @@ int conf_write_autoconf(int overwrite) } static bool conf_changed; -static void (*conf_changed_callback)(void); +static void (*conf_changed_callback)(bool); void conf_set_changed(bool val) { - bool changed = conf_changed != val; + if (conf_changed_callback && conf_changed != val) + conf_changed_callback(val); conf_changed = val; - - if (conf_changed_callback && changed) - conf_changed_callback(); } bool conf_get_changed(void) @@ -1158,7 +1156,7 @@ bool conf_get_changed(void) return conf_changed; } -void conf_set_changed_callback(void (*fn)(void)) +void conf_set_changed_callback(void (*fn)(bool)) { conf_changed_callback = fn; } diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 2bf74aee5eff..baa1c512de3c 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -84,11 +84,10 @@ static void replace_button_icon(GladeXML *xml, GdkDrawable *window, gtk_tool_button_set_icon_widget(button, image); } -static void conf_changed(void) +static void conf_changed(bool dirty) { - bool changed = conf_get_changed(); - gtk_widget_set_sensitive(save_btn, changed); - gtk_widget_set_sensitive(save_menu_item, changed); + gtk_widget_set_sensitive(save_btn, dirty); + gtk_widget_set_sensitive(save_menu_item, dirty); } /* Main Window Initialization */ diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index d76aaf4ea117..c663fd8b35d2 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -13,7 +13,7 @@ int conf_write(const char *name); int conf_write_autoconf(int overwrite); void conf_set_changed(bool val); bool conf_get_changed(void); -void conf_set_changed_callback(void (*fn)(void)); +void conf_set_changed_callback(void (*fn)(bool)); void conf_set_message_callback(void (*fn)(const char *s)); bool conf_errors(void); diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index e62e862ea283..03fa096074b4 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -1849,10 +1849,10 @@ void ConfigMainWindow::saveSettings(void) configSettings->writeSizes("/split2", split2->sizes()); } -void ConfigMainWindow::conf_changed(void) +void ConfigMainWindow::conf_changed(bool dirty) { if (saveAction) - saveAction->setEnabled(conf_get_changed()); + saveAction->setEnabled(dirty); } void fixup_rootmenu(struct menu *menu) diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index 78b0a1dfcd53..53373064d90a 100644 --- a/scripts/kconfig/qconf.h +++ b/scripts/kconfig/qconf.h @@ -239,7 +239,7 @@ class ConfigMainWindow : public QMainWindow { char *configname; static QAction *saveAction; - static void conf_changed(void); + static void conf_changed(bool); public: ConfigMainWindow(void); public slots: From fde192511bdbff554320b31574bb8a9cb3275522 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 2 Jun 2024 21:54:14 +0900 Subject: [PATCH 074/155] kconfig: remove tristate choice support I previously submitted a fix for a bug in the choice feature [1], where I mentioned, "Another (much cleaner) approach would be to remove the tristate choice support entirely". There are more issues in the tristate choice feature. For example, you can observe a couple of bugs in the following test code. [Test Code] config MODULES def_bool y modules choice prompt "tristate choice" default A config A tristate "A" config B tristate "B" endchoice Bug 1: the 'default' property is not correctly processed 'make alldefconfig' produces: CONFIG_MODULES=y # CONFIG_A is not set # CONFIG_B is not set However, the correct output should be: CONFIG_MODULES=y CONFIG_A=y # CONFIG_B is not set The unit test file, scripts/kconfig/tests/choice/alldef_expected_config, is wrong as well. Bug 2: choice members never get 'y' with randconfig For the test code above, the following combinations are possible: A B (1) y n (2) n y (3) m m (4) m n (5) n m (6) n n 'make randconfig' never produces (1) or (2). These bugs are fixable, but a more critical problem is the lack of a sensible syntax to specify the default for the tristate choice. The default for the choice must be one of the choice members, which cannot specify any of the patterns (3) through (6) above. In addition, I have never seen it being used in a useful way. The following commits removed unnecessary use of tristate choices: - df8df5e4bc37 ("usb: get rid of 'choice' for legacy gadget drivers") - bfb57ef0544a ("rapidio: remove choice for enumeration") This commit removes the tristate choice support entirely, which allows me to delete a lot of code, making further refactoring easier. Note: This includes the revert of commit fa64e5f6a35e ("kconfig/symbol.c: handle choice_values that depend on 'm' symbols"). It was suspicious because it did not address the root cause but introduced inconsistency in visibility between choice members and other symbols. [1]: https://lore.kernel.org/linux-kbuild/20240427104231.2728905-1-masahiroy@kernel.org/T/#m0a1bb6992581462ceca861b409bb33cb8fd7dbae Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier --- Documentation/kbuild/kconfig-language.rst | 11 +--- scripts/kconfig/conf.c | 56 ++----------------- scripts/kconfig/confdata.c | 17 +----- scripts/kconfig/gconf.c | 6 +- scripts/kconfig/mconf.c | 28 +++------- scripts/kconfig/menu.c | 31 +--------- scripts/kconfig/nconf.c | 28 ++-------- scripts/kconfig/parser.y | 23 +++++--- scripts/kconfig/qconf.cc | 2 +- scripts/kconfig/symbol.c | 22 +------- scripts/kconfig/tests/choice/Kconfig | 17 ------ scripts/kconfig/tests/choice/__init__.py | 10 ---- .../tests/choice/alldef_expected_config | 3 - .../tests/choice/allmod_expected_config | 3 - .../tests/choice/allno_expected_config | 3 - .../tests/choice/allyes_expected_config | 3 - .../tests/choice/oldask0_expected_stdout | 4 -- scripts/kconfig/tests/choice/oldask1_config | 1 - .../tests/choice/oldask1_expected_stdout | 9 --- .../tests/choice_value_with_m_dep/Kconfig | 21 ------- .../tests/choice_value_with_m_dep/__init__.py | 16 ------ .../tests/choice_value_with_m_dep/config | 2 - .../choice_value_with_m_dep/expected_config | 3 - .../choice_value_with_m_dep/expected_stdout | 4 -- scripts/kconfig/tests/inter_choice/Kconfig | 25 --------- .../kconfig/tests/inter_choice/__init__.py | 15 ----- scripts/kconfig/tests/inter_choice/defconfig | 1 - .../tests/inter_choice/expected_config | 4 -- 28 files changed, 44 insertions(+), 324 deletions(-) delete mode 100644 scripts/kconfig/tests/choice/oldask1_config delete mode 100644 scripts/kconfig/tests/choice/oldask1_expected_stdout delete mode 100644 scripts/kconfig/tests/choice_value_with_m_dep/Kconfig delete mode 100644 scripts/kconfig/tests/choice_value_with_m_dep/__init__.py delete mode 100644 scripts/kconfig/tests/choice_value_with_m_dep/config delete mode 100644 scripts/kconfig/tests/choice_value_with_m_dep/expected_config delete mode 100644 scripts/kconfig/tests/choice_value_with_m_dep/expected_stdout delete mode 100644 scripts/kconfig/tests/inter_choice/Kconfig delete mode 100644 scripts/kconfig/tests/inter_choice/__init__.py delete mode 100644 scripts/kconfig/tests/inter_choice/defconfig delete mode 100644 scripts/kconfig/tests/inter_choice/expected_config diff --git a/Documentation/kbuild/kconfig-language.rst b/Documentation/kbuild/kconfig-language.rst index 1fb3f5e6193c..71b38a7670f3 100644 --- a/Documentation/kbuild/kconfig-language.rst +++ b/Documentation/kbuild/kconfig-language.rst @@ -409,16 +409,9 @@ choices:: "endchoice" This defines a choice group and accepts any of the above attributes as -options. A choice can only be of type bool or tristate. If no type is -specified for a choice, its type will be determined by the type of -the first choice element in the group or remain unknown if none of the -choice elements have a type specified, as well. +options. -While a boolean choice only allows a single config entry to be -selected, a tristate choice also allows any number of config entries -to be set to 'm'. This can be used if multiple drivers for a single -hardware exists and only a single driver can be compiled/loaded into -the kernel, but all drivers can be compiled as modules. +A choice only allows a single config entry to be selected. comment:: diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 8ad2c52d9b1f..9a20e9e9bdad 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -114,21 +114,13 @@ static void set_randconfig_seed(void) srand(seed); } -static bool randomize_choice_values(struct symbol *csym) +static void randomize_choice_values(struct symbol *csym) { struct property *prop; struct symbol *sym; struct expr *e; int cnt, def; - /* - * If choice is mod then we may have more items selected - * and if no then no-one. - * In both cases stop. - */ - if (csym->curr.tri != yes) - return false; - prop = sym_get_choice_prop(csym); /* count entries in choice block */ @@ -157,8 +149,6 @@ static bool randomize_choice_values(struct symbol *csym) csym->flags |= SYMBOL_DEF_USER; /* clear VALID to get value calculated */ csym->flags &= ~SYMBOL_VALID; - - return true; } enum conf_def_mode { @@ -269,15 +259,6 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode) sym_clear_all_valid(); - /* - * We have different type of choice blocks. - * If curr.tri equals to mod then we can select several - * choice symbols in one block. - * In this case we do nothing. - * If curr.tri equals yes then only one symbol can be - * selected in a choice block and we set it to yes, - * and the rest to no. - */ if (mode != def_random) { for_all_symbols(csym) { if ((sym_is_choice(csym) && !sym_has_value(csym)) || @@ -292,11 +273,10 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode) sym_calc_value(csym); if (mode == def_random) - has_changed |= randomize_choice_values(csym); - else { + randomize_choice_values(csym); + else set_all_choice_values(csym); - has_changed = true; - } + has_changed = true; } return has_changed; @@ -454,27 +434,6 @@ static void conf_choice(struct menu *menu) sym = menu->sym; is_new = !sym_has_value(sym); - if (sym_is_changeable(sym)) { - conf_sym(menu); - sym_calc_value(sym); - switch (sym_get_tristate_value(sym)) { - case no: - case mod: - return; - case yes: - break; - } - } else { - switch (sym_get_tristate_value(sym)) { - case no: - return; - case mod: - printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); - return; - case yes: - break; - } - } while (1) { int cnt, def; @@ -596,9 +555,7 @@ static void conf(struct menu *menu) if (sym_is_choice(sym)) { conf_choice(menu); - if (sym->curr.tri != mod) - return; - goto conf_childs; + return; } switch (sym->type) { @@ -631,8 +588,7 @@ static void check_conf(struct menu *menu) sym = menu->sym; if (sym && !sym_has_value(sym) && - (sym_is_changeable(sym) || - (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes))) { + (sym_is_changeable(sym) || sym_is_choice(sym))) { switch (input_mode) { case listnewconfig: diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 946185506380..25c327ae3c5c 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -462,22 +462,12 @@ load: if (sym && sym_is_choice_value(sym)) { struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); - switch (sym->def[def].tri) { - case no: - break; - case mod: - if (cs->def[def].tri == yes) { - conf_warning("%s creates inconsistent choice state", sym->name); - cs->flags &= ~def_flags; - } - break; - case yes: + if (sym->def[def].tri == yes) { if (cs->def[def].tri != no) conf_warning("override: %s changes choice state", sym->name); cs->def[def].val = sym; - break; + cs->def[def].tri = yes; } - cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri); } } free(line); @@ -806,8 +796,7 @@ int conf_write_defconfig(const char *filename) ds = sym_choice_default(choice->sym); if (sym == ds) { - if ((sym->type == S_BOOLEAN) && - sym_get_tristate_value(sym) == yes) + if (sym_get_tristate_value(sym) == yes) continue; } } diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index baa1c512de3c..380421a5cfb2 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1067,10 +1067,8 @@ static gchar **fill_row(struct menu *menu) row[COL_VALUE] = g_strdup(menu_get_prompt(def_menu)); - if (sym_get_type(sym) == S_BOOLEAN) { - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - return row; - } + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + return row; } if (sym->flags & SYMBOL_CHOICEVAL) row[COL_BTNRAD] = GINT_TO_POINTER(TRUE); diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index d6a61ca1a984..e6227af51658 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -523,28 +523,14 @@ static void build_conf(struct menu *menu) def_menu = child; } - val = sym_get_tristate_value(sym); - if (sym_is_changeable(sym)) { - switch (val) { - case yes: ch = '*'; break; - case mod: ch = 'M'; break; - default: ch = ' '; break; - } - item_make("<%c>", ch); - item_set_tag('t'); - item_set_data(menu); - } else { - item_make(" "); - item_set_tag(def_menu ? 't' : ':'); - item_set_data(menu); - } + item_make(" "); + item_set_tag(def_menu ? 't' : ':'); + item_set_data(menu); item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu)); - if (val == yes) { - if (def_menu) - item_add_str(" (%s) --->", menu_get_prompt(def_menu)); - return; - } + if (def_menu) + item_add_str(" (%s) --->", menu_get_prompt(def_menu)); + return; } else { if (menu == current_menu) { item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu)); @@ -814,7 +800,7 @@ static void conf(struct menu *menu, struct menu *active_menu) conf(submenu, NULL); break; case 't': - if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) + if (sym_is_choice(sym)) conf_choice(submenu); else if (submenu->prompt->type == P_MENU) conf(submenu, NULL); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index eef9b63cdf11..bf5dcc05350b 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -321,17 +321,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) is_choice = true; if (is_choice) { - if (sym->type == S_UNKNOWN) { - /* find the first choice value to find out choice type */ - current_entry = parent; - for (menu = parent->list; menu; menu = menu->next) { - if (menu->sym && menu->sym->type != S_UNKNOWN) { - menu_set_type(menu->sym->type); - break; - } - } - } - /* * Use the choice itself as the parent dependency of * the contained items. This turns the mode of the @@ -503,22 +492,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) menu->sym && !sym_is_choice_value(menu->sym)) { current_entry = menu; menu->sym->flags |= SYMBOL_CHOICEVAL; - /* Non-tristate choice values of tristate choices must - * depend on the choice being set to Y. The choice - * values' dependencies were propagated to their - * properties above, so the change here must be re- - * propagated. - */ - if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { - basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); - menu->dep = expr_alloc_and(basedep, menu->dep); - for (prop = menu->sym->prop; prop; prop = prop->next) { - if (prop->menu != menu) - continue; - prop->visible.expr = expr_alloc_and(expr_copy(basedep), - prop->visible.expr); - } - } menu_add_symbol(P_CHOICE, sym, NULL); prop = sym_get_choice_prop(sym); for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) @@ -578,13 +551,13 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) /* * For choices, add a reverse dependency (corresponding to a select) of - * ' && m'. This prevents the user from setting the choice + * ' && y'. This prevents the user from setting the choice * mode to 'n' when the choice is visible. */ if (sym && sym_is_choice(sym) && parent->prompt) { sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, expr_alloc_and(parent->prompt->visible.expr, - expr_alloc_symbol(&symbol_mod))); + expr_alloc_symbol(&symbol_yes))); } } diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index e1cb09418cbe..addc89ee61d4 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -825,30 +825,13 @@ static void build_conf(struct menu *menu) } val = sym_get_tristate_value(sym); - if (sym_is_changeable(sym)) { - switch (val) { - case yes: - ch = '*'; - break; - case mod: - ch = 'M'; - break; - default: - ch = ' '; - break; - } - item_make(menu, 't', "<%c>", ch); - } else { - item_make(menu, def_menu ? 't' : ':', " "); - } + item_make(menu, def_menu ? 't' : ':', " "); item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu)); - if (val == yes) { - if (def_menu) - item_add_str(" (%s) --->", menu_get_prompt(def_menu)); - return; - } + if (def_menu) + item_add_str(" (%s) --->", menu_get_prompt(def_menu)); + return; } else { if (menu == current_menu) { item_make(menu, ':', @@ -1191,8 +1174,7 @@ static void selected_conf(struct menu *menu, struct menu *active_menu) conf(submenu); break; case 't': - if (sym_is_choice(sym) && - sym_get_tristate_value(sym) == yes) + if (sym_is_choice(sym)) conf_choice(submenu); else if (submenu->prompt && submenu->prompt->type == P_MENU) diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index ff709001b1f0..8adb2f70121e 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -90,7 +90,7 @@ static bool inside_choice = false; %type nonconst_symbol %type symbol -%type type logic_type default +%type type default %type expr %type if_expr %type end @@ -153,6 +153,12 @@ config_stmt: config_entry_start config_option_list current_entry->filename, current_entry->lineno); yynerrs++; } + + if (current_entry->sym->type != S_BOOLEAN) { + fprintf(stderr, "%s:%d: error: choice member must be bool\n", + current_entry->filename, current_entry->lineno); + yynerrs++; + } } printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); @@ -235,6 +241,8 @@ choice: T_CHOICE T_EOL menu_add_entry(sym); menu_add_expr(P_CHOICE, NULL, NULL); + menu_set_type(S_BOOLEAN); + printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno); }; @@ -277,10 +285,10 @@ choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno); }; -choice_option: logic_type prompt_stmt_opt T_EOL +choice_option: T_BOOL T_WORD_QUOTE if_expr T_EOL { - menu_set_type($1); - printd(DEBUG_PARSE, "%s:%d:type(%u)\n", cur_filename, cur_lineno, $1); + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:bool\n", cur_filename, cur_lineno); }; choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL @@ -290,15 +298,12 @@ choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL }; type: - logic_type + T_BOOL { $$ = S_BOOLEAN; } + | T_TRISTATE { $$ = S_TRISTATE; } | T_INT { $$ = S_INT; } | T_HEX { $$ = S_HEX; } | T_STRING { $$ = S_STRING; } -logic_type: - T_BOOL { $$ = S_BOOLEAN; } - | T_TRISTATE { $$ = S_TRISTATE; } - default: T_DEFAULT { $$ = S_UNKNOWN; } | T_DEF_BOOL { $$ = S_BOOLEAN; } diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 03fa096074b4..30346e294d1a 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -147,7 +147,7 @@ void ConfigItem::updateMenu(void) expr = sym_get_tristate_value(sym); switch (expr) { case yes: - if (sym_is_choice_value(sym) && type == S_BOOLEAN) + if (sym_is_choice_value(sym)) setIcon(promptColIdx, choiceYesIcon); else setIcon(promptColIdx, symbolYesIcon); diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 0e439d3d48d1..eaff7ac496bd 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -44,12 +44,8 @@ enum symbol_type sym_get_type(struct symbol *sym) { enum symbol_type type = sym->type; - if (type == S_TRISTATE) { - if (sym_is_choice_value(sym) && sym->visible == yes) - type = S_BOOLEAN; - else if (modules_val == no) - type = S_BOOLEAN; - } + if (type == S_TRISTATE && modules_val == no) + type = S_BOOLEAN; return type; } @@ -208,26 +204,12 @@ static void sym_set_all_changed(void) static void sym_calc_visibility(struct symbol *sym) { struct property *prop; - struct symbol *choice_sym = NULL; tristate tri; /* any prompt visible? */ tri = no; - - if (sym_is_choice_value(sym)) - choice_sym = prop_get_symbol(sym_get_choice_prop(sym)); - for_all_prompts(sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); - /* - * Tristate choice_values with visibility 'mod' are - * not visible if the corresponding choice's value is - * 'yes'. - */ - if (choice_sym && sym->type == S_TRISTATE && - prop->visible.tri == mod && choice_sym->curr.tri == yes) - prop->visible.tri = no; - tri = EXPR_OR(tri, prop->visible.tri); } if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) diff --git a/scripts/kconfig/tests/choice/Kconfig b/scripts/kconfig/tests/choice/Kconfig index 8cdda40868a1..cd252579a623 100644 --- a/scripts/kconfig/tests/choice/Kconfig +++ b/scripts/kconfig/tests/choice/Kconfig @@ -1,10 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -config MODULES - bool "Enable loadable module support" - modules - default y - choice prompt "boolean choice" default BOOL_CHOICE1 @@ -16,15 +11,3 @@ config BOOL_CHOICE1 bool "choice 1" endchoice - -choice - prompt "tristate choice" - default TRI_CHOICE1 - -config TRI_CHOICE0 - tristate "choice 0" - -config TRI_CHOICE1 - tristate "choice 1" - -endchoice diff --git a/scripts/kconfig/tests/choice/__init__.py b/scripts/kconfig/tests/choice/__init__.py index 05e162220085..0fc7bf9b5c78 100644 --- a/scripts/kconfig/tests/choice/__init__.py +++ b/scripts/kconfig/tests/choice/__init__.py @@ -1,11 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 """ Basic choice tests. - -The handling of 'choice' is a bit complicated part in Kconfig. - -The behavior of 'y' choice is intuitive. If choice values are tristate, -the choice can be 'm' where each value can be enabled independently. """ @@ -14,11 +9,6 @@ def test_oldask0(conf): assert conf.stdout_contains('oldask0_expected_stdout') -def test_oldask1(conf): - assert conf.oldaskconfig('oldask1_config') == 0 - assert conf.stdout_contains('oldask1_expected_stdout') - - def test_allyes(conf): assert conf.allyesconfig() == 0 assert conf.config_contains('allyes_expected_config') diff --git a/scripts/kconfig/tests/choice/alldef_expected_config b/scripts/kconfig/tests/choice/alldef_expected_config index 7a754bf4be94..b359a2e6493e 100644 --- a/scripts/kconfig/tests/choice/alldef_expected_config +++ b/scripts/kconfig/tests/choice/alldef_expected_config @@ -1,5 +1,2 @@ -CONFIG_MODULES=y # CONFIG_BOOL_CHOICE0 is not set CONFIG_BOOL_CHOICE1=y -# CONFIG_TRI_CHOICE0 is not set -# CONFIG_TRI_CHOICE1 is not set diff --git a/scripts/kconfig/tests/choice/allmod_expected_config b/scripts/kconfig/tests/choice/allmod_expected_config index d1f51651740c..b359a2e6493e 100644 --- a/scripts/kconfig/tests/choice/allmod_expected_config +++ b/scripts/kconfig/tests/choice/allmod_expected_config @@ -1,5 +1,2 @@ -CONFIG_MODULES=y # CONFIG_BOOL_CHOICE0 is not set CONFIG_BOOL_CHOICE1=y -CONFIG_TRI_CHOICE0=m -CONFIG_TRI_CHOICE1=m diff --git a/scripts/kconfig/tests/choice/allno_expected_config b/scripts/kconfig/tests/choice/allno_expected_config index b88ee7a43136..b359a2e6493e 100644 --- a/scripts/kconfig/tests/choice/allno_expected_config +++ b/scripts/kconfig/tests/choice/allno_expected_config @@ -1,5 +1,2 @@ -# CONFIG_MODULES is not set # CONFIG_BOOL_CHOICE0 is not set CONFIG_BOOL_CHOICE1=y -# CONFIG_TRI_CHOICE0 is not set -CONFIG_TRI_CHOICE1=y diff --git a/scripts/kconfig/tests/choice/allyes_expected_config b/scripts/kconfig/tests/choice/allyes_expected_config index 8a76c1816893..b359a2e6493e 100644 --- a/scripts/kconfig/tests/choice/allyes_expected_config +++ b/scripts/kconfig/tests/choice/allyes_expected_config @@ -1,5 +1,2 @@ -CONFIG_MODULES=y # CONFIG_BOOL_CHOICE0 is not set CONFIG_BOOL_CHOICE1=y -# CONFIG_TRI_CHOICE0 is not set -CONFIG_TRI_CHOICE1=y diff --git a/scripts/kconfig/tests/choice/oldask0_expected_stdout b/scripts/kconfig/tests/choice/oldask0_expected_stdout index d2257db46423..80ec34c61ebc 100644 --- a/scripts/kconfig/tests/choice/oldask0_expected_stdout +++ b/scripts/kconfig/tests/choice/oldask0_expected_stdout @@ -1,8 +1,4 @@ -Enable loadable module support (MODULES) [Y/n/?] (NEW) boolean choice 1. choice 0 (BOOL_CHOICE0) (NEW) > 2. choice 1 (BOOL_CHOICE1) (NEW) choice[1-2?]: -tristate choice [M/y/?] (NEW) - choice 0 (TRI_CHOICE0) [N/m/?] (NEW) - choice 1 (TRI_CHOICE1) [N/m/?] (NEW) diff --git a/scripts/kconfig/tests/choice/oldask1_config b/scripts/kconfig/tests/choice/oldask1_config deleted file mode 100644 index 0f417856c81c..000000000000 --- a/scripts/kconfig/tests/choice/oldask1_config +++ /dev/null @@ -1 +0,0 @@ -# CONFIG_MODULES is not set diff --git a/scripts/kconfig/tests/choice/oldask1_expected_stdout b/scripts/kconfig/tests/choice/oldask1_expected_stdout deleted file mode 100644 index ffa20ad7f38e..000000000000 --- a/scripts/kconfig/tests/choice/oldask1_expected_stdout +++ /dev/null @@ -1,9 +0,0 @@ -Enable loadable module support (MODULES) [N/y/?] -boolean choice - 1. choice 0 (BOOL_CHOICE0) (NEW) -> 2. choice 1 (BOOL_CHOICE1) (NEW) -choice[1-2?]: -tristate choice - 1. choice 0 (TRI_CHOICE0) (NEW) -> 2. choice 1 (TRI_CHOICE1) (NEW) -choice[1-2?]: diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/Kconfig b/scripts/kconfig/tests/choice_value_with_m_dep/Kconfig deleted file mode 100644 index bd970cec07d6..000000000000 --- a/scripts/kconfig/tests/choice_value_with_m_dep/Kconfig +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -config MODULES - def_bool y - modules - -config DEP - tristate - default m - -choice - prompt "Tristate Choice" - -config CHOICE0 - tristate "Choice 0" - -config CHOICE1 - tristate "Choice 1" - depends on DEP - -endchoice diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/__init__.py b/scripts/kconfig/tests/choice_value_with_m_dep/__init__.py deleted file mode 100644 index 075b4e08696e..000000000000 --- a/scripts/kconfig/tests/choice_value_with_m_dep/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -""" -Hide tristate choice values with mod dependency in y choice. - -If tristate choice values depend on symbols set to 'm', they should be -hidden when the choice containing them is changed from 'm' to 'y' -(i.e. exclusive choice). - -Related Linux commit: fa64e5f6a35efd5e77d639125d973077ca506074 -""" - - -def test(conf): - assert conf.oldaskconfig('config', 'y') == 0 - assert conf.config_contains('expected_config') - assert conf.stdout_contains('expected_stdout') diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/config b/scripts/kconfig/tests/choice_value_with_m_dep/config deleted file mode 100644 index 3a126b7a2546..000000000000 --- a/scripts/kconfig/tests/choice_value_with_m_dep/config +++ /dev/null @@ -1,2 +0,0 @@ -CONFIG_CHOICE0=m -CONFIG_CHOICE1=m diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/expected_config b/scripts/kconfig/tests/choice_value_with_m_dep/expected_config deleted file mode 100644 index 4d07b449540e..000000000000 --- a/scripts/kconfig/tests/choice_value_with_m_dep/expected_config +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_MODULES=y -CONFIG_DEP=m -CONFIG_CHOICE0=y diff --git a/scripts/kconfig/tests/choice_value_with_m_dep/expected_stdout b/scripts/kconfig/tests/choice_value_with_m_dep/expected_stdout deleted file mode 100644 index 2b50ab65c86a..000000000000 --- a/scripts/kconfig/tests/choice_value_with_m_dep/expected_stdout +++ /dev/null @@ -1,4 +0,0 @@ -Tristate Choice [M/y/?] y -Tristate Choice -> 1. Choice 0 (CHOICE0) -choice[1]: 1 diff --git a/scripts/kconfig/tests/inter_choice/Kconfig b/scripts/kconfig/tests/inter_choice/Kconfig deleted file mode 100644 index 26c25f68695b..000000000000 --- a/scripts/kconfig/tests/inter_choice/Kconfig +++ /dev/null @@ -1,25 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -config MODULES - def_bool y - modules - -choice - prompt "Choice" - -config CHOICE_VAL0 - tristate "Choice 0" - -config CHOIVE_VAL1 - tristate "Choice 1" - -endchoice - -choice - prompt "Another choice" - depends on CHOICE_VAL0 - -config DUMMY - bool "dummy" - -endchoice diff --git a/scripts/kconfig/tests/inter_choice/__init__.py b/scripts/kconfig/tests/inter_choice/__init__.py deleted file mode 100644 index ffea6b1148a6..000000000000 --- a/scripts/kconfig/tests/inter_choice/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -""" -Do not affect user-assigned choice value by another choice. - -Handling of state flags for choices is complecated. In old days, -the defconfig result of a choice could be affected by another choice -if those choices interact by 'depends on', 'select', etc. - -Related Linux commit: fbe98bb9ed3dae23e320c6b113e35f129538d14a -""" - - -def test(conf): - assert conf.defconfig('defconfig') == 0 - assert conf.config_contains('expected_config') diff --git a/scripts/kconfig/tests/inter_choice/defconfig b/scripts/kconfig/tests/inter_choice/defconfig deleted file mode 100644 index 162c4148e2a5..000000000000 --- a/scripts/kconfig/tests/inter_choice/defconfig +++ /dev/null @@ -1 +0,0 @@ -CONFIG_CHOICE_VAL0=y diff --git a/scripts/kconfig/tests/inter_choice/expected_config b/scripts/kconfig/tests/inter_choice/expected_config deleted file mode 100644 index 5dceefb054e3..000000000000 --- a/scripts/kconfig/tests/inter_choice/expected_config +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_MODULES=y -CONFIG_CHOICE_VAL0=y -# CONFIG_CHOIVE_VAL1 is not set -CONFIG_DUMMY=y From 826ee96dd4f72028b98366a21d986d35e1d781d5 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 2 Jun 2024 21:54:15 +0900 Subject: [PATCH 075/155] kconfig: refactor conf_set_all_new_symbols() to reduce indentation level The outer switch statement can be avoided by continue'ing earlier the loop when the symbol type is neither S_BOOLEAN nor S_TRISTATE. Remove it to reduce the indentation level by one. In addition, avoid the repetition of sym->def[S_DEF_USER].tri. No functional change intended. Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier --- scripts/kconfig/conf.c | 61 +++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 9a20e9e9bdad..cf8193fc00fc 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -218,43 +218,42 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode) } for_all_symbols(sym) { - if (sym_has_value(sym) || sym->flags & SYMBOL_VALID) + tristate val; + + if (sym_has_value(sym) || sym->flags & SYMBOL_VALID || + (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)) continue; - switch (sym_get_type(sym)) { - case S_BOOLEAN: - case S_TRISTATE: - has_changed = true; - switch (mode) { - case def_yes: - sym->def[S_DEF_USER].tri = yes; - break; - case def_mod: - sym->def[S_DEF_USER].tri = mod; - break; - case def_no: - sym->def[S_DEF_USER].tri = no; - break; - case def_random: - sym->def[S_DEF_USER].tri = no; - cnt = rand() % 100; - if (sym->type == S_TRISTATE) { - if (cnt < pty) - sym->def[S_DEF_USER].tri = yes; - else if (cnt < pty + ptm) - sym->def[S_DEF_USER].tri = mod; - } else if (cnt < pby) - sym->def[S_DEF_USER].tri = yes; - break; - default: - continue; + + has_changed = true; + switch (mode) { + case def_yes: + val = yes; + break; + case def_mod: + val = mod; + break; + case def_no: + val = no; + break; + case def_random: + val = no; + cnt = rand() % 100; + if (sym->type == S_TRISTATE) { + if (cnt < pty) + val = yes; + else if (cnt < pty + ptm) + val = mod; + } else if (cnt < pby) { + val = yes; } - if (!(sym_is_choice(sym) && mode == def_random)) - sym->flags |= SYMBOL_DEF_USER; break; default: - break; + continue; } + sym->def[S_DEF_USER].tri = val; + if (!(sym_is_choice(sym) && mode == def_random)) + sym->flags |= SYMBOL_DEF_USER; } sym_clear_all_valid(); From 995150e4de13cee75b28265dabcd0c289b4ed3fa Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 2 Jun 2024 21:54:16 +0900 Subject: [PATCH 076/155] kconfig: refactor conf_write_defconfig() to reduce indentation level Reduce the indentation level by continue'ing the loop earlier if (!sym || sym_is_choice(sym)). No functional change intended. Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier --- scripts/kconfig/confdata.c | 47 ++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 25c327ae3c5c..1ac7fc9ad756 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -774,34 +774,31 @@ int conf_write_defconfig(const char *filename) struct menu *choice; sym = menu->sym; - if (sym && !sym_is_choice(sym)) { - sym_calc_value(sym); - if (!(sym->flags & SYMBOL_WRITE)) - continue; - sym->flags &= ~SYMBOL_WRITE; - /* If we cannot change the symbol - skip */ - if (!sym_is_changeable(sym)) - continue; - /* If symbol equals to default value - skip */ - if (strcmp(sym_get_string_value(sym), sym_get_string_default(sym)) == 0) - continue; - /* - * If symbol is a choice value and equals to the - * default for a choice - skip. - */ - choice = sym_get_choice_menu(sym); - if (choice) { - struct symbol *ds; + if (!sym || sym_is_choice(sym)) + continue; - ds = sym_choice_default(choice->sym); - if (sym == ds) { - if (sym_get_tristate_value(sym) == yes) - continue; - } - } - print_symbol_for_dotconfig(out, sym); + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE)) + continue; + sym->flags &= ~SYMBOL_WRITE; + /* Skip unchangeable symbols */ + if (!sym_is_changeable(sym)) + continue; + /* Skip symbols that are equal to the default */ + if (!strcmp(sym_get_string_value(sym), sym_get_string_default(sym))) + continue; + + /* Skip choice values that are equal to the default */ + choice = sym_get_choice_menu(sym); + if (choice) { + struct symbol *ds; + + ds = sym_choice_default(choice->sym); + if (sym == ds && sym_get_tristate_value(sym) == yes) + continue; } + print_symbol_for_dotconfig(out, sym); } fclose(out); return 0; From ddf41329839f49dadf26973cd845ea160ac1784d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 10 Jun 2024 20:25:16 +0900 Subject: [PATCH 077/155] kbuild: refactor variables in scripts/link-vmlinux.sh Clean up the variables in scripts/link-vmlinux.sh - Specify the extra objects directly in vmlinux_link() - Move the AS rule to kallsyms() - Set kallsymso and btf_vmlinux_bin_o where they are generated - Remove unneeded variable, kallsymso_prev - Introduce the btf_data variable - Introduce the strip_debug flag instead of checking the output name No functional change intended. Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier --- scripts/link-vmlinux.sh | 67 +++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 518c70b8db50..3d9d7257143a 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -45,7 +45,6 @@ info() # Link of vmlinux # ${1} - output file -# ${2}, ${3}, ... - optional extra .o files vmlinux_link() { local output=${1} @@ -90,7 +89,7 @@ vmlinux_link() ldflags="${ldflags} ${wl}--script=${objtree}/${KBUILD_LDS}" # The kallsyms linking does not need debug symbols included. - if [ "$output" != "${output#.tmp_vmlinux.kallsyms}" ] ; then + if [ -n "${strip_debug}" ] ; then ldflags="${ldflags} ${wl}--strip-debug" fi @@ -101,7 +100,7 @@ vmlinux_link() ${ld} ${ldflags} -o ${output} \ ${wl}--whole-archive ${objs} ${wl}--no-whole-archive \ ${wl}--start-group ${libs} ${wl}--end-group \ - $@ ${ldlibs} + ${kallsymso} ${btf_vmlinux_bin_o} ${ldlibs} } # generate .BTF typeinfo from DWARF debuginfo @@ -110,6 +109,7 @@ vmlinux_link() gen_btf() { local pahole_ver + local btf_data=${2} if ! [ -x "$(command -v ${PAHOLE})" ]; then echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" @@ -124,16 +124,16 @@ gen_btf() vmlinux_link ${1} - info "BTF" ${2} + info BTF "${btf_data}" LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1} - # Create ${2} which contains just .BTF section but no symbols. Add + # Create ${btf_data} which contains just .BTF section but no symbols. Add # SHF_ALLOC because .BTF will be part of the vmlinux image. --strip-all # deletes all symbols including __start_BTF and __stop_BTF, which will # be redefined in the linker script. Add 2>/dev/null to suppress GNU # objcopy warnings: "empty loadable segment detected at ..." ${OBJCOPY} --only-section=.BTF --set-section-flags .BTF=alloc,readonly \ - --strip-all ${1} ${2} 2>/dev/null + --strip-all ${1} "${btf_data}" 2>/dev/null # Change e_type to ET_REL so that it can be used to link final vmlinux. # GNU ld 2.35+ and lld do not allow an ET_EXEC input. if is_enabled CONFIG_CPU_BIG_ENDIAN; then @@ -141,10 +141,12 @@ gen_btf() else et_rel='\1\0' fi - printf "${et_rel}" | dd of=${2} conv=notrunc bs=1 seek=16 status=none + printf "${et_rel}" | dd of="${btf_data}" conv=notrunc bs=1 seek=16 status=none + + btf_vmlinux_bin_o=${btf_data} } -# Create ${2} .S file with all symbols from the ${1} object file +# Create ${2}.o file with all symbols from the ${1} object file kallsyms() { local kallsymopt; @@ -165,27 +167,25 @@ kallsyms() kallsymopt="${kallsymopt} --lto-clang" fi - info KSYMS ${2} - scripts/kallsyms ${kallsymopt} ${1} > ${2} + info KSYMS "${2}.S" + scripts/kallsyms ${kallsymopt} "${1}" > "${2}.S" + + info AS "${2}.o" + ${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \ + ${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} -c -o "${2}.o" "${2}.S" + + kallsymso=${2}.o } # Perform one step in kallsyms generation, including temporary linking of # vmlinux. kallsyms_step() { - kallsymso_prev=${kallsymso} kallsyms_vmlinux=.tmp_vmlinux.kallsyms${1} - kallsymso=${kallsyms_vmlinux}.o - kallsyms_S=${kallsyms_vmlinux}.S - vmlinux_link ${kallsyms_vmlinux} "${kallsymso_prev}" ${btf_vmlinux_bin_o} - mksysmap ${kallsyms_vmlinux} ${kallsyms_vmlinux}.syms - kallsyms ${kallsyms_vmlinux}.syms ${kallsyms_S} - - info AS ${kallsymso} - ${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \ - ${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \ - -c -o ${kallsymso} ${kallsyms_S} + vmlinux_link "${kallsyms_vmlinux}" + mksysmap "${kallsyms_vmlinux}" "${kallsyms_vmlinux}.syms" + kallsyms "${kallsyms_vmlinux}.syms" "${kallsyms_vmlinux}" } # Create map file with all symbols from ${1} @@ -223,19 +223,18 @@ fi ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init init/version-timestamp.o -btf_vmlinux_bin_o="" +btf_vmlinux_bin_o= +kallsymso= +strip_debug= + if is_enabled CONFIG_DEBUG_INFO_BTF; then - btf_vmlinux_bin_o=.btf.vmlinux.bin.o - if ! gen_btf .tmp_vmlinux.btf $btf_vmlinux_bin_o ; then + if ! gen_btf .tmp_vmlinux.btf .btf.vmlinux.bin.o ; then echo >&2 "Failed to generate BTF for vmlinux" echo >&2 "Try to disable CONFIG_DEBUG_INFO_BTF" exit 1 fi fi -kallsymso="" -kallsymso_prev="" -kallsyms_vmlinux="" if is_enabled CONFIG_KALLSYMS; then # kallsyms support @@ -261,11 +260,13 @@ if is_enabled CONFIG_KALLSYMS; then # a) Verify that the System.map from vmlinux matches the map from # ${kallsymso}. - kallsyms_step 1 - kallsyms_step 2 + # The kallsyms linking does not need debug symbols included. + strip_debug=1 - # step 3 - size1=$(${CONFIG_SHELL} "${srctree}/scripts/file-size.sh" ${kallsymso_prev}) + kallsyms_step 1 + size1=$(${CONFIG_SHELL} "${srctree}/scripts/file-size.sh" ${kallsymso}) + + kallsyms_step 2 size2=$(${CONFIG_SHELL} "${srctree}/scripts/file-size.sh" ${kallsymso}) if [ $size1 -ne $size2 ] || [ -n "${KALLSYMS_EXTRA_PASS}" ]; then @@ -273,7 +274,9 @@ if is_enabled CONFIG_KALLSYMS; then fi fi -vmlinux_link vmlinux "${kallsymso}" ${btf_vmlinux_bin_o} +strip_debug= + +vmlinux_link vmlinux # fill in BTF IDs if is_enabled CONFIG_DEBUG_INFO_BTF && is_enabled CONFIG_BPF; then From c442db3f49f27e5a60a641b2ac9a3c6320796ed6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 10 Jun 2024 20:25:17 +0900 Subject: [PATCH 078/155] kbuild: remove PROVIDE() for kallsyms symbols This reimplements commit 951bcae6c5a0 ("kallsyms: Avoid weak references for kallsyms symbols") because I am not a big fan of PROVIDE(). As an alternative solution, this commit prepends one more kallsyms step. KSYMS .tmp_vmlinux.kallsyms0.S # added AS .tmp_vmlinux.kallsyms0.o # added LD .tmp_vmlinux.btf BTF .btf.vmlinux.bin.o LD .tmp_vmlinux.kallsyms1 NM .tmp_vmlinux.kallsyms1.syms KSYMS .tmp_vmlinux.kallsyms1.S AS .tmp_vmlinux.kallsyms1.o LD .tmp_vmlinux.kallsyms2 NM .tmp_vmlinux.kallsyms2.syms KSYMS .tmp_vmlinux.kallsyms2.S AS .tmp_vmlinux.kallsyms2.o LD vmlinux Step 0 takes /dev/null as input, and generates .tmp_vmlinux.kallsyms0.o, which has a valid kallsyms format with the empty symbol list, and can be linked to vmlinux. Since it is really small, the added compile-time cost is negligible. Signed-off-by: Masahiro Yamada Acked-by: Ard Biesheuvel Reviewed-by: Nicolas Schier --- include/asm-generic/vmlinux.lds.h | 19 ------------------- kernel/kallsyms_internal.h | 5 ----- scripts/kallsyms.c | 6 ------ scripts/link-vmlinux.sh | 9 +++++++-- 4 files changed, 7 insertions(+), 32 deletions(-) diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 5703526d6ebf..62b4cb0462e6 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -451,30 +451,11 @@ #endif #endif -/* - * Some symbol definitions will not exist yet during the first pass of the - * link, but are guaranteed to exist in the final link. Provide preliminary - * definitions that will be superseded in the final link to avoid having to - * rely on weak external linkage, which requires a GOT when used in position - * independent code. - */ -#define PRELIMINARY_SYMBOL_DEFINITIONS \ - PROVIDE(kallsyms_addresses = .); \ - PROVIDE(kallsyms_offsets = .); \ - PROVIDE(kallsyms_names = .); \ - PROVIDE(kallsyms_num_syms = .); \ - PROVIDE(kallsyms_relative_base = .); \ - PROVIDE(kallsyms_token_table = .); \ - PROVIDE(kallsyms_token_index = .); \ - PROVIDE(kallsyms_markers = .); \ - PROVIDE(kallsyms_seqs_of_names = .); - /* * Read only Data */ #define RO_DATA(align) \ . = ALIGN((align)); \ - PRELIMINARY_SYMBOL_DEFINITIONS \ .rodata : AT(ADDR(.rodata) - LOAD_OFFSET) { \ __start_rodata = .; \ *(.rodata) *(.rodata.*) \ diff --git a/kernel/kallsyms_internal.h b/kernel/kallsyms_internal.h index 85480274fc8f..925f2ab22639 100644 --- a/kernel/kallsyms_internal.h +++ b/kernel/kallsyms_internal.h @@ -4,11 +4,6 @@ #include -/* - * These will be re-linked against their real values during the second link - * stage. Preliminary values must be provided in the linker script using the - * PROVIDE() directive so that the first link stage can complete successfully. - */ extern const unsigned long kallsyms_addresses[]; extern const int kallsyms_offsets[]; extern const u8 kallsyms_names[]; diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 47978efe4797..fa53b5eef553 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -259,12 +259,6 @@ static void shrink_table(void) } } table_cnt = pos; - - /* When valid symbol is not registered, exit to error */ - if (!table_cnt) { - fprintf(stderr, "No valid symbol.\n"); - exit(1); - } } static void read_map(const char *in) diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 3d9d7257143a..83d605ba7241 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -227,6 +227,10 @@ btf_vmlinux_bin_o= kallsymso= strip_debug= +if is_enabled CONFIG_KALLSYMS; then + kallsyms /dev/null .tmp_vmlinux.kallsyms0 +fi + if is_enabled CONFIG_DEBUG_INFO_BTF; then if ! gen_btf .tmp_vmlinux.btf .btf.vmlinux.bin.o ; then echo >&2 "Failed to generate BTF for vmlinux" @@ -239,9 +243,10 @@ if is_enabled CONFIG_KALLSYMS; then # kallsyms support # Generate section listing all symbols and add it into vmlinux - # It's a three step process: + # It's a four step process: + # 0) Generate a dummy __kallsyms with empty symbol list. # 1) Link .tmp_vmlinux.kallsyms1 so it has all symbols and sections, - # but __kallsyms is empty. + # with a dummy __kallsyms. # Running kallsyms on that gives us .tmp_kallsyms1.o with # the right size # 2) Link .tmp_vmlinux.kallsyms2 so it now has a __kallsyms section of From b1a9a5e04767e2a78783e19c9e55c25812ceccc3 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 10 Jun 2024 20:25:18 +0900 Subject: [PATCH 079/155] kbuild: merge temporary vmlinux for BTF and kallsyms CONFIG_DEBUG_INFO_BTF=y requires one additional link step. (.tmp_vmlinux.btf) CONFIG_KALLSYMS=y requires two additional link steps. (.tmp_vmlinux.kallsyms1 and .tmp_vmlinux.kallsyms2) Enabling both requires three additional link steps. When CONFIG_DEBUG_INFO_BTF=y and CONFIG_KALLSYMS=y, the current build process is as follows: KSYMS .tmp_vmlinux.kallsyms0.S AS .tmp_vmlinux.kallsyms0.o LD .tmp_vmlinux.btf # temporary vmlinux for BTF BTF .btf.vmlinux.bin.o LD .tmp_vmlinux.kallsyms1 # temporary vmlinux for kallsyms step 1 NM .tmp_vmlinux.kallsyms1.syms KSYMS .tmp_vmlinux.kallsyms1.S AS .tmp_vmlinux.kallsyms1.o LD .tmp_vmlinux.kallsyms2 # temporary vmlinux for kallsyms step 2 NM .tmp_vmlinux.kallsyms2.syms KSYMS .tmp_vmlinux.kallsyms2.S AS .tmp_vmlinux.kallsyms2.o LD vmlinux # final vmlinux This is redundant because the BTF generation and the kallsyms step 1 can be performed against the same temporary vmlinux. When both CONFIG_DEBUG_INFO_BTF and CONFIG_KALLSYMS are enabled, we can reduce the number of link steps by one. This commit changes the build process as follows: KSYMS .tmp_vmlinux0.kallsyms.S AS .tmp_vmlinux0.kallsyms.o LD .tmp_vmlinux1 # temporary vmlinux for BTF and kallsyms step 1 BTF .tmp_vmlinux1.btf.o NM .tmp_vmlinux1.syms KSYMS .tmp_vmlinux1.kallsyms.S AS .tmp_vmlinux1.kallsyms.o LD .tmp_vmlinux2 # temporary vmlinux for kallsyms step 2 NM .tmp_vmlinux2.syms KSYMS .tmp_vmlinux2.kallsyms.S AS .tmp_vmlinux2.kallsyms.o LD vmlinux # final vmlinux Signed-off-by: Masahiro Yamada Acked-by: Andrii Nakryiko --- scripts/link-vmlinux.sh | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 83d605ba7241..c64158a04f82 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -105,11 +105,10 @@ vmlinux_link() # generate .BTF typeinfo from DWARF debuginfo # ${1} - vmlinux image -# ${2} - file to dump raw BTF data into gen_btf() { local pahole_ver - local btf_data=${2} + local btf_data=${1}.btf.o if ! [ -x "$(command -v ${PAHOLE})" ]; then echo >&2 "BTF: ${1}: pahole (${PAHOLE}) is not available" @@ -122,8 +121,6 @@ gen_btf() return 1 fi - vmlinux_link ${1} - info BTF "${btf_data}" LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1} @@ -177,15 +174,13 @@ kallsyms() kallsymso=${2}.o } -# Perform one step in kallsyms generation, including temporary linking of -# vmlinux. -kallsyms_step() +# Perform kallsyms for the given temporary vmlinux. +sysmap_and_kallsyms() { - kallsyms_vmlinux=.tmp_vmlinux.kallsyms${1} + mksysmap "${1}" "${1}.syms" + kallsyms "${1}.syms" "${1}.kallsyms" - vmlinux_link "${kallsyms_vmlinux}" - mksysmap "${kallsyms_vmlinux}" "${kallsyms_vmlinux}.syms" - kallsyms "${kallsyms_vmlinux}.syms" "${kallsyms_vmlinux}" + kallsyms_sysmap=${1}.syms } # Create map file with all symbols from ${1} @@ -228,11 +223,21 @@ kallsymso= strip_debug= if is_enabled CONFIG_KALLSYMS; then - kallsyms /dev/null .tmp_vmlinux.kallsyms0 + kallsyms /dev/null .tmp_vmlinux0.kallsyms +fi + +if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then + + # The kallsyms linking does not need debug symbols, but the BTF does. + if ! is_enabled CONFIG_DEBUG_INFO_BTF; then + strip_debug=1 + fi + + vmlinux_link .tmp_vmlinux1 fi if is_enabled CONFIG_DEBUG_INFO_BTF; then - if ! gen_btf .tmp_vmlinux.btf .btf.vmlinux.bin.o ; then + if ! gen_btf .tmp_vmlinux1; then echo >&2 "Failed to generate BTF for vmlinux" echo >&2 "Try to disable CONFIG_DEBUG_INFO_BTF" exit 1 @@ -268,14 +273,16 @@ if is_enabled CONFIG_KALLSYMS; then # The kallsyms linking does not need debug symbols included. strip_debug=1 - kallsyms_step 1 + sysmap_and_kallsyms .tmp_vmlinux1 size1=$(${CONFIG_SHELL} "${srctree}/scripts/file-size.sh" ${kallsymso}) - kallsyms_step 2 + vmlinux_link .tmp_vmlinux2 + sysmap_and_kallsyms .tmp_vmlinux2 size2=$(${CONFIG_SHELL} "${srctree}/scripts/file-size.sh" ${kallsymso}) if [ $size1 -ne $size2 ] || [ -n "${KALLSYMS_EXTRA_PASS}" ]; then - kallsyms_step 3 + vmlinux_link .tmp_vmlinux3 + sysmap_and_kallsyms .tmp_vmlinux3 fi fi @@ -301,7 +308,7 @@ fi # step a (see comment above) if is_enabled CONFIG_KALLSYMS; then - if ! cmp -s System.map ${kallsyms_vmlinux}.syms; then + if ! cmp -s System.map "${kallsyms_sysmap}"; then echo >&2 Inconsistent kallsyms data echo >&2 'Try "make KALLSYMS_EXTRA_PASS=1" as a workaround' exit 1 From e570ef438fcd279514691f0aae903272d45e45e9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 12 Jun 2024 01:08:05 +0900 Subject: [PATCH 080/155] kconfig: add -e and -u options to *conf-cfg.sh scripts Set -e to make these scripts fail on the first error. Set -u because these scripts are invoked by Makefile, and do not work properly without necessary variables defined. Both options are described in POSIX. [1] [1]: https://pubs.opengroup.org/onlinepubs/009604499/utilities/set.html Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier --- scripts/kconfig/gconf-cfg.sh | 2 ++ scripts/kconfig/mconf-cfg.sh | 2 ++ scripts/kconfig/nconf-cfg.sh | 2 ++ scripts/kconfig/qconf-cfg.sh | 2 ++ 4 files changed, 8 insertions(+) diff --git a/scripts/kconfig/gconf-cfg.sh b/scripts/kconfig/gconf-cfg.sh index 040d8f338820..fc954c0538fa 100755 --- a/scripts/kconfig/gconf-cfg.sh +++ b/scripts/kconfig/gconf-cfg.sh @@ -1,6 +1,8 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 +set -eu + cflags=$1 libs=$2 diff --git a/scripts/kconfig/mconf-cfg.sh b/scripts/kconfig/mconf-cfg.sh index 1e61f50a5905..1bc304dc2f7d 100755 --- a/scripts/kconfig/mconf-cfg.sh +++ b/scripts/kconfig/mconf-cfg.sh @@ -1,6 +1,8 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 +set -eu + cflags=$1 libs=$2 diff --git a/scripts/kconfig/nconf-cfg.sh b/scripts/kconfig/nconf-cfg.sh index f871a2160e36..a20290b1a37d 100755 --- a/scripts/kconfig/nconf-cfg.sh +++ b/scripts/kconfig/nconf-cfg.sh @@ -1,6 +1,8 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 +set -eu + cflags=$1 libs=$2 diff --git a/scripts/kconfig/qconf-cfg.sh b/scripts/kconfig/qconf-cfg.sh index 0e113b0f2455..bb2df66363a8 100755 --- a/scripts/kconfig/qconf-cfg.sh +++ b/scripts/kconfig/qconf-cfg.sh @@ -1,6 +1,8 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 +set -eu + cflags=$1 libs=$2 bin=$3 From cd90952101c86267193b263ed3f0753f0715700e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 12 Jun 2024 02:55:10 +0900 Subject: [PATCH 081/155] kconfig: remove unneeded code in expr_compare_type() The condition (t2 == 0) never becomes true because the zero value (i.e., E_NONE) is only used as a dummy type for prevtoken. It can be passed to t1, but not to t2. The caller of this function only checks expr_compare_type() > 0. Therefore, the distinction between 0 and -1 is unnecessary. Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index fcc190b67b6f..31737b2cb8e2 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -1096,11 +1096,8 @@ static int expr_compare_type(enum expr_type t1, enum expr_type t2) case E_OR: if (t2 == E_LIST) return 1; - case E_LIST: - if (t2 == 0) - return 1; default: - return -1; + break; } return 0; } From dfe8e56fc604a6000550174d1002c6076efd8003 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 12 Jun 2024 02:55:11 +0900 Subject: [PATCH 082/155] kconfig: add fallthrough comments to expr_compare_type() Clarify the missing 'break' is intentional. Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index 31737b2cb8e2..bea82d5cac83 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -1083,19 +1083,24 @@ static int expr_compare_type(enum expr_type t1, enum expr_type t2) case E_GTH: if (t2 == E_EQUAL || t2 == E_UNEQUAL) return 1; + /* fallthrough */ case E_EQUAL: case E_UNEQUAL: if (t2 == E_NOT) return 1; + /* fallthrough */ case E_NOT: if (t2 == E_AND) return 1; + /* fallthrough */ case E_AND: if (t2 == E_OR) return 1; + /* fallthrough */ case E_OR: if (t2 == E_LIST) return 1; + /* fallthrough */ default: break; } From bd988e7cb84a7f27e8ec100c5f68498b7d4fa69c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 12 Jun 2024 02:55:12 +0900 Subject: [PATCH 083/155] kconfig: introduce choice_set_value() helper Currently, sym_set_tristate_value() is used to set 'y' to a choice member, which is confusing because it not only sets 'y' to the given symbol but also tweaks flags of other symbols as a side effect. Add a dedicated function for setting the value of the given choice. Signed-off-by: Masahiro Yamada --- scripts/kconfig/conf.c | 2 +- scripts/kconfig/lkc_proto.h | 1 + scripts/kconfig/mconf.c | 2 +- scripts/kconfig/nconf.c | 2 +- scripts/kconfig/symbol.c | 62 +++++++++++++++++++++++++------------ 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index cf8193fc00fc..5dbdd9459f21 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -507,7 +507,7 @@ static void conf_choice(struct menu *menu) print_help(child); continue; } - sym_set_tristate_value(child->sym, yes); + choice_set_value(menu, child->sym); return; } } diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index c663fd8b35d2..1221709efac1 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -28,6 +28,7 @@ bool sym_dep_errors(void); enum symbol_type sym_get_type(struct symbol *sym); bool sym_tristate_within_range(struct symbol *sym,tristate tri); bool sym_set_tristate_value(struct symbol *sym,tristate tri); +void choice_set_value(struct menu *choice, struct symbol *sym); tristate sym_toggle_tristate_value(struct symbol *sym); bool sym_string_valid(struct symbol *sym, const char *newval); bool sym_string_within_range(struct symbol *sym, const char *str); diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index e6227af51658..03709eb734ae 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -636,7 +636,7 @@ static void conf_choice(struct menu *menu) if (!child->sym) break; - sym_set_tristate_value(child->sym, yes); + choice_set_value(menu, child->sym); } return; case 1: diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index addc89ee61d4..eb5fc3ccaf9d 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -1331,7 +1331,7 @@ static void conf_choice(struct menu *menu) case ' ': case 10: case KEY_RIGHT: - sym_set_tristate_value(child->sym, yes); + choice_set_value(menu, child->sym); return; case 'h': case '?': diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index eaff7ac496bd..8df0a75f40b9 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -516,8 +516,6 @@ bool sym_tristate_within_range(struct symbol *sym, tristate val) return false; if (sym->visible <= sym->rev_dep.tri) return false; - if (sym_is_choice_value(sym) && sym->visible == yes) - return val == yes; return val >= sym->rev_dep.tri && val <= sym->visible; } @@ -532,23 +530,6 @@ bool sym_set_tristate_value(struct symbol *sym, tristate val) sym->flags |= SYMBOL_DEF_USER; sym_set_changed(sym); } - /* - * setting a choice value also resets the new flag of the choice - * symbol and all other choice values. - */ - if (sym_is_choice_value(sym) && val == yes) { - struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); - struct property *prop; - struct expr *e; - - cs->def[S_DEF_USER].val = sym; - cs->flags |= SYMBOL_DEF_USER; - prop = sym_get_choice_prop(cs); - for (e = prop->expr; e; e = e->left.expr) { - if (e->right.sym->visible != no) - e->right.sym->flags |= SYMBOL_DEF_USER; - } - } sym->def[S_DEF_USER].tri = val; if (oldval != val) @@ -557,10 +538,53 @@ bool sym_set_tristate_value(struct symbol *sym, tristate val) return true; } +/** + * choice_set_value - set the user input to a choice + * + * @choice: menu entry for the choice + * @sym: selected symbol + */ +void choice_set_value(struct menu *choice, struct symbol *sym) +{ + struct menu *menu; + bool changed = false; + + menu_for_each_sub_entry(menu, choice) { + tristate val; + + if (!menu->sym) + continue; + + if (menu->sym->visible == no) + continue; + + val = menu->sym == sym ? yes : no; + + if (menu->sym->curr.tri != val) + changed = true; + + menu->sym->def[S_DEF_USER].tri = val; + menu->sym->flags |= SYMBOL_DEF_USER; + } + + choice->sym->def[S_DEF_USER].val = sym; + choice->sym->flags |= SYMBOL_DEF_USER; + + if (changed) + sym_clear_all_valid(); +} + tristate sym_toggle_tristate_value(struct symbol *sym) { + struct menu *choice; tristate oldval, newval; + choice = sym_get_choice_menu(sym); + if (choice) { + choice_set_value(choice, sym); + return yes; + } + oldval = newval = sym_get_tristate_value(sym); do { switch (newval) { From 9b114520837a5f08b8eeeee30947bb9e7f44be1e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 12 Jun 2024 02:55:13 +0900 Subject: [PATCH 084/155] kconfig: remember the current choice while parsing the choice block Instead of the boolean flag, it will be more useful to remember the current choice being parsed. Signed-off-by: Masahiro Yamada --- scripts/kconfig/parser.y | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index 8adb2f70121e..20538e1d3788 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -28,9 +28,7 @@ static void zconf_error(const char *err, ...); static bool zconf_endtoken(const char *tokenname, const char *expected_tokenname); -struct menu *current_menu, *current_entry; - -static bool inside_choice = false; +struct menu *current_menu, *current_entry, *current_choice; %} @@ -147,7 +145,7 @@ config_entry_start: T_CONFIG nonconst_symbol T_EOL config_stmt: config_entry_start config_option_list { - if (inside_choice) { + if (current_choice) { if (!current_entry->prompt) { fprintf(stderr, "%s:%d: error: choice member must have a prompt\n", current_entry->filename, current_entry->lineno); @@ -256,12 +254,12 @@ choice_entry: choice choice_option_list $$ = menu_add_menu(); - inside_choice = true; + current_choice = current_entry; }; choice_end: end { - inside_choice = false; + current_choice = NULL; if (zconf_endtoken($1, "choice")) { menu_end_menu(); From ae4c4cee8110a986f5a884c5d91d137e2b994303 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 12 Jun 2024 03:24:47 +0900 Subject: [PATCH 085/155] kbuild: move init/build-version to scripts/ At first, I thought this script would be needed only in init/Makefile. However, commit 5db8face97f8 ("kbuild: Restore .version auto-increment behaviour for Debian packages") and commit 1789fc912541 ("kbuild: rpm-pkg: invoke the kernel build from rpmbuild for binrpm-pkg") revealed that it was actually needed for scripts/package/mk* as well. After all, scripts/ is a better place for it. Signed-off-by: Masahiro Yamada Reviewed-by: Nathan Chancellor --- init/Makefile | 2 +- {init => scripts}/build-version | 0 scripts/package/mkdebian | 2 +- scripts/package/mkspec | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename {init => scripts}/build-version (100%) diff --git a/init/Makefile b/init/Makefile index ab71cedc5fd6..10b652d33e87 100644 --- a/init/Makefile +++ b/init/Makefile @@ -52,7 +52,7 @@ CFLAGS_version.o := -include $(obj)/utsversion-tmp.h # Build version-timestamp.c with final UTS_VERSION # -include/generated/utsversion.h: build-version-auto = $(shell $(src)/build-version) +include/generated/utsversion.h: build-version-auto = $(shell $(srctree)/scripts/build-version) include/generated/utsversion.h: build-timestamp-auto = $(shell LC_ALL=C date) include/generated/utsversion.h: FORCE $(call filechk,uts_version) diff --git a/init/build-version b/scripts/build-version similarity index 100% rename from init/build-version rename to scripts/build-version diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian index 070149c985fe..b9a5b789c655 100755 --- a/scripts/package/mkdebian +++ b/scripts/package/mkdebian @@ -150,7 +150,7 @@ version=$KERNELRELEASE if [ -n "$KDEB_PKGVERSION" ]; then packageversion=$KDEB_PKGVERSION else - packageversion=$(${srctree}/scripts/setlocalversion --no-local ${srctree})-$($srctree/init/build-version) + packageversion=$(${srctree}/scripts/setlocalversion --no-local ${srctree})-$($srctree/scripts/build-version) fi sourcename=${KDEB_SOURCENAME:-linux-upstream} diff --git a/scripts/package/mkspec b/scripts/package/mkspec index ce201bfa8377..cffc2567bef2 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -24,7 +24,7 @@ fi cat< Date: Tue, 11 Jun 2024 22:59:00 +0200 Subject: [PATCH 086/155] modpost: Enable section warning from *driver to .exit.text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There used to be several offenders, but now that for all of them patches were sent and most of them were applied, enable the warning also for builds without W=1. Signed-off-by: Uwe Kleine-König Reviewed-by: Nathan Chancellor Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index f48d72d22dc2..11731fc62140 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -965,17 +965,6 @@ static int secref_whitelist(const char *fromsec, const char *fromsym, match(fromsym, PATTERNS("*_ops", "*_probe", "*_console"))) return 0; - /* - * symbols in data sections must not refer to .exit.*, but there are - * quite a few offenders, so hide these unless for W=1 builds until - * these are fixed. - */ - if (!extra_warn && - match(fromsec, PATTERNS(DATA_SECTIONS)) && - match(tosec, PATTERNS(ALL_EXIT_SECTIONS)) && - match(fromsym, PATTERNS("*driver"))) - return 0; - /* Check for pattern 3 */ if (strstarts(fromsec, ".head.text") && match(tosec, PATTERNS(ALL_INIT_SECTIONS))) From e61b190b1a273201bf8a4169fb52e54b633f5349 Mon Sep 17 00:00:00 2001 From: Rafael Aquini Date: Tue, 11 Jun 2024 17:11:21 -0400 Subject: [PATCH 087/155] kbuild: rpm-pkg: make sure to have versioned 'Obsoletes' for kernel.spec Fix the following rpmbuild warning: $ make srcrpm-pkg ... RPM build warnings: line 34: It's not recommended to have unversioned Obsoletes: Obsoletes: kernel-headers Signed-off-by: Rafael Aquini Tested-by: Nathan Chancellor Signed-off-by: Masahiro Yamada --- scripts/package/kernel.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec index fffc8af8deb1..4b7df76076c4 100644 --- a/scripts/package/kernel.spec +++ b/scripts/package/kernel.spec @@ -27,7 +27,7 @@ The Linux Kernel, the operating system core itself %package headers Summary: Header files for the Linux kernel for use by glibc Group: Development/System -Obsoletes: kernel-headers +Obsoletes: kernel-headers < %{version} Provides: kernel-headers = %{version} %description headers Kernel-headers includes the C header files that specify the interface From 17c31aded9a1ee87e37f0ea0e3737797ef3f8c97 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 13 Jun 2024 17:34:32 +0800 Subject: [PATCH 088/155] scripts/make_fit: Support decomposing DTBs The kernel tree builds some "composite" DTBs, where the final DTB is the result of applying one or more DTB overlays on top of a base DTB with fdtoverlay. The FIT image specification already supports configurations having one base DTB and overlays applied on top. It is then up to the bootloader to apply said overlays and either use or pass on the final result. This allows the FIT image builder to reuse the same FDT images for multiple configurations, if such cases exist. The decomposition function depends on the kernel build system, reading back the .cmd files for the to-be-packaged DTB files to check for the fdtoverlay command being called. This will not work outside the kernel tree. The function is off by default to keep compatibility with possible existing users. To facilitate the decomposition and keep the code clean, the model and compatitble string extraction have been moved out of the output_dtb function. The FDT image description is replaced with the base file name of the included image. Signed-off-by: Chen-Yu Tsai Reviewed-by: Simon Glass Signed-off-by: Masahiro Yamada --- scripts/Makefile.lib | 1 + scripts/make_fit.py | 86 ++++++++++++++++++++++++++++++++------------ 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 9f06f6aaf7fc..d78b5d38beaa 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -522,6 +522,7 @@ quiet_cmd_fit = FIT $@ cmd_fit = $(MAKE_FIT) -o $@ --arch $(UIMAGE_ARCH) --os linux \ --name '$(UIMAGE_NAME)' \ $(if $(findstring 1,$(KBUILD_VERBOSE)),-v) \ + $(if $(FIT_DECOMPOSE_DTBS),--decompose-dtbs) \ --compress $(FIT_COMPRESSION) -k $< @$(word 2,$^) # XZ diff --git a/scripts/make_fit.py b/scripts/make_fit.py index 263147df80a4..4a1bb2f55861 100755 --- a/scripts/make_fit.py +++ b/scripts/make_fit.py @@ -22,6 +22,11 @@ the entire FIT. Use -c to compress the data, using bzip2, gzip, lz4, lzma, lzo and zstd algorithms. +Use -D to decompose "composite" DTBs into their base components and +deduplicate the resulting base DTBs and DTB overlays. This requires the +DTBs to be sourced from the kernel build directory, as the implementation +looks at the .cmd files produced by the kernel build. + The resulting FIT can be booted by bootloaders which support FIT, such as U-Boot, Linuxboot, Tianocore, etc. @@ -64,6 +69,8 @@ def parse_args(): help='Specifies the architecture') parser.add_argument('-c', '--compress', type=str, default='none', help='Specifies the compression') + parser.add_argument('-D', '--decompose-dtbs', action='store_true', + help='Decompose composite DTBs into base DTB and overlays') parser.add_argument('-E', '--external', action='store_true', help='Convert the FIT to use external data') parser.add_argument('-n', '--name', type=str, required=True, @@ -140,12 +147,12 @@ def finish_fit(fsw, entries): fsw.end_node() seq = 0 with fsw.add_node('configurations'): - for model, compat in entries: + for model, compat, files in entries: seq += 1 with fsw.add_node(f'conf-{seq}'): fsw.property('compatible', bytes(compat)) fsw.property_string('description', model) - fsw.property_string('fdt', f'fdt-{seq}') + fsw.property('fdt', bytes(''.join(f'fdt-{x}\x00' for x in files), "ascii")) fsw.property_string('kernel', 'kernel') fsw.end_node() @@ -193,21 +200,9 @@ def output_dtb(fsw, seq, fname, arch, compress): fname (str): Filename containing the DTB arch: FIT architecture, e.g. 'arm64' compress (str): Compressed algorithm, e.g. 'gzip' - - Returns: - tuple: - str: Model name - bytes: Compatible stringlist """ with fsw.add_node(f'fdt-{seq}'): - # Get the compatible / model information - with open(fname, 'rb') as inf: - data = inf.read() - fdt = libfdt.FdtRo(data) - model = fdt.getprop(0, 'model').as_str() - compat = fdt.getprop(0, 'compatible') - - fsw.property_string('description', model) + fsw.property_string('description', os.path.basename(fname)) fsw.property_string('type', 'flat_dt') fsw.property_string('arch', arch) fsw.property_string('compression', compress) @@ -215,9 +210,45 @@ def output_dtb(fsw, seq, fname, arch, compress): with open(fname, 'rb') as inf: compressed = compress_data(inf, compress) fsw.property('data', compressed) - return model, compat +def process_dtb(fname, args): + """Process an input DTB, decomposing it if requested and is possible + + Args: + fname (str): Filename containing the DTB + args (Namespace): Program arguments + Returns: + tuple: + str: Model name string + str: Root compatible string + files: list of filenames corresponding to the DTB + """ + # Get the compatible / model information + with open(fname, 'rb') as inf: + data = inf.read() + fdt = libfdt.FdtRo(data) + model = fdt.getprop(0, 'model').as_str() + compat = fdt.getprop(0, 'compatible') + + if args.decompose_dtbs: + # Check if the DTB needs to be decomposed + path, basename = os.path.split(fname) + cmd_fname = os.path.join(path, f'.{basename}.cmd') + with open(cmd_fname, 'r', encoding='ascii') as inf: + cmd = inf.read() + + if 'scripts/dtc/fdtoverlay' in cmd: + # This depends on the structure of the composite DTB command + files = cmd.split() + files = files[files.index('-i') + 1:] + else: + files = [fname] + else: + files = [fname] + + return (model, compat, files) + def build_fit(args): """Build the FIT from the provided files and arguments @@ -235,6 +266,7 @@ def build_fit(args): fsw = libfdt.FdtSw() setup_fit(fsw, args.name) entries = [] + fdts = {} # Handle the kernel with open(args.kernel, 'rb') as inf: @@ -243,12 +275,22 @@ def build_fit(args): write_kernel(fsw, comp_data, args) for fname in args.dtbs: - # Ignore overlay (.dtbo) files - if os.path.splitext(fname)[1] == '.dtb': - seq += 1 - size += os.path.getsize(fname) - model, compat = output_dtb(fsw, seq, fname, args.arch, args.compress) - entries.append([model, compat]) + # Ignore non-DTB (*.dtb) files + if os.path.splitext(fname)[1] != '.dtb': + continue + + (model, compat, files) = process_dtb(fname, args) + + for fn in files: + if fn not in fdts: + seq += 1 + size += os.path.getsize(fn) + output_dtb(fsw, seq, fn, args.arch, args.compress) + fdts[fn] = seq + + files_seq = [fdts[fn] for fn in files] + + entries.append([model, compat, files_seq]) finish_fit(fsw, entries) From ee29e6204c32dce013ac6d1078d98dce5607ce86 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:20 +0900 Subject: [PATCH 089/155] kconfig: import list_move(_tail) and list_for_each_entry_reverse macros Import more macros from include/linux/list.h. These will be used in the next commit. Signed-off-by: Masahiro Yamada --- scripts/kconfig/list.h | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/scripts/kconfig/list.h b/scripts/kconfig/list.h index 882859ddf9f4..409201cd495b 100644 --- a/scripts/kconfig/list.h +++ b/scripts/kconfig/list.h @@ -127,6 +127,29 @@ static inline void list_del(struct list_head *entry) entry->prev = LIST_POISON2; } +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del_entry(list); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del_entry(list); + list_add_tail(list, head); +} + /** * list_is_head - tests whether @list is the list @head * @list: the entry to test @@ -166,6 +189,17 @@ static inline int list_empty(const struct list_head *head) #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) +/** + * list_last_entry - get the last element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + /** * list_next_entry - get the next element in list * @pos: the type * to cursor @@ -174,6 +208,14 @@ static inline int list_empty(const struct list_head *head) #define list_next_entry(pos, member) \ list_entry((pos)->member.next, typeof(*(pos)), member) +/** + * list_prev_entry - get the prev element in list + * @pos: the type * to cursor + * @member: the name of the list_head within the struct. + */ +#define list_prev_entry(pos, member) \ + list_entry((pos)->member.prev, typeof(*(pos)), member) + /** * list_entry_is_head - test if the entry points to the head of the list * @pos: the type * to cursor @@ -194,6 +236,17 @@ static inline int list_empty(const struct list_head *head) !list_entry_is_head(pos, head, member); \ pos = list_next_entry(pos, member)) +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_last_entry(head, typeof(*pos), member); \ + !list_entry_is_head(pos, head, member); \ + pos = list_prev_entry(pos, member)) + /** * list_for_each_entry_safe - iterate over list of given type. Safe against removal of list entry * @pos: the type * to use as a loop cursor. From f79dc03fe68c79d388908182e68d702f7f1786bc Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:21 +0900 Subject: [PATCH 090/155] kconfig: refactor choice value calculation Handling choices has always been in a PITA in Kconfig. For example, fixes and reverts were repeated for randconfig with KCONFIG_ALLCONFIG: - 422c809f03f0 ("kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG") - 23a5dfdad22a ("Revert "kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG"") - 8357b48549e1 ("kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG") - 490f16171119 ("Revert "kconfig: fix randomising choice entries in presence of KCONFIG_ALLCONFIG"") As these commits pointed out, randconfig does not randomize choices when KCONFIG_ALLCONFIG is used. This issue still remains. [Test Case] choice prompt "choose" config A bool "A" config B bool "B" endchoice $ echo > all.config $ make KCONFIG_ALLCONFIG=1 randconfig The output is always as follows: CONFIG_A=y # CONFIG_B is not set Not only randconfig, but other all*config variants are also broken with KCONFIG_ALLCONFIG. With the same Kconfig, $ echo '# CONFIG_A is not set' > all.config $ make KCONFIG_ALLCONFIG=1 allyesconfig You will get this: CONFIG_A=y # CONFIG_B is not set This is incorrect because it does not respect all.config. The correct output should be: # CONFIG_A is not set CONFIG_B=y To handle user inputs more accurately, this commit refactors the code based on the following principles: - When a user value is given, Kconfig must set it immediately. Do not defer it by setting SYMBOL_NEED_SET_CHOICE_VALUES. - The SYMBOL_DEF_USER flag must not be cleared, unless a new config file is loaded. Kconfig must not forget user inputs. In addition, user values for choices must be managed with priority. If user inputs conflict within a choice block, the newest value wins. The values given by randconfig have lower priority than explicit user inputs. This commit implements it by using a linked list. Every time a choice block gets a new input, it is moved to the top of the list. Let me explain how it works. Let's say, we have a choice block that consists of five symbols: A, B, C, D, and E. Initially, the linked list looks like this: A(=?) --> B(=?) --> C(=?) --> D(=?) --> E(=?) Suppose randconfig is executed with the following KCONFIG_ALLCONFIG: CONFIG_C=y # CONFIG_A is not set CONFIG_D=y First, CONFIG_C=y is read. C is set to 'y' and moved to the top. C(=y) --> A(=?) --> B(=?) --> D(=?) --> E(=?) Next, '# CONFIG_A is not set' is read. A is set to 'n' and moved to the top. A(=n) --> C(=y) --> B(=?) --> D(=?) --> E(=?) Then, 'CONFIG_D=y' is read. D is set to 'y' and moved to the top. D(=y) --> A(=n) --> C(=y) --> B(=?) --> E(=?) Lastly, randconfig shuffles the order of the remaining symbols, resulting in: D(=y) --> A(=n) --> C(=y) --> B(=y) --> E(=y) or D(=y) --> A(=n) --> C(=y) --> E(=y) --> B(=y) When calculating the output, the linked list is traversed and the first visible symbol with 'y' is taken. In this case, it is D if visible. If D is hidden by 'depends on', the next node, A, is examined. Since it is already specified as 'n', it is skipped. Next, C is checked, and selected if it is visible. If C is also invisible, either B or E is chosen as a result of the randomization. If B and E are also invisible, the linked list is traversed in the reverse order, and the least prioritized 'n' symbol is chosen. It is A in this case. Now, Kconfig remembers all user values. This is a big difference from the previous implementation, where Kconfig would forget CONFIG_C=y when CONFIG_D=y appeared in the same input file. The new appaorch respects user-specified values as much as possible. Signed-off-by: Masahiro Yamada --- scripts/kconfig/conf.c | 131 +++++++++++++++--------------- scripts/kconfig/confdata.c | 54 +++---------- scripts/kconfig/expr.h | 12 ++- scripts/kconfig/lkc.h | 7 +- scripts/kconfig/menu.c | 17 +--- scripts/kconfig/parser.y | 4 + scripts/kconfig/symbol.c | 159 +++++++++++++++++++++++-------------- 7 files changed, 187 insertions(+), 197 deletions(-) diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 5dbdd9459f21..1c59998a62f7 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -114,41 +114,54 @@ static void set_randconfig_seed(void) srand(seed); } -static void randomize_choice_values(struct symbol *csym) +/** + * randomize_choice_values - randomize choice block + * + * @choice: menu entry for the choice + */ +static void randomize_choice_values(struct menu *choice) { - struct property *prop; - struct symbol *sym; - struct expr *e; - int cnt, def; - - prop = sym_get_choice_prop(csym); - - /* count entries in choice block */ - cnt = 0; - expr_list_for_each_sym(prop->expr, e, sym) - cnt++; + struct menu *menu; + int x; + int cnt = 0; /* - * find a random value and set it to yes, - * set the rest to no so we have only one set + * First, count the number of symbols to randomize. If sym_has_value() + * is true, it was specified by KCONFIG_ALLCONFIG. It needs to be + * respected. */ - def = rand() % cnt; + menu_for_each_sub_entry(menu, choice) { + struct symbol *sym = menu->sym; - cnt = 0; - expr_list_for_each_sym(prop->expr, e, sym) { - if (def == cnt++) { - sym->def[S_DEF_USER].tri = yes; - csym->def[S_DEF_USER].val = sym; - } else { - sym->def[S_DEF_USER].tri = no; - } - sym->flags |= SYMBOL_DEF_USER; - /* clear VALID to get value calculated */ - sym->flags &= ~SYMBOL_VALID; + if (sym && !sym_has_value(sym)) + cnt++; + } + + while (cnt > 0) { + x = rand() % cnt; + + menu_for_each_sub_entry(menu, choice) { + struct symbol *sym = menu->sym; + + if (sym && !sym_has_value(sym)) + x--; + + if (x < 0) { + sym->def[S_DEF_USER].tri = yes; + sym->flags |= SYMBOL_DEF_USER; + /* + * Move the selected item to the _tail_ because + * this needs to have a lower priority than the + * user input from KCONFIG_ALLCONFIG. + */ + list_move_tail(&sym->choice_link, + &choice->choice_members); + + break; + } + } + cnt--; } - csym->flags |= SYMBOL_DEF_USER; - /* clear VALID to get value calculated */ - csym->flags &= ~SYMBOL_VALID; } enum conf_def_mode { @@ -159,9 +172,9 @@ enum conf_def_mode { def_random }; -static bool conf_set_all_new_symbols(enum conf_def_mode mode) +static void conf_set_all_new_symbols(enum conf_def_mode mode) { - struct symbol *sym, *csym; + struct menu *menu; int cnt; /* * can't go as the default in switch-case below, otherwise gcc whines @@ -170,7 +183,6 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode) int pby = 50; /* probability of bool = y */ int pty = 33; /* probability of tristate = y */ int ptm = 33; /* probability of tristate = m */ - bool has_changed = false; if (mode == def_random) { int n, p[3]; @@ -217,14 +229,21 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode) } } - for_all_symbols(sym) { + menu_for_each_entry(menu) { + struct symbol *sym = menu->sym; tristate val; - if (sym_has_value(sym) || sym->flags & SYMBOL_VALID || - (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)) + if (!sym || !menu->prompt || sym_has_value(sym) || + (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) || + sym_is_choice_value(sym)) continue; - has_changed = true; + if (sym_is_choice(sym)) { + if (mode == def_random) + randomize_choice_values(menu); + continue; + } + switch (mode) { case def_yes: val = yes; @@ -251,34 +270,10 @@ static bool conf_set_all_new_symbols(enum conf_def_mode mode) continue; } sym->def[S_DEF_USER].tri = val; - - if (!(sym_is_choice(sym) && mode == def_random)) - sym->flags |= SYMBOL_DEF_USER; + sym->flags |= SYMBOL_DEF_USER; } sym_clear_all_valid(); - - if (mode != def_random) { - for_all_symbols(csym) { - if ((sym_is_choice(csym) && !sym_has_value(csym)) || - sym_is_choice_value(csym)) - csym->flags |= SYMBOL_NEED_SET_CHOICE_VALUES; - } - } - - for_all_symbols(csym) { - if (sym_has_value(csym) || !sym_is_choice(csym)) - continue; - - sym_calc_value(csym); - if (mode == def_random) - randomize_choice_values(csym); - else - set_all_choice_values(csym); - has_changed = true; - } - - return has_changed; } static void conf_rewrite_tristates(tristate old_val, tristate new_val) @@ -429,10 +424,9 @@ static void conf_choice(struct menu *menu) { struct symbol *sym, *def_sym; struct menu *child; - bool is_new; + bool is_new = false; sym = menu->sym; - is_new = !sym_has_value(sym); while (1) { int cnt, def; @@ -456,8 +450,10 @@ static void conf_choice(struct menu *menu) printf("%*c", indent, ' '); printf(" %d. %s (%s)", cnt, menu_get_prompt(child), child->sym->name); - if (!sym_has_value(child->sym)) + if (!sym_has_value(child->sym)) { + is_new = true; printf(" (NEW)"); + } printf("\n"); } printf("%*schoice", indent - 1, ""); @@ -586,9 +582,7 @@ static void check_conf(struct menu *menu) return; sym = menu->sym; - if (sym && !sym_has_value(sym) && - (sym_is_changeable(sym) || sym_is_choice(sym))) { - + if (sym && !sym_has_value(sym) && sym_is_changeable(sym)) { switch (input_mode) { case listnewconfig: if (sym->name) @@ -804,8 +798,7 @@ int main(int ac, char **av) conf_set_all_new_symbols(def_default); break; case randconfig: - /* Really nothing to do in this loop */ - while (conf_set_all_new_symbols(def_random)) ; + conf_set_all_new_symbols(def_random); break; case defconfig: conf_set_all_new_symbols(def_default); diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 1ac7fc9ad756..05823f85402a 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -382,10 +382,7 @@ load: def_flags = SYMBOL_DEF << def; for_all_symbols(sym) { - sym->flags |= SYMBOL_CHANGED; sym->flags &= ~(def_flags|SYMBOL_VALID); - if (sym_is_choice(sym)) - sym->flags |= def_flags; switch (sym->type) { case S_INT: case S_HEX: @@ -399,6 +396,8 @@ load: } while (getline_stripped(&line, &line_asize, in) != -1) { + struct menu *choice; + conf_lineno++; if (!line[0]) /* blank line */ @@ -460,15 +459,14 @@ load: if (conf_set_sym_val(sym, def, def_flags, val)) continue; - if (sym && sym_is_choice_value(sym)) { - struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); - if (sym->def[def].tri == yes) { - if (cs->def[def].tri != no) - conf_warning("override: %s changes choice state", sym->name); - cs->def[def].val = sym; - cs->def[def].tri = yes; - } - } + /* + * If this is a choice member, give it the highest priority. + * If conflicting CONFIG options are given from an input file, + * the last one wins. + */ + choice = sym_get_choice_menu(sym); + if (choice) + list_move(&sym->choice_link, &choice->choice_members); } free(line); fclose(in); @@ -514,18 +512,6 @@ int conf_read(const char *name) /* maybe print value in verbose mode... */ } - for_all_symbols(sym) { - if (sym_has_value(sym) && !sym_is_choice_value(sym)) { - /* Reset values of generates values, so they'll appear - * as new, if they should become visible, but that - * doesn't quite work if the Kconfig and the saved - * configuration disagree. - */ - if (sym->visible == no && !conf_unsaved) - sym->flags &= ~SYMBOL_DEF_USER; - } - } - if (conf_warnings || conf_unsaved) conf_set_changed(true); @@ -1146,23 +1132,3 @@ void conf_set_changed_callback(void (*fn)(bool)) { conf_changed_callback = fn; } - -void set_all_choice_values(struct symbol *csym) -{ - struct property *prop; - struct symbol *sym; - struct expr *e; - - prop = sym_get_choice_prop(csym); - - /* - * Set all non-assinged choice values to no - */ - expr_list_for_each_sym(prop->expr, e, sym) { - if (!sym_has_value(sym)) - sym->def[S_DEF_USER].tri = no; - } - csym->flags |= SYMBOL_DEF_USER; - /* clear VALID to get value calculated */ - csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES); -} diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 7c0c242318bc..7acf27a4f454 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -73,6 +73,8 @@ enum { * Represents a configuration symbol. * * Choices are represented as a special kind of symbol with null name. + * + * @choice_link: linked to menu::choice_members */ struct symbol { /* link node for the hash table */ @@ -110,6 +112,8 @@ struct symbol { /* config entries associated with this symbol */ struct list_head menus; + struct list_head choice_link; + /* SYMBOL_* flags */ int flags; @@ -133,7 +137,6 @@ struct symbol { #define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */ #define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */ #define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */ -#define SYMBOL_CHANGED 0x0400 /* ? */ #define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */ #define SYMBOL_CHECKED 0x2000 /* used during dependency checking */ #define SYMBOL_WARNED 0x8000 /* warning has been issued */ @@ -145,9 +148,6 @@ struct symbol { #define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */ #define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */ -/* choice values need to be set before calculating this symbol value */ -#define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000 - #define SYMBOL_MAXLENGTH 256 /* A property represent the config options that can be associated @@ -204,6 +204,8 @@ struct property { * for all front ends). Each symbol, menu, etc. defined in the Kconfig files * gets a node. A symbol defined in multiple locations gets one node at each * location. + * + * @choice_members: list of choice members with priority. */ struct menu { /* The next menu node at the same level */ @@ -223,6 +225,8 @@ struct menu { struct list_head link; /* link to symbol::menus */ + struct list_head choice_members; + /* * The prompt associated with the node. This holds the prompt for a * symbol as well as the text for a menu or comment, along with the diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 64dfc354dd5c..bdd37a16b040 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -40,7 +40,6 @@ void zconf_nextfile(const char *name); /* confdata.c */ extern struct gstr autoconf_cmd; const char *conf_get_configname(void); -void set_all_choice_values(struct symbol *csym); /* confdata.c and expr.c */ static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out) @@ -121,11 +120,7 @@ static inline tristate sym_get_tristate_value(struct symbol *sym) return sym->curr.tri; } - -static inline struct symbol *sym_get_choice_value(struct symbol *sym) -{ - return (struct symbol *)sym->curr.val; -} +struct symbol *sym_get_choice_value(struct symbol *sym); static inline bool sym_is_choice(struct symbol *sym) { diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index bf5dcc05350b..170a269a8d7c 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -591,7 +591,6 @@ bool menu_is_empty(struct menu *menu) bool menu_is_visible(struct menu *menu) { - struct menu *child; struct symbol *sym; tristate visible; @@ -610,21 +609,7 @@ bool menu_is_visible(struct menu *menu) } else visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); - if (visible != no) - return true; - - if (!sym || sym_get_tristate_value(menu->sym) == no) - return false; - - for (child = menu->list; child; child = child->next) { - if (menu_is_visible(child)) { - if (sym) - sym->flags |= SYMBOL_DEF_USER; - return true; - } - } - - return false; + return visible != no; } const char *menu_get_prompt(struct menu *menu) diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index 20538e1d3788..9d58544b0255 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -157,6 +157,9 @@ config_stmt: config_entry_start config_option_list current_entry->filename, current_entry->lineno); yynerrs++; } + + list_add_tail(¤t_entry->sym->choice_link, + ¤t_choice->choice_members); } printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); @@ -240,6 +243,7 @@ choice: T_CHOICE T_EOL menu_add_entry(sym); menu_add_expr(P_CHOICE, NULL, NULL); menu_set_type(S_BOOLEAN); + INIT_LIST_HEAD(¤t_entry->choice_members); printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno); }; diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 8df0a75f40b9..329c7bd314cf 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -188,7 +188,6 @@ static void sym_set_changed(struct symbol *sym) { struct menu *menu; - sym->flags |= SYMBOL_CHANGED; list_for_each_entry(menu, &sym->menus, link) menu->flags |= MENU_CHANGED; } @@ -282,36 +281,95 @@ struct symbol *sym_choice_default(struct symbol *sym) return NULL; } -static struct symbol *sym_calc_choice(struct symbol *sym) +/* + * sym_calc_choice - calculate symbol values in a choice + * + * @choice: a menu of the choice + * + * Return: a chosen symbol + */ +static struct symbol *sym_calc_choice(struct menu *choice) { - struct symbol *def_sym; - struct property *prop; - struct expr *e; - int flags; + struct symbol *res = NULL; + struct symbol *sym; + struct menu *menu; - /* first calculate all choice values' visibilities */ - flags = sym->flags; - prop = sym_get_choice_prop(sym); - expr_list_for_each_sym(prop->expr, e, def_sym) { - sym_calc_visibility(def_sym); - if (def_sym->visible != no) - flags &= def_sym->flags; + /* Traverse the list of choice members in the priority order. */ + list_for_each_entry(sym, &choice->choice_members, choice_link) { + sym_calc_visibility(sym); + if (sym->visible == no) + continue; + + /* The first visible symble with the user value 'y'. */ + if (sym_has_value(sym) && sym->def[S_DEF_USER].tri == yes) { + res = sym; + break; + } } - sym->flags &= flags | ~SYMBOL_DEF_USER; + /* + * If 'y' is not found in the user input, use the default, unless it is + * explicitly set to 'n'. + */ + if (!res) { + res = sym_choice_default(choice->sym); + if (res && sym_has_value(res) && res->def[S_DEF_USER].tri == no) + res = NULL; + } - /* is the user choice visible? */ - def_sym = sym->def[S_DEF_USER].val; - if (def_sym && def_sym->visible != no) - return def_sym; + /* Still not found. Pick up the first visible, user-unspecified symbol. */ + if (!res) { + menu_for_each_sub_entry(menu, choice) { + sym = menu->sym; - def_sym = sym_choice_default(sym); + if (!sym || sym->visible == no || sym_has_value(sym)) + continue; - if (def_sym == NULL) - /* no choice? reset tristate value */ - sym->curr.tri = no; + res = sym; + break; + } + } - return def_sym; + /* + * Still not found. Traverse the linked list in the _reverse_ order to + * pick up the least prioritized 'n'. + */ + if (!res) { + list_for_each_entry_reverse(sym, &choice->choice_members, + choice_link) { + if (sym->visible == no) + continue; + + res = sym; + break; + } + } + + menu_for_each_sub_entry(menu, choice) { + tristate val; + + sym = menu->sym; + + if (!sym || sym->visible == no) + continue; + + val = sym == res ? yes : no; + + if (sym->curr.tri != val) + sym_set_changed(sym); + + sym->curr.tri = val; + sym->flags |= SYMBOL_VALID | SYMBOL_WRITE; + } + + return res; +} + +struct symbol *sym_get_choice_value(struct symbol *sym) +{ + struct menu *menu = list_first_entry(&sym->menus, struct menu, link); + + return sym_calc_choice(menu); } static void sym_warn_unmet_dep(struct symbol *sym) @@ -347,7 +405,7 @@ void sym_calc_value(struct symbol *sym) { struct symbol_value newval, oldval; struct property *prop; - struct expr *e; + struct menu *choice_menu; if (!sym) return; @@ -355,13 +413,6 @@ void sym_calc_value(struct symbol *sym) if (sym->flags & SYMBOL_VALID) return; - if (sym_is_choice_value(sym) && - sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) { - sym->flags &= ~SYMBOL_NEED_SET_CHOICE_VALUES; - prop = sym_get_choice_prop(sym); - sym_calc_value(prop_get_symbol(prop)); - } - sym->flags |= SYMBOL_VALID; oldval = sym->curr; @@ -400,9 +451,11 @@ void sym_calc_value(struct symbol *sym) switch (sym_get_type(sym)) { case S_BOOLEAN: case S_TRISTATE: - if (sym_is_choice_value(sym) && sym->visible == yes) { - prop = sym_get_choice_prop(sym); - newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no; + choice_menu = sym_get_choice_menu(sym); + + if (choice_menu) { + sym_calc_choice(choice_menu); + newval.tri = sym->curr.tri; } else { if (sym->visible != no) { /* if the symbol is visible use the user value @@ -461,8 +514,6 @@ void sym_calc_value(struct symbol *sym) } sym->curr = newval; - if (sym_is_choice(sym) && newval.tri == yes) - sym->curr.val = sym_calc_choice(sym); sym_validate_range(sym); if (memcmp(&oldval, &sym->curr, sizeof(oldval))) { @@ -473,23 +524,8 @@ void sym_calc_value(struct symbol *sym) } } - if (sym_is_choice(sym)) { - struct symbol *choice_sym; - - prop = sym_get_choice_prop(sym); - expr_list_for_each_sym(prop->expr, e, choice_sym) { - if ((sym->flags & SYMBOL_WRITE) && - choice_sym->visible != no) - choice_sym->flags |= SYMBOL_WRITE; - if (sym->flags & SYMBOL_CHANGED) - sym_set_changed(choice_sym); - } - + if (sym_is_choice(sym)) sym->flags &= ~SYMBOL_WRITE; - } - - if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) - set_all_choice_values(sym); } void sym_clear_all_valid(void) @@ -523,15 +559,15 @@ bool sym_set_tristate_value(struct symbol *sym, tristate val) { tristate oldval = sym_get_tristate_value(sym); - if (oldval != val && !sym_tristate_within_range(sym, val)) + if (!sym_tristate_within_range(sym, val)) return false; - if (!(sym->flags & SYMBOL_DEF_USER)) { + if (!(sym->flags & SYMBOL_DEF_USER) || sym->def[S_DEF_USER].tri != val) { + sym->def[S_DEF_USER].tri = val; sym->flags |= SYMBOL_DEF_USER; sym_set_changed(sym); } - sym->def[S_DEF_USER].tri = val; if (oldval != val) sym_clear_all_valid(); @@ -565,10 +601,17 @@ void choice_set_value(struct menu *choice, struct symbol *sym) menu->sym->def[S_DEF_USER].tri = val; menu->sym->flags |= SYMBOL_DEF_USER; - } - choice->sym->def[S_DEF_USER].val = sym; - choice->sym->flags |= SYMBOL_DEF_USER; + /* + * Now, the user has explicitly enabled or disabled this symbol, + * it should be given the highest priority. We are possibly + * setting multiple symbols to 'n', where the first symbol is + * given the least prioritized 'n'. This works well when the + * choice block ends up with selecting 'n' symbol. + * (see sym_calc_choice()) + */ + list_move(&menu->sym->choice_link, &choice->choice_members); + } if (changed) sym_clear_all_valid(); From bd0db4b6ad03abb39708e3c09d9d275973052b6f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:22 +0900 Subject: [PATCH 091/155] kconfig: remove sym_get_choice_value() sym_get_choice_value(menu->sym) is equivalent to sym_calc_choice(menu). Convert all call sites of sym_get_choice_value() and then remove it. Signed-off-by: Masahiro Yamada --- scripts/kconfig/conf.c | 6 ++---- scripts/kconfig/gconf.c | 2 +- scripts/kconfig/lkc.h | 3 +-- scripts/kconfig/mconf.c | 6 +++--- scripts/kconfig/nconf.c | 6 +++--- scripts/kconfig/symbol.c | 9 +-------- 6 files changed, 11 insertions(+), 21 deletions(-) diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 1c59998a62f7..3d7d454c54da 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -422,17 +422,15 @@ help: static void conf_choice(struct menu *menu) { - struct symbol *sym, *def_sym; + struct symbol *def_sym; struct menu *child; bool is_new = false; - sym = menu->sym; - while (1) { int cnt, def; printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu)); - def_sym = sym_get_choice_value(sym); + def_sym = sym_calc_choice(menu); cnt = def = 0; line[0] = 0; for (child = menu->list; child; child = child->next) { diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 380421a5cfb2..6b50e25133e3 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1054,7 +1054,7 @@ static gchar **fill_row(struct menu *menu) if (sym_is_choice(sym)) { // parse childs for getting final value struct menu *child; - struct symbol *def_sym = sym_get_choice_value(sym); + struct symbol *def_sym = sym_calc_choice(menu); struct menu *def_menu = NULL; for (child = menu->list; child; child = child->next) { diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index bdd37a16b040..d820272a85fb 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -110,6 +110,7 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help); /* symbol.c */ void sym_clear_all_valid(void); struct symbol *sym_choice_default(struct symbol *sym); +struct symbol *sym_calc_choice(struct menu *choice); struct property *sym_get_range_prop(struct symbol *sym); const char *sym_get_string_default(struct symbol *sym); struct symbol *sym_check_deps(struct symbol *sym); @@ -120,8 +121,6 @@ static inline tristate sym_get_tristate_value(struct symbol *sym) return sym->curr.tri; } -struct symbol *sym_get_choice_value(struct symbol *sym); - static inline bool sym_is_choice(struct symbol *sym) { /* A choice is a symbol with no name */ diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index 03709eb734ae..4a0a97bb342f 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -514,7 +514,7 @@ static void build_conf(struct menu *menu) type = sym_get_type(sym); if (sym_is_choice(sym)) { - struct symbol *def_sym = sym_get_choice_value(sym); + struct symbol *def_sym = sym_calc_choice(menu); struct menu *def_menu = NULL; child_count++; @@ -600,7 +600,7 @@ static void conf_choice(struct menu *menu) struct menu *child; struct symbol *active; - active = sym_get_choice_value(menu->sym); + active = sym_calc_choice(menu); while (1) { int res; int selected; @@ -619,7 +619,7 @@ static void conf_choice(struct menu *menu) item_set_data(child); if (child->sym == active) item_set_selected(1); - if (child->sym == sym_get_choice_value(menu->sym)) + if (child->sym == sym_calc_choice(menu)) item_set_tag('X'); } dialog_clear(); diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index eb5fc3ccaf9d..1456e24969aa 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -815,7 +815,7 @@ static void build_conf(struct menu *menu) type = sym_get_type(sym); if (sym_is_choice(sym)) { - struct symbol *def_sym = sym_get_choice_value(sym); + struct symbol *def_sym = sym_calc_choice(menu); struct menu *def_menu = NULL; child_count++; @@ -1239,7 +1239,7 @@ static void conf_choice(struct menu *menu) .pattern = "", }; - active = sym_get_choice_value(menu->sym); + active = sym_calc_choice(menu); /* this is mostly duplicated from the conf() function. */ while (!global_exit) { reset_menu(); @@ -1248,7 +1248,7 @@ static void conf_choice(struct menu *menu) if (!show_all_items && !menu_is_visible(child)) continue; - if (child->sym == sym_get_choice_value(menu->sym)) + if (child->sym == sym_calc_choice(menu)) item_make(child, ':', " %s", menu_get_prompt(child)); else if (child->sym) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 329c7bd314cf..344a241e1e94 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -288,7 +288,7 @@ struct symbol *sym_choice_default(struct symbol *sym) * * Return: a chosen symbol */ -static struct symbol *sym_calc_choice(struct menu *choice) +struct symbol *sym_calc_choice(struct menu *choice) { struct symbol *res = NULL; struct symbol *sym; @@ -365,13 +365,6 @@ static struct symbol *sym_calc_choice(struct menu *choice) return res; } -struct symbol *sym_get_choice_value(struct symbol *sym) -{ - struct menu *menu = list_first_entry(&sym->menus, struct menu, link); - - return sym_calc_choice(menu); -} - static void sym_warn_unmet_dep(struct symbol *sym) { struct gstr gs = str_new(); From cca318378d6dcb38acd0ba8801b34d1a9be16028 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:23 +0900 Subject: [PATCH 092/155] kconfig: remove conf_unsaved in conf_read_simple() This variable is unnecessary. Call conf_set_changed(true) directly. Signed-off-by: Masahiro Yamada --- scripts/kconfig/confdata.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 05823f85402a..4359fbc9255b 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -477,7 +477,6 @@ load: int conf_read(const char *name) { struct symbol *sym; - int conf_unsaved = 0; conf_set_changed(false); @@ -508,11 +507,11 @@ int conf_read(const char *name) } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE)) /* no previous value and not saved */ continue; - conf_unsaved++; + conf_set_changed(true); /* maybe print value in verbose mode... */ } - if (conf_warnings || conf_unsaved) + if (conf_warnings) conf_set_changed(true); return 0; From e8fcd915e3c07a2ef90341fa307a224cfd5d865d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:24 +0900 Subject: [PATCH 093/155] kconfig: change sym_choice_default() to take the choice menu Change the argument of sym_choice_default() to ease further cleanups. Signed-off-by: Masahiro Yamada --- scripts/kconfig/confdata.c | 2 +- scripts/kconfig/lkc.h | 2 +- scripts/kconfig/symbol.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 4359fbc9255b..76193ce5a792 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -779,7 +779,7 @@ int conf_write_defconfig(const char *filename) if (choice) { struct symbol *ds; - ds = sym_choice_default(choice->sym); + ds = sym_choice_default(choice); if (sym == ds && sym_get_tristate_value(sym) == yes) continue; } diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index d820272a85fb..586a5e11f51e 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -109,7 +109,7 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help); /* symbol.c */ void sym_clear_all_valid(void); -struct symbol *sym_choice_default(struct symbol *sym); +struct symbol *sym_choice_default(struct menu *choice); struct symbol *sym_calc_choice(struct menu *choice); struct property *sym_get_range_prop(struct symbol *sym); const char *sym_get_string_default(struct symbol *sym); diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 344a241e1e94..3d68ab8e1eb4 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -255,14 +255,14 @@ static void sym_calc_visibility(struct symbol *sym) * Next locate the first visible choice value * Return NULL if none was found */ -struct symbol *sym_choice_default(struct symbol *sym) +struct symbol *sym_choice_default(struct menu *choice) { struct symbol *def_sym; struct property *prop; struct expr *e; /* any of the defaults visible? */ - for_all_defaults(sym, prop) { + for_all_defaults(choice->sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri == no) continue; @@ -272,7 +272,7 @@ struct symbol *sym_choice_default(struct symbol *sym) } /* just get the first visible value */ - prop = sym_get_choice_prop(sym); + prop = sym_get_choice_prop(choice->sym); expr_list_for_each_sym(prop->expr, e, def_sym) if (def_sym->visible != no) return def_sym; @@ -312,7 +312,7 @@ struct symbol *sym_calc_choice(struct menu *choice) * explicitly set to 'n'. */ if (!res) { - res = sym_choice_default(choice->sym); + res = sym_choice_default(choice); if (res && sym_has_value(res) && res->def[S_DEF_USER].tri == no) res = NULL; } From 6e6d0e917a9714297614b41fbbfde1625249e197 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:25 +0900 Subject: [PATCH 094/155] kconfig: use menu_list_for_each_sym() in sym_choice_default() Choices and their members are associated via the P_CHOICE property. Currently, sym_get_choice_prop() and expr_list_for_each_sym() are used to iterate on choice members. Replace them with menu_for_each_sub_entry(), which achieves the same without relying on P_CHOICE. Signed-off-by: Masahiro Yamada --- scripts/kconfig/symbol.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 3d68ab8e1eb4..56e7a76e7a77 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -257,9 +257,9 @@ static void sym_calc_visibility(struct symbol *sym) */ struct symbol *sym_choice_default(struct menu *choice) { + struct menu *menu; struct symbol *def_sym; struct property *prop; - struct expr *e; /* any of the defaults visible? */ for_all_defaults(choice->sym, prop) { @@ -272,10 +272,9 @@ struct symbol *sym_choice_default(struct menu *choice) } /* just get the first visible value */ - prop = sym_get_choice_prop(choice->sym); - expr_list_for_each_sym(prop->expr, e, def_sym) - if (def_sym->visible != no) - return def_sym; + menu_for_each_sub_entry(menu, choice) + if (menu->sym && menu->sym->visible != no) + return menu->sym; /* failed to locate any defaults */ return NULL; From 8926bc901d617f21d5ebd2eda17aeea55bbecd9a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:26 +0900 Subject: [PATCH 095/155] kconfig: remove expr_list_for_each_sym() macro All users of this macro have been converted. Remove it. Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 7acf27a4f454..1d1c4442c941 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -43,9 +43,6 @@ struct expr { #define EXPR_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) #define EXPR_NOT(dep) (2-(dep)) -#define expr_list_for_each_sym(l, e, s) \ - for (e = (l); e && (s = e->right.sym); e = e->left.expr) - struct expr_value { struct expr *expr; tristate tri; From d8f8bbcf4b94fefbf9e2f0d3a67b7190507afc48 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:27 +0900 Subject: [PATCH 096/155] kconfig: use sym_get_choice_menu() in sym_check_print_recursive() Choices and their members are associated via the P_CHOICE property. Currently, prop_get_symbol(sym_get_choice_prop()) is used to obtain the choice of the given choice member. Replace it with sym_get_choice_menu(), which retrieves the choice without relying on P_CHOICE. Signed-off-by: Masahiro Yamada --- scripts/kconfig/symbol.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 56e7a76e7a77..79f1b5e1cc9e 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -1078,12 +1078,14 @@ static void sym_check_print_recursive(struct symbol *last_sym) struct dep_stack *stack; struct symbol *sym, *next_sym; struct menu *menu = NULL; + struct menu *choice; struct property *prop; struct dep_stack cv_stack; - if (sym_is_choice_value(last_sym)) { + choice = sym_get_choice_menu(last_sym); + if (choice) { dep_stack_insert(&cv_stack, last_sym); - last_sym = prop_get_symbol(sym_get_choice_prop(last_sym)); + last_sym = choice->sym; } for (stack = check_top; stack != NULL; stack = stack->prev) From 609fc4099b3482ba774712376d56316c561432ff Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:28 +0900 Subject: [PATCH 097/155] kconfig: use sym_get_choice_menu() in sym_check_choice_deps() Choices and their members are associated via the P_CHOICE property. Currently, prop_get_symbol(sym_get_choice_prop()) is used to obtain the choice of the given choice member. Replace it with sym_get_choice_menu(), which retrieves the choice without relying on P_CHOICE. Signed-off-by: Masahiro Yamada --- scripts/kconfig/symbol.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 79f1b5e1cc9e..22c15a103371 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -1280,9 +1280,13 @@ out: if (menu->sym) menu->sym->flags &= ~SYMBOL_CHECK; - if (sym2 && sym_is_choice_value(sym2) && - prop_get_symbol(sym_get_choice_prop(sym2)) == choice) - sym2 = choice; + if (sym2) { + struct menu *choice_menu2; + + choice_menu2 = sym_get_choice_menu(sym2); + if (choice_menu2 == choice_menu) + sym2 = choice; + } dep_stack_remove(); From b139b43e9d47552b3f998fade184ed19e45d0c32 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:29 +0900 Subject: [PATCH 098/155] kconfig: use sym_get_choice_menu() in sym_check_deps() Choices and their members are associated via the P_CHOICE property. Currently, prop_get_symbol(sym_get_choice_prop()) is used to obtain the choice of the given choice member. Replace it with sym_get_choice_menu(), which retrieves the choice without relying on P_CHOICE. Signed-off-by: Masahiro Yamada --- scripts/kconfig/symbol.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 22c15a103371..b50911bcb08d 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -1295,8 +1295,8 @@ out: struct symbol *sym_check_deps(struct symbol *sym) { + struct menu *choice; struct symbol *sym2; - struct property *prop; if (sym->flags & SYMBOL_CHECK) { sym_check_print_recursive(sym); @@ -1305,13 +1305,13 @@ struct symbol *sym_check_deps(struct symbol *sym) if (sym->flags & SYMBOL_CHECKED) return NULL; - if (sym_is_choice_value(sym)) { + choice = sym_get_choice_menu(sym); + if (choice) { struct dep_stack stack; /* for choice groups start the check with main choice symbol */ dep_stack_insert(&stack, sym); - prop = sym_get_choice_prop(sym); - sym2 = sym_check_deps(prop_get_symbol(prop)); + sym2 = sym_check_deps(choice->sym); dep_stack_remove(); } else if (sym_is_choice(sym)) { sym2 = sym_check_choice_deps(sym); From ca4c74ba306e28cebf53908e69b773dcbb700cbc Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:30 +0900 Subject: [PATCH 099/155] kconfig: remove P_CHOICE property P_CHOICE is a pseudo property used to link a choice with its members. There is no more code relying on this, except for some debug code. Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.h | 4 +--- scripts/kconfig/lkc_proto.h | 1 - scripts/kconfig/menu.c | 9 +-------- scripts/kconfig/parser.y | 4 ---- scripts/kconfig/qconf.cc | 8 -------- scripts/kconfig/symbol.c | 14 +------------- 6 files changed, 3 insertions(+), 37 deletions(-) diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 1d1c4442c941..58fd4c8c3762 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -167,7 +167,6 @@ enum prop_type { P_COMMENT, /* text associated with a comment */ P_MENU, /* prompt associated with a menu or menuconfig symbol */ P_DEFAULT, /* default y */ - P_CHOICE, /* choice value */ P_SELECT, /* select BAR */ P_IMPLY, /* imply BAR */ P_RANGE, /* range 7..100 (for a symbol) */ @@ -181,7 +180,7 @@ struct property { struct expr_value visible; struct expr *expr; /* the optional conditional part of the property */ struct menu *menu; /* the menu the property are associated with - * valid for: P_SELECT, P_RANGE, P_CHOICE, + * valid for: P_SELECT, P_RANGE, * P_PROMPT, P_DEFAULT, P_MENU, P_COMMENT */ const char *filename; /* what file was this property defined */ int lineno; /* what lineno was this property defined */ @@ -191,7 +190,6 @@ struct property { for (st = sym->prop; st; st = st->next) \ if (st->type == (tok)) #define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) -#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE) #define for_all_prompts(sym, st) \ for (st = sym->prop; st; st = st->next) \ if (st->text) diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index 1221709efac1..49cc649d2810 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -34,7 +34,6 @@ bool sym_string_valid(struct symbol *sym, const char *newval); bool sym_string_within_range(struct symbol *sym, const char *str); bool sym_set_string_value(struct symbol *sym, const char *newval); bool sym_is_changeable(struct symbol *sym); -struct property * sym_get_choice_prop(struct symbol *sym); struct menu *sym_get_choice_menu(struct symbol *sym); const char * sym_get_string_value(struct symbol *sym); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 170a269a8d7c..23c95e54660d 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -306,7 +306,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) struct menu *menu, *last_menu; struct symbol *sym; struct property *prop; - struct expr *parentdep, *basedep, *dep, *dep2, **ep; + struct expr *parentdep, *basedep, *dep, *dep2; sym = parent->sym; if (parent->list) { @@ -490,14 +490,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) for (menu = parent->list; menu; menu = menu->next) { if (sym && sym_is_choice(sym) && menu->sym && !sym_is_choice_value(menu->sym)) { - current_entry = menu; menu->sym->flags |= SYMBOL_CHOICEVAL; - menu_add_symbol(P_CHOICE, sym, NULL); - prop = sym_get_choice_prop(sym); - for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) - ; - *ep = expr_alloc_one(E_LIST, NULL); - (*ep)->right.sym = menu->sym; } /* diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index 9d58544b0255..745c82ee15d0 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -241,7 +241,6 @@ choice: T_CHOICE T_EOL struct symbol *sym = sym_lookup(NULL, 0); menu_add_entry(sym); - menu_add_expr(P_CHOICE, NULL, NULL); menu_set_type(S_BOOLEAN); INIT_LIST_HEAD(¤t_entry->choice_members); @@ -696,9 +695,6 @@ static void print_symbol(FILE *out, struct menu *menu) } fputc('\n', out); break; - case P_CHOICE: - fputs(" #choice value\n", out); - break; case P_SELECT: fputs( " select ", out); expr_fprint(prop->expr, out); diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 30346e294d1a..7d239c032b3d 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -1101,14 +1101,6 @@ QString ConfigInfoView::debug_info(struct symbol *sym) &stream, E_NONE); stream << "
"; break; - case P_CHOICE: - if (sym_is_choice(sym)) { - stream << "choice: "; - expr_print(prop->expr, expr_print_help, - &stream, E_NONE); - stream << "
"; - } - break; default: stream << "unknown property: "; stream << prop_get_type_name(prop->type); diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index b50911bcb08d..cf682a8a3f1e 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -68,15 +68,6 @@ const char *sym_type_name(enum symbol_type type) return "???"; } -struct property *sym_get_choice_prop(struct symbol *sym) -{ - struct property *prop; - - for_all_choices(sym, prop) - return prop; - return NULL; -} - /** * sym_get_choice_menu - get the parent choice menu if present * @@ -1225,8 +1216,7 @@ static struct symbol *sym_check_sym_deps(struct symbol *sym) stack.expr = NULL; for (prop = sym->prop; prop; prop = prop->next) { - if (prop->type == P_CHOICE || prop->type == P_SELECT || - prop->type == P_IMPLY) + if (prop->type == P_SELECT || prop->type == P_IMPLY) continue; stack.prop = prop; sym2 = sym_check_expr_deps(prop->visible.expr); @@ -1343,8 +1333,6 @@ const char *prop_get_type_name(enum prop_type type) return "menu"; case P_DEFAULT: return "default"; - case P_CHOICE: - return "choice"; case P_SELECT: return "select"; case P_IMPLY: From 7c9bb07a6e9439fb7bdeee15eb188fe127a0d0e0 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 18 Jun 2024 19:35:31 +0900 Subject: [PATCH 100/155] kconfig: remove E_LIST expression type E_LIST was preveously used to form an expression tree consisting of choice members. It is no longer used. Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.c | 15 --------------- scripts/kconfig/expr.h | 2 +- scripts/kconfig/symbol.c | 3 +-- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index bea82d5cac83..6d4b5a5a1e62 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -90,7 +90,6 @@ struct expr *expr_copy(const struct expr *org) break; case E_AND: case E_OR: - case E_LIST: e->left.expr = expr_copy(org->left.expr); e->right.expr = expr_copy(org->right.expr); break; @@ -286,7 +285,6 @@ int expr_eq(struct expr *e1, struct expr *e2) expr_free(e2); trans_count = old_count; return res; - case E_LIST: case E_RANGE: case E_NONE: /* panic */; @@ -676,7 +674,6 @@ struct expr *expr_transform(struct expr *e) case E_LTH: case E_UNEQUAL: case E_SYMBOL: - case E_LIST: break; default: e->left.expr = expr_transform(e->left.expr); @@ -947,7 +944,6 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb break; case E_SYMBOL: return expr_alloc_comp(type, e->left.sym, sym); - case E_LIST: case E_RANGE: case E_NONE: /* panic */; @@ -1097,10 +1093,6 @@ static int expr_compare_type(enum expr_type t1, enum expr_type t2) if (t2 == E_OR) return 1; /* fallthrough */ - case E_OR: - if (t2 == E_LIST) - return 1; - /* fallthrough */ default: break; } @@ -1173,13 +1165,6 @@ void expr_print(struct expr *e, fn(data, NULL, " && "); expr_print(e->right.expr, fn, data, E_AND); break; - case E_LIST: - fn(data, e->right.sym, e->right.sym->name); - if (e->left.expr) { - fn(data, NULL, " ^ "); - expr_print(e->left.expr, fn, data, E_LIST); - } - break; case E_RANGE: fn(data, NULL, "["); fn(data, e->left.sym, e->left.sym->name); diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 58fd4c8c3762..8849a243b5e7 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -26,7 +26,7 @@ typedef enum tristate { enum expr_type { E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_LTH, E_LEQ, E_GTH, E_GEQ, - E_LIST, E_SYMBOL, E_RANGE + E_SYMBOL, E_RANGE }; union expr_data { diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index cf682a8a3f1e..e5441378c4b0 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -1316,8 +1316,7 @@ struct symbol *sym_check_deps(struct symbol *sym) struct symbol *prop_get_symbol(struct property *prop) { - if (prop->expr && (prop->expr->type == E_SYMBOL || - prop->expr->type == E_LIST)) + if (prop->expr && prop->expr->type == E_SYMBOL) return prop->expr->left.sym; return NULL; } From b9d73218d78778effd2924664ed93d78ff6949de Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 27 Jun 2024 03:22:00 +0900 Subject: [PATCH 101/155] treewide: change conditional prompt for choices to 'depends on' While Documentation/kbuild/kconfig-language.rst provides a brief explanation, there are recurring confusions regarding the usage of a prompt followed by 'if '. This conditional controls _only_ the prompt. A typical usage is as follows: menuconfig BLOCK bool "Enable the block layer" if EXPERT default y When EXPERT=n, the prompt is hidden, but this config entry is still active, and BLOCK is set to its default value 'y'. This is reasonable because you are likely want to enable the block device support. When EXPERT=y, the prompt is shown, allowing you to toggle BLOCK. Please note that it is different from 'depends on EXPERT', which would enable and disable the entire config entry. However, this conditional prompt has never worked in a choice block. The following two work in the same way: when EXPERT is disabled, the choice block is entirely disabled. [Test Code 1] choice prompt "choose" if EXPERT config A bool "A" config B bool "B" endchoice [Test Code 2] choice prompt "choose" depends on EXPERT config A bool "A" config B bool "B" endchoice I believe the first case should hide only the prompt, producing the default: CONFIG_A=y # CONFIG_B is not set The next commit will change (fix) the behavior of the conditional prompt in choice blocks. I see several choice blocks wrongly using a conditional prompt, where 'depends on' makes more sense. To preserve the current behavior, this commit converts such misuses. I did not touch the following entry in arch/x86/Kconfig: choice prompt "Memory split" if EXPERT default VMSPLIT_3G This is truly the correct use of the conditional prompt; when EXPERT=n, this choice block should silently select the reasonable VMSPLIT_3G, although the resulting PAGE_OFFSET will not be affected anyway. Presumably, the one in fs/jffs2/Kconfig is also correct, but I converted it to 'depends on' to avoid any potential behavioral change. Signed-off-by: Masahiro Yamada --- arch/arm/Kconfig | 6 ++++-- arch/arm64/Kconfig | 3 ++- arch/mips/Kconfig | 6 ++++-- arch/powerpc/Kconfig | 3 ++- arch/riscv/Kconfig | 3 ++- fs/jffs2/Kconfig | 3 ++- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ee5115252aac..a5bf65b06c53 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1482,7 +1482,8 @@ config ARM_ATAG_DTB_COMPAT from the ATAG list and store it at run time into the appended DTB. choice - prompt "Kernel command line type" if ARM_ATAG_DTB_COMPAT + prompt "Kernel command line type" + depends on ARM_ATAG_DTB_COMPAT default ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER config ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER @@ -1511,7 +1512,8 @@ config CMDLINE memory size and the root device (e.g., mem=64M root=/dev/nfs). choice - prompt "Kernel command line type" if CMDLINE != "" + prompt "Kernel command line type" + depends on CMDLINE != "" default CMDLINE_FROM_BOOTLOADER config CMDLINE_FROM_BOOTLOADER diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 5d91259ee7b5..c87d16b12e9b 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2302,7 +2302,8 @@ config CMDLINE root device (e.g. root=/dev/nfs). choice - prompt "Kernel command line type" if CMDLINE != "" + prompt "Kernel command line type" + depends on CMDLINE != "" default CMDLINE_FROM_BOOTLOADER help Choose how the kernel will handle the provided default kernel diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index f1aa1bf11166..8cbc23f0c1a7 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -2924,7 +2924,8 @@ config BUILTIN_DTB bool choice - prompt "Kernel appended dtb support" if USE_OF + prompt "Kernel appended dtb support" + depends on USE_OF default MIPS_NO_APPENDED_DTB config MIPS_NO_APPENDED_DTB @@ -2965,7 +2966,8 @@ choice endchoice choice - prompt "Kernel command line type" if !CMDLINE_OVERRIDE + prompt "Kernel command line type" + depends on !CMDLINE_OVERRIDE default MIPS_CMDLINE_FROM_DTB if USE_OF && !ATH79 && !MACH_INGENIC && \ !MACH_LOONGSON64 && !MIPS_MALTA && \ !CAVIUM_OCTEON_SOC diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index c88c6d46a5bc..68e35b33e123 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -965,7 +965,8 @@ config CMDLINE most cases you will need to specify the root device here. choice - prompt "Kernel command line type" if CMDLINE != "" + prompt "Kernel command line type" + depends on CMDLINE != "" default CMDLINE_FROM_BOOTLOADER config CMDLINE_FROM_BOOTLOADER diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 0525ee2d63c7..48b7faf62d0b 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -914,7 +914,8 @@ config CMDLINE line here and choose how the kernel should use it later on. choice - prompt "Built-in command line usage" if CMDLINE != "" + prompt "Built-in command line usage" + depends on CMDLINE != "" default CMDLINE_FALLBACK help Choose how the kernel will handle the provided built-in command diff --git a/fs/jffs2/Kconfig b/fs/jffs2/Kconfig index 7c96bc107218..560187d61562 100644 --- a/fs/jffs2/Kconfig +++ b/fs/jffs2/Kconfig @@ -151,8 +151,9 @@ config JFFS2_RUBIN RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. choice - prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS + prompt "JFFS2 default compression mode" default JFFS2_CMODE_PRIORITY + depends on JFFS2_COMPRESSION_OPTIONS depends on JFFS2_FS help You can set here the default compression mode of JFFS2 from From d533828ef3cafc2178dda44851d7cd8875781d55 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 27 Jun 2024 03:22:01 +0900 Subject: [PATCH 102/155] kconfig: fix conditional prompt behavior for choice When a prompt is followed by "if ", the symbol is configurable when the if-conditional evaluates to true. A typical usage is as follows: menuconfig BLOCK bool "Enable the block layer" if EXPERT default y When EXPERT=n, the prompt is hidden, but this config entry is still active, and BLOCK is set to its default value 'y'. When EXPERT=y, the prompt is shown, making BLOCK a user-configurable option. This usage is common throughout the kernel tree, but it has never worked within a choice block. [Test Code] config EXPERT bool "Allow expert users to modify more options" choice prompt "Choose" if EXPERT config A bool "A" config B bool "B" endchoice [Result] # CONFIG_EXPERT is not set When the prompt is hidden, the choice block should produce the default without asking for the user's preference. Hence, the output should be: # CONFIG_EXPERT is not set CONFIG_A=y # CONFIG_B is not set Removing unnecessary hacks fixes the issue. This commit also changes the behavior of 'select' by choice members. [Test Code 2] config MODULES def_bool y modules config DEP def_tristate m if DEP choice prompt "choose" config A bool "A" select C endchoice config B def_bool y select D endif config C tristate config D tristate The current output is as follows: CONFIG_MODULES=y CONFIG_DEP=m CONFIG_A=y CONFIG_B=y CONFIG_C=y CONFIG_D=m With this commit, the output will be changed as follows: CONFIG_MODULES=y CONFIG_DEP=m CONFIG_A=y CONFIG_B=y CONFIG_C=m CONFIG_D=m CONFIG_C will be changed to 'm' because 'select C' will inherit the dependency on DEP, which is 'm'. This change is aligned with the behavior of 'select' outside a choice block; 'select D' depends on DEP, therefore D is selected by (B && DEP). Note: With this commit, allmodconfig will set CONFIG_USB_ROLE_SWITCH to 'm' instead of 'y'. I did not see any build regression with this change. Signed-off-by: Masahiro Yamada --- scripts/kconfig/menu.c | 38 +++----------------------------------- scripts/kconfig/symbol.c | 2 +- 2 files changed, 4 insertions(+), 36 deletions(-) diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 23c95e54660d..b1fbaf2ff792 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -306,7 +306,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) struct menu *menu, *last_menu; struct symbol *sym; struct property *prop; - struct expr *parentdep, *basedep, *dep, *dep2; + struct expr *basedep, *dep, *dep2; sym = parent->sym; if (parent->list) { @@ -315,24 +315,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) * and propagate parent dependencies before moving on. */ - bool is_choice = false; - - if (sym && sym_is_choice(sym)) - is_choice = true; - - if (is_choice) { - /* - * Use the choice itself as the parent dependency of - * the contained items. This turns the mode of the - * choice into an upper bound on the visibility of the - * choice value symbols. - */ - parentdep = expr_alloc_symbol(sym); - } else { - /* Menu node for 'menu', 'if' */ - parentdep = parent->dep; - } - /* For each child menu node... */ for (menu = parent->list; menu; menu = menu->next) { /* @@ -341,7 +323,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) */ basedep = rewrite_m(menu->dep); basedep = expr_transform(basedep); - basedep = expr_alloc_and(expr_copy(parentdep), basedep); + basedep = expr_alloc_and(expr_copy(parent->dep), basedep); basedep = expr_eliminate_dups(basedep); menu->dep = basedep; @@ -405,15 +387,12 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) } } - if (is_choice) - expr_free(parentdep); - /* * Recursively process children in the same fashion before * moving on */ for (menu = parent->list; menu; menu = menu->next) - _menu_finalize(menu, is_choice); + _menu_finalize(menu, sym && sym_is_choice(sym)); } else if (!inside_choice && sym) { /* * Automatic submenu creation. If sym is a symbol and A, B, C, @@ -541,17 +520,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) sym_check_prop(sym); sym->flags |= SYMBOL_WARNED; } - - /* - * For choices, add a reverse dependency (corresponding to a select) of - * ' && y'. This prevents the user from setting the choice - * mode to 'n' when the choice is visible. - */ - if (sym && sym_is_choice(sym) && parent->prompt) { - sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, - expr_alloc_and(parent->prompt->visible.expr, - expr_alloc_symbol(&symbol_yes))); - } } void menu_finalize(void) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index e5441378c4b0..1cb8b6a22c5a 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -868,7 +868,7 @@ const char *sym_get_string_value(struct symbol *sym) bool sym_is_changeable(struct symbol *sym) { - return sym->visible > sym->rev_dep.tri; + return !sym_is_choice(sym) && sym->visible > sym->rev_dep.tri; } HASHTABLE_DEFINE(sym_hashtable, SYMBOL_HASHSIZE); From 1a7d0ea83e620fd8d7b3ced00a1c31f64cb70730 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 27 Jun 2024 03:22:02 +0900 Subject: [PATCH 103/155] kconfig: improve error message for dependency between choice members A choice member must not depend on another member within the same choice block. Kconfig detects this, but the error message is not sensible. [Test Code] choice prompt "choose" config A bool "A" depends on B config B bool "B" endchoice [Result] Kconfig:1:error: recursive dependency detected! Kconfig:1: choice contains symbol A Kconfig:4: symbol A is part of choice B Kconfig:8: symbol B is part of choice For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" The phrase "part of choice B" is weird because B is not a choice block, but a choice member. To determine whether the current symbol is a part of a choice block, sym_is_choice(next_sym) must be checked. This commit improves the error message to: Kconfig:1:error: recursive dependency detected! Kconfig:1: choice contains symbol A Kconfig:4: symbol A symbol is visible depending on B Kconfig:8: symbol B is part of choice For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" Signed-off-by: Masahiro Yamada --- scripts/kconfig/symbol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 1cb8b6a22c5a..0c4b2894ac4e 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -1111,7 +1111,7 @@ static void sym_check_print_recursive(struct symbol *last_sym) menu->filename, menu->lineno, sym->name ? sym->name : "", next_sym->name ? next_sym->name : ""); - } else if (sym_is_choice_value(sym)) { + } else if (sym_is_choice(next_sym)) { fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n", menu->filename, menu->lineno, sym->name ? sym->name : "", From d67624d814ae40a655981992b0f0d652e6f591b8 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 27 Jun 2024 03:22:03 +0900 Subject: [PATCH 104/155] kconfig: improve error message for recursive dependency in choice Kconfig detects recursive dependencies in a choice block, but the error message is unclear. [Test Code] choice prompt "choose" depends on A config A bool "A" config B bool "B" endchoice [Result] Kconfig:1:error: recursive dependency detected! Kconfig:1: choice contains symbol A Kconfig:5: symbol A is part of choice For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" The phrase "contains symbol A" does not accurately describe the problem. The issue is that the choice depends on A, which is a member of itself. The first if-block does not print a sensible message. Remove it. This commit improves the error message to: Kconfig:1:error: recursive dependency detected! Kconfig:1: symbol symbol is visible depending on A Kconfig:5: symbol A is part of choice For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" Signed-off-by: Masahiro Yamada --- scripts/kconfig/symbol.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 0c4b2894ac4e..787f0667836b 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -1106,12 +1106,7 @@ static void sym_check_print_recursive(struct symbol *last_sym) fprintf(stderr, "%s:%d:error: recursive dependency detected!\n", prop->filename, prop->lineno); - if (sym_is_choice(sym)) { - fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n", - menu->filename, menu->lineno, - sym->name ? sym->name : "", - next_sym->name ? next_sym->name : ""); - } else if (sym_is_choice(next_sym)) { + if (sym_is_choice(next_sym)) { fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n", menu->filename, menu->lineno, sym->name ? sym->name : "", From d5afb4824f142205900aa4a3a133b5dd68720e67 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 27 Jun 2024 03:22:04 +0900 Subject: [PATCH 105/155] kconfig: refactor error messages in sym_check_print_recursive() Improve the error messages and clean up redundant code. [1] remove redundant next_sym->name checks If 'next_sym' is a choice, the first 'if' block is executed. In the subsequent 'else if' blocks, 'next_sym" is not a choice, hence next_sym->name is not NULL. [2] remove redundant sym->name checks A choice is never selected or implied by anyone because it has no name (it is syntactically impossible). If it is, sym->name is not NULL. [3] Show the location of choice instead of "" "part of choice " does not convey useful information. Since a choice has no name, it is more informative to display the file name and line number. Signed-off-by: Masahiro Yamada --- scripts/kconfig/symbol.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 787f0667836b..c05d188a1857 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -1107,37 +1107,37 @@ static void sym_check_print_recursive(struct symbol *last_sym) prop->filename, prop->lineno); if (sym_is_choice(next_sym)) { - fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n", + choice = list_first_entry(&next_sym->menus, struct menu, link); + + fprintf(stderr, "%s:%d:\tsymbol %s is part of choice block at %s:%d\n", menu->filename, menu->lineno, sym->name ? sym->name : "", - next_sym->name ? next_sym->name : ""); + choice->filename, choice->lineno); } else if (stack->expr == &sym->dir_dep.expr) { fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n", prop->filename, prop->lineno, sym->name ? sym->name : "", - next_sym->name ? next_sym->name : ""); + next_sym->name); } else if (stack->expr == &sym->rev_dep.expr) { fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n", prop->filename, prop->lineno, - sym->name ? sym->name : "", - next_sym->name ? next_sym->name : ""); + sym->name, next_sym->name); } else if (stack->expr == &sym->implied.expr) { fprintf(stderr, "%s:%d:\tsymbol %s is implied by %s\n", prop->filename, prop->lineno, - sym->name ? sym->name : "", - next_sym->name ? next_sym->name : ""); + sym->name, next_sym->name); } else if (stack->expr) { fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n", prop->filename, prop->lineno, sym->name ? sym->name : "", prop_get_type_name(prop->type), - next_sym->name ? next_sym->name : ""); + next_sym->name); } else { fprintf(stderr, "%s:%d:\tsymbol %s %s is visible depending on %s\n", prop->filename, prop->lineno, sym->name ? sym->name : "", prop_get_type_name(prop->type), - next_sym->name ? next_sym->name : ""); + next_sym->name); } } From 6276761955817fd8259093a6e6a9ad88ab466b21 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 3 Jul 2024 03:02:40 +0900 Subject: [PATCH 106/155] kbuild: deb-pkg: remove support for EMAIL environment variable Commit edec611db047 ("kbuild, deb-pkg: improve maintainer identification") added the EMAIL and NAME environment variables. Commit d5940c60e057 ("kbuild: deb-pkg improve maintainer address generation") removed support for NAME, but kept support for EMAIL. The EMAIL and NAME environment variables are supported by some tools (see 'man debchange'), but not by all. We should support both of them, or neither of them. We should not stop halfway. Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier --- scripts/package/mkdebian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian index b9a5b789c655..589f92b88c42 100755 --- a/scripts/package/mkdebian +++ b/scripts/package/mkdebian @@ -125,7 +125,7 @@ gen_source () rm -rf debian mkdir debian -email=${DEBEMAIL-$EMAIL} +email=${DEBEMAIL} # use email string directly if it contains if echo "${email}" | grep -q '<.*>'; then From c52090805382387f60c8e4cf8d613d0cf77ee1e9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 3 Jul 2024 03:02:41 +0900 Subject: [PATCH 107/155] kbuild: deb-pkg: remove support for "name " form for DEBEMAIL Commit d5940c60e057 ("kbuild: deb-pkg improve maintainer address generation") supported the "name " form for DEBEMAIL, with behavior slightly different from devscripts. In Kbuild, if DEBEMAIL is given in the form "name ", it is used as-is, and DEBFULLNAME is ignored. In contrast, debchange takes the name from DEBFULLNAME (or NAME) if set, as described in 'man debchange': If this variable has the form "name ", then the maintainer name will also be taken from here if neither DEBFULLNAME nor NAME is set. This commit removes support for the "name form for DEBEMAIL, as the current behavior is already different from debchange, and the Debian manual suggests setting the email address and name separately in DEBEMAIL and DEBFULLNAME. [1] If there are any complaints about this removal, we can re-add it, with better alignment with the debchange implementation. [2] [1]: https://www.debian.org/doc/manuals/debmake-doc/ch03.en.html#email-setup [2]: https://salsa.debian.org/debian/devscripts/-/blob/v2.23.7/scripts/debchange.pl#L802 Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier --- scripts/package/mkdebian | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian index 589f92b88c42..83c6636fadb8 100755 --- a/scripts/package/mkdebian +++ b/scripts/package/mkdebian @@ -125,21 +125,15 @@ gen_source () rm -rf debian mkdir debian -email=${DEBEMAIL} - -# use email string directly if it contains -if echo "${email}" | grep -q '<.*>'; then - maintainer=${email} +user=${KBUILD_BUILD_USER-$(id -nu)} +name=${DEBFULLNAME-${user}} +if [ "${DEBEMAIL:+set}" ]; then + email=${DEBEMAIL} else - # or construct the maintainer string - user=${KBUILD_BUILD_USER-$(id -nu)} - name=${DEBFULLNAME-${user}} - if [ -z "${email}" ]; then - buildhost=${KBUILD_BUILD_HOST-$(hostname -f 2>/dev/null || hostname)} - email="${user}@${buildhost}" - fi - maintainer="${name} <${email}>" + buildhost=${KBUILD_BUILD_HOST-$(hostname -f 2>/dev/null || hostname)} + email="${user}@${buildhost}" fi +maintainer="${name} <${email}>" if [ "$1" = --need-source ]; then gen_source From c0414419758b2521c2038847960af258c678db8d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 3 Jul 2024 03:02:42 +0900 Subject: [PATCH 108/155] kbuild: package: add -e and -u options to some shell scripts Set -e to make these scripts fail on the first error. Set -u because these scripts are invoked by Makefile, and do not work properly without necessary variables defined. I tweaked mkdebian to cope with optional environment variables. Remove the explicit "test -n ..." from install-extmod-build. Both options are described in POSIX. [1] [1]: https://pubs.opengroup.org/onlinepubs/009604499/utilities/set.html Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier --- scripts/package/builddeb | 2 +- scripts/package/buildtar | 2 +- scripts/package/gen-diff-patch | 2 ++ scripts/package/install-extmod-build | 5 +---- scripts/package/mkdebian | 22 +++++++++++++++------- scripts/package/mkspec | 2 ++ 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/scripts/package/builddeb b/scripts/package/builddeb index e797ad360f7a..c1757db6aa8a 100755 --- a/scripts/package/builddeb +++ b/scripts/package/builddeb @@ -10,7 +10,7 @@ # specified in KDEB_HOOKDIR) that will be called on package install and # removal. -set -e +set -eu is_enabled() { grep -q "^$1=y" include/config/auto.conf diff --git a/scripts/package/buildtar b/scripts/package/buildtar index eb67787f8673..cc87a473c01f 100755 --- a/scripts/package/buildtar +++ b/scripts/package/buildtar @@ -11,7 +11,7 @@ # Wichert Akkerman . # -set -e +set -eu # # Some variables and settings used throughout the script diff --git a/scripts/package/gen-diff-patch b/scripts/package/gen-diff-patch index 8a98b7bb78a0..f272f7770ea3 100755 --- a/scripts/package/gen-diff-patch +++ b/scripts/package/gen-diff-patch @@ -1,6 +1,8 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-only +set -eu + diff_patch=$1 mkdir -p "$(dirname "${diff_patch}")" diff --git a/scripts/package/install-extmod-build b/scripts/package/install-extmod-build index 76e0765dfcd6..8cc9e13403ae 100755 --- a/scripts/package/install-extmod-build +++ b/scripts/package/install-extmod-build @@ -1,13 +1,10 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-only -set -e +set -eu destdir=${1} -test -n "${srctree}" -test -n "${SRCARCH}" - is_enabled() { grep -q "^$1=y" include/config/auto.conf } diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian index 83c6636fadb8..f962d303502b 100755 --- a/scripts/package/mkdebian +++ b/scripts/package/mkdebian @@ -4,7 +4,7 @@ # # Simple script to generate a debian/ directory for a Linux kernel. -set -e +set -eu is_enabled() { grep -q "^$1=y" include/config/auto.conf @@ -19,7 +19,7 @@ if_enabled_echo() { } set_debarch() { - if [ -n "$KBUILD_DEBARCH" ] ; then + if [ "${KBUILD_DEBARCH:+set}" ]; then debarch="$KBUILD_DEBARCH" return fi @@ -135,13 +135,21 @@ else fi maintainer="${name} <${email}>" -if [ "$1" = --need-source ]; then - gen_source -fi +while [ $# -gt 0 ]; do + case "$1" in + --need-source) + gen_source + shift + ;; + *) + break + ;; + esac +done # Some variables and settings used throughout the script version=$KERNELRELEASE -if [ -n "$KDEB_PKGVERSION" ]; then +if [ "${KDEB_PKGVERSION:+set}" ]; then packageversion=$KDEB_PKGVERSION else packageversion=$(${srctree}/scripts/setlocalversion --no-local ${srctree})-$($srctree/scripts/build-version) @@ -158,7 +166,7 @@ debarch= set_debarch # Try to determine distribution -if [ -n "$KDEB_CHANGELOG_DIST" ]; then +if [ "${KDEB_CHANGELOG_DIST:+set}" ]; then distribution=$KDEB_CHANGELOG_DIST # In some cases lsb_release returns the codename as n/a, which breaks dpkg-parsechangelog elif distribution=$(lsb_release -cs 2>/dev/null) && [ -n "$distribution" ] && [ "$distribution" != "n/a" ]; then diff --git a/scripts/package/mkspec b/scripts/package/mkspec index cffc2567bef2..77d25dda37e3 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -9,6 +9,8 @@ # Patched for non-x86 by Opencon (L) 2002 # +set -eu + output=$1 mkdir -p "$(dirname "${output}")" From 712aba5543b88996bc4682086471076fbf048927 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 4 Jul 2024 22:13:58 +0900 Subject: [PATCH 109/155] kbuild: avoid build error when single DTB is turned into composite DTB As commit afa974b77128 ("kbuild: add real-prereqs shorthand for $(filter-out FORCE,$^)") explained, $(real-prereqs) is not just a list of objects when linking a multi-object module. If a single-object module is turned into a multi-object module, $^ (and therefore $(real-prereqs) as well) contains header files recorded in the *.cmd file. Such headers must be filtered out. Now that a DTB can be built either from a single source or multiple source files, the same issue can occur. Consider the following scenario: First, foo.dtb is implemented as a single-blob device tree. The code looks something like this: [Sample Code 1] Makefile: dtb-y += foo.dtb foo.dts: #include /dts-v1/; / { }; When it is compiled, .foo.dtb.cmd records that foo.dtb depends on scripts/dtc/include-prefixes/dt-bindings/gpio/gpio.h. Later, foo.dtb is split into a base and an overlay. The code looks something like this: [Sample Code 2] Makefile: dtb-y += foo.dtb foo-dtbs := foo-base.dtb foo-addon.dtbo foo-base.dts: #include /dts-v1/; / { }; foo-addon.dtso: /dts-v1/; /plugin/; / { }; If you rebuild foo.dtb without 'make clean', you will get this error: Overlay 'scripts/dtc/include-prefixes/dt-bindings/gpio/gpio.h' is incomplete $(real-prereqs) contains not only foo-base.dtb and foo-addon.dtbo but also scripts/dtc/include-prefixes/dt-bindings/gpio/gpio.h, which is passed to scripts/dtc/fdtoverlay. Fixes: 15d16d6dadf6 ("kbuild: Add generic rule to apply fdtoverlay") Signed-off-by: Masahiro Yamada --- scripts/Makefile.lib | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index d78b5d38beaa..636119dc4403 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -407,8 +407,12 @@ cmd_dtc = $(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; -d $(depfile).dtc.tmp $(dtc-tmp) ; \ cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) +# NOTE: +# Do not replace $(filter %.dtb %.dtbo, $^) with $(real-prereqs). When a single +# DTB is turned into a multi-blob DTB, $^ will contain header file dependencies +# recorded in the .*.cmd file. quiet_cmd_fdtoverlay = DTOVL $@ - cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(real-prereqs) + cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(filter %.dtb %.dtbo, $^) $(multi-dtb-y): FORCE $(call if_changed,fdtoverlay) From 5f99665ee8f4335f334a5292b6d5b41a577fc2c0 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 4 Jul 2024 22:47:55 +0900 Subject: [PATCH 110/155] kbuild: raise the minimum GNU Make requirement to 4.0 RHEL/CentOS 7, popular distributions that install GNU Make 3.82, reached EOM/EOL on June 30, 2024. While you may get extended support, it is a good time to raise the minimum GNU Make version. The new requirement, GNU Make 4.0, was released in October, 2013. I did not touch the Makefiles under tools/ because I do not know the requirements for building tools. I do not find any GNU Make version checks under tools/. Signed-off-by: Masahiro Yamada Reviewed-by: Nathan Chancellor --- Documentation/process/changes.rst | 4 ++-- Makefile | 22 +++------------------- scripts/Kbuild.include | 2 +- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst index 5685d7bfe4d0..415ac8eeb46c 100644 --- a/Documentation/process/changes.rst +++ b/Documentation/process/changes.rst @@ -33,7 +33,7 @@ GNU C 5.1 gcc --version Clang/LLVM (optional) 13.0.1 clang --version Rust (optional) 1.78.0 rustc --version bindgen (optional) 0.65.1 bindgen --version -GNU make 3.82 make --version +GNU make 4.0 make --version bash 4.2 bash --version binutils 2.25 ld -v flex 2.5.35 flex --version @@ -111,7 +111,7 @@ It depends on ``libclang``. Make ---- -You will need GNU make 3.82 or later to build the kernel. +You will need GNU make 4.0 or later to build the kernel. Bash ---- diff --git a/Makefile b/Makefile index b25b5b44af10..7372ea45ed3f 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,8 @@ NAME = Baby Opossum Posse # Comments in this file are targeted only to the developer, do not # expect to learn how to build the kernel reading this file. -ifeq ($(filter undefine,$(.FEATURES)),) -$(error GNU Make >= 3.82 is required. Your Make version is $(MAKE_VERSION)) +ifeq ($(filter output-sync,$(.FEATURES)),) +$(error GNU Make >= 4.0 is required. Your Make version is $(MAKE_VERSION)) endif $(if $(filter __%, $(MAKECMDGOALS)), \ @@ -93,15 +93,7 @@ endif # If the user is running make -s (silent mode), suppress echoing of # commands -# make-4.0 (and later) keep single letter options in the 1st word of MAKEFLAGS. - -ifeq ($(filter 3.%,$(MAKE_VERSION)),) -short-opts := $(firstword -$(MAKEFLAGS)) -else -short-opts := $(filter-out --%,$(MAKEFLAGS)) -endif - -ifneq ($(findstring s,$(short-opts)),) +ifneq ($(findstring s,$(firstword -$(MAKEFLAGS))),) quiet=silent_ override KBUILD_VERBOSE := endif @@ -201,14 +193,6 @@ ifneq ($(words $(subst :, ,$(abs_srctree))), 1) $(error source directory cannot contain spaces or colons) endif -ifneq ($(filter 3.%,$(MAKE_VERSION)),) -# 'MAKEFLAGS += -rR' does not immediately become effective for GNU Make 3.x -# We need to invoke sub-make to avoid implicit rules in the top Makefile. -need-sub-make := 1 -# Cancel implicit rules for this Makefile. -$(this-makefile): ; -endif - export sub_make_done := 1 endif # sub_make_done diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index faf37bafa3f8..ed8a7493524b 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -68,7 +68,7 @@ kbuild-file = $(or $(wildcard $(src)/Kbuild),$(src)/Makefile) # Read a file, replacing newlines with spaces # # Make 4.2 or later can read a file by using its builtin function. -ifneq ($(filter-out 3.% 4.0 4.1, $(MAKE_VERSION)),) +ifneq ($(filter-out 4.0 4.1, $(MAKE_VERSION)),) read-file = $(subst $(newline),$(space),$(file < $1)) else read-file = $(shell cat $1 2>/dev/null) From 3914cdde89c29b681e73e507a04ca50f147482a3 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 4 Jul 2024 22:47:56 +0900 Subject: [PATCH 111/155] modpost: remove self-definitions of R_ARM_* macros Commit f5983dab0ead ("modpost: define more R_ARM_* for old distributions") added self-definitions for the R_ARM_* macros to fix build errors on CentOS 7. RHEL/CentOS 7 were retired at the end of June. Remove all the R_ARM_* definitions (except for R_ARM_THM_CALL), which should be available in recent distributions. glibc and musl added most of R_ARM_* macros in 2013. [1] [2] [1]: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=08cbd996d33114ca50644d060fbe3a08260430fb [2]: https://git.musl-libc.org/cgit/musl/commit/?id=268375c1c017c0bdefeed1a330811e433c4dfaef Signed-off-by: Masahiro Yamada Reviewed-by: Nathan Chancellor --- scripts/mod/modpost.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 11731fc62140..e9aae1b7ff77 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1168,39 +1168,9 @@ static Elf_Addr addend_386_rel(uint32_t *location, unsigned int r_type) return (Elf_Addr)(-1); } -#ifndef R_ARM_CALL -#define R_ARM_CALL 28 -#endif -#ifndef R_ARM_JUMP24 -#define R_ARM_JUMP24 29 -#endif - #ifndef R_ARM_THM_CALL #define R_ARM_THM_CALL 10 #endif -#ifndef R_ARM_THM_JUMP24 -#define R_ARM_THM_JUMP24 30 -#endif - -#ifndef R_ARM_MOVW_ABS_NC -#define R_ARM_MOVW_ABS_NC 43 -#endif - -#ifndef R_ARM_MOVT_ABS -#define R_ARM_MOVT_ABS 44 -#endif - -#ifndef R_ARM_THM_MOVW_ABS_NC -#define R_ARM_THM_MOVW_ABS_NC 47 -#endif - -#ifndef R_ARM_THM_MOVT_ABS -#define R_ARM_THM_MOVT_ABS 48 -#endif - -#ifndef R_ARM_THM_JUMP19 -#define R_ARM_THM_JUMP19 51 -#endif static int32_t sign_extend32(int32_t value, int index) { From f58437aba0a35825dc14dbbf7a1e452662ef797d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 4 Jul 2024 22:47:57 +0900 Subject: [PATCH 112/155] modpost: rename R_ARM_THM_CALL to R_ARM_THM_PC22 /usr/include/elf.h, which originates from the glibc/musl, defines R_ARM_THM_PC22 instead of R_ARM_THM_CALL. Signed-off-by: Masahiro Yamada Reviewed-by: Nathan Chancellor --- scripts/mod/modpost.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index e9aae1b7ff77..3e5313ed6065 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1168,10 +1168,6 @@ static Elf_Addr addend_386_rel(uint32_t *location, unsigned int r_type) return (Elf_Addr)(-1); } -#ifndef R_ARM_THM_CALL -#define R_ARM_THM_CALL 10 -#endif - static int32_t sign_extend32(int32_t value, int index) { uint8_t shift = 31 - index; @@ -1232,7 +1228,7 @@ static Elf_Addr addend_arm_rel(void *loc, Elf_Sym *sym, unsigned int r_type) ((lower & 0x07ff) << 1), 20); return offset + sym->st_value + 4; - case R_ARM_THM_CALL: + case R_ARM_THM_PC22: case R_ARM_THM_JUMP24: /* * Encoding T4: From d67015eedd9722cfbf9d929be743ce27dfcd0744 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 5 Jul 2024 00:23:32 +0900 Subject: [PATCH 113/155] kbuild: deb-pkg: use default string when variable is unset or null ${DEBFULLNAME-${user}} falls back to ${user} when DEBFULLNAME is unset. It is more reasonable to do so when DEBFULLNAME is unset or null. Otherwise, the command: $ DEBFULLNAME= make deb-pkg will leave the name field blank. The same applies to KBUILD_BUILD_USER and KBUILD_BUILD_HOST. Signed-off-by: Masahiro Yamada Reviewed-by: Nathan Chancellor --- scripts/package/mkdebian | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian index f962d303502b..10637d403777 100755 --- a/scripts/package/mkdebian +++ b/scripts/package/mkdebian @@ -125,12 +125,12 @@ gen_source () rm -rf debian mkdir debian -user=${KBUILD_BUILD_USER-$(id -nu)} -name=${DEBFULLNAME-${user}} +user=${KBUILD_BUILD_USER:-$(id -nu)} +name=${DEBFULLNAME:-${user}} if [ "${DEBEMAIL:+set}" ]; then email=${DEBEMAIL} else - buildhost=${KBUILD_BUILD_HOST-$(hostname -f 2>/dev/null || hostname)} + buildhost=${KBUILD_BUILD_HOST:-$(hostname -f 2>/dev/null || hostname)} email="${user}@${buildhost}" fi maintainer="${name} <${email}>" From 8bfd6f0923cd7e48aa5d9e5a4e20af818a32c30a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 8 Jul 2024 00:38:04 +0900 Subject: [PATCH 114/155] kconfig: call expr_eliminate_yn() at least once in expr_eliminate_dups() Kconfig simplifies expressions, but redundant '&&' and '||' operators involving constant symbols 'y' and 'n' are sometimes trimmed and sometimes not. [Test Code] config DEP def_bool y config A bool "A" depends on DEP && y config B bool "B" depends on DEP && y && y [Result] $ make helpnewconfig [ snip ] ----- There is no help available for this option. Symbol: A [=n] Type : bool Defined at Kconfig:4 Prompt: A Depends on: DEP [=y] && y [=y] Location: -> A (A [=n]) ----- ----- There is no help available for this option. Symbol: B [=n] Type : bool Defined at Kconfig:8 Prompt: B Depends on: DEP [=y] Location: -> B (B [=n]) ----- The dependency for A, 'DEP && y', remains as-is, while that for B, 'DEP && y && y', has been reduced to 'DEP'. Currently, expr_eliminate_dups() calls expr_eliminate_yn() only when trans_count != 0, in other words, only when expr_eliminate_dups1() has trimmed at least one leaf. It fails to trim a single '&& y', etc. To fix this inconsistent behavior, expr_eliminate_yn() should be called at least once even if no leaf has been trimmed. Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index 6d4b5a5a1e62..b2dfd3123a5d 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -637,7 +637,7 @@ struct expr *expr_eliminate_dups(struct expr *e) return e; oldcount = trans_count; - while (1) { + do { trans_count = 0; switch (e->type) { case E_OR: case E_AND: @@ -645,11 +645,8 @@ struct expr *expr_eliminate_dups(struct expr *e) default: ; } - if (!trans_count) - /* No simplifications done in this pass. We're done */ - break; e = expr_eliminate_yn(e); - } + } while (trans_count); /* repeat until we get no more simplifications */ trans_count = oldcount; return e; } From 6425e3b247b1eff04c64091b2af8811d05546a86 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 8 Jul 2024 00:38:05 +0900 Subject: [PATCH 115/155] kconfig: add const qualifiers to several function arguments Clarify that the given structures are not modified. Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.c | 4 ++-- scripts/kconfig/expr.h | 4 ++-- scripts/kconfig/lkc.h | 21 +++++++++++---------- scripts/kconfig/lkc_proto.h | 12 +++++++----- scripts/kconfig/menu.c | 15 ++++++++------- scripts/kconfig/parser.y | 4 ++-- scripts/kconfig/symbol.c | 14 +++++++------- scripts/kconfig/util.c | 2 +- 8 files changed, 40 insertions(+), 36 deletions(-) diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index b2dfd3123a5d..a85e0d603322 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -1096,7 +1096,7 @@ static int expr_compare_type(enum expr_type t1, enum expr_type t2) return 0; } -void expr_print(struct expr *e, +void expr_print(const struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken) { @@ -1221,7 +1221,7 @@ static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *s str_printf(gs, " [=%s]", sym_str); } -void expr_gstr_print(struct expr *e, struct gstr *gs) +void expr_gstr_print(const struct expr *e, struct gstr *gs) { expr_print(e, expr_print_gstr_helper, gs, E_NONE); } diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 8849a243b5e7..54b008c0161d 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -291,11 +291,11 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb void expr_fprint(struct expr *e, FILE *out); struct gstr; /* forward */ -void expr_gstr_print(struct expr *e, struct gstr *gs); +void expr_gstr_print(const struct expr *e, struct gstr *gs); void expr_gstr_print_revdep(struct expr *e, struct gstr *gs, tristate pr_type, const char *title); -static inline int expr_is_yes(struct expr *e) +static inline int expr_is_yes(const struct expr *e) { return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes); } diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 586a5e11f51e..3fa46610f25f 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -75,7 +75,7 @@ struct gstr str_new(void); void str_free(struct gstr *gs); void str_append(struct gstr *gs, const char *s); void str_printf(struct gstr *gs, const char *fmt, ...); -char *str_get(struct gstr *gs); +char *str_get(const struct gstr *gs); /* menu.c */ struct menu *menu_next(struct menu *menu, struct menu *root); @@ -84,13 +84,14 @@ struct menu *menu_next(struct menu *menu, struct menu *root); #define menu_for_each_entry(menu) \ menu_for_each_sub_entry(menu, &rootmenu) void _menu_init(void); -void menu_warn(struct menu *menu, const char *fmt, ...); +void menu_warn(const struct menu *menu, const char *fmt, ...); struct menu *menu_add_menu(void); void menu_end_menu(void); void menu_add_entry(struct symbol *sym); void menu_add_dep(struct expr *dep); void menu_add_visibility(struct expr *dep); -struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); +struct property *menu_add_prompt(enum prop_type type, const char *prompt, + struct expr *dep); void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); void menu_finalize(void); @@ -100,8 +101,8 @@ extern struct menu rootmenu; bool menu_is_empty(struct menu *menu); bool menu_is_visible(struct menu *menu); -bool menu_has_prompt(struct menu *menu); -const char *menu_get_prompt(struct menu *menu); +bool menu_has_prompt(const struct menu *menu); +const char *menu_get_prompt(const struct menu *menu); struct menu *menu_get_parent_menu(struct menu *menu); int get_jump_key_char(void); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); @@ -114,25 +115,25 @@ struct symbol *sym_calc_choice(struct menu *choice); struct property *sym_get_range_prop(struct symbol *sym); const char *sym_get_string_default(struct symbol *sym); struct symbol *sym_check_deps(struct symbol *sym); -struct symbol *prop_get_symbol(struct property *prop); +struct symbol *prop_get_symbol(const struct property *prop); -static inline tristate sym_get_tristate_value(struct symbol *sym) +static inline tristate sym_get_tristate_value(const struct symbol *sym) { return sym->curr.tri; } -static inline bool sym_is_choice(struct symbol *sym) +static inline bool sym_is_choice(const struct symbol *sym) { /* A choice is a symbol with no name */ return sym->name == NULL; } -static inline bool sym_is_choice_value(struct symbol *sym) +static inline bool sym_is_choice_value(const struct symbol *sym) { return sym->flags & SYMBOL_CHOICEVAL ? true : false; } -static inline bool sym_has_value(struct symbol *sym) +static inline bool sym_has_value(const struct symbol *sym) { return sym->flags & SYMBOL_DEF_USER ? true : false; } diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index 49cc649d2810..63519cd24bc7 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -25,21 +25,23 @@ struct symbol ** sym_re_search(const char *pattern); const char * sym_type_name(enum symbol_type type); void sym_calc_value(struct symbol *sym); bool sym_dep_errors(void); -enum symbol_type sym_get_type(struct symbol *sym); -bool sym_tristate_within_range(struct symbol *sym,tristate tri); +enum symbol_type sym_get_type(const struct symbol *sym); +bool sym_tristate_within_range(const struct symbol *sym, tristate tri); bool sym_set_tristate_value(struct symbol *sym,tristate tri); void choice_set_value(struct menu *choice, struct symbol *sym); tristate sym_toggle_tristate_value(struct symbol *sym); bool sym_string_valid(struct symbol *sym, const char *newval); bool sym_string_within_range(struct symbol *sym, const char *str); bool sym_set_string_value(struct symbol *sym, const char *newval); -bool sym_is_changeable(struct symbol *sym); -struct menu *sym_get_choice_menu(struct symbol *sym); +bool sym_is_changeable(const struct symbol *sym); +struct menu *sym_get_choice_menu(const struct symbol *sym); const char * sym_get_string_value(struct symbol *sym); const char * prop_get_type_name(enum prop_type type); /* expr.c */ -void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken); +void expr_print(const struct expr *e, + void (*fn)(void *, struct symbol *, const char *), + void *data, int prevtoken); #endif /* LKC_PROTO_H */ diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index b1fbaf2ff792..2a9b4c4f4428 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -38,7 +38,7 @@ struct menu *menu_next(struct menu *menu, struct menu *root) return menu->next; } -void menu_warn(struct menu *menu, const char *fmt, ...) +void menu_warn(const struct menu *menu, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -48,7 +48,7 @@ void menu_warn(struct menu *menu, const char *fmt, ...) va_end(ap); } -static void prop_warn(struct property *prop, const char *fmt, ...) +static void prop_warn(const struct property *prop, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -175,7 +175,7 @@ static struct property *menu_add_prop(enum prop_type type, struct expr *expr, return prop; } -struct property *menu_add_prompt(enum prop_type type, char *prompt, +struct property *menu_add_prompt(enum prop_type type, const char *prompt, struct expr *dep) { struct property *prop = menu_add_prop(type, NULL, dep); @@ -527,7 +527,7 @@ void menu_finalize(void) _menu_finalize(&rootmenu, false); } -bool menu_has_prompt(struct menu *menu) +bool menu_has_prompt(const struct menu *menu) { if (!menu->prompt) return false; @@ -573,7 +573,7 @@ bool menu_is_visible(struct menu *menu) return visible != no; } -const char *menu_get_prompt(struct menu *menu) +const char *menu_get_prompt(const struct menu *menu) { if (menu->prompt) return menu->prompt->text; @@ -594,13 +594,14 @@ struct menu *menu_get_parent_menu(struct menu *menu) return menu; } -static void get_def_str(struct gstr *r, struct menu *menu) +static void get_def_str(struct gstr *r, const struct menu *menu) { str_printf(r, "Defined at %s:%d\n", menu->filename, menu->lineno); } -static void get_dep_str(struct gstr *r, struct expr *expr, const char *prefix) +static void get_dep_str(struct gstr *r, const struct expr *expr, + const char *prefix) { if (!expr_is_yes(expr)) { str_append(r, prefix); diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index 745c82ee15d0..61900feb4254 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -489,7 +489,7 @@ assign_val: * * Return: -1 if an error is found, 0 otherwise. */ -static int choice_check_sanity(struct menu *menu) +static int choice_check_sanity(const struct menu *menu) { struct property *prop; int ret = 0; @@ -644,7 +644,7 @@ static void print_quoted_string(FILE *out, const char *str) putc('"', out); } -static void print_symbol(FILE *out, struct menu *menu) +static void print_symbol(FILE *out, const struct menu *menu) { struct symbol *sym = menu->sym; struct property *prop; diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index c05d188a1857..3255bf310cb2 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -40,7 +40,7 @@ struct symbol *modules_sym; static tristate modules_val; static int sym_warnings; -enum symbol_type sym_get_type(struct symbol *sym) +enum symbol_type sym_get_type(const struct symbol *sym) { enum symbol_type type = sym->type; @@ -75,7 +75,7 @@ const char *sym_type_name(enum symbol_type type) * * Return: a choice menu if this function is called against a choice member. */ -struct menu *sym_get_choice_menu(struct symbol *sym) +struct menu *sym_get_choice_menu(const struct symbol *sym) { struct menu *menu = NULL; struct menu *m; @@ -355,7 +355,7 @@ struct symbol *sym_calc_choice(struct menu *choice) return res; } -static void sym_warn_unmet_dep(struct symbol *sym) +static void sym_warn_unmet_dep(const struct symbol *sym) { struct gstr gs = str_new(); @@ -521,7 +521,7 @@ void sym_clear_all_valid(void) sym_calc_value(modules_sym); } -bool sym_tristate_within_range(struct symbol *sym, tristate val) +bool sym_tristate_within_range(const struct symbol *sym, tristate val) { int type = sym_get_type(sym); @@ -866,7 +866,7 @@ const char *sym_get_string_value(struct symbol *sym) return (const char *)sym->curr.val; } -bool sym_is_changeable(struct symbol *sym) +bool sym_is_changeable(const struct symbol *sym) { return !sym_is_choice(sym) && sym->visible > sym->rev_dep.tri; } @@ -1150,7 +1150,7 @@ static void sym_check_print_recursive(struct symbol *last_sym) dep_stack_remove(); } -static struct symbol *sym_check_expr_deps(struct expr *e) +static struct symbol *sym_check_expr_deps(const struct expr *e) { struct symbol *sym; @@ -1309,7 +1309,7 @@ struct symbol *sym_check_deps(struct symbol *sym) return sym2; } -struct symbol *prop_get_symbol(struct property *prop) +struct symbol *prop_get_symbol(const struct property *prop) { if (prop->expr && prop->expr->type == E_SYMBOL) return prop->expr->left.sym; diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c index 439c131b424e..1ea78927121d 100644 --- a/scripts/kconfig/util.c +++ b/scripts/kconfig/util.c @@ -98,7 +98,7 @@ void str_printf(struct gstr *gs, const char *fmt, ...) } /* Retrieve value of growable string */ -char *str_get(struct gstr *gs) +char *str_get(const struct gstr *gs) { return gs->s; } From 94a4b0a4cb4340273a2d67be893f9032fe7b7e26 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 8 Jul 2024 00:38:06 +0900 Subject: [PATCH 116/155] kconfig: remove SYMBOL_CHOICEVAL flag This flag is unneeded because a choice member can be detected by other means. Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.h | 1 - scripts/kconfig/gconf.c | 2 +- scripts/kconfig/lkc.h | 5 +---- scripts/kconfig/menu.c | 5 ----- scripts/kconfig/symbol.c | 6 ++++++ 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 54b008c0161d..6e47e0ad6e6e 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -131,7 +131,6 @@ struct symbol { #define SYMBOL_CONST 0x0001 /* symbol is const */ #define SYMBOL_CHECK 0x0008 /* used during dependency checking */ -#define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */ #define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */ #define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */ #define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */ diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 6b50e25133e3..c0f46f189060 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1070,7 +1070,7 @@ static gchar **fill_row(struct menu *menu) row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); return row; } - if (sym->flags & SYMBOL_CHOICEVAL) + if (sym_is_choice_value(sym)) row[COL_BTNRAD] = GINT_TO_POINTER(TRUE); stype = sym_get_type(sym); diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 3fa46610f25f..401bdf36323a 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -128,10 +128,7 @@ static inline bool sym_is_choice(const struct symbol *sym) return sym->name == NULL; } -static inline bool sym_is_choice_value(const struct symbol *sym) -{ - return sym->flags & SYMBOL_CHOICEVAL ? true : false; -} +bool sym_is_choice_value(const struct symbol *sym); static inline bool sym_has_value(const struct symbol *sym) { diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 2a9b4c4f4428..cd34cc5aefcf 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -467,11 +467,6 @@ static void _menu_finalize(struct menu *parent, bool inside_choice) sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep); } for (menu = parent->list; menu; menu = menu->next) { - if (sym && sym_is_choice(sym) && - menu->sym && !sym_is_choice_value(menu->sym)) { - menu->sym->flags |= SYMBOL_CHOICEVAL; - } - /* * This code serves two purposes: * diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 3255bf310cb2..6c6f238c4f7b 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -871,6 +871,11 @@ bool sym_is_changeable(const struct symbol *sym) return !sym_is_choice(sym) && sym->visible > sym->rev_dep.tri; } +bool sym_is_choice_value(const struct symbol *sym) +{ + return !list_empty(&sym->choice_link); +} + HASHTABLE_DEFINE(sym_hashtable, SYMBOL_HASHSIZE); struct symbol *sym_lookup(const char *name, int flags) @@ -908,6 +913,7 @@ struct symbol *sym_lookup(const char *name, int flags) symbol->type = S_UNKNOWN; symbol->flags = flags; INIT_LIST_HEAD(&symbol->menus); + INIT_LIST_HEAD(&symbol->choice_link); hash_add(sym_hashtable, &symbol->node, hash); From 8fe76a1c2264a02155895cda9f97a6a5a9b97d91 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Tue, 2 Jul 2024 12:29:38 -0700 Subject: [PATCH 117/155] sh: push-switch: Add missing MODULE_DESCRIPTION() macro Fixes the following warning when building allmodconfig with W=1 C=1: WARNING: modpost: missing MODULE_DESCRIPTION() in arch/sh/drivers/push-switch.o Signed-off-by: Jeff Johnson Reviewed-by: John Paul Adrian Glaubitz Link: https://lore.kernel.org/r/20240702-md-sh-arch-sh-drivers-v1-1-2c5d439a5479@quicinc.com Signed-off-by: John Paul Adrian Glaubitz --- arch/sh/drivers/push-switch.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sh/drivers/push-switch.c b/arch/sh/drivers/push-switch.c index 362e4860bf52..1dea43381b5a 100644 --- a/arch/sh/drivers/push-switch.c +++ b/arch/sh/drivers/push-switch.c @@ -131,4 +131,5 @@ module_exit(switch_exit); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR("Paul Mundt"); +MODULE_DESCRIPTION("Generic push-switch framework"); MODULE_LICENSE("GPL v2"); From 37ac347f87b616525e0fd397cfc9a163328173aa Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 17 Jul 2024 19:47:35 +0200 Subject: [PATCH 118/155] perf build: Warn if libtracefs is not found Signed-off-by: Guilherme Amadio Tested-by: Thorsten Leemhuis Tested-by: Leo Yan Acked-by: Steven Rostedt (Google) Cc: linux-trace-devel@vger.kernel.org Link: https://lore.kernel.org/r/20240717174739.186988-2-amadio@gentoo.org Signed-off-by: Namhyung Kim --- tools/perf/Makefile.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index a4829b6532d8..44f010b9f562 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -1206,6 +1206,8 @@ ifneq ($(NO_LIBTRACEEVENT),1) LIBTRACEFS_VERSION_3 := $(word 3, $(subst ., ,$(LIBTRACEFS_VERSION))) LIBTRACEFS_VERSION_CPP := $(shell expr $(LIBTRACEFS_VERSION_1) \* 255 \* 255 + $(LIBTRACEFS_VERSION_2) \* 255 + $(LIBTRACEFS_VERSION_3)) CFLAGS += -DLIBTRACEFS_VERSION=$(LIBTRACEFS_VERSION_CPP) + else + $(warning libtracefs is missing. Please install libtracefs-dev/libtracefs-devel) endif endif From 8f61e98ad51f167de148488b1881de72fec0d563 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 17 Jul 2024 19:47:36 +0200 Subject: [PATCH 119/155] tools: Make pkg-config dependency checks usable by other tools Other tools, in tools/verification and tools/tracing, make use of libtraceevent and libtracefs as dependencies. This allows setting up the feature check flags for them as well. Signed-off-by: Guilherme Amadio Tested-by: Thorsten Leemhuis Tested-by: Leo Yan Acked-by: Steven Rostedt (Google) Cc: linux-trace-devel@vger.kernel.org Link: https://lore.kernel.org/r/20240717174739.186988-3-amadio@gentoo.org Signed-off-by: Namhyung Kim --- tools/build/Makefile.feature | 18 ++++++++++++++++++ tools/perf/Makefile.config | 11 +++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 1e2ab148d5db..e1900abd44f6 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -149,6 +149,24 @@ FEATURE_DISPLAY ?= \ # FEATURE_GROUP_MEMBERS-libbfd = libbfd-liberty libbfd-liberty-z +# +# Declare list of feature dependency packages that provide pkg-config files. +# +FEATURE_PKG_CONFIG ?= \ + libtraceevent \ + libtracefs + +feature_pkg_config = $(eval $(feature_pkg_config_code)) +define feature_pkg_config_code + FEATURE_CHECK_CFLAGS-$(1) := $(shell $(PKG_CONFIG) --cflags $(1) 2>/dev/null) + FEATURE_CHECK_LDFLAGS-$(1) := $(shell $(PKG_CONFIG) --libs $(1) 2>/dev/null) +endef + +# Set FEATURE_CHECK_(C|LD)FLAGS-$(package) for packages using pkg-config. +ifneq ($(PKG_CONFIG),) + $(foreach package,$(FEATURE_PKG_CONFIG),$(call feature_pkg_config,$(package))) +endif + # Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features. # If in the future we need per-feature checks/flags for features not # mentioned in this list we need to refactor this ;-). diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 44f010b9f562..c896babf7a74 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -182,20 +182,15 @@ endif FEATURE_CHECK_CFLAGS-libzstd := $(LIBZSTD_CFLAGS) FEATURE_CHECK_LDFLAGS-libzstd := $(LIBZSTD_LDFLAGS) +# for linking with debug library, run like: +# make DEBUG=1 PKG_CONFIG_PATH=/opt/libtraceevent/(lib|lib64)/pkgconfig + ifneq ($(NO_LIBTRACEEVENT),1) ifeq ($(call get-executable,$(PKG_CONFIG)),) $(error Error: $(PKG_CONFIG) needed by libtraceevent is missing on this system, please install it) endif endif -# for linking with debug library, run like: -# make DEBUG=1 PKG_CONFIG_PATH=/opt/libtraceevent/(lib|lib64)/pkgconfig -FEATURE_CHECK_CFLAGS-libtraceevent := $(shell $(PKG_CONFIG) --cflags libtraceevent 2>/dev/null) -FEATURE_CHECK_LDFLAGS-libtraceevent := $(shell $(PKG_CONFIG) --libs libtraceevent 2>/dev/null) - -FEATURE_CHECK_CFLAGS-libtracefs := $(shell $(PKG_CONFIG) --cflags libtracefs 2>/dev/null) -FEATURE_CHECK_LDFLAGS-libtracefs := $(shell $(PKG_CONFIG) --libs libtracefs 2>/dev/null) - FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(SRCARCH)/include/uapi -I$(srctree)/tools/include/uapi # include ARCH specific config -include $(src-perf)/arch/$(SRCARCH)/Makefile From e525eff7cc334e1c7450c6bc952bfc35b9d74500 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Fri, 12 Jul 2024 21:40:48 +0200 Subject: [PATCH 120/155] tools/verification: Use pkg-config in lib_setup of Makefile.config This allows to build against libtraceevent and libtracefs installed in non-standard locations. Signed-off-by: Guilherme Amadio Tested-by: Thorsten Leemhuis Tested-by: Leo Yan Acked-by: Steven Rostedt (Google) Link: https://lore.kernel.org/r/20240712194511.3973899-4-amadio@gentoo.org Signed-off-by: Namhyung Kim --- tools/verification/rv/Makefile.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/verification/rv/Makefile.config b/tools/verification/rv/Makefile.config index 6d4ba77847b6..066302230eb2 100644 --- a/tools/verification/rv/Makefile.config +++ b/tools/verification/rv/Makefile.config @@ -7,7 +7,8 @@ LIBTRACEFS_MIN_VERSION = 1.3 define lib_setup $(eval LIB_INCLUDES += $(shell sh -c "$(PKG_CONFIG) --cflags lib$(1)")) - $(eval EXTLIBS += -l$(1)) + $(eval LDFLAGS += $(shell sh -c "$(PKG_CONFIG) --libs-only-L lib$(1)")) + $(eval EXTLIBS += $(shell sh -c "$(PKG_CONFIG) --libs-only-l lib$(1)")) endef $(call feature_check,libtraceevent) From eb545a42d0f1da8dd1a09072cff18f5605add05b Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Fri, 12 Jul 2024 21:40:49 +0200 Subject: [PATCH 121/155] tools/rtla: Use pkg-config in lib_setup of Makefile.config This allows to build against libtraceevent and libtracefs installed in non-standard locations. Signed-off-by: Guilherme Amadio Tested-by: Thorsten Leemhuis Tested-by: Leo Yan Acked-by: Steven Rostedt (Google) Link: https://lore.kernel.org/r/20240712194511.3973899-5-amadio@gentoo.org Signed-off-by: Namhyung Kim --- tools/tracing/rtla/Makefile.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/tracing/rtla/Makefile.config b/tools/tracing/rtla/Makefile.config index 0b7ecfb30d19..5f8c286712d4 100644 --- a/tools/tracing/rtla/Makefile.config +++ b/tools/tracing/rtla/Makefile.config @@ -7,7 +7,8 @@ LIBTRACEFS_MIN_VERSION = 1.6 define lib_setup $(eval LIB_INCLUDES += $(shell sh -c "$(PKG_CONFIG) --cflags lib$(1)")) - $(eval EXTLIBS += -l$(1)) + $(eval LDFLAGS += $(shell sh -c "$(PKG_CONFIG) --libs-only-L lib$(1)")) + $(eval EXTLIBS += $(shell sh -c "$(PKG_CONFIG) --libs-only-l lib$(1)")) endef $(call feature_check,libtraceevent) From 2085948e5f8b31c1ec1bc92413794d11ff749bb2 Mon Sep 17 00:00:00 2001 From: Guilherme Amadio Date: Wed, 17 Jul 2024 19:47:39 +0200 Subject: [PATCH 122/155] tools/latency: Use pkg-config in lib_setup of Makefile.config This allows to build against libtraceevent and libtracefs installed in non-standard locations. Signed-off-by: Guilherme Amadio Tested-by: Thorsten Leemhuis Tested-by: Leo Yan Acked-by: Steven Rostedt (Google) Cc: linux-trace-devel@vger.kernel.org Link: https://lore.kernel.org/r/20240717174739.186988-6-amadio@gentoo.org Signed-off-by: Namhyung Kim --- tools/tracing/latency/Makefile.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/tracing/latency/Makefile.config b/tools/tracing/latency/Makefile.config index b25e531a1f95..0fe6b50f029b 100644 --- a/tools/tracing/latency/Makefile.config +++ b/tools/tracing/latency/Makefile.config @@ -3,8 +3,9 @@ STOP_ERROR := define lib_setup - $(eval EXTLIBS += -l$(1)) $(eval LIB_INCLUDES += $(shell sh -c "$(PKG_CONFIG) --cflags lib$(1)")) + $(eval LDFLAGS += $(shell sh -c "$(PKG_CONFIG) --libs-only-L lib$(1)")) + $(eval EXTLIBS += $(shell sh -c "$(PKG_CONFIG) --libs-only-l lib$(1)")) endef $(call feature_check,libtraceevent) From 92717bc077892d1ce60fee07aee3a33f33909b85 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 15 Jul 2024 10:47:13 +0100 Subject: [PATCH 123/155] perf dso: Fix build when libunwind is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that symsrc_filename is always accessed through an accessor, we also need a free() function for it to avoid the following compilation error: util/unwind-libunwind-local.c:416:12: error: lvalue required as unary ‘&’ operand 416 | zfree(&dso__symsrc_filename(dso)); Fixes: 1553419c3c10 ("perf dso: Fix address sanitizer build") Signed-off-by: James Clark Reviewed-by: Ian Rogers Tested-by: Leo Yan Tested-by: Florian Fainelli Cc: Yunseong Kim Cc: Athira Rajeev Link: https://lore.kernel.org/r/20240715094715.3914813-1-james.clark@linaro.org Signed-off-by: Namhyung Kim --- tools/perf/util/dso.c | 2 +- tools/perf/util/dso.h | 5 +++++ tools/perf/util/unwind-libunwind-local.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 2340c4f6d0c2..67414944f245 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1501,7 +1501,7 @@ void dso__delete(struct dso *dso) auxtrace_cache__free(RC_CHK_ACCESS(dso)->auxtrace_cache); dso_cache__free(dso); dso__free_a2l(dso); - zfree(&RC_CHK_ACCESS(dso)->symsrc_filename); + dso__free_symsrc_filename(dso); nsinfo__zput(RC_CHK_ACCESS(dso)->nsinfo); mutex_destroy(dso__lock(dso)); RC_CHK_FREE(dso); diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 878c1f441868..ed0068251c65 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -602,6 +602,11 @@ static inline void dso__set_symsrc_filename(struct dso *dso, char *val) RC_CHK_ACCESS(dso)->symsrc_filename = val; } +static inline void dso__free_symsrc_filename(struct dso *dso) +{ + zfree(&RC_CHK_ACCESS(dso)->symsrc_filename); +} + static inline enum dso_binary_type dso__symtab_type(const struct dso *dso) { return RC_CHK_ACCESS(dso)->symtab_type; diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index f6a6f6a91030..16c2b03831f3 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -413,7 +413,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso, __func__, dso__symsrc_filename(dso), debuglink); - zfree(&dso__symsrc_filename(dso)); + dso__free_symsrc_filename(dso); } dso__set_symsrc_filename(dso, debuglink); } else { From 3c2f84cedaeb2fe9575dd3f6edddf0d875b3b97c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 8 Jul 2024 00:38:07 +0900 Subject: [PATCH 124/155] kconfig: remove 'e1' and 'e2' macros from expression deduplication I do not think the macros 'e1' and 'e2' are readable. The statement: e1 = expr_alloc_symbol(...); affects the caller's variable, but this is not sufficiently clear from the code. Remove the macros. No functional change intended. Signed-off-by: Masahiro Yamada --- scripts/kconfig/expr.c | 94 +++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 52 deletions(-) diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index a85e0d603322..c349da7fe3f8 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -135,9 +135,6 @@ void expr_free(struct expr *e) static int trans_count; -#define e1 (*ep1) -#define e2 (*ep2) - /* * expr_eliminate_eq() helper. * @@ -150,38 +147,38 @@ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct e { /* Recurse down to leaves */ - if (e1->type == type) { - __expr_eliminate_eq(type, &e1->left.expr, &e2); - __expr_eliminate_eq(type, &e1->right.expr, &e2); + if ((*ep1)->type == type) { + __expr_eliminate_eq(type, &(*ep1)->left.expr, ep2); + __expr_eliminate_eq(type, &(*ep1)->right.expr, ep2); return; } - if (e2->type == type) { - __expr_eliminate_eq(type, &e1, &e2->left.expr); - __expr_eliminate_eq(type, &e1, &e2->right.expr); + if ((*ep2)->type == type) { + __expr_eliminate_eq(type, ep1, &(*ep2)->left.expr); + __expr_eliminate_eq(type, ep1, &(*ep2)->right.expr); return; } - /* e1 and e2 are leaves. Compare them. */ + /* *ep1 and *ep2 are leaves. Compare them. */ - if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && - e1->left.sym == e2->left.sym && - (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no)) + if ((*ep1)->type == E_SYMBOL && (*ep2)->type == E_SYMBOL && + (*ep1)->left.sym == (*ep2)->left.sym && + ((*ep1)->left.sym == &symbol_yes || (*ep1)->left.sym == &symbol_no)) return; - if (!expr_eq(e1, e2)) + if (!expr_eq(*ep1, *ep2)) return; - /* e1 and e2 are equal leaves. Prepare them for elimination. */ + /* *ep1 and *ep2 are equal leaves. Prepare them for elimination. */ trans_count++; - expr_free(e1); expr_free(e2); + expr_free(*ep1); expr_free(*ep2); switch (type) { case E_OR: - e1 = expr_alloc_symbol(&symbol_no); - e2 = expr_alloc_symbol(&symbol_no); + *ep1 = expr_alloc_symbol(&symbol_no); + *ep2 = expr_alloc_symbol(&symbol_no); break; case E_AND: - e1 = expr_alloc_symbol(&symbol_yes); - e2 = expr_alloc_symbol(&symbol_yes); + *ep1 = expr_alloc_symbol(&symbol_yes); + *ep2 = expr_alloc_symbol(&symbol_yes); break; default: ; @@ -219,29 +216,26 @@ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct e */ void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) { - if (!e1 || !e2) + if (!*ep1 || !*ep2) return; - switch (e1->type) { + switch ((*ep1)->type) { case E_OR: case E_AND: - __expr_eliminate_eq(e1->type, ep1, ep2); + __expr_eliminate_eq((*ep1)->type, ep1, ep2); default: ; } - if (e1->type != e2->type) switch (e2->type) { + if ((*ep1)->type != (*ep2)->type) switch ((*ep2)->type) { case E_OR: case E_AND: - __expr_eliminate_eq(e2->type, ep1, ep2); + __expr_eliminate_eq((*ep2)->type, ep1, ep2); default: ; } - e1 = expr_eliminate_yn(e1); - e2 = expr_eliminate_yn(e2); + *ep1 = expr_eliminate_yn(*ep1); + *ep2 = expr_eliminate_yn(*ep2); } -#undef e1 -#undef e2 - /* * Returns true if 'e1' and 'e2' are equal, after minor simplification. Two * &&/|| expressions are considered equal if every operand in one expression @@ -564,59 +558,55 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2) */ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2) { -#define e1 (*ep1) -#define e2 (*ep2) struct expr *tmp; /* Recurse down to leaves */ - if (e1->type == type) { - expr_eliminate_dups1(type, &e1->left.expr, &e2); - expr_eliminate_dups1(type, &e1->right.expr, &e2); + if ((*ep1)->type == type) { + expr_eliminate_dups1(type, &(*ep1)->left.expr, ep2); + expr_eliminate_dups1(type, &(*ep1)->right.expr, ep2); return; } - if (e2->type == type) { - expr_eliminate_dups1(type, &e1, &e2->left.expr); - expr_eliminate_dups1(type, &e1, &e2->right.expr); + if ((*ep2)->type == type) { + expr_eliminate_dups1(type, ep1, &(*ep2)->left.expr); + expr_eliminate_dups1(type, ep1, &(*ep2)->right.expr); return; } - /* e1 and e2 are leaves. Compare and process them. */ + /* *ep1 and *ep2 are leaves. Compare and process them. */ - if (e1 == e2) + if (*ep1 == *ep2) return; - switch (e1->type) { + switch ((*ep1)->type) { case E_OR: case E_AND: - expr_eliminate_dups1(e1->type, &e1, &e1); + expr_eliminate_dups1((*ep1)->type, ep1, ep1); default: ; } switch (type) { case E_OR: - tmp = expr_join_or(e1, e2); + tmp = expr_join_or(*ep1, *ep2); if (tmp) { - expr_free(e1); expr_free(e2); - e1 = expr_alloc_symbol(&symbol_no); - e2 = tmp; + expr_free(*ep1); expr_free(*ep2); + *ep1 = expr_alloc_symbol(&symbol_no); + *ep2 = tmp; trans_count++; } break; case E_AND: - tmp = expr_join_and(e1, e2); + tmp = expr_join_and(*ep1, *ep2); if (tmp) { - expr_free(e1); expr_free(e2); - e1 = expr_alloc_symbol(&symbol_yes); - e2 = tmp; + expr_free(*ep1); expr_free(*ep2); + *ep1 = expr_alloc_symbol(&symbol_yes); + *ep2 = tmp; trans_count++; } break; default: ; } -#undef e1 -#undef e2 } /* From af7925d82096d30bf971d41a109bd42f6a027ec2 Mon Sep 17 00:00:00 2001 From: Zhang Bingwu Date: Sun, 14 Jul 2024 16:57:50 +0800 Subject: [PATCH 125/155] kbuild: Abort make on install failures Setting '-e' flag tells shells to exit with error exit code immediately after any of commands fails, and causes make(1) to regard recipes as failed. Before this, make will still continue to succeed even after the installation failed, for example, for insufficient permission or directory does not exist. Signed-off-by: Zhang Bingwu Signed-off-by: Masahiro Yamada --- arch/arm/boot/install.sh | 2 ++ arch/arm64/boot/install.sh | 2 ++ arch/m68k/install.sh | 2 ++ arch/nios2/boot/install.sh | 2 ++ arch/parisc/install.sh | 2 ++ arch/riscv/boot/install.sh | 2 ++ arch/s390/boot/install.sh | 2 ++ arch/sparc/boot/install.sh | 2 ++ arch/x86/boot/install.sh | 2 ++ 9 files changed, 18 insertions(+) diff --git a/arch/arm/boot/install.sh b/arch/arm/boot/install.sh index 9ec11fac7d8d..34e2c6e31fd1 100755 --- a/arch/arm/boot/install.sh +++ b/arch/arm/boot/install.sh @@ -17,6 +17,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ "$(basename $2)" = "zImage" ]; then # Compressed install echo "Installing compressed kernel" diff --git a/arch/arm64/boot/install.sh b/arch/arm64/boot/install.sh index 9b7a09808a3d..cc2f4ccca6c0 100755 --- a/arch/arm64/boot/install.sh +++ b/arch/arm64/boot/install.sh @@ -17,6 +17,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ "$(basename $2)" = "Image.gz" ] || [ "$(basename $2)" = "vmlinuz.efi" ] then # Compressed install diff --git a/arch/m68k/install.sh b/arch/m68k/install.sh index af65e16e5147..b6829b3942b3 100755 --- a/arch/m68k/install.sh +++ b/arch/m68k/install.sh @@ -16,6 +16,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ -f $4/vmlinuz ]; then mv $4/vmlinuz $4/vmlinuz.old fi diff --git a/arch/nios2/boot/install.sh b/arch/nios2/boot/install.sh index 34a2feec42c8..1161f2bf59ec 100755 --- a/arch/nios2/boot/install.sh +++ b/arch/nios2/boot/install.sh @@ -16,6 +16,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ -f $4/vmlinuz ]; then mv $4/vmlinuz $4/vmlinuz.old fi diff --git a/arch/parisc/install.sh b/arch/parisc/install.sh index 933d031c249a..664c2d77f776 100755 --- a/arch/parisc/install.sh +++ b/arch/parisc/install.sh @@ -16,6 +16,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ "$(basename $2)" = "vmlinuz" ]; then # Compressed install echo "Installing compressed kernel" diff --git a/arch/riscv/boot/install.sh b/arch/riscv/boot/install.sh index a8df7591513a..4b3d8bf91cc6 100755 --- a/arch/riscv/boot/install.sh +++ b/arch/riscv/boot/install.sh @@ -17,6 +17,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + case "${2##*/}" in # Compressed install Image.*|vmlinuz.efi) diff --git a/arch/s390/boot/install.sh b/arch/s390/boot/install.sh index a13dd2f2aa1c..fa41486258ee 100755 --- a/arch/s390/boot/install.sh +++ b/arch/s390/boot/install.sh @@ -15,6 +15,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + echo "Warning: '${INSTALLKERNEL}' command not available - additional " \ "bootloader config required" >&2 if [ -f "$4/vmlinuz-$1" ]; then mv -- "$4/vmlinuz-$1" "$4/vmlinuz-$1.old"; fi diff --git a/arch/sparc/boot/install.sh b/arch/sparc/boot/install.sh index 4f130f3f30d6..68de67c5621e 100755 --- a/arch/sparc/boot/install.sh +++ b/arch/sparc/boot/install.sh @@ -16,6 +16,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ -f $4/vmlinuz ]; then mv $4/vmlinuz $4/vmlinuz.old fi diff --git a/arch/x86/boot/install.sh b/arch/x86/boot/install.sh index 0849f4b42745..93784abcd66d 100755 --- a/arch/x86/boot/install.sh +++ b/arch/x86/boot/install.sh @@ -16,6 +16,8 @@ # $3 - kernel map file # $4 - default install path (blank if root directory) +set -e + if [ -f $4/vmlinuz ]; then mv $4/vmlinuz $4/vmlinuz.old fi From 7efcb39e0da99de8295e81a2f98b7fdeebcdaa73 Mon Sep 17 00:00:00 2001 From: Zhang Bingwu Date: Sun, 14 Jul 2024 16:57:51 +0800 Subject: [PATCH 126/155] kbuild: Create INSTALL_PATH directory if it does not exist If INSTALL_PATH is not a valid directory, create it, like what modules_install and dtbs_install will do in the same situation. Signed-off-by: Zhang Bingwu Reviewed-by: Nicolas Schier Signed-off-by: Masahiro Yamada --- scripts/install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/install.sh b/scripts/install.sh index 9bb0fb44f04a..05d62ac513ee 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -20,6 +20,10 @@ do fi done +if [ -n "${INSTALL_PATH}" ] && ! [ -e "${INSTALL_PATH}" ]; then + mkdir -p "${INSTALL_PATH}" +fi + # User/arch may have a custom install script for file in "${HOME}/bin/${INSTALLKERNEL}" \ "/sbin/${INSTALLKERNEL}" \ From 64e166099b69bfc09f667253358a15160b86ea43 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Wed, 21 Feb 2024 21:26:53 +0100 Subject: [PATCH 127/155] kallsyms: get rid of code for absolute kallsyms Commit cf8e8658100d ("arch: Remove Itanium (IA-64) architecture") removed the last use of the absolute kallsyms. Signed-off-by: Jann Horn Acked-by: Arnd Bergmann Link: https://lore.kernel.org/all/20240221202655.2423854-1-jannh@google.com/ [masahiroy@kernel.org: rebase the code and reword the commit description] Signed-off-by: Masahiro Yamada --- init/Kconfig | 18 ------- kernel/kallsyms.c | 5 +- kernel/kallsyms_internal.h | 1 - kernel/vmcore_info.c | 4 -- scripts/kallsyms.c | 76 ++++++++++++----------------- scripts/link-vmlinux.sh | 4 -- tools/perf/tests/vmlinux-kallsyms.c | 1 - 7 files changed, 32 insertions(+), 77 deletions(-) diff --git a/init/Kconfig b/init/Kconfig index febdea2afc3b..8e21ce38dee6 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1789,24 +1789,6 @@ config KALLSYMS_ABSOLUTE_PERCPU depends on KALLSYMS default X86_64 && SMP -config KALLSYMS_BASE_RELATIVE - bool - depends on KALLSYMS - default y - help - Instead of emitting them as absolute values in the native word size, - emit the symbol references in the kallsyms table as 32-bit entries, - each containing a relative value in the range [base, base + U32_MAX] - or, when KALLSYMS_ABSOLUTE_PERCPU is in effect, each containing either - an absolute value in the range [0, S32_MAX] or a relative value in the - range [base, base + S32_MAX], where base is the lowest relative symbol - address encountered in the image. - - On 64-bit builds, this reduces the size of the address table by 50%, - but more importantly, it results in entries whose values are build - time constants, and no relocation pass is required at runtime to fix - up the entries based on the runtime load address of the kernel. - # end of the "standard kernel features (expert users)" menu config ARCH_HAS_MEMBARRIER_CALLBACKS diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 98b9622d372e..fb2c77368d18 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -148,9 +148,6 @@ static unsigned int get_symbol_offset(unsigned long pos) unsigned long kallsyms_sym_address(int idx) { - if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE)) - return kallsyms_addresses[idx]; - /* values are unsigned offsets if --absolute-percpu is not in effect */ if (!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU)) return kallsyms_relative_base + (u32)kallsyms_offsets[idx]; @@ -325,7 +322,7 @@ static unsigned long get_symbol_pos(unsigned long addr, unsigned long symbol_start = 0, symbol_end = 0; unsigned long i, low, high, mid; - /* Do a binary search on the sorted kallsyms_addresses array. */ + /* Do a binary search on the sorted kallsyms_offsets array. */ low = 0; high = kallsyms_num_syms; diff --git a/kernel/kallsyms_internal.h b/kernel/kallsyms_internal.h index 925f2ab22639..9633782f8250 100644 --- a/kernel/kallsyms_internal.h +++ b/kernel/kallsyms_internal.h @@ -4,7 +4,6 @@ #include -extern const unsigned long kallsyms_addresses[]; extern const int kallsyms_offsets[]; extern const u8 kallsyms_names[]; diff --git a/kernel/vmcore_info.c b/kernel/vmcore_info.c index 1d5eadd9dd61..8b4f8cc2e0ec 100644 --- a/kernel/vmcore_info.c +++ b/kernel/vmcore_info.c @@ -216,12 +216,8 @@ static int __init crash_save_vmcoreinfo_init(void) VMCOREINFO_SYMBOL(kallsyms_num_syms); VMCOREINFO_SYMBOL(kallsyms_token_table); VMCOREINFO_SYMBOL(kallsyms_token_index); -#ifdef CONFIG_KALLSYMS_BASE_RELATIVE VMCOREINFO_SYMBOL(kallsyms_offsets); VMCOREINFO_SYMBOL(kallsyms_relative_base); -#else - VMCOREINFO_SYMBOL(kallsyms_addresses); -#endif /* CONFIG_KALLSYMS_BASE_RELATIVE */ #endif /* CONFIG_KALLSYMS */ arch_crash_save_vmcoreinfo(); diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index fa53b5eef553..55a423519f2e 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -6,7 +6,7 @@ * of the GNU General Public License, incorporated herein by reference. * * Usage: kallsyms [--all-symbols] [--absolute-percpu] - * [--base-relative] [--lto-clang] in.map > out.S + * [--lto-clang] in.map > out.S * * Table compression uses all the unused char codes on the symbols and * maps these to the most used substrings (tokens). For instance, it might @@ -63,7 +63,6 @@ static struct sym_entry **table; static unsigned int table_size, table_cnt; static int all_symbols; static int absolute_percpu; -static int base_relative; static int lto_clang; static int token_profit[0x10000]; @@ -76,7 +75,7 @@ static unsigned char best_table_len[256]; static void usage(void) { fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] " - "[--base-relative] [--lto-clang] in.map > out.S\n"); + "[--lto-clang] in.map > out.S\n"); exit(1); } @@ -491,54 +490,43 @@ static void write_src(void) printf("\t.short\t%d\n", best_idx[i]); printf("\n"); - if (!base_relative) - output_label("kallsyms_addresses"); - else - output_label("kallsyms_offsets"); + output_label("kallsyms_offsets"); for (i = 0; i < table_cnt; i++) { - if (base_relative) { - /* - * Use the offset relative to the lowest value - * encountered of all relative symbols, and emit - * non-relocatable fixed offsets that will be fixed - * up at runtime. - */ + /* + * Use the offset relative to the lowest value + * encountered of all relative symbols, and emit + * non-relocatable fixed offsets that will be fixed + * up at runtime. + */ - long long offset; - int overflow; + long long offset; + int overflow; - if (!absolute_percpu) { - offset = table[i]->addr - relative_base; - overflow = (offset < 0 || offset > UINT_MAX); - } else if (symbol_absolute(table[i])) { - offset = table[i]->addr; - overflow = (offset < 0 || offset > INT_MAX); - } else { - offset = relative_base - table[i]->addr - 1; - overflow = (offset < INT_MIN || offset >= 0); - } - if (overflow) { - fprintf(stderr, "kallsyms failure: " - "%s symbol value %#llx out of range in relative mode\n", - symbol_absolute(table[i]) ? "absolute" : "relative", - table[i]->addr); - exit(EXIT_FAILURE); - } - printf("\t.long\t%#x /* %s */\n", (int)offset, table[i]->sym); - } else if (!symbol_absolute(table[i])) { - output_address(table[i]->addr); + if (!absolute_percpu) { + offset = table[i]->addr - relative_base; + overflow = (offset < 0 || offset > UINT_MAX); + } else if (symbol_absolute(table[i])) { + offset = table[i]->addr; + overflow = (offset < 0 || offset > INT_MAX); } else { - printf("\tPTR\t%#llx\n", table[i]->addr); + offset = relative_base - table[i]->addr - 1; + overflow = (offset < INT_MIN || offset >= 0); } + if (overflow) { + fprintf(stderr, "kallsyms failure: " + "%s symbol value %#llx out of range in relative mode\n", + symbol_absolute(table[i]) ? "absolute" : "relative", + table[i]->addr); + exit(EXIT_FAILURE); + } + printf("\t.long\t%#x /* %s */\n", (int)offset, table[i]->sym); } printf("\n"); - if (base_relative) { - output_label("kallsyms_relative_base"); - output_address(relative_base); - printf("\n"); - } + output_label("kallsyms_relative_base"); + output_address(relative_base); + printf("\n"); if (lto_clang) for (i = 0; i < table_cnt; i++) @@ -820,7 +808,6 @@ int main(int argc, char **argv) static const struct option long_options[] = { {"all-symbols", no_argument, &all_symbols, 1}, {"absolute-percpu", no_argument, &absolute_percpu, 1}, - {"base-relative", no_argument, &base_relative, 1}, {"lto-clang", no_argument, <o_clang, 1}, {}, }; @@ -841,8 +828,7 @@ int main(int argc, char **argv) if (absolute_percpu) make_percpus_absolute(); sort_symbols(); - if (base_relative) - record_relative_base(); + record_relative_base(); optimize_token_table(); write_src(); diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index c64158a04f82..f7b2503cdba9 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -156,10 +156,6 @@ kallsyms() kallsymopt="${kallsymopt} --absolute-percpu" fi - if is_enabled CONFIG_KALLSYMS_BASE_RELATIVE; then - kallsymopt="${kallsymopt} --base-relative" - fi - if is_enabled CONFIG_LTO_CLANG; then kallsymopt="${kallsymopt} --lto-clang" fi diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index e30fd55f8e51..cd3b480d20bd 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -26,7 +26,6 @@ static bool is_ignored_symbol(const char *name, char type) * when --all-symbols is specified so exclude them to get a * stable symbol list. */ - "kallsyms_addresses", "kallsyms_offsets", "kallsyms_relative_base", "kallsyms_num_syms", From 301c10908e42657c3e6142055aa772adab6a6ef7 Mon Sep 17 00:00:00 2001 From: Rafael Aquini Date: Tue, 16 Jul 2024 08:25:05 -0400 Subject: [PATCH 128/155] kbuild: rpm-pkg: introduce a simple changelog section for kernel.spec Fix the following rpmbuild warning: $ make srcrpm-pkg ... RPM build warnings: source_date_epoch_from_changelog set but %changelog is missing Signed-off-by: Rafael Aquini Signed-off-by: Masahiro Yamada --- scripts/package/mkspec | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/scripts/package/mkspec b/scripts/package/mkspec index 77d25dda37e3..ead54d67a024 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -30,3 +30,26 @@ cat</dev/null || hostname)} + builduser=${KBUILD_BUILD_USER:-$(id -nu)} + email="${builduser}@${buildhost}" +fi + +cat << EOF + +%changelog +* $(LC_ALL=C; date +'%a %b %d %Y') ${name} <${email}> +- Custom built Linux kernel. +EOF From 9d0d266046571f4b3e733c7eb9cf7c959f37fbdd Mon Sep 17 00:00:00 2001 From: HONG Yifan Date: Wed, 17 Jul 2024 01:50:41 +0000 Subject: [PATCH 129/155] kconfig: recursive checks drop file/lineno This prevents segfault when getting filename and lineno in recursive checks. If the following snippet is found in Kconfig: [Test code 1] config FOO bool depends on BAR select BAR ... without BAR defined; then there is a segfault. Kconfig:34:error: recursive dependency detected! Kconfig:34: symbol FOO depends on BAR make[4]: *** [scripts/kconfig/Makefile:85: allnoconfig] Segmentation fault This is because of the following. BAR is a fake entry created by sym_lookup() with prop being NULL. In the recursive check, there is a NULL check for prop to fall back to stack->sym->prop if stack->prop is NULL. However, in this case, stack->sym points to the fake BAR entry created by sym_lookup(), so prop is still NULL. prop was then referenced without additional NULL checks, causing segfault. As the previous email thread suggests, the file and lineno for select is also wrong: [Test code 2] config FOO bool config BAR bool config FOO bool "FOO" depends on BAR select BAR $ make defconfig *** Default configuration is based on 'x86_64_defconfig' Kconfig:1:error: recursive dependency detected! Kconfig:1: symbol FOO depends on BAR Kconfig:4: symbol BAR is selected by FOO [...] Kconfig:4 should be Kconfig:10. This patch deletes the wrong and segfault-prone filename/lineno inference completely. With this patch, Test code 1 yields: error: recursive dependency detected! symbol FOO depends on BAR symbol BAR is selected by FOO Signed-off-by: HONG Yifan Signed-off-by: Masahiro Yamada --- scripts/kconfig/symbol.c | 40 +++++-------------- .../tests/err_recursive_dep/expected_stderr | 36 ++++++++--------- 2 files changed, 29 insertions(+), 47 deletions(-) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 6c6f238c4f7b..71502abd3b12 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -1074,10 +1074,9 @@ static void sym_check_print_recursive(struct symbol *last_sym) { struct dep_stack *stack; struct symbol *sym, *next_sym; - struct menu *menu = NULL; struct menu *choice; - struct property *prop; struct dep_stack cv_stack; + enum prop_type type; choice = sym_get_choice_menu(last_sym); if (choice) { @@ -1096,53 +1095,36 @@ static void sym_check_print_recursive(struct symbol *last_sym) for (; stack; stack = stack->next) { sym = stack->sym; next_sym = stack->next ? stack->next->sym : last_sym; - prop = stack->prop; - if (prop == NULL) - prop = stack->sym->prop; + type = stack->prop ? stack->prop->type : P_UNKNOWN; - /* for choice values find the menu entry (used below) */ - if (sym_is_choice(sym) || sym_is_choice_value(sym)) { - for (prop = sym->prop; prop; prop = prop->next) { - menu = prop->menu; - if (prop->menu) - break; - } - } if (stack->sym == last_sym) - fprintf(stderr, "%s:%d:error: recursive dependency detected!\n", - prop->filename, prop->lineno); + fprintf(stderr, "error: recursive dependency detected!\n"); if (sym_is_choice(next_sym)) { choice = list_first_entry(&next_sym->menus, struct menu, link); - fprintf(stderr, "%s:%d:\tsymbol %s is part of choice block at %s:%d\n", - menu->filename, menu->lineno, + fprintf(stderr, "\tsymbol %s is part of choice block at %s:%d\n", sym->name ? sym->name : "", choice->filename, choice->lineno); } else if (stack->expr == &sym->dir_dep.expr) { - fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n", - prop->filename, prop->lineno, + fprintf(stderr, "\tsymbol %s depends on %s\n", sym->name ? sym->name : "", next_sym->name); } else if (stack->expr == &sym->rev_dep.expr) { - fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n", - prop->filename, prop->lineno, + fprintf(stderr, "\tsymbol %s is selected by %s\n", sym->name, next_sym->name); } else if (stack->expr == &sym->implied.expr) { - fprintf(stderr, "%s:%d:\tsymbol %s is implied by %s\n", - prop->filename, prop->lineno, + fprintf(stderr, "\tsymbol %s is implied by %s\n", sym->name, next_sym->name); } else if (stack->expr) { - fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n", - prop->filename, prop->lineno, + fprintf(stderr, "\tsymbol %s %s value contains %s\n", sym->name ? sym->name : "", - prop_get_type_name(prop->type), + prop_get_type_name(type), next_sym->name); } else { - fprintf(stderr, "%s:%d:\tsymbol %s %s is visible depending on %s\n", - prop->filename, prop->lineno, + fprintf(stderr, "\tsymbol %s %s is visible depending on %s\n", sym->name ? sym->name : "", - prop_get_type_name(prop->type), + prop_get_type_name(type), next_sym->name); } } diff --git a/scripts/kconfig/tests/err_recursive_dep/expected_stderr b/scripts/kconfig/tests/err_recursive_dep/expected_stderr index 05d4ced70320..fc2e860af082 100644 --- a/scripts/kconfig/tests/err_recursive_dep/expected_stderr +++ b/scripts/kconfig/tests/err_recursive_dep/expected_stderr @@ -1,38 +1,38 @@ -Kconfig:5:error: recursive dependency detected! -Kconfig:5: symbol A depends on A +error: recursive dependency detected! + symbol A depends on A For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:11:error: recursive dependency detected! -Kconfig:11: symbol B is selected by B +error: recursive dependency detected! + symbol B is selected by B For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:17:error: recursive dependency detected! -Kconfig:17: symbol C1 depends on C2 -Kconfig:21: symbol C2 depends on C1 +error: recursive dependency detected! + symbol C1 depends on C2 + symbol C2 depends on C1 For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:27:error: recursive dependency detected! -Kconfig:27: symbol D1 depends on D2 -Kconfig:32: symbol D2 is selected by D1 +error: recursive dependency detected! + symbol D1 depends on D2 + symbol D2 is selected by D1 For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:37:error: recursive dependency detected! -Kconfig:37: symbol E1 depends on E2 -Kconfig:42: symbol E2 is implied by E1 +error: recursive dependency detected! + symbol E1 depends on E2 + symbol E2 is implied by E1 For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:49:error: recursive dependency detected! -Kconfig:49: symbol F1 default value contains F2 -Kconfig:51: symbol F2 depends on F1 +error: recursive dependency detected! + symbol F1 default value contains F2 + symbol F2 depends on F1 For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" -Kconfig:60:error: recursive dependency detected! -Kconfig:60: symbol G depends on G +error: recursive dependency detected! + symbol G depends on G For a resolution refer to Documentation/kbuild/kconfig-language.rst subsection "Kconfig recursive dependency limitations" From 44ad2814ca58fc43ab473d8fbb3b46a2b39a0392 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 18 Jul 2024 03:13:22 +0900 Subject: [PATCH 130/155] kbuild: clean up scripts/remove-stale-files These lines have been here for more than a year. Remove them. Signed-off-by: Masahiro Yamada --- scripts/remove-stale-files | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/scripts/remove-stale-files b/scripts/remove-stale-files index 385610fe3936..f38d26b78c2a 100755 --- a/scripts/remove-stale-files +++ b/scripts/remove-stale-files @@ -20,22 +20,4 @@ set -e # yard. Stale files stay in this file for a while (for some release cycles?), # then will be really dead and removed from the code base entirely. -rm -f arch/powerpc/purgatory/kexec-purgatory.c -rm -f arch/riscv/purgatory/kexec-purgatory.c -rm -f arch/x86/purgatory/kexec-purgatory.c - -rm -f scripts/extract-cert - -rm -f scripts/kconfig/[gmnq]conf-cfg - -rm -f rust/target.json - -rm -f scripts/bin2c - -rm -f .scmversion - -rm -rf include/ksym - -find . -name '*.usyms' | xargs rm -f - rm -f *.spec From 6e6ef2da3a28f3e02fd204b4f8821030b61f8cd4 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 18 Jul 2024 03:28:19 +0900 Subject: [PATCH 131/155] Makefile: add comment to discourage tools/* addition for kernel builds Kbuild provides scripts/Makefile.host to build host programs used for building the kernel. Unfortunately, there are two exceptions that opt out of Kbuild. The build system under tools/ is a cheesy replica, and cause issues. I was recently poked about a problem in the tools build system, which I do not maintain (and nobody maintains). [1] Without a comment, people might believe this is the right location because that is where objtool lives, even if a more robust Kbuild syntax satisfies their needs. [2] [1]: https://lore.kernel.org/linux-kbuild/ZnIYWBgrJ-IJtqK8@google.com/T/#m8ece130dd0e23c6f2395ed89070161948dee8457 [2]: https://lore.kernel.org/all/20240618200501.GA1611012@google.com/ Signed-off-by: Masahiro Yamada Acked-by: Nicolas Schier Reviewed-by: Brian Norris Reviewed-by: Sami Tolvanen --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 7372ea45ed3f..c97d6404b891 100644 --- a/Makefile +++ b/Makefile @@ -1328,6 +1328,12 @@ prepare: tools/bpf/resolve_btfids endif endif +# The tools build system is not a part of Kbuild and tends to introduce +# its own unique issues. If you need to integrate a new tool into Kbuild, +# please consider locating that tool outside the tools/ tree and using the +# standard Kbuild "hostprogs" syntax instead of adding a new tools/* entry +# here. See Documentation/kbuild/makefiles.rst for details. + PHONY += resolve_btfids_clean resolve_btfids_O = $(abspath $(objtree))/tools/bpf/resolve_btfids From fbaf242c956aff6a07d9e97eaa3a0a48d947de33 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 20 Jul 2024 16:27:38 +0900 Subject: [PATCH 132/155] kbuild: move some helper headers from scripts/kconfig/ to scripts/include/ Move array_size.h, hashtable.h, list.h, list_types.h from scripts/kconfig/ to scripts/include/. These headers will be useful for other host programs. Remove scripts/mod/list.h. Signed-off-by: Masahiro Yamada --- MAINTAINERS | 1 + Makefile | 6 +- scripts/{kconfig => include}/array_size.h | 0 scripts/{kconfig => include}/hashtable.h | 0 scripts/{kconfig => include}/list.h | 0 scripts/{kconfig => include}/list_types.h | 0 scripts/kconfig/expr.h | 3 +- scripts/kconfig/internal.h | 2 +- scripts/kconfig/mconf.c | 2 +- scripts/kconfig/menu.c | 2 +- scripts/kconfig/mnconf-common.c | 2 +- scripts/kconfig/mnconf-common.h | 2 + scripts/kconfig/nconf.c | 2 +- scripts/kconfig/preprocess.c | 4 +- scripts/kconfig/util.c | 2 +- scripts/mod/list.h | 213 ---------------------- scripts/mod/modpost.c | 2 + scripts/mod/modpost.h | 2 +- 18 files changed, 19 insertions(+), 226 deletions(-) rename scripts/{kconfig => include}/array_size.h (100%) rename scripts/{kconfig => include}/hashtable.h (100%) rename scripts/{kconfig => include}/list.h (100%) rename scripts/{kconfig => include}/list_types.h (100%) delete mode 100644 scripts/mod/list.h diff --git a/MAINTAINERS b/MAINTAINERS index da5352dbd4f3..0fe40cf1929f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11943,6 +11943,7 @@ F: scripts/Makefile* F: scripts/basic/ F: scripts/clang-tools/ F: scripts/dummy-tools/ +F: scripts/include/ F: scripts/mk* F: scripts/mod/ F: scripts/package/ diff --git a/Makefile b/Makefile index c97d6404b891..cbad2bbe4561 100644 --- a/Makefile +++ b/Makefile @@ -458,8 +458,10 @@ export rust_common_flags := --edition=2021 \ -Dclippy::no_mangle_with_rust_abi \ -Wclippy::dbg_macro -KBUILD_HOSTCFLAGS := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) $(HOSTCFLAGS) -KBUILD_HOSTCXXFLAGS := -Wall -O2 $(HOST_LFS_CFLAGS) $(HOSTCXXFLAGS) +KBUILD_HOSTCFLAGS := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) \ + $(HOSTCFLAGS) -I $(srctree)/scripts/include +KBUILD_HOSTCXXFLAGS := -Wall -O2 $(HOST_LFS_CFLAGS) $(HOSTCXXFLAGS) \ + -I $(srctree)/scripts/include KBUILD_HOSTRUSTFLAGS := $(rust_common_flags) -O -Cstrip=debuginfo \ -Zallow-features= $(HOSTRUSTFLAGS) KBUILD_HOSTLDFLAGS := $(HOST_LFS_LDFLAGS) $(HOSTLDFLAGS) diff --git a/scripts/kconfig/array_size.h b/scripts/include/array_size.h similarity index 100% rename from scripts/kconfig/array_size.h rename to scripts/include/array_size.h diff --git a/scripts/kconfig/hashtable.h b/scripts/include/hashtable.h similarity index 100% rename from scripts/kconfig/hashtable.h rename to scripts/include/hashtable.h diff --git a/scripts/kconfig/list.h b/scripts/include/list.h similarity index 100% rename from scripts/kconfig/list.h rename to scripts/include/list.h diff --git a/scripts/kconfig/list_types.h b/scripts/include/list_types.h similarity index 100% rename from scripts/kconfig/list_types.h rename to scripts/include/list_types.h diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 6e47e0ad6e6e..2bc96cd28253 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -12,12 +12,11 @@ extern "C" { #include #include -#include "list_types.h" #ifndef __cplusplus #include #endif -#include "list_types.h" +#include typedef enum tristate { no, mod, yes diff --git a/scripts/kconfig/internal.h b/scripts/kconfig/internal.h index 6c721c4cfd72..02106eb7815e 100644 --- a/scripts/kconfig/internal.h +++ b/scripts/kconfig/internal.h @@ -2,7 +2,7 @@ #ifndef INTERNAL_H #define INTERNAL_H -#include "hashtable.h" +#include #define SYMBOL_HASHSIZE (1U << 14) diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index 4a0a97bb342f..3887eac75289 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -19,7 +19,7 @@ #include #include -#include "list.h" +#include #include "lkc.h" #include "lxdialog/dialog.h" #include "mnconf-common.h" diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index cd34cc5aefcf..323cc0b62be6 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -8,9 +8,9 @@ #include #include +#include #include "lkc.h" #include "internal.h" -#include "list.h" static const char nohelp_text[] = "There is no help available for this option."; diff --git a/scripts/kconfig/mnconf-common.c b/scripts/kconfig/mnconf-common.c index 18cb9a6c5aaa..8e24b07121df 100644 --- a/scripts/kconfig/mnconf-common.c +++ b/scripts/kconfig/mnconf-common.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include "expr.h" -#include "list.h" #include "mnconf-common.h" int jump_key_char; diff --git a/scripts/kconfig/mnconf-common.h b/scripts/kconfig/mnconf-common.h index ab6292cc4bf2..53bd7292e931 100644 --- a/scripts/kconfig/mnconf-common.h +++ b/scripts/kconfig/mnconf-common.h @@ -4,6 +4,8 @@ #include +#include + struct search_data { struct list_head *head; struct menu *target; diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index 1456e24969aa..b91ca47e9e9a 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -11,7 +11,7 @@ #include #include -#include "list.h" +#include #include "lkc.h" #include "mnconf-common.h" #include "nconf.h" diff --git a/scripts/kconfig/preprocess.c b/scripts/kconfig/preprocess.c index f0a4a218c4a5..67d1fb95c491 100644 --- a/scripts/kconfig/preprocess.c +++ b/scripts/kconfig/preprocess.c @@ -9,9 +9,9 @@ #include #include -#include "array_size.h" +#include +#include #include "internal.h" -#include "list.h" #include "lkc.h" #include "preprocess.h" diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c index 1ea78927121d..696ff477671e 100644 --- a/scripts/kconfig/util.c +++ b/scripts/kconfig/util.c @@ -8,7 +8,7 @@ #include #include -#include "hashtable.h" +#include #include "lkc.h" unsigned int strhash(const char *s) diff --git a/scripts/mod/list.h b/scripts/mod/list.h deleted file mode 100644 index a924a6c4aa4d..000000000000 --- a/scripts/mod/list.h +++ /dev/null @@ -1,213 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef LIST_H -#define LIST_H - -#include -#include - -/* Are two types/vars the same type (ignoring qualifiers)? */ -#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) - -/** - * container_of - cast a member of a structure out to the containing structure - * @ptr: the pointer to the member. - * @type: the type of the container struct this is embedded in. - * @member: the name of the member within the struct. - * - */ -#define container_of(ptr, type, member) ({ \ - void *__mptr = (void *)(ptr); \ - _Static_assert(__same_type(*(ptr), ((type *)0)->member) || \ - __same_type(*(ptr), void), \ - "pointer type mismatch in container_of()"); \ - ((type *)(__mptr - offsetof(type, member))); }) - -#define LIST_POISON1 ((void *) 0x100) -#define LIST_POISON2 ((void *) 0x122) - -/* - * Circular doubly linked list implementation. - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ - -struct list_head { - struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -/** - * INIT_LIST_HEAD - Initialize a list_head structure - * @list: list_head structure to be initialized. - * - * Initializes the list_head to point to itself. If it is a list header, - * the result is an empty list. - */ -static inline void INIT_LIST_HEAD(struct list_head *list) -{ - list->next = list; - list->prev = list; -} - -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -static inline void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} - -/** - * list_add_tail - add a new entry - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) -{ - __list_add(new, head->prev, head); -} - -/* - * Delete a list entry by making the prev/next entries - * point to each other. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_del(struct list_head *prev, struct list_head *next) -{ - next->prev = prev; - prev->next = next; -} - -static inline void __list_del_entry(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * Note: list_empty() on entry does not return true after this, the entry is - * in an undefined state. - */ -static inline void list_del(struct list_head *entry) -{ - __list_del_entry(entry); - entry->next = LIST_POISON1; - entry->prev = LIST_POISON2; -} - -/** - * list_is_head - tests whether @list is the list @head - * @list: the entry to test - * @head: the head of the list - */ -static inline int list_is_head(const struct list_head *list, const struct list_head *head) -{ - return list == head; -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static inline int list_empty(const struct list_head *head) -{ - return head->next == head; -} - -/** - * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_head within the struct. - */ -#define list_entry(ptr, type, member) \ - container_of(ptr, type, member) - -/** - * list_first_entry - get the first element from a list - * @ptr: the list head to take the element from. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_head within the struct. - * - * Note, that list is expected to be not empty. - */ -#define list_first_entry(ptr, type, member) \ - list_entry((ptr)->next, type, member) - -/** - * list_next_entry - get the next element in list - * @pos: the type * to cursor - * @member: the name of the list_head within the struct. - */ -#define list_next_entry(pos, member) \ - list_entry((pos)->member.next, typeof(*(pos)), member) - -/** - * list_entry_is_head - test if the entry points to the head of the list - * @pos: the type * to cursor - * @head: the head for your list. - * @member: the name of the list_head within the struct. - */ -#define list_entry_is_head(pos, head, member) \ - (&pos->member == (head)) - -/** - * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop cursor. - * @head: the head for your list. - * @member: the name of the list_head within the struct. - */ -#define list_for_each_entry(pos, head, member) \ - for (pos = list_first_entry(head, typeof(*pos), member); \ - !list_entry_is_head(pos, head, member); \ - pos = list_next_entry(pos, member)) - -/** - * list_for_each_entry_safe - iterate over list of given type. Safe against removal of list entry - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_head within the struct. - */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_first_entry(head, typeof(*pos), member), \ - n = list_next_entry(pos, member); \ - !list_entry_is_head(pos, head, member); \ - pos = n, n = list_next_entry(n, member)) - -#endif /* LIST_H */ diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 3e5313ed6065..9eade18b4388 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -20,6 +20,8 @@ #include #include #include + +#include #include "modpost.h" #include "../../include/linux/license.h" diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index ee43c7950636..58197b34a3c8 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -13,7 +13,7 @@ #include #include "../../include/linux/module_symbol.h" -#include "list.h" +#include #include "elfconfig.h" /* On BSD-alike OSes elf.h defines these according to host's word size */ From 3554a45297c0f6c5de5dfdba0d218b0eb9274207 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 20 Jul 2024 16:27:39 +0900 Subject: [PATCH 133/155] modpost: use generic macros for hash table implementation Use macros provided by hashtable.h Signed-off-by: Masahiro Yamada --- scripts/mod/modpost.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 9eade18b4388..b78d93919712 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -21,6 +21,7 @@ #include #include +#include #include #include "modpost.h" #include "../../include/linux/license.h" @@ -201,13 +202,8 @@ static struct module *new_module(const char *name, size_t namelen) return mod; } -/* A hash of all exported symbols, - * struct symbol is also used for lists of unresolved symbols */ - -#define SYMBOL_HASH_SIZE 1024 - struct symbol { - struct symbol *next; + struct hlist_node hnode;/* link to hash table */ struct list_head list; /* link to module::exported_symbols or module::unresolved_symbols */ struct module *module; char *namespace; @@ -220,7 +216,7 @@ struct symbol { char name[]; }; -static struct symbol *symbolhash[SYMBOL_HASH_SIZE]; +static HASHTABLE_DEFINE(symbol_hashtable, 1U << 10); /* This is based on the hash algorithm from gdbm, via tdb */ static inline unsigned int tdb_hash(const char *name) @@ -252,11 +248,7 @@ static struct symbol *alloc_symbol(const char *name) /* For the hash of exported symbols */ static void hash_add_symbol(struct symbol *sym) { - unsigned int hash; - - hash = tdb_hash(sym->name) % SYMBOL_HASH_SIZE; - sym->next = symbolhash[hash]; - symbolhash[hash] = sym; + hash_add(symbol_hashtable, &sym->hnode, tdb_hash(sym->name)); } static void sym_add_unresolved(const char *name, struct module *mod, bool weak) @@ -277,7 +269,7 @@ static struct symbol *sym_find_with_module(const char *name, struct module *mod) if (name[0] == '.') name++; - for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next) { + hash_for_each_possible(symbol_hashtable, s, hnode, tdb_hash(name)) { if (strcmp(s->name, name) == 0 && (!mod || s->module == mod)) return s; } From c8578539debaedfbb4671e1954be8ebbd1307c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 20 Jul 2024 11:18:12 +0200 Subject: [PATCH 134/155] kbuild: add script and target to generate pacman package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pacman is the package manager used by Arch Linux and its derivates. Creating native packages from the kernel tree has multiple advantages: * The package triggers the correct hooks for initramfs generation and bootloader configuration * Uninstallation is complete and also invokes the relevant hooks * New UAPI headers can be installed without any manual bookkeeping The PKGBUILD file is a modified version of the one used for the downstream Arch Linux "linux" package. Extra steps that should not be necessary for a development kernel have been removed and an UAPI header package has been added. Signed-off-by: Thomas Weißschuh Reviewed-by: Nathan Chancellor Tested-by: Nathan Chancellor Signed-off-by: Masahiro Yamada --- .gitignore | 6 +++ MAINTAINERS | 7 +++ Makefile | 2 +- scripts/Makefile.package | 14 +++++ scripts/package/PKGBUILD | 108 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 scripts/package/PKGBUILD diff --git a/.gitignore b/.gitignore index c59dc60ba62e..7902adf4f7f1 100644 --- a/.gitignore +++ b/.gitignore @@ -92,6 +92,12 @@ modules.order # /tar-install/ +# +# pacman files (make pacman-pkg) +# +/PKGBUILD +/pacman/ + # # We don't want to ignore the following even if they are dot-files # diff --git a/MAINTAINERS b/MAINTAINERS index 0fe40cf1929f..a48ae15e5bdb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11998,6 +11998,13 @@ F: include/uapi/linux/nfsd/ F: include/uapi/linux/sunrpc/ F: net/sunrpc/ +KERNEL PACMAN PACKAGING (in addition to generic KERNEL BUILD) +M: Thomas Weißschuh +R: Christian Heusel +R: Nathan Chancellor +S: Maintained +F: scripts/package/PKGBUILD + KERNEL REGRESSIONS M: Thorsten Leemhuis L: regressions@lists.linux.dev diff --git a/Makefile b/Makefile index cbad2bbe4561..2750024208be 100644 --- a/Makefile +++ b/Makefile @@ -1489,7 +1489,7 @@ CLEAN_FILES += vmlinux.symvers modules-only.symvers \ # Directories & files removed with 'make mrproper' MRPROPER_FILES += include/config include/generated \ arch/$(SRCARCH)/include/generated .objdiff \ - debian snap tar-install \ + debian snap tar-install PKGBUILD pacman \ .config .config.old .version \ Module.symvers \ certs/signing_key.pem \ diff --git a/scripts/Makefile.package b/scripts/Makefile.package index bf016af8bf8a..4a80584ec771 100644 --- a/scripts/Makefile.package +++ b/scripts/Makefile.package @@ -141,6 +141,19 @@ snap-pkg: cd $(objtree)/snap && \ snapcraft --target-arch=$(UTS_MACHINE) +# pacman-pkg +# --------------------------------------------------------------------------- + +PHONY += pacman-pkg +pacman-pkg: + @ln -srf $(srctree)/scripts/package/PKGBUILD $(objtree)/PKGBUILD + +objtree="$(realpath $(objtree))" \ + BUILDDIR="$(realpath $(objtree))/pacman" \ + CARCH="$(UTS_MACHINE)" \ + KBUILD_MAKEFLAGS="$(MAKEFLAGS)" \ + KBUILD_REVISION="$(shell $(srctree)/scripts/build-version)" \ + makepkg $(MAKEPKGOPTS) + # dir-pkg tar*-pkg - tarball targets # --------------------------------------------------------------------------- @@ -221,6 +234,7 @@ help: @echo ' bindeb-pkg - Build only the binary kernel deb package' @echo ' snap-pkg - Build only the binary kernel snap package' @echo ' (will connect to external hosts)' + @echo ' pacman-pkg - Build only the binary kernel pacman package' @echo ' dir-pkg - Build the kernel as a plain directory structure' @echo ' tar-pkg - Build the kernel as an uncompressed tarball' @echo ' targz-pkg - Build the kernel as a gzip compressed tarball' diff --git a/scripts/package/PKGBUILD b/scripts/package/PKGBUILD new file mode 100644 index 000000000000..663ce300dd06 --- /dev/null +++ b/scripts/package/PKGBUILD @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Maintainer: Thomas Weißschuh +# Contributor: Jan Alexander Steffens (heftig) + +pkgbase=${PACMAN_PKGBASE:-linux-upstream} +pkgname=("${pkgbase}" "${pkgbase}-api-headers") +if grep -q CONFIG_MODULES=y include/config/auto.conf; then + pkgname+=("${pkgbase}-headers") +fi +pkgver="${KERNELRELEASE//-/_}" +# The PKGBUILD is evaluated multiple times. +# Running scripts/build-version from here would introduce inconsistencies. +pkgrel="${KBUILD_REVISION}" +pkgdesc='Upstream Linux' +url='https://www.kernel.org/' +# Enable flexible cross-compilation +arch=(${CARCH}) +license=(GPL-2.0-only) +makedepends=( + bc + bison + cpio + flex + gettext + kmod + libelf + openssl + pahole + perl + python + rsync + tar +) +options=(!debug !strip !buildflags !makeflags) + +build() { + # MAKEFLAGS from makepkg.conf override the ones inherited from kbuild. + # Bypass this override with a custom variable. + export MAKEFLAGS="${KBUILD_MAKEFLAGS}" + cd "${objtree}" + + ${MAKE} KERNELRELEASE="${KERNELRELEASE}" KBUILD_BUILD_VERSION="${pkgrel}" +} + +_package() { + pkgdesc="The ${pkgdesc} kernel and modules" + + export MAKEFLAGS="${KBUILD_MAKEFLAGS}" + cd "${objtree}" + local modulesdir="${pkgdir}/usr/${MODLIB}" + + echo "Installing boot image..." + # systemd expects to find the kernel here to allow hibernation + # https://github.com/systemd/systemd/commit/edda44605f06a41fb86b7ab8128dcf99161d2344 + install -Dm644 "$(${MAKE} -s image_name)" "${modulesdir}/vmlinuz" + + # Used by mkinitcpio to name the kernel + echo "${pkgbase}" > "${modulesdir}/pkgbase" + + echo "Installing modules..." + ${MAKE} INSTALL_MOD_PATH="${pkgdir}/usr" INSTALL_MOD_STRIP=1 \ + DEPMOD=true modules_install + + if [ -d "${srctree}/arch/${SRCARCH}/boot/dts" ]; then + echo "Installing dtbs..." + ${MAKE} INSTALL_DTBS_PATH="${modulesdir}/dtb" dtbs_install + fi + + # remove build link, will be part of -headers package + rm -f "${modulesdir}/build" +} + +_package-headers() { + pkgdesc="Headers and scripts for building modules for the ${pkgdesc} kernel" + + export MAKEFLAGS="${KBUILD_MAKEFLAGS}" + cd "${objtree}" + local builddir="${pkgdir}/usr/${MODLIB}/build" + + echo "Installing build files..." + "${srctree}/scripts/package/install-extmod-build" "${builddir}" + + echo "Installing System.map and config..." + cp System.map "${builddir}/System.map" + cp .config "${builddir}/.config" + + echo "Adding symlink..." + mkdir -p "${pkgdir}/usr/src" + ln -sr "${builddir}" "${pkgdir}/usr/src/${pkgbase}" +} + +_package-api-headers() { + pkgdesc="Kernel headers sanitized for use in userspace" + provides=(linux-api-headers) + conflicts=(linux-api-headers) + + export MAKEFLAGS="${KBUILD_MAKEFLAGS}" + cd "${objtree}" + + ${MAKE} headers_install INSTALL_HDR_PATH="${pkgdir}/usr" +} + +for _p in "${pkgname[@]}"; do + eval "package_$_p() { + $(declare -f "_package${_p#$pkgbase}") + _package${_p#$pkgbase} + }" +done From bde6fb37bbc3a41eb63b1e4d06e2ff217d4a2f18 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 20 Jul 2024 19:30:12 +0900 Subject: [PATCH 135/155] kallsyms: avoid repeated calculation of array size for markers Introduce the markers_cnt variable for readability. No functional change intended. Signed-off-by: Masahiro Yamada --- scripts/kallsyms.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 55a423519f2e..f0ea8c922dc8 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -393,7 +393,7 @@ static void write_src(void) { unsigned int i, k, off; unsigned int best_idx[256]; - unsigned int *markers; + unsigned int *markers, markers_cnt; char buf[KSYM_NAME_LEN]; printf("#include \n"); @@ -413,7 +413,8 @@ static void write_src(void) /* table of offset markers, that give the offset in the compressed stream * every 256 symbols */ - markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256)); + markers_cnt = (table_cnt + 255) / 256; + markers = malloc(sizeof(*markers) * markers_cnt); if (!markers) { fprintf(stderr, "kallsyms failure: " "unable to allocate required memory\n"); @@ -469,7 +470,7 @@ static void write_src(void) } output_label("kallsyms_markers"); - for (i = 0; i < ((table_cnt + 255) >> 8); i++) + for (i = 0; i < markers_cnt; i++) printf("\t.long\t%u\n", markers[i]); printf("\n"); From b07e18100940418803d289a91c7d859af006b8e7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 20 Jul 2024 19:30:13 +0900 Subject: [PATCH 136/155] kallsyms: use \t instead of a tab in printf() This string literal uses a mixture of \t escape sequences and a tab. Use \t consistently. Signed-off-by: Masahiro Yamada --- scripts/kallsyms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index f0ea8c922dc8..164c04d22061 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -521,7 +521,7 @@ static void write_src(void) table[i]->addr); exit(EXIT_FAILURE); } - printf("\t.long\t%#x /* %s */\n", (int)offset, table[i]->sym); + printf("\t.long\t%#x\t/* %s */\n", (int)offset, table[i]->sym); } printf("\n"); From f9c3d671dd816ea4d5604dd04ef98d42921656a7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 20 Jul 2024 19:30:14 +0900 Subject: [PATCH 137/155] kallsyms: add more original symbol type/name in comment lines Commit bea5b7450474 ("kallsyms: expand symbol name into comment for debugging") added the uncompressed type/name in the comment lines of kallsyms_offsets. It would be useful to do the same for kallsyms_names and kallsyms_seqs_of_names. Signed-off-by: Masahiro Yamada --- scripts/kallsyms.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 164c04d22061..e291e34a450b 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -456,18 +456,16 @@ static void write_src(void) } for (k = 0; k < table[i]->len; k++) printf(", 0x%02x", table[i]->sym[k]); - printf("\n"); - } - printf("\n"); - /* - * Now that we wrote out the compressed symbol names, restore the - * original names, which are needed in some of the later steps. - */ - for (i = 0; i < table_cnt; i++) { + /* + * Now that we wrote out the compressed symbol name, restore the + * original name and print it in the comment. + */ expand_symbol(table[i]->sym, table[i]->len, buf); strcpy((char *)table[i]->sym, buf); + printf("\t/* %s */\n", table[i]->sym); } + printf("\n"); output_label("kallsyms_markers"); for (i = 0; i < markers_cnt; i++) @@ -536,10 +534,11 @@ static void write_src(void) sort_symbols_by_name(); output_label("kallsyms_seqs_of_names"); for (i = 0; i < table_cnt; i++) - printf("\t.byte 0x%02x, 0x%02x, 0x%02x\n", + printf("\t.byte 0x%02x, 0x%02x, 0x%02x\t/* %s */\n", (unsigned char)(table[i]->seq >> 16), (unsigned char)(table[i]->seq >> 8), - (unsigned char)(table[i]->seq >> 0)); + (unsigned char)(table[i]->seq >> 0), + table[i]->sym); printf("\n"); } From 1d48f8feb86ee773e65c6d54856bbfb7702bd35d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 21 Jul 2024 01:21:13 +0900 Subject: [PATCH 138/155] kallsyms: unify seq and start_pos fields of struct sym_entry The struct sym_entry uses the 'seq' and 'start_pos' fields to remember the index in the symbol table. They serve the same purpose and are not used simultaneously. Unify them. Signed-off-by: Masahiro Yamada --- scripts/kallsyms.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index e291e34a450b..9a0535c59702 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -36,7 +36,6 @@ struct sym_entry { unsigned long long addr; unsigned int len; unsigned int seq; - unsigned int start_pos; unsigned int percpu_absolute; unsigned char sym[]; }; @@ -278,7 +277,7 @@ static void read_map(const char *in) if (!sym) continue; - sym->start_pos = table_cnt; + sym->seq = table_cnt; if (table_cnt >= table_size) { table_size += 10000; @@ -762,7 +761,7 @@ static int compare_symbols(const void *a, const void *b) return wa - wb; /* sort by initial order, so that other symbols are left undisturbed */ - return sa->start_pos - sb->start_pos; + return sa->seq - sb->seq; } static void sort_symbols(void) From 1a7c8d2459edb8e082cbce1b7e516495ca913740 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 21 Jul 2024 01:21:14 +0900 Subject: [PATCH 139/155] kallsyms: change sym_entry::percpu_absolute to bool type This field is boolean. Signed-off-by: Masahiro Yamada --- scripts/kallsyms.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 9a0535c59702..0ed873491bf5 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -36,7 +36,7 @@ struct sym_entry { unsigned long long addr; unsigned int len; unsigned int seq; - unsigned int percpu_absolute; + bool percpu_absolute; unsigned char sym[]; }; @@ -181,7 +181,7 @@ static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len) sym->len = len; sym->sym[0] = type; strcpy(sym_name(sym), name); - sym->percpu_absolute = 0; + sym->percpu_absolute = false; return sym; } @@ -339,7 +339,7 @@ static int expand_symbol(const unsigned char *data, int len, char *result) return total; } -static int symbol_absolute(const struct sym_entry *s) +static bool symbol_absolute(const struct sym_entry *s) { return s->percpu_absolute; } @@ -781,7 +781,7 @@ static void make_percpus_absolute(void) * versions of this tool. */ table[i]->sym[0] = 'A'; - table[i]->percpu_absolute = 1; + table[i]->percpu_absolute = true; } } From b6f5ee4d53019443fb99dd23bc08680b1244ccfa Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sat, 20 Jul 2024 10:03:14 -0700 Subject: [PATCH 140/155] execve: Move KUnit tests to tests/ subdirectory Move the exec KUnit tests into a separate directory to avoid polluting the local directory namespace. Additionally update MAINTAINERS for the new files. Reviewed-by: David Gow Reviewed-by: SeongJae Park Acked-by: Christian Brauner Link: https://lore.kernel.org/r/20240720170310.it.942-kees@kernel.org Signed-off-by: Kees Cook --- MAINTAINERS | 3 ++- fs/binfmt_elf.c | 2 +- fs/exec.c | 2 +- fs/{binfmt_elf_test.c => tests/binfmt_elf_kunit.c} | 0 fs/{exec_test.c => tests/exec_kunit.c} | 0 5 files changed, 4 insertions(+), 3 deletions(-) rename fs/{binfmt_elf_test.c => tests/binfmt_elf_kunit.c} (100%) rename fs/{exec_test.c => tests/exec_kunit.c} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 8dfbe998f175..5fa7e129a59e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8220,7 +8220,8 @@ F: Documentation/userspace-api/ELF.rst F: fs/*binfmt_*.c F: fs/Kconfig.binfmt F: fs/exec.c -F: fs/exec_test.c +F: fs/tests/binfmt_*_kunit.c +F: fs/tests/exec_kunit.c F: include/linux/binfmts.h F: include/linux/elf.h F: include/uapi/linux/binfmts.h diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 40111451aa95..04e748c5955f 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -2152,5 +2152,5 @@ core_initcall(init_elf_binfmt); module_exit(exit_elf_binfmt); #ifdef CONFIG_BINFMT_ELF_KUNIT_TEST -#include "binfmt_elf_test.c" +#include "tests/binfmt_elf_kunit.c" #endif diff --git a/fs/exec.c b/fs/exec.c index 5b580ff8d955..32d6537ece07 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2244,5 +2244,5 @@ fs_initcall(init_fs_exec_sysctls); #endif /* CONFIG_SYSCTL */ #ifdef CONFIG_EXEC_KUNIT_TEST -#include "exec_test.c" +#include "tests/exec_kunit.c" #endif diff --git a/fs/binfmt_elf_test.c b/fs/tests/binfmt_elf_kunit.c similarity index 100% rename from fs/binfmt_elf_test.c rename to fs/tests/binfmt_elf_kunit.c diff --git a/fs/exec_test.c b/fs/tests/exec_kunit.c similarity index 100% rename from fs/exec_test.c rename to fs/tests/exec_kunit.c From 13c239a2c088e91e453d26517b562c9a116444fa Mon Sep 17 00:00:00 2001 From: Ivan Davydov Date: Mon, 22 Jul 2024 08:57:31 +0300 Subject: [PATCH 141/155] kbuild: doc: gcc to CC change In this part of the documentation, $(CC) is meant, but gcc is written. Signed-off-by: Ivan Davydov Signed-off-by: Masahiro Yamada --- Documentation/kbuild/makefiles.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index 991ce6081e35..be43990f1e7f 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -578,7 +578,7 @@ cc-option Note: cc-option uses KBUILD_CFLAGS for $(CC) options cc-option-yn - cc-option-yn is used to check if gcc supports a given option + cc-option-yn is used to check if $(CC) supports a given option and return "y" if supported, otherwise "n". Example:: @@ -596,7 +596,7 @@ cc-option-yn Note: cc-option-yn uses KBUILD_CFLAGS for $(CC) options cc-disable-warning - cc-disable-warning checks if gcc supports a given warning and returns + cc-disable-warning checks if $(CC) supports a given warning and returns the commandline switch to disable it. This special function is needed, because gcc 4.4 and later accept any unknown -Wno-* option and only warn about it if there is another warning in the source file. @@ -606,7 +606,7 @@ cc-disable-warning KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable) In the above example, -Wno-unused-but-set-variable will be added to - KBUILD_CFLAGS only if gcc really accepts it. + KBUILD_CFLAGS only if $(CC) really accepts it. gcc-min-version gcc-min-version tests if the value of $(CONFIG_GCC_VERSION) is greater than From a9d47a50cf257ff1019a4e30d573777882fd785c Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 18 Jul 2024 21:07:32 +0100 Subject: [PATCH 142/155] netfs: Revert "netfs: Switch debug logging to pr_debug()" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert commit 163eae0fb0d4c610c59a8de38040f8e12f89fd43 to get back the original operation of the debugging macros. Signed-off-by: David Howells Link: https://lore.kernel.org/r/20240608151352.22860-2-ukleinek@kernel.org Link: https://lore.kernel.org/r/1410685.1721333252@warthog.procyon.org.uk cc: Uwe Kleine-König cc: Christian Brauner cc: Jeff Layton cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner --- fs/netfs/buffered_read.c | 14 +++++++------- fs/netfs/buffered_write.c | 12 ++++++------ fs/netfs/direct_read.c | 2 +- fs/netfs/direct_write.c | 8 ++++---- fs/netfs/fscache_cache.c | 4 ++-- fs/netfs/fscache_cookie.c | 28 ++++++++++++++-------------- fs/netfs/fscache_io.c | 12 ++++++------ fs/netfs/fscache_main.c | 2 +- fs/netfs/fscache_volume.c | 4 ++-- fs/netfs/internal.h | 33 ++++++++++++++++++++++++++++++++- fs/netfs/io.c | 12 ++++++------ fs/netfs/main.c | 4 ++++ fs/netfs/misc.c | 4 ++-- fs/netfs/write_collect.c | 16 ++++++++-------- fs/netfs/write_issue.c | 36 ++++++++++++++++++------------------ 15 files changed, 113 insertions(+), 78 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index a6d5d07cd436..a688d4c75d99 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -117,7 +117,7 @@ void netfs_rreq_unlock_folios(struct netfs_io_request *rreq) if (!test_bit(NETFS_RREQ_DONT_UNLOCK_FOLIOS, &rreq->flags)) { if (folio->index == rreq->no_unlock_folio && test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) - kdebug("no unlock"); + _debug("no unlock"); else folio_unlock(folio); } @@ -204,7 +204,7 @@ void netfs_readahead(struct readahead_control *ractl) struct netfs_inode *ctx = netfs_inode(ractl->mapping->host); int ret; - kenter("%lx,%x", readahead_index(ractl), readahead_count(ractl)); + _enter("%lx,%x", readahead_index(ractl), readahead_count(ractl)); if (readahead_count(ractl) == 0) return; @@ -268,7 +268,7 @@ int netfs_read_folio(struct file *file, struct folio *folio) struct folio *sink = NULL; int ret; - kenter("%lx", folio->index); + _enter("%lx", folio->index); rreq = netfs_alloc_request(mapping, file, folio_pos(folio), folio_size(folio), @@ -508,7 +508,7 @@ retry: have_folio: *_folio = folio; - kleave(" = 0"); + _leave(" = 0"); return 0; error_put: @@ -518,7 +518,7 @@ error: folio_unlock(folio); folio_put(folio); } - kleave(" = %d", ret); + _leave(" = %d", ret); return ret; } EXPORT_SYMBOL(netfs_write_begin); @@ -536,7 +536,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio, size_t flen = folio_size(folio); int ret; - kenter("%zx @%llx", flen, start); + _enter("%zx @%llx", flen, start); ret = -ENOMEM; @@ -567,7 +567,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio, error_put: netfs_put_request(rreq, false, netfs_rreq_trace_put_discard); error: - kleave(" = %d", ret); + _leave(" = %d", ret); return ret; } diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 68a3f1383cee..4726c315453c 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -56,7 +56,7 @@ static enum netfs_how_to_modify netfs_how_to_modify(struct netfs_inode *ctx, struct netfs_group *group = netfs_folio_group(folio); loff_t pos = folio_pos(folio); - kenter(""); + _enter(""); if (group != netfs_group && group != NETFS_FOLIO_COPY_TO_CACHE) return NETFS_FLUSH_CONTENT; @@ -272,12 +272,12 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, */ howto = netfs_how_to_modify(ctx, file, folio, netfs_group, flen, offset, part, maybe_trouble); - kdebug("howto %u", howto); + _debug("howto %u", howto); switch (howto) { case NETFS_JUST_PREFETCH: ret = netfs_prefetch_for_write(file, folio, offset, part); if (ret < 0) { - kdebug("prefetch = %zd", ret); + _debug("prefetch = %zd", ret); goto error_folio_unlock; } break; @@ -418,7 +418,7 @@ out: } iocb->ki_pos += written; - kleave(" = %zd [%zd]", written, ret); + _leave(" = %zd [%zd]", written, ret); return written ? written : ret; error_folio_unlock: @@ -491,7 +491,7 @@ ssize_t netfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct netfs_inode *ictx = netfs_inode(inode); ssize_t ret; - kenter("%llx,%zx,%llx", iocb->ki_pos, iov_iter_count(from), i_size_read(inode)); + _enter("%llx,%zx,%llx", iocb->ki_pos, iov_iter_count(from), i_size_read(inode)); if (!iov_iter_count(from)) return 0; @@ -529,7 +529,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr vm_fault_t ret = VM_FAULT_RETRY; int err; - kenter("%lx", folio->index); + _enter("%lx", folio->index); sb_start_pagefault(inode->i_sb); diff --git a/fs/netfs/direct_read.c b/fs/netfs/direct_read.c index b6debac6205f..10a1e4da6bda 100644 --- a/fs/netfs/direct_read.c +++ b/fs/netfs/direct_read.c @@ -33,7 +33,7 @@ ssize_t netfs_unbuffered_read_iter_locked(struct kiocb *iocb, struct iov_iter *i size_t orig_count = iov_iter_count(iter); bool async = !is_sync_kiocb(iocb); - kenter(""); + _enter(""); if (!orig_count) return 0; /* Don't update atime */ diff --git a/fs/netfs/direct_write.c b/fs/netfs/direct_write.c index 792ef17bae21..88f2adfab75e 100644 --- a/fs/netfs/direct_write.c +++ b/fs/netfs/direct_write.c @@ -37,7 +37,7 @@ ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter * size_t len = iov_iter_count(iter); bool async = !is_sync_kiocb(iocb); - kenter(""); + _enter(""); /* We're going to need a bounce buffer if what we transmit is going to * be different in some way to the source buffer, e.g. because it gets @@ -45,7 +45,7 @@ ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter * */ // TODO - kdebug("uw %llx-%llx", start, end); + _debug("uw %llx-%llx", start, end); wreq = netfs_create_write_req(iocb->ki_filp->f_mapping, iocb->ki_filp, start, iocb->ki_flags & IOCB_DIRECT ? @@ -96,7 +96,7 @@ ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter * wreq->cleanup = netfs_cleanup_dio_write; ret = netfs_unbuffered_write(wreq, is_sync_kiocb(iocb), wreq->len); if (ret < 0) { - kdebug("begin = %zd", ret); + _debug("begin = %zd", ret); goto out; } @@ -143,7 +143,7 @@ ssize_t netfs_unbuffered_write_iter(struct kiocb *iocb, struct iov_iter *from) loff_t pos = iocb->ki_pos; unsigned long long end = pos + iov_iter_count(from) - 1; - kenter("%llx,%zx,%llx", pos, iov_iter_count(from), i_size_read(inode)); + _enter("%llx,%zx,%llx", pos, iov_iter_count(from), i_size_read(inode)); if (!iov_iter_count(from)) return 0; diff --git a/fs/netfs/fscache_cache.c b/fs/netfs/fscache_cache.c index 288a73c3072d..9397ed39b0b4 100644 --- a/fs/netfs/fscache_cache.c +++ b/fs/netfs/fscache_cache.c @@ -237,7 +237,7 @@ int fscache_add_cache(struct fscache_cache *cache, { int n_accesses; - kenter("{%s,%s}", ops->name, cache->name); + _enter("{%s,%s}", ops->name, cache->name); BUG_ON(fscache_cache_state(cache) != FSCACHE_CACHE_IS_PREPARING); @@ -257,7 +257,7 @@ int fscache_add_cache(struct fscache_cache *cache, up_write(&fscache_addremove_sem); pr_notice("Cache \"%s\" added (type %s)\n", cache->name, ops->name); - kleave(" = 0 [%s]", cache->name); + _leave(" = 0 [%s]", cache->name); return 0; } EXPORT_SYMBOL(fscache_add_cache); diff --git a/fs/netfs/fscache_cookie.c b/fs/netfs/fscache_cookie.c index 4d1e8bf4c615..bce2492186d0 100644 --- a/fs/netfs/fscache_cookie.c +++ b/fs/netfs/fscache_cookie.c @@ -456,7 +456,7 @@ struct fscache_cookie *__fscache_acquire_cookie( { struct fscache_cookie *cookie; - kenter("V=%x", volume->debug_id); + _enter("V=%x", volume->debug_id); if (!index_key || !index_key_len || index_key_len > 255 || aux_data_len > 255) return NULL; @@ -484,7 +484,7 @@ struct fscache_cookie *__fscache_acquire_cookie( trace_fscache_acquire(cookie); fscache_stat(&fscache_n_acquires_ok); - kleave(" = c=%08x", cookie->debug_id); + _leave(" = c=%08x", cookie->debug_id); return cookie; } EXPORT_SYMBOL(__fscache_acquire_cookie); @@ -505,7 +505,7 @@ static void fscache_perform_lookup(struct fscache_cookie *cookie) enum fscache_access_trace trace = fscache_access_lookup_cookie_end_failed; bool need_withdraw = false; - kenter(""); + _enter(""); if (!cookie->volume->cache_priv) { fscache_create_volume(cookie->volume, true); @@ -519,7 +519,7 @@ static void fscache_perform_lookup(struct fscache_cookie *cookie) if (cookie->state != FSCACHE_COOKIE_STATE_FAILED) fscache_set_cookie_state(cookie, FSCACHE_COOKIE_STATE_QUIESCENT); need_withdraw = true; - kleave(" [fail]"); + _leave(" [fail]"); goto out; } @@ -572,7 +572,7 @@ void __fscache_use_cookie(struct fscache_cookie *cookie, bool will_modify) bool queue = false; int n_active; - kenter("c=%08x", cookie->debug_id); + _enter("c=%08x", cookie->debug_id); if (WARN(test_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags), "Trying to use relinquished cookie\n")) @@ -636,7 +636,7 @@ again: spin_unlock(&cookie->lock); if (queue) fscache_queue_cookie(cookie, fscache_cookie_get_use_work); - kleave(""); + _leave(""); } EXPORT_SYMBOL(__fscache_use_cookie); @@ -702,7 +702,7 @@ static void fscache_cookie_state_machine(struct fscache_cookie *cookie) enum fscache_cookie_state state; bool wake = false; - kenter("c=%x", cookie->debug_id); + _enter("c=%x", cookie->debug_id); again: spin_lock(&cookie->lock); @@ -820,7 +820,7 @@ out: spin_unlock(&cookie->lock); if (wake) wake_up_cookie_state(cookie); - kleave(""); + _leave(""); } static void fscache_cookie_worker(struct work_struct *work) @@ -867,7 +867,7 @@ static void fscache_cookie_lru_do_one(struct fscache_cookie *cookie) set_bit(FSCACHE_COOKIE_DO_LRU_DISCARD, &cookie->flags); spin_unlock(&cookie->lock); fscache_stat(&fscache_n_cookies_lru_expired); - kdebug("lru c=%x", cookie->debug_id); + _debug("lru c=%x", cookie->debug_id); __fscache_withdraw_cookie(cookie); } @@ -971,7 +971,7 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie, bool retire) if (retire) fscache_stat(&fscache_n_relinquishes_retire); - kenter("c=%08x{%d},%d", + _enter("c=%08x{%d},%d", cookie->debug_id, atomic_read(&cookie->n_active), retire); if (WARN(test_and_set_bit(FSCACHE_COOKIE_RELINQUISHED, &cookie->flags), @@ -1050,7 +1050,7 @@ void __fscache_invalidate(struct fscache_cookie *cookie, { bool is_caching; - kenter("c=%x", cookie->debug_id); + _enter("c=%x", cookie->debug_id); fscache_stat(&fscache_n_invalidates); @@ -1072,7 +1072,7 @@ void __fscache_invalidate(struct fscache_cookie *cookie, case FSCACHE_COOKIE_STATE_INVALIDATING: /* is_still_valid will catch it */ default: spin_unlock(&cookie->lock); - kleave(" [no %u]", cookie->state); + _leave(" [no %u]", cookie->state); return; case FSCACHE_COOKIE_STATE_LOOKING_UP: @@ -1081,7 +1081,7 @@ void __fscache_invalidate(struct fscache_cookie *cookie, fallthrough; case FSCACHE_COOKIE_STATE_CREATING: spin_unlock(&cookie->lock); - kleave(" [look %x]", cookie->inval_counter); + _leave(" [look %x]", cookie->inval_counter); return; case FSCACHE_COOKIE_STATE_ACTIVE: @@ -1094,7 +1094,7 @@ void __fscache_invalidate(struct fscache_cookie *cookie, if (is_caching) fscache_queue_cookie(cookie, fscache_cookie_get_inval_work); - kleave(" [inv]"); + _leave(" [inv]"); return; } } diff --git a/fs/netfs/fscache_io.c b/fs/netfs/fscache_io.c index bf4eaeec44fb..38637e5c9b57 100644 --- a/fs/netfs/fscache_io.c +++ b/fs/netfs/fscache_io.c @@ -28,12 +28,12 @@ bool fscache_wait_for_operation(struct netfs_cache_resources *cres, again: if (!fscache_cache_is_live(cookie->volume->cache)) { - kleave(" [broken]"); + _leave(" [broken]"); return false; } state = fscache_cookie_state(cookie); - kenter("c=%08x{%u},%x", cookie->debug_id, state, want_state); + _enter("c=%08x{%u},%x", cookie->debug_id, state, want_state); switch (state) { case FSCACHE_COOKIE_STATE_CREATING: @@ -52,7 +52,7 @@ again: case FSCACHE_COOKIE_STATE_DROPPED: case FSCACHE_COOKIE_STATE_RELINQUISHING: default: - kleave(" [not live]"); + _leave(" [not live]"); return false; } @@ -92,7 +92,7 @@ again: spin_lock(&cookie->lock); state = fscache_cookie_state(cookie); - kenter("c=%08x{%u},%x", cookie->debug_id, state, want_state); + _enter("c=%08x{%u},%x", cookie->debug_id, state, want_state); switch (state) { case FSCACHE_COOKIE_STATE_LOOKING_UP: @@ -140,7 +140,7 @@ failed: cres->cache_priv = NULL; cres->ops = NULL; fscache_end_cookie_access(cookie, fscache_access_io_not_live); - kleave(" = -ENOBUFS"); + _leave(" = -ENOBUFS"); return -ENOBUFS; } @@ -224,7 +224,7 @@ void __fscache_write_to_cache(struct fscache_cookie *cookie, if (len == 0) goto abandon; - kenter("%llx,%zx", start, len); + _enter("%llx,%zx", start, len); wreq = kzalloc(sizeof(struct fscache_write_request), GFP_NOFS); if (!wreq) diff --git a/fs/netfs/fscache_main.c b/fs/netfs/fscache_main.c index bf9b33d26e31..42e98bb523e3 100644 --- a/fs/netfs/fscache_main.c +++ b/fs/netfs/fscache_main.c @@ -99,7 +99,7 @@ error_wq: */ void __exit fscache_exit(void) { - kenter(""); + _enter(""); kmem_cache_destroy(fscache_cookie_jar); fscache_proc_cleanup(); diff --git a/fs/netfs/fscache_volume.c b/fs/netfs/fscache_volume.c index 2e2a405ca9b0..cb75c07b5281 100644 --- a/fs/netfs/fscache_volume.c +++ b/fs/netfs/fscache_volume.c @@ -264,7 +264,7 @@ static struct fscache_volume *fscache_alloc_volume(const char *volume_key, fscache_see_volume(volume, fscache_volume_new_acquire); fscache_stat(&fscache_n_volumes); up_write(&fscache_addremove_sem); - kleave(" = v=%x", volume->debug_id); + _leave(" = v=%x", volume->debug_id); return volume; err_vol: @@ -466,7 +466,7 @@ void fscache_withdraw_volume(struct fscache_volume *volume) { int n_accesses; - kdebug("withdraw V=%x", volume->debug_id); + _debug("withdraw V=%x", volume->debug_id); /* Allow wakeups on dec-to-0 */ n_accesses = atomic_dec_return(&volume->n_accesses); diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index 21e46bc9aa49..7773f3d855a9 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -34,6 +34,7 @@ int netfs_begin_read(struct netfs_io_request *rreq, bool sync); /* * main.c */ +extern unsigned int netfs_debug; extern struct list_head netfs_io_requests; extern spinlock_t netfs_proc_lock; extern mempool_t netfs_request_pool; @@ -353,12 +354,42 @@ void fscache_create_volume(struct fscache_volume *volume, bool wait); * debug tracing */ #define dbgprintk(FMT, ...) \ - pr_debug("[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__) + printk("[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__) #define kenter(FMT, ...) dbgprintk("==> %s("FMT")", __func__, ##__VA_ARGS__) #define kleave(FMT, ...) dbgprintk("<== %s()"FMT"", __func__, ##__VA_ARGS__) #define kdebug(FMT, ...) dbgprintk(FMT, ##__VA_ARGS__) +#ifdef __KDEBUG +#define _enter(FMT, ...) kenter(FMT, ##__VA_ARGS__) +#define _leave(FMT, ...) kleave(FMT, ##__VA_ARGS__) +#define _debug(FMT, ...) kdebug(FMT, ##__VA_ARGS__) + +#elif defined(CONFIG_NETFS_DEBUG) +#define _enter(FMT, ...) \ +do { \ + if (netfs_debug) \ + kenter(FMT, ##__VA_ARGS__); \ +} while (0) + +#define _leave(FMT, ...) \ +do { \ + if (netfs_debug) \ + kleave(FMT, ##__VA_ARGS__); \ +} while (0) + +#define _debug(FMT, ...) \ +do { \ + if (netfs_debug) \ + kdebug(FMT, ##__VA_ARGS__); \ +} while (0) + +#else +#define _enter(FMT, ...) no_printk("==> %s("FMT")", __func__, ##__VA_ARGS__) +#define _leave(FMT, ...) no_printk("<== %s()"FMT"", __func__, ##__VA_ARGS__) +#define _debug(FMT, ...) no_printk(FMT, ##__VA_ARGS__) +#endif + /* * assertions */ diff --git a/fs/netfs/io.c b/fs/netfs/io.c index c7576481c321..c93851b98368 100644 --- a/fs/netfs/io.c +++ b/fs/netfs/io.c @@ -130,7 +130,7 @@ static void netfs_reset_subreq_iter(struct netfs_io_request *rreq, if (count == remaining) return; - kdebug("R=%08x[%u] ITER RESUB-MISMATCH %zx != %zx-%zx-%llx %x\n", + _debug("R=%08x[%u] ITER RESUB-MISMATCH %zx != %zx-%zx-%llx %x\n", rreq->debug_id, subreq->debug_index, iov_iter_count(&subreq->io_iter), subreq->transferred, subreq->len, rreq->i_size, @@ -326,7 +326,7 @@ void netfs_subreq_terminated(struct netfs_io_subrequest *subreq, struct netfs_io_request *rreq = subreq->rreq; int u; - kenter("R=%x[%x]{%llx,%lx},%zd", + _enter("R=%x[%x]{%llx,%lx},%zd", rreq->debug_id, subreq->debug_index, subreq->start, subreq->flags, transferred_or_error); @@ -435,7 +435,7 @@ netfs_rreq_prepare_read(struct netfs_io_request *rreq, struct netfs_inode *ictx = netfs_inode(rreq->inode); size_t lsize; - kenter("%llx-%llx,%llx", subreq->start, subreq->start + subreq->len, rreq->i_size); + _enter("%llx-%llx,%llx", subreq->start, subreq->start + subreq->len, rreq->i_size); if (rreq->origin != NETFS_DIO_READ) { source = netfs_cache_prepare_read(subreq, rreq->i_size); @@ -518,7 +518,7 @@ static bool netfs_rreq_submit_slice(struct netfs_io_request *rreq, subreq->start = rreq->start + rreq->submitted; subreq->len = io_iter->count; - kdebug("slice %llx,%zx,%llx", subreq->start, subreq->len, rreq->submitted); + _debug("slice %llx,%zx,%llx", subreq->start, subreq->len, rreq->submitted); list_add_tail(&subreq->rreq_link, &rreq->subrequests); /* Call out to the cache to find out what it can do with the remaining @@ -570,7 +570,7 @@ int netfs_begin_read(struct netfs_io_request *rreq, bool sync) struct iov_iter io_iter; int ret; - kenter("R=%x %llx-%llx", + _enter("R=%x %llx-%llx", rreq->debug_id, rreq->start, rreq->start + rreq->len - 1); if (rreq->len == 0) { @@ -593,7 +593,7 @@ int netfs_begin_read(struct netfs_io_request *rreq, bool sync) atomic_set(&rreq->nr_outstanding, 1); io_iter = rreq->io_iter; do { - kdebug("submit %llx + %llx >= %llx", + _debug("submit %llx + %llx >= %llx", rreq->start, rreq->submitted, rreq->i_size); if (rreq->origin == NETFS_DIO_READ && rreq->start + rreq->submitted >= rreq->i_size) diff --git a/fs/netfs/main.c b/fs/netfs/main.c index db824c372842..5f0f438e5d21 100644 --- a/fs/netfs/main.c +++ b/fs/netfs/main.c @@ -20,6 +20,10 @@ MODULE_LICENSE("GPL"); EXPORT_TRACEPOINT_SYMBOL(netfs_sreq); +unsigned netfs_debug; +module_param_named(debug, netfs_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(netfs_debug, "Netfs support debugging mask"); + static struct kmem_cache *netfs_request_slab; static struct kmem_cache *netfs_subrequest_slab; mempool_t netfs_request_pool; diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index 172808e83ca8..83e644bd518f 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -26,7 +26,7 @@ bool netfs_dirty_folio(struct address_space *mapping, struct folio *folio) struct fscache_cookie *cookie = netfs_i_cookie(ictx); bool need_use = false; - kenter(""); + _enter(""); if (!filemap_dirty_folio(mapping, folio)) return false; @@ -99,7 +99,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) struct netfs_folio *finfo; size_t flen = folio_size(folio); - kenter("{%lx},%zx,%zx", folio->index, offset, length); + _enter("{%lx},%zx,%zx", folio->index, offset, length); if (!folio_test_private(folio)) return; diff --git a/fs/netfs/write_collect.c b/fs/netfs/write_collect.c index 488147439fe0..426cf87aaf2e 100644 --- a/fs/netfs/write_collect.c +++ b/fs/netfs/write_collect.c @@ -161,7 +161,7 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq, { struct list_head *next; - kenter("R=%x[%x:]", wreq->debug_id, stream->stream_nr); + _enter("R=%x[%x:]", wreq->debug_id, stream->stream_nr); if (list_empty(&stream->subrequests)) return; @@ -374,7 +374,7 @@ static void netfs_collect_write_results(struct netfs_io_request *wreq) unsigned int notes; int s; - kenter("%llx-%llx", wreq->start, wreq->start + wreq->len); + _enter("%llx-%llx", wreq->start, wreq->start + wreq->len); trace_netfs_collect(wreq); trace_netfs_rreq(wreq, netfs_rreq_trace_collect); @@ -409,7 +409,7 @@ reassess_streams: front = stream->front; while (front) { trace_netfs_collect_sreq(wreq, front); - //kdebug("sreq [%x] %llx %zx/%zx", + //_debug("sreq [%x] %llx %zx/%zx", // front->debug_index, front->start, front->transferred, front->len); /* Stall if there may be a discontinuity. */ @@ -598,7 +598,7 @@ reassess_streams: out: netfs_put_group_many(wreq->group, wreq->nr_group_rel); wreq->nr_group_rel = 0; - kleave(" = %x", notes); + _leave(" = %x", notes); return; need_retry: @@ -606,7 +606,7 @@ need_retry: * that any partially completed op will have had any wholly transferred * folios removed from it. */ - kdebug("retry"); + _debug("retry"); netfs_retry_writes(wreq); goto out; } @@ -621,7 +621,7 @@ void netfs_write_collection_worker(struct work_struct *work) size_t transferred; int s; - kenter("R=%x", wreq->debug_id); + _enter("R=%x", wreq->debug_id); netfs_see_request(wreq, netfs_rreq_trace_see_work); if (!test_bit(NETFS_RREQ_IN_PROGRESS, &wreq->flags)) { @@ -684,7 +684,7 @@ void netfs_write_collection_worker(struct work_struct *work) if (wreq->origin == NETFS_DIO_WRITE) inode_dio_end(wreq->inode); - kdebug("finished"); + _debug("finished"); trace_netfs_rreq(wreq, netfs_rreq_trace_wake_ip); clear_bit_unlock(NETFS_RREQ_IN_PROGRESS, &wreq->flags); wake_up_bit(&wreq->flags, NETFS_RREQ_IN_PROGRESS); @@ -744,7 +744,7 @@ void netfs_write_subrequest_terminated(void *_op, ssize_t transferred_or_error, struct netfs_io_request *wreq = subreq->rreq; struct netfs_io_stream *stream = &wreq->io_streams[subreq->stream_nr]; - kenter("%x[%x] %zd", wreq->debug_id, subreq->debug_index, transferred_or_error); + _enter("%x[%x] %zd", wreq->debug_id, subreq->debug_index, transferred_or_error); switch (subreq->source) { case NETFS_UPLOAD_TO_SERVER: diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index d7c971df8866..ec6cf8707fb0 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -99,7 +99,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping, if (IS_ERR(wreq)) return wreq; - kenter("R=%x", wreq->debug_id); + _enter("R=%x", wreq->debug_id); ictx = netfs_inode(wreq->inode); if (test_bit(NETFS_RREQ_WRITE_TO_CACHE, &wreq->flags)) @@ -159,7 +159,7 @@ static void netfs_prepare_write(struct netfs_io_request *wreq, subreq->max_nr_segs = INT_MAX; subreq->stream_nr = stream->stream_nr; - kenter("R=%x[%x]", wreq->debug_id, subreq->debug_index); + _enter("R=%x[%x]", wreq->debug_id, subreq->debug_index); trace_netfs_sreq_ref(wreq->debug_id, subreq->debug_index, refcount_read(&subreq->ref), @@ -215,7 +215,7 @@ static void netfs_do_issue_write(struct netfs_io_stream *stream, { struct netfs_io_request *wreq = subreq->rreq; - kenter("R=%x[%x],%zx", wreq->debug_id, subreq->debug_index, subreq->len); + _enter("R=%x[%x],%zx", wreq->debug_id, subreq->debug_index, subreq->len); if (test_bit(NETFS_SREQ_FAILED, &subreq->flags)) return netfs_write_subrequest_terminated(subreq, subreq->error, false); @@ -272,11 +272,11 @@ int netfs_advance_write(struct netfs_io_request *wreq, size_t part; if (!stream->avail) { - kleave("no write"); + _leave("no write"); return len; } - kenter("R=%x[%x]", wreq->debug_id, subreq ? subreq->debug_index : 0); + _enter("R=%x[%x]", wreq->debug_id, subreq ? subreq->debug_index : 0); if (subreq && start != subreq->start + subreq->len) { netfs_issue_write(wreq, stream); @@ -288,7 +288,7 @@ int netfs_advance_write(struct netfs_io_request *wreq, subreq = stream->construct; part = min(subreq->max_len - subreq->len, len); - kdebug("part %zx/%zx %zx/%zx", subreq->len, subreq->max_len, part, len); + _debug("part %zx/%zx %zx/%zx", subreq->len, subreq->max_len, part, len); subreq->len += part; subreq->nr_segs++; @@ -319,7 +319,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, bool to_eof = false, streamw = false; bool debug = false; - kenter(""); + _enter(""); /* netfs_perform_write() may shift i_size around the page or from out * of the page to beyond it, but cannot move i_size into or through the @@ -329,7 +329,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, if (fpos >= i_size) { /* mmap beyond eof. */ - kdebug("beyond eof"); + _debug("beyond eof"); folio_start_writeback(folio); folio_unlock(folio); wreq->nr_group_rel += netfs_folio_written_back(folio); @@ -363,7 +363,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, } flen -= foff; - kdebug("folio %zx %zx %zx", foff, flen, fsize); + _debug("folio %zx %zx %zx", foff, flen, fsize); /* Deal with discontinuities in the stream of dirty pages. These can * arise from a number of sources: @@ -487,7 +487,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, for (int s = 0; s < NR_IO_STREAMS; s++) netfs_issue_write(wreq, &wreq->io_streams[s]); - kleave(" = 0"); + _leave(" = 0"); return 0; } @@ -522,7 +522,7 @@ int netfs_writepages(struct address_space *mapping, netfs_stat(&netfs_n_wh_writepages); do { - kdebug("wbiter %lx %llx", folio->index, wreq->start + wreq->submitted); + _debug("wbiter %lx %llx", folio->index, wreq->start + wreq->submitted); /* It appears we don't have to handle cyclic writeback wrapping. */ WARN_ON_ONCE(wreq && folio_pos(folio) < wreq->start + wreq->submitted); @@ -546,14 +546,14 @@ int netfs_writepages(struct address_space *mapping, mutex_unlock(&ictx->wb_lock); netfs_put_request(wreq, false, netfs_rreq_trace_put_return); - kleave(" = %d", error); + _leave(" = %d", error); return error; couldnt_start: netfs_kill_dirty_pages(mapping, wbc, folio); out: mutex_unlock(&ictx->wb_lock); - kleave(" = %d", error); + _leave(" = %d", error); return error; } EXPORT_SYMBOL(netfs_writepages); @@ -590,7 +590,7 @@ int netfs_advance_writethrough(struct netfs_io_request *wreq, struct writeback_c struct folio *folio, size_t copied, bool to_page_end, struct folio **writethrough_cache) { - kenter("R=%x ic=%zu ws=%u cp=%zu tp=%u", + _enter("R=%x ic=%zu ws=%u cp=%zu tp=%u", wreq->debug_id, wreq->iter.count, wreq->wsize, copied, to_page_end); if (!*writethrough_cache) { @@ -624,7 +624,7 @@ int netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_contr struct netfs_inode *ictx = netfs_inode(wreq->inode); int ret; - kenter("R=%x", wreq->debug_id); + _enter("R=%x", wreq->debug_id); if (writethrough_cache) netfs_write_folio(wreq, wbc, writethrough_cache); @@ -657,7 +657,7 @@ int netfs_unbuffered_write(struct netfs_io_request *wreq, bool may_wait, size_t loff_t start = wreq->start; int error = 0; - kenter("%zx", len); + _enter("%zx", len); if (wreq->origin == NETFS_DIO_WRITE) inode_dio_begin(wreq->inode); @@ -665,7 +665,7 @@ int netfs_unbuffered_write(struct netfs_io_request *wreq, bool may_wait, size_t while (len) { // TODO: Prepare content encryption - kdebug("unbuffered %zx", len); + _debug("unbuffered %zx", len); part = netfs_advance_write(wreq, upload, start, len, false); start += part; len -= part; @@ -684,6 +684,6 @@ int netfs_unbuffered_write(struct netfs_io_request *wreq, bool may_wait, size_t if (list_empty(&upload->subrequests)) netfs_wake_write_collector(wreq, false); - kleave(" = %d", error); + _leave(" = %d", error); return error; } From fcad93360df4d04b172dba85b976c9f38ee0d5e0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 18 Jul 2024 21:10:06 +0100 Subject: [PATCH 143/155] netfs: Rename CONFIG_FSCACHE_DEBUG to CONFIG_NETFS_DEBUG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIG_FSCACHE_DEBUG should have been renamed to CONFIG_NETFS_DEBUG, so do that now. Signed-off-by: David Howells Link: https://lore.kernel.org/r/1410796.1721333406@warthog.procyon.org.uk cc: Uwe Kleine-König cc: Christian Brauner cc: Jeff Layton cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner --- fs/netfs/Kconfig | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/fs/netfs/Kconfig b/fs/netfs/Kconfig index bec805e0c44c..1b78e8b65ebc 100644 --- a/fs/netfs/Kconfig +++ b/fs/netfs/Kconfig @@ -22,6 +22,14 @@ config NETFS_STATS between CPUs. On the other hand, the stats are very useful for debugging purposes. Saying 'Y' here is recommended. +config NETFS_DEBUG + bool "Enable dynamic debugging netfslib and FS-Cache" + depends on NETFS + help + This permits debugging to be dynamically enabled in the local caching + management module. If this is set, the debugging output may be + enabled by setting bits in /sys/module/netfs/parameters/debug. + config FSCACHE bool "General filesystem local caching manager" depends on NETFS_SUPPORT @@ -50,13 +58,3 @@ config FSCACHE_STATS debugging purposes. Saying 'Y' here is recommended. See Documentation/filesystems/caching/fscache.rst for more information. - -config FSCACHE_DEBUG - bool "Debug FS-Cache" - depends on FSCACHE - help - This permits debugging to be dynamically enabled in the local caching - management module. If this is set, the debugging output may be - enabled by setting bits in /sys/modules/fscache/parameter/debug. - - See Documentation/filesystems/caching/fscache.rst for more information. From 5bc9ad78c2f836bd2fe9b5c911f8499364ee5b6e Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Thu, 18 Jul 2024 17:18:37 +0200 Subject: [PATCH 144/155] vfs: handle __wait_on_freeing_inode() and evict() race Lockless hash lookup can find and lock the inode after it gets the I_FREEING flag set, at which point it blocks waiting for teardown in evict() to finish. However, the flag is still set even after evict() wakes up all waiters. This results in a race where if the inode lock is taken late enough, it can happen after both hash removal and wakeups, meaning there is nobody to wake the racing thread up. This worked prior to RCU-based lookup because the entire ordeal was synchronized with the inode hash lock. Since unhashing requires the inode lock, we can safely check whether it happened after acquiring it. Link: https://lore.kernel.org/v9fs/20240717102458.649b60be@kernel.org/ Reported-by: Dominique Martinet Fixes: 7180f8d91fcb ("vfs: add rcu-based find_inode variants for iget ops") Signed-off-by: Mateusz Guzik Link: https://lore.kernel.org/r/20240718151838.611807-1-mjguzik@gmail.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/inode.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fs/inode.c b/fs/inode.c index f356fe2ec2b6..05613745fad6 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -676,6 +676,16 @@ static void evict(struct inode *inode) remove_inode_hash(inode); + /* + * Wake up waiters in __wait_on_freeing_inode(). + * + * Lockless hash lookup may end up finding the inode before we removed + * it above, but only lock it *after* we are done with the wakeup below. + * In this case the potential waiter cannot safely block. + * + * The inode being unhashed after the call to remove_inode_hash() is + * used as an indicator whether blocking on it is safe. + */ spin_lock(&inode->i_lock); wake_up_bit(&inode->i_state, __I_NEW); BUG_ON(inode->i_state != (I_FREEING | I_CLEAR)); @@ -2291,6 +2301,16 @@ static void __wait_on_freeing_inode(struct inode *inode, bool locked) { wait_queue_head_t *wq; DEFINE_WAIT_BIT(wait, &inode->i_state, __I_NEW); + + /* + * Handle racing against evict(), see that routine for more details. + */ + if (unlikely(inode_unhashed(inode))) { + WARN_ON(locked); + spin_unlock(&inode->i_lock); + return; + } + wq = bit_waitqueue(&inode->i_state, __I_NEW); prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE); spin_unlock(&inode->i_lock); From b40c8e7a033ff2cafd33adbe50e2a516f88fa223 Mon Sep 17 00:00:00 2001 From: Congjie Zhou Date: Fri, 19 Jul 2024 00:25:45 +0800 Subject: [PATCH 145/155] vfs: correct the comments of vfs_*() helpers correct the comments of vfs_*() helpers in fs/namei.c, including: 1. vfs_create() 2. vfs_mknod() 3. vfs_mkdir() 4. vfs_rmdir() 5. vfs_symlink() All of them come from the same commit: 6521f8917082 "namei: prepare for idmapped mounts" The @dentry is actually the dentry of child directory rather than base directory(parent directory), and thus the @dir has to be modified due to the change of @dentry. Signed-off-by: Congjie Zhou Link: https://lore.kernel.org/r/tencent_2FCF6CC9E10DC8A27AE58A5A0FE4FCE96D0A@qq.com Signed-off-by: Christian Brauner --- fs/namei.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 3a4c40e12f78..5512cb10fa89 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3248,9 +3248,9 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap, /** * vfs_create - create new file * @idmap: idmap of the mount the inode was found from - * @dir: inode of @dentry - * @dentry: pointer to dentry of the base directory - * @mode: mode of the new file + * @dir: inode of the parent directory + * @dentry: dentry of the child file + * @mode: mode of the child file * @want_excl: whether the file must not yet exist * * Create a new file. @@ -4047,9 +4047,9 @@ EXPORT_SYMBOL(user_path_create); /** * vfs_mknod - create device node or file * @idmap: idmap of the mount the inode was found from - * @dir: inode of @dentry - * @dentry: pointer to dentry of the base directory - * @mode: mode of the new device node or file + * @dir: inode of the parent directory + * @dentry: dentry of the child device node + * @mode: mode of the child device node * @dev: device number of device to create * * Create a device node or file. @@ -4174,9 +4174,9 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d /** * vfs_mkdir - create directory * @idmap: idmap of the mount the inode was found from - * @dir: inode of @dentry - * @dentry: pointer to dentry of the base directory - * @mode: mode of the new directory + * @dir: inode of the parent directory + * @dentry: dentry of the child directory + * @mode: mode of the child directory * * Create a directory. * @@ -4256,8 +4256,8 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) /** * vfs_rmdir - remove directory * @idmap: idmap of the mount the inode was found from - * @dir: inode of @dentry - * @dentry: pointer to dentry of the base directory + * @dir: inode of the parent directory + * @dentry: dentry of the child directory * * Remove a directory. * @@ -4537,8 +4537,8 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname) /** * vfs_symlink - create symlink * @idmap: idmap of the mount the inode was found from - * @dir: inode of @dentry - * @dentry: pointer to dentry of the base directory + * @dir: inode of the parent directory + * @dentry: dentry of the child symlink file * @oldname: name of the file to link to * * Create a symlink. From f60d38cb02d03f39576f9c7ad13652babded2410 Mon Sep 17 00:00:00 2001 From: Edward Adam Davis Date: Sun, 21 Jul 2024 14:23:12 +0800 Subject: [PATCH 146/155] pidfs: when time ns disabled add check for ioctl syzbot call pidfd_ioctl() with cmd "PIDFD_GET_TIME_NAMESPACE" and disabled CONFIG_TIME_NS, since time_ns is NULL, it will make NULL ponter deref in open_namespace. Fixes: 5b08bd408534 ("pidfs: allow retrieval of namespace file descriptors") # mainline only Reported-and-tested-by: syzbot+34a0ee986f61f15da35d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=34a0ee986f61f15da35d Signed-off-by: Edward Adam Davis Link: https://lore.kernel.org/r/tencent_7FAE8DB725EE0DD69236DDABDDDE195E4F07@qq.com Signed-off-by: Christian Brauner --- fs/pidfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/pidfs.c b/fs/pidfs.c index c9cb14181def..fe0ddab48f57 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -168,6 +168,8 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PIDFD_GET_TIME_NAMESPACE: get_time_ns(nsp->time_ns); ns_common = to_ns_common(nsp->time_ns); + if (!nsp->time_ns) + return -EINVAL; break; case PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE: get_time_ns(nsp->time_ns_for_children); From 9b3e15046437d0e647e1f29ac955e2a3eb94b675 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 22 Jul 2024 15:13:54 +0200 Subject: [PATCH 147/155] pidfs: handle kernels without namespaces cleanly The nsproxy structure contains nearly all of the namespaces associated with a task. When a given namespace type is not supported by this kernel the rules whether the corresponding pointer in struct nsproxy is NULL or always init__ns differ per namespace. Ideally, that wouldn't be the case and for all namespace types we'd always set it to init__ns when the corresponding namespace type isn't supported. Make sure we handle all namespaces where the pointer in struct nsproxy can be NULL when the namespace type isn't supported. Link: https://lore.kernel.org/r/20240722-work-pidfs-e6a83030f63e@brauner Fixes: 5b08bd408534 ("pidfs: allow retrieval of namespace file descriptors") # mainline only Signed-off-by: Christian Brauner --- fs/pidfs.c | 65 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/fs/pidfs.c b/fs/pidfs.c index fe0ddab48f57..7ffdc88dfb52 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -119,7 +119,7 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct task_struct *task __free(put_task) = NULL; struct nsproxy *nsp __free(put_nsproxy) = NULL; struct pid *pid = pidfd_pid(file); - struct ns_common *ns_common; + struct ns_common *ns_common = NULL; if (arg) return -EINVAL; @@ -146,54 +146,73 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { /* Namespaces that hang of nsproxy. */ case PIDFD_GET_CGROUP_NAMESPACE: - get_cgroup_ns(nsp->cgroup_ns); - ns_common = to_ns_common(nsp->cgroup_ns); + if (IS_ENABLED(CONFIG_CGROUPS)) { + get_cgroup_ns(nsp->cgroup_ns); + ns_common = to_ns_common(nsp->cgroup_ns); + } break; case PIDFD_GET_IPC_NAMESPACE: - get_ipc_ns(nsp->ipc_ns); - ns_common = to_ns_common(nsp->ipc_ns); + if (IS_ENABLED(CONFIG_IPC_NS)) { + get_ipc_ns(nsp->ipc_ns); + ns_common = to_ns_common(nsp->ipc_ns); + } break; case PIDFD_GET_MNT_NAMESPACE: get_mnt_ns(nsp->mnt_ns); ns_common = to_ns_common(nsp->mnt_ns); break; case PIDFD_GET_NET_NAMESPACE: - ns_common = to_ns_common(nsp->net_ns); - get_net_ns(ns_common); + if (IS_ENABLED(CONFIG_NET_NS)) { + ns_common = to_ns_common(nsp->net_ns); + get_net_ns(ns_common); + } break; case PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE: - get_pid_ns(nsp->pid_ns_for_children); - ns_common = to_ns_common(nsp->pid_ns_for_children); + if (IS_ENABLED(CONFIG_PID_NS)) { + get_pid_ns(nsp->pid_ns_for_children); + ns_common = to_ns_common(nsp->pid_ns_for_children); + } break; case PIDFD_GET_TIME_NAMESPACE: - get_time_ns(nsp->time_ns); - ns_common = to_ns_common(nsp->time_ns); - if (!nsp->time_ns) - return -EINVAL; + if (IS_ENABLED(CONFIG_TIME_NS)) { + get_time_ns(nsp->time_ns); + ns_common = to_ns_common(nsp->time_ns); + } break; case PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE: - get_time_ns(nsp->time_ns_for_children); - ns_common = to_ns_common(nsp->time_ns_for_children); + if (IS_ENABLED(CONFIG_TIME_NS)) { + get_time_ns(nsp->time_ns_for_children); + ns_common = to_ns_common(nsp->time_ns_for_children); + } break; case PIDFD_GET_UTS_NAMESPACE: - get_uts_ns(nsp->uts_ns); - ns_common = to_ns_common(nsp->uts_ns); + if (IS_ENABLED(CONFIG_UTS_NS)) { + get_uts_ns(nsp->uts_ns); + ns_common = to_ns_common(nsp->uts_ns); + } break; /* Namespaces that don't hang of nsproxy. */ case PIDFD_GET_USER_NAMESPACE: - rcu_read_lock(); - ns_common = to_ns_common(get_user_ns(task_cred_xxx(task, user_ns))); - rcu_read_unlock(); + if (IS_ENABLED(CONFIG_USER_NS)) { + rcu_read_lock(); + ns_common = to_ns_common(get_user_ns(task_cred_xxx(task, user_ns))); + rcu_read_unlock(); + } break; case PIDFD_GET_PID_NAMESPACE: - rcu_read_lock(); - ns_common = to_ns_common(get_pid_ns(task_active_pid_ns(task))); - rcu_read_unlock(); + if (IS_ENABLED(CONFIG_PID_NS)) { + rcu_read_lock(); + ns_common = to_ns_common( get_pid_ns(task_active_pid_ns(task))); + rcu_read_unlock(); + } break; default: return -ENOIOCTLCMD; } + if (!ns_common) + return -EOPNOTSUPP; + /* open_namespace() unconditionally consumes the reference */ return open_namespace(ns_common); } From 1bb8dce5dfa9d11ff83a683fab2cda2916384c12 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 22 Jul 2024 15:13:55 +0200 Subject: [PATCH 148/155] pidfs: add selftests for new namespace ioctls Add selftests to verify that deriving namespace file descriptors from pidfd file descriptors works correctly. Link: https://lore.kernel.org/r/20240722-work-pidfs-69dbea91edab@brauner Signed-off-by: Christian Brauner --- .../selftests/pidfd/pidfd_setns_test.c | 258 +++++++++++++++--- 1 file changed, 227 insertions(+), 31 deletions(-) diff --git a/tools/testing/selftests/pidfd/pidfd_setns_test.c b/tools/testing/selftests/pidfd/pidfd_setns_test.c index 47746b0c6acd..7c2a4349170a 100644 --- a/tools/testing/selftests/pidfd/pidfd_setns_test.c +++ b/tools/testing/selftests/pidfd/pidfd_setns_test.c @@ -16,11 +16,56 @@ #include #include #include +#include #include "pidfd.h" #include "../clone3/clone3_selftests.h" #include "../kselftest_harness.h" +#ifndef PIDFS_IOCTL_MAGIC +#define PIDFS_IOCTL_MAGIC 0xFF +#endif + +#ifndef PIDFD_GET_CGROUP_NAMESPACE +#define PIDFD_GET_CGROUP_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 1) +#endif + +#ifndef PIDFD_GET_IPC_NAMESPACE +#define PIDFD_GET_IPC_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 2) +#endif + +#ifndef PIDFD_GET_MNT_NAMESPACE +#define PIDFD_GET_MNT_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 3) +#endif + +#ifndef PIDFD_GET_NET_NAMESPACE +#define PIDFD_GET_NET_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 4) +#endif + +#ifndef PIDFD_GET_PID_NAMESPACE +#define PIDFD_GET_PID_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 5) +#endif + +#ifndef PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE +#define PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 6) +#endif + +#ifndef PIDFD_GET_TIME_NAMESPACE +#define PIDFD_GET_TIME_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 7) +#endif + +#ifndef PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE +#define PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 8) +#endif + +#ifndef PIDFD_GET_USER_NAMESPACE +#define PIDFD_GET_USER_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 9) +#endif + +#ifndef PIDFD_GET_UTS_NAMESPACE +#define PIDFD_GET_UTS_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 10) +#endif + enum { PIDFD_NS_USER, PIDFD_NS_MNT, @@ -31,22 +76,25 @@ enum { PIDFD_NS_CGROUP, PIDFD_NS_PIDCLD, PIDFD_NS_TIME, + PIDFD_NS_TIMECLD, PIDFD_NS_MAX }; const struct ns_info { const char *name; int flag; + unsigned int pidfd_ioctl; } ns_info[] = { - [PIDFD_NS_USER] = { "user", CLONE_NEWUSER, }, - [PIDFD_NS_MNT] = { "mnt", CLONE_NEWNS, }, - [PIDFD_NS_PID] = { "pid", CLONE_NEWPID, }, - [PIDFD_NS_UTS] = { "uts", CLONE_NEWUTS, }, - [PIDFD_NS_IPC] = { "ipc", CLONE_NEWIPC, }, - [PIDFD_NS_NET] = { "net", CLONE_NEWNET, }, - [PIDFD_NS_CGROUP] = { "cgroup", CLONE_NEWCGROUP, }, - [PIDFD_NS_PIDCLD] = { "pid_for_children", 0, }, - [PIDFD_NS_TIME] = { "time", CLONE_NEWTIME, }, + [PIDFD_NS_USER] = { "user", CLONE_NEWUSER, PIDFD_GET_USER_NAMESPACE, }, + [PIDFD_NS_MNT] = { "mnt", CLONE_NEWNS, PIDFD_GET_MNT_NAMESPACE, }, + [PIDFD_NS_PID] = { "pid", CLONE_NEWPID, PIDFD_GET_PID_NAMESPACE, }, + [PIDFD_NS_UTS] = { "uts", CLONE_NEWUTS, PIDFD_GET_UTS_NAMESPACE, }, + [PIDFD_NS_IPC] = { "ipc", CLONE_NEWIPC, PIDFD_GET_IPC_NAMESPACE, }, + [PIDFD_NS_NET] = { "net", CLONE_NEWNET, PIDFD_GET_NET_NAMESPACE, }, + [PIDFD_NS_CGROUP] = { "cgroup", CLONE_NEWCGROUP, PIDFD_GET_CGROUP_NAMESPACE, }, + [PIDFD_NS_TIME] = { "time", CLONE_NEWTIME, PIDFD_GET_TIME_NAMESPACE, }, + [PIDFD_NS_PIDCLD] = { "pid_for_children", 0, PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE, }, + [PIDFD_NS_TIMECLD] = { "time_for_children", 0, PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE, }, }; FIXTURE(current_nsset) @@ -54,6 +102,7 @@ FIXTURE(current_nsset) pid_t pid; int pidfd; int nsfds[PIDFD_NS_MAX]; + int child_pidfd_derived_nsfds[PIDFD_NS_MAX]; pid_t child_pid_exited; int child_pidfd_exited; @@ -61,10 +110,12 @@ FIXTURE(current_nsset) pid_t child_pid1; int child_pidfd1; int child_nsfds1[PIDFD_NS_MAX]; + int child_pidfd_derived_nsfds1[PIDFD_NS_MAX]; pid_t child_pid2; int child_pidfd2; int child_nsfds2[PIDFD_NS_MAX]; + int child_pidfd_derived_nsfds2[PIDFD_NS_MAX]; }; static int sys_waitid(int which, pid_t pid, int options) @@ -128,9 +179,12 @@ FIXTURE_SETUP(current_nsset) char c; for (i = 0; i < PIDFD_NS_MAX; i++) { - self->nsfds[i] = -EBADF; - self->child_nsfds1[i] = -EBADF; - self->child_nsfds2[i] = -EBADF; + self->nsfds[i] = -EBADF; + self->child_nsfds1[i] = -EBADF; + self->child_nsfds2[i] = -EBADF; + self->child_pidfd_derived_nsfds[i] = -EBADF; + self->child_pidfd_derived_nsfds1[i] = -EBADF; + self->child_pidfd_derived_nsfds2[i] = -EBADF; } proc_fd = open("/proc/self/ns", O_DIRECTORY | O_CLOEXEC); @@ -139,6 +193,11 @@ FIXTURE_SETUP(current_nsset) } self->pid = getpid(); + self->pidfd = sys_pidfd_open(self->pid, 0); + EXPECT_GT(self->pidfd, 0) { + TH_LOG("%m - Failed to open pidfd for process %d", self->pid); + } + for (i = 0; i < PIDFD_NS_MAX; i++) { const struct ns_info *info = &ns_info[i]; self->nsfds[i] = openat(proc_fd, info->name, O_RDONLY | O_CLOEXEC); @@ -148,20 +207,27 @@ FIXTURE_SETUP(current_nsset) info->name, self->pid); } } - } - self->pidfd = sys_pidfd_open(self->pid, 0); - EXPECT_GT(self->pidfd, 0) { - TH_LOG("%m - Failed to open pidfd for process %d", self->pid); + self->child_pidfd_derived_nsfds[i] = ioctl(self->pidfd, info->pidfd_ioctl, 0); + if (self->child_pidfd_derived_nsfds[i] < 0) { + EXPECT_EQ(errno, EOPNOTSUPP) { + TH_LOG("%m - Failed to derive %s namespace from pidfd of process %d", + info->name, self->pid); + } + } } /* Create task that exits right away. */ - self->child_pid_exited = create_child(&self->child_pidfd_exited, - CLONE_NEWUSER | CLONE_NEWNET); + self->child_pid_exited = create_child(&self->child_pidfd_exited, 0); EXPECT_GE(self->child_pid_exited, 0); - if (self->child_pid_exited == 0) + if (self->child_pid_exited == 0) { + if (self->nsfds[PIDFD_NS_USER] >= 0 && unshare(CLONE_NEWUSER) < 0) + _exit(EXIT_FAILURE); + if (self->nsfds[PIDFD_NS_NET] >= 0 && unshare(CLONE_NEWNET) < 0) + _exit(EXIT_FAILURE); _exit(EXIT_SUCCESS); + } ASSERT_EQ(sys_waitid(P_PID, self->child_pid_exited, WEXITED | WNOWAIT), 0); @@ -174,18 +240,43 @@ FIXTURE_SETUP(current_nsset) EXPECT_EQ(ret, 0); /* Create tasks that will be stopped. */ - self->child_pid1 = create_child(&self->child_pidfd1, - CLONE_NEWUSER | CLONE_NEWNS | - CLONE_NEWCGROUP | CLONE_NEWIPC | - CLONE_NEWUTS | CLONE_NEWPID | - CLONE_NEWNET); + if (self->nsfds[PIDFD_NS_USER] >= 0 && self->nsfds[PIDFD_NS_PID] >= 0) + self->child_pid1 = create_child(&self->child_pidfd1, CLONE_NEWUSER | CLONE_NEWPID); + else if (self->nsfds[PIDFD_NS_PID] >= 0) + self->child_pid1 = create_child(&self->child_pidfd1, CLONE_NEWPID); + else if (self->nsfds[PIDFD_NS_USER] >= 0) + self->child_pid1 = create_child(&self->child_pidfd1, CLONE_NEWUSER); + else + self->child_pid1 = create_child(&self->child_pidfd1, 0); EXPECT_GE(self->child_pid1, 0); if (self->child_pid1 == 0) { close(ipc_sockets[0]); - if (!switch_timens()) + if (self->nsfds[PIDFD_NS_MNT] >= 0 && unshare(CLONE_NEWNS) < 0) { + TH_LOG("%m - Failed to unshare mount namespace for process %d", self->pid); _exit(EXIT_FAILURE); + } + if (self->nsfds[PIDFD_NS_CGROUP] >= 0 && unshare(CLONE_NEWCGROUP) < 0) { + TH_LOG("%m - Failed to unshare cgroup namespace for process %d", self->pid); + _exit(EXIT_FAILURE); + } + if (self->nsfds[PIDFD_NS_IPC] >= 0 && unshare(CLONE_NEWIPC) < 0) { + TH_LOG("%m - Failed to unshare ipc namespace for process %d", self->pid); + _exit(EXIT_FAILURE); + } + if (self->nsfds[PIDFD_NS_UTS] >= 0 && unshare(CLONE_NEWUTS) < 0) { + TH_LOG("%m - Failed to unshare uts namespace for process %d", self->pid); + _exit(EXIT_FAILURE); + } + if (self->nsfds[PIDFD_NS_NET] >= 0 && unshare(CLONE_NEWNET) < 0) { + TH_LOG("%m - Failed to unshare net namespace for process %d", self->pid); + _exit(EXIT_FAILURE); + } + if (self->nsfds[PIDFD_NS_TIME] >= 0 && !switch_timens()) { + TH_LOG("%m - Failed to unshare time namespace for process %d", self->pid); + _exit(EXIT_FAILURE); + } if (write_nointr(ipc_sockets[1], "1", 1) < 0) _exit(EXIT_FAILURE); @@ -203,18 +294,43 @@ FIXTURE_SETUP(current_nsset) ret = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); EXPECT_EQ(ret, 0); - self->child_pid2 = create_child(&self->child_pidfd2, - CLONE_NEWUSER | CLONE_NEWNS | - CLONE_NEWCGROUP | CLONE_NEWIPC | - CLONE_NEWUTS | CLONE_NEWPID | - CLONE_NEWNET); + if (self->nsfds[PIDFD_NS_USER] >= 0 && self->nsfds[PIDFD_NS_PID] >= 0) + self->child_pid2 = create_child(&self->child_pidfd2, CLONE_NEWUSER | CLONE_NEWPID); + else if (self->nsfds[PIDFD_NS_PID] >= 0) + self->child_pid2 = create_child(&self->child_pidfd2, CLONE_NEWPID); + else if (self->nsfds[PIDFD_NS_USER] >= 0) + self->child_pid2 = create_child(&self->child_pidfd2, CLONE_NEWUSER); + else + self->child_pid2 = create_child(&self->child_pidfd2, 0); EXPECT_GE(self->child_pid2, 0); if (self->child_pid2 == 0) { close(ipc_sockets[0]); - if (!switch_timens()) + if (self->nsfds[PIDFD_NS_MNT] >= 0 && unshare(CLONE_NEWNS) < 0) { + TH_LOG("%m - Failed to unshare mount namespace for process %d", self->pid); _exit(EXIT_FAILURE); + } + if (self->nsfds[PIDFD_NS_CGROUP] >= 0 && unshare(CLONE_NEWCGROUP) < 0) { + TH_LOG("%m - Failed to unshare cgroup namespace for process %d", self->pid); + _exit(EXIT_FAILURE); + } + if (self->nsfds[PIDFD_NS_IPC] >= 0 && unshare(CLONE_NEWIPC) < 0) { + TH_LOG("%m - Failed to unshare ipc namespace for process %d", self->pid); + _exit(EXIT_FAILURE); + } + if (self->nsfds[PIDFD_NS_UTS] >= 0 && unshare(CLONE_NEWUTS) < 0) { + TH_LOG("%m - Failed to unshare uts namespace for process %d", self->pid); + _exit(EXIT_FAILURE); + } + if (self->nsfds[PIDFD_NS_NET] >= 0 && unshare(CLONE_NEWNET) < 0) { + TH_LOG("%m - Failed to unshare net namespace for process %d", self->pid); + _exit(EXIT_FAILURE); + } + if (self->nsfds[PIDFD_NS_TIME] >= 0 && !switch_timens()) { + TH_LOG("%m - Failed to unshare time namespace for process %d", self->pid); + _exit(EXIT_FAILURE); + } if (write_nointr(ipc_sockets[1], "1", 1) < 0) _exit(EXIT_FAILURE); @@ -267,6 +383,22 @@ FIXTURE_SETUP(current_nsset) info->name, self->child_pid1); } } + + self->child_pidfd_derived_nsfds1[i] = ioctl(self->child_pidfd1, info->pidfd_ioctl, 0); + if (self->child_pidfd_derived_nsfds1[i] < 0) { + EXPECT_EQ(errno, EOPNOTSUPP) { + TH_LOG("%m - Failed to derive %s namespace from pidfd of process %d", + info->name, self->child_pid1); + } + } + + self->child_pidfd_derived_nsfds2[i] = ioctl(self->child_pidfd2, info->pidfd_ioctl, 0); + if (self->child_pidfd_derived_nsfds2[i] < 0) { + EXPECT_EQ(errno, EOPNOTSUPP) { + TH_LOG("%m - Failed to derive %s namespace from pidfd of process %d", + info->name, self->child_pid2); + } + } } close(proc_fd); @@ -288,6 +420,12 @@ FIXTURE_TEARDOWN(current_nsset) close(self->child_nsfds1[i]); if (self->child_nsfds2[i] >= 0) close(self->child_nsfds2[i]); + if (self->child_pidfd_derived_nsfds[i] >= 0) + close(self->child_pidfd_derived_nsfds[i]); + if (self->child_pidfd_derived_nsfds1[i] >= 0) + close(self->child_pidfd_derived_nsfds1[i]); + if (self->child_pidfd_derived_nsfds2[i] >= 0) + close(self->child_pidfd_derived_nsfds2[i]); } if (self->child_pidfd1 >= 0) @@ -446,6 +584,42 @@ TEST_F(current_nsset, nsfd_incremental_setns) } } +TEST_F(current_nsset, pidfd_derived_nsfd_incremental_setns) +{ + int i; + pid_t pid; + + pid = getpid(); + for (i = 0; i < PIDFD_NS_MAX; i++) { + const struct ns_info *info = &ns_info[i]; + int nsfd; + + if (self->child_pidfd_derived_nsfds1[i] < 0) + continue; + + if (info->flag) { + ASSERT_EQ(setns(self->child_pidfd_derived_nsfds1[i], info->flag), 0) { + TH_LOG("%m - Failed to setns to %s namespace of %d via nsfd %d", + info->name, self->child_pid1, + self->child_pidfd_derived_nsfds1[i]); + } + } + + /* Verify that we have changed to the correct namespaces. */ + if (info->flag == CLONE_NEWPID) + nsfd = self->child_pidfd_derived_nsfds[i]; + else + nsfd = self->child_pidfd_derived_nsfds1[i]; + ASSERT_EQ(in_same_namespace(nsfd, pid, info->name), 1) { + TH_LOG("setns failed to place us correctly into %s namespace of %d via nsfd %d", + info->name, self->child_pid1, + self->child_pidfd_derived_nsfds1[i]); + } + TH_LOG("Managed to correctly setns to %s namespace of %d via nsfd %d", + info->name, self->child_pid1, self->child_pidfd_derived_nsfds1[i]); + } +} + TEST_F(current_nsset, pidfd_one_shot_setns) { unsigned flags = 0; @@ -542,6 +716,28 @@ TEST_F(current_nsset, no_foul_play) info->name, self->child_pid2, self->child_nsfds2[i]); } + + /* + * Can't setns to a user namespace outside of our hierarchy since we + * don't have caps in there and didn't create it. That means that under + * no circumstances should we be able to setns to any of the other + * ones since they aren't owned by our user namespace. + */ + for (i = 0; i < PIDFD_NS_MAX; i++) { + const struct ns_info *info = &ns_info[i]; + + if (self->child_pidfd_derived_nsfds2[i] < 0 || !info->flag) + continue; + + ASSERT_NE(setns(self->child_pidfd_derived_nsfds2[i], info->flag), 0) { + TH_LOG("Managed to setns to %s namespace of %d via nsfd %d", + info->name, self->child_pid2, + self->child_pidfd_derived_nsfds2[i]); + } + TH_LOG("%m - Correctly failed to setns to %s namespace of %d via nsfd %d", + info->name, self->child_pid2, + self->child_pidfd_derived_nsfds2[i]); + } } TEST(setns_einval) From 212be98aa19303cbf376d61faf9de3ec9997c1cd Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 19 Jul 2024 15:20:18 +0100 Subject: [PATCH 149/155] netfs: Fix writeback that needs to go to both server and cache When netfslib is performing writeback (ie. ->writepages), it maintains two parallel streams of writes, one to the server and one to the cache, but it doesn't mark either stream of writes as active until it gets some data that needs to be written to that stream. This is done because some folios will only be written to the cache (e.g. copying to the cache on read is done by marking the folios and letting writeback do the actual work) and sometimes we'll only be writing to the server (e.g. if there's no cache). Now, since we don't actually dispatch uploads and cache writes in parallel, but rather flip between the streams, depending on which has the lowest so-far-issued offset, and don't wait for the subreqs to finish before flipping, we can end up in a situation where, say, we issue a write to the server and this completes before we start the write to the cache. But because we only activate a stream when we first add a subreq to it, the result collection code may run before we manage to activate the stream - resulting in the folio being cleaned and having the writeback-in-progress mark removed. At this point, the folio no longer belongs to us. This is only really a problem for folios that need to be written to both streams - and in that case, the upload to the server is started first, followed by the write to the cache - and the cache write may see a bad folio. Fix this by activating the cache stream up front if there's a cache available. If there's a cache, then all data is going to be written to it. Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Signed-off-by: David Howells Link: https://lore.kernel.org/r/1599053.1721398818@warthog.procyon.org.uk cc: Jeff Layton cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner --- fs/netfs/write_issue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index ec6cf8707fb0..9258d30cffe3 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -122,6 +122,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping, wreq->io_streams[1].transferred = LONG_MAX; if (fscache_resources_valid(&wreq->cache_resources)) { wreq->io_streams[1].avail = true; + wreq->io_streams[1].active = true; wreq->io_streams[1].prepare_write = wreq->cache_resources.ops->prepare_write_subreq; wreq->io_streams[1].issue_write = wreq->cache_resources.ops->issue_write; } From 51d37982bbac3ea0ca21b2797a9cb0044272b3aa Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 19 Jul 2024 15:19:02 +0100 Subject: [PATCH 150/155] cachefiles: Set the max subreq size for cache writes to MAX_RW_COUNT Set the maximum size of a subrequest that writes to cachefiles to be MAX_RW_COUNT so that we don't overrun the maximum write we can make to the backing filesystem. Signed-off-by: David Howells Link: https://lore.kernel.org/r/1599005.1721398742@warthog.procyon.org.uk cc: Jeff Layton cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner --- fs/cachefiles/io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cachefiles/io.c b/fs/cachefiles/io.c index e667dbcd20e8..a91acd03ee12 100644 --- a/fs/cachefiles/io.c +++ b/fs/cachefiles/io.c @@ -630,7 +630,7 @@ static void cachefiles_prepare_write_subreq(struct netfs_io_subrequest *subreq) _enter("W=%x[%x] %llx", wreq->debug_id, subreq->debug_index, subreq->start); - subreq->max_len = ULONG_MAX; + subreq->max_len = MAX_RW_COUNT; subreq->max_nr_segs = BIO_MAX_VECS; if (!cachefiles_cres_file(cres)) { From 8eac5358ad3bbc007156a0f9ea5637ee7ae421b5 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 19 Jul 2024 13:41:48 +0200 Subject: [PATCH 151/155] fs: use all available ids The counter is unconditionally incremented for each mount allocation. If we set it to 1ULL << 32 we're losing 4294967296 as the first valid non-32 bit mount id. Link: https://lore.kernel.org/r/20240719-work-mount-namespace-v1-1-834113cab0d2@kernel.org Reviewed-by: Josef Bacik Reviewed-by: Jeff Layton Signed-off-by: Christian Brauner --- fs/namespace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/namespace.c b/fs/namespace.c index 221db9de4729..328087a4df8a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -70,7 +70,7 @@ static DEFINE_IDA(mnt_id_ida); static DEFINE_IDA(mnt_group_ida); /* Don't allow confusion with old 32bit mount ID */ -#define MNT_UNIQUE_ID_OFFSET (1ULL << 32) +#define MNT_UNIQUE_ID_OFFSET (1ULL << 31) static atomic64_t mnt_id_ctr = ATOMIC64_INIT(MNT_UNIQUE_ID_OFFSET); static struct hlist_head *mount_hashtable __ro_after_init; From f8138f2ad2f745b9a1c696a05b749eabe44337ea Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Tue, 23 Jul 2024 17:03:56 +0200 Subject: [PATCH 152/155] filelock: Fix fcntl/close race recovery compat path When I wrote commit 3cad1bc01041 ("filelock: Remove locks reliably when fcntl/close race is detected"), I missed that there are two copies of the code I was patching: The normal version, and the version for 64-bit offsets on 32-bit kernels. Thanks to Greg KH for stumbling over this while doing the stable backport... Apply exactly the same fix to the compat path for 32-bit kernels. Fixes: c293621bbf67 ("[PATCH] stale POSIX lock handling") Cc: stable@kernel.org Link: https://bugs.chromium.org/p/project-zero/issues/detail?id=2563 Signed-off-by: Jann Horn Link: https://lore.kernel.org/r/20240723-fs-lock-recover-compatfix-v1-1-148096719529@google.com Signed-off-by: Christian Brauner --- fs/locks.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index bdd94c32256f..9afb16e0683f 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -2570,8 +2570,9 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd, error = do_lock_file_wait(filp, cmd, file_lock); /* - * Attempt to detect a close/fcntl race and recover by releasing the - * lock that was just acquired. There is no need to do that when we're + * Detect close/fcntl races and recover by zapping all POSIX locks + * associated with this file and our files_struct, just like on + * filp_flush(). There is no need to do that when we're * unlocking though, or for OFD locks. */ if (!error && file_lock->c.flc_type != F_UNLCK && @@ -2586,9 +2587,7 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd, f = files_lookup_fd_locked(files, fd); spin_unlock(&files->file_lock); if (f != filp) { - file_lock->c.flc_type = F_UNLCK; - error = do_lock_file_wait(filp, cmd, file_lock); - WARN_ON_ONCE(error); + locks_remove_posix(filp, files); error = -EBADF; } } From c3a5e3e872f3688ae0dc57bb78ca633921d96a91 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 23 Jul 2024 09:59:54 +0100 Subject: [PATCH 153/155] vfs: Fix potential circular locking through setxattr() and removexattr() When using cachefiles, lockdep may emit something similar to the circular locking dependency notice below. The problem appears to stem from the following: (1) Cachefiles manipulates xattrs on the files in its cache when called from ->writepages(). (2) The setxattr() and removexattr() system call handlers get the name (and value) from userspace after taking the sb_writers lock, putting accesses of the vma->vm_lock and mm->mmap_lock inside of that. (3) The afs filesystem uses a per-inode lock to prevent multiple revalidation RPCs and in writeback vs truncate to prevent parallel operations from deadlocking against the server on one side and local page locks on the other. Fix this by moving the getting of the name and value in {get,remove}xattr() outside of the sb_writers lock. This also has the minor benefits that we don't need to reget these in the event of a retry and we never try to take the sb_writers lock in the event we can't pull the name and value into the kernel. Alternative approaches that might fix this include moving the dispatch of a write to the cache off to a workqueue or trying to do without the validation lock in afs. Note that this might also affect other filesystems that use netfslib and/or cachefiles. ====================================================== WARNING: possible circular locking dependency detected 6.10.0-build2+ #956 Not tainted ------------------------------------------------------ fsstress/6050 is trying to acquire lock: ffff888138fd82f0 (mapping.invalidate_lock#3){++++}-{3:3}, at: filemap_fault+0x26e/0x8b0 but task is already holding lock: ffff888113f26d18 (&vma->vm_lock->lock){++++}-{3:3}, at: lock_vma_under_rcu+0x165/0x250 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #4 (&vma->vm_lock->lock){++++}-{3:3}: __lock_acquire+0xaf0/0xd80 lock_acquire.part.0+0x103/0x280 down_write+0x3b/0x50 vma_start_write+0x6b/0xa0 vma_link+0xcc/0x140 insert_vm_struct+0xb7/0xf0 alloc_bprm+0x2c1/0x390 kernel_execve+0x65/0x1a0 call_usermodehelper_exec_async+0x14d/0x190 ret_from_fork+0x24/0x40 ret_from_fork_asm+0x1a/0x30 -> #3 (&mm->mmap_lock){++++}-{3:3}: __lock_acquire+0xaf0/0xd80 lock_acquire.part.0+0x103/0x280 __might_fault+0x7c/0xb0 strncpy_from_user+0x25/0x160 removexattr+0x7f/0x100 __do_sys_fremovexattr+0x7e/0xb0 do_syscall_64+0x9f/0x100 entry_SYSCALL_64_after_hwframe+0x76/0x7e -> #2 (sb_writers#14){.+.+}-{0:0}: __lock_acquire+0xaf0/0xd80 lock_acquire.part.0+0x103/0x280 percpu_down_read+0x3c/0x90 vfs_iocb_iter_write+0xe9/0x1d0 __cachefiles_write+0x367/0x430 cachefiles_issue_write+0x299/0x2f0 netfs_advance_write+0x117/0x140 netfs_write_folio.isra.0+0x5ca/0x6e0 netfs_writepages+0x230/0x2f0 afs_writepages+0x4d/0x70 do_writepages+0x1e8/0x3e0 filemap_fdatawrite_wbc+0x84/0xa0 __filemap_fdatawrite_range+0xa8/0xf0 file_write_and_wait_range+0x59/0x90 afs_release+0x10f/0x270 __fput+0x25f/0x3d0 __do_sys_close+0x43/0x70 do_syscall_64+0x9f/0x100 entry_SYSCALL_64_after_hwframe+0x76/0x7e -> #1 (&vnode->validate_lock){++++}-{3:3}: __lock_acquire+0xaf0/0xd80 lock_acquire.part.0+0x103/0x280 down_read+0x95/0x200 afs_writepages+0x37/0x70 do_writepages+0x1e8/0x3e0 filemap_fdatawrite_wbc+0x84/0xa0 filemap_invalidate_inode+0x167/0x1e0 netfs_unbuffered_write_iter+0x1bd/0x2d0 vfs_write+0x22e/0x320 ksys_write+0xbc/0x130 do_syscall_64+0x9f/0x100 entry_SYSCALL_64_after_hwframe+0x76/0x7e -> #0 (mapping.invalidate_lock#3){++++}-{3:3}: check_noncircular+0x119/0x160 check_prev_add+0x195/0x430 __lock_acquire+0xaf0/0xd80 lock_acquire.part.0+0x103/0x280 down_read+0x95/0x200 filemap_fault+0x26e/0x8b0 __do_fault+0x57/0xd0 do_pte_missing+0x23b/0x320 __handle_mm_fault+0x2d4/0x320 handle_mm_fault+0x14f/0x260 do_user_addr_fault+0x2a2/0x500 exc_page_fault+0x71/0x90 asm_exc_page_fault+0x22/0x30 other info that might help us debug this: Chain exists of: mapping.invalidate_lock#3 --> &mm->mmap_lock --> &vma->vm_lock->lock Possible unsafe locking scenario: CPU0 CPU1 ---- ---- rlock(&vma->vm_lock->lock); lock(&mm->mmap_lock); lock(&vma->vm_lock->lock); rlock(mapping.invalidate_lock#3); *** DEADLOCK *** 1 lock held by fsstress/6050: #0: ffff888113f26d18 (&vma->vm_lock->lock){++++}-{3:3}, at: lock_vma_under_rcu+0x165/0x250 stack backtrace: CPU: 0 PID: 6050 Comm: fsstress Not tainted 6.10.0-build2+ #956 Hardware name: ASUS All Series/H97-PLUS, BIOS 2306 10/09/2014 Call Trace: dump_stack_lvl+0x57/0x80 check_noncircular+0x119/0x160 ? queued_spin_lock_slowpath+0x4be/0x510 ? __pfx_check_noncircular+0x10/0x10 ? __pfx_queued_spin_lock_slowpath+0x10/0x10 ? mark_lock+0x47/0x160 ? init_chain_block+0x9c/0xc0 ? add_chain_block+0x84/0xf0 check_prev_add+0x195/0x430 __lock_acquire+0xaf0/0xd80 ? __pfx___lock_acquire+0x10/0x10 ? __lock_release.isra.0+0x13b/0x230 lock_acquire.part.0+0x103/0x280 ? filemap_fault+0x26e/0x8b0 ? __pfx_lock_acquire.part.0+0x10/0x10 ? rcu_is_watching+0x34/0x60 ? lock_acquire+0xd7/0x120 down_read+0x95/0x200 ? filemap_fault+0x26e/0x8b0 ? __pfx_down_read+0x10/0x10 ? __filemap_get_folio+0x25/0x1a0 filemap_fault+0x26e/0x8b0 ? __pfx_filemap_fault+0x10/0x10 ? find_held_lock+0x7c/0x90 ? __pfx___lock_release.isra.0+0x10/0x10 ? __pte_offset_map+0x99/0x110 __do_fault+0x57/0xd0 do_pte_missing+0x23b/0x320 __handle_mm_fault+0x2d4/0x320 ? __pfx___handle_mm_fault+0x10/0x10 handle_mm_fault+0x14f/0x260 do_user_addr_fault+0x2a2/0x500 exc_page_fault+0x71/0x90 asm_exc_page_fault+0x22/0x30 Signed-off-by: David Howells Link: https://lore.kernel.org/r/2136178.1721725194@warthog.procyon.org.uk cc: Alexander Viro cc: Christian Brauner cc: Jan Kara cc: Jeff Layton cc: Gao Xiang cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-erofs@lists.ozlabs.org cc: linux-fsdevel@vger.kernel.org [brauner: fix minor issues] Signed-off-by: Christian Brauner --- fs/xattr.c | 91 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/fs/xattr.c b/fs/xattr.c index f8b643f91a98..7672ce5486c5 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -630,10 +630,9 @@ int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry, ctx->kvalue, ctx->size, ctx->flags); } -static long -setxattr(struct mnt_idmap *idmap, struct dentry *d, - const char __user *name, const void __user *value, size_t size, - int flags) +static int path_setxattr(const char __user *pathname, + const char __user *name, const void __user *value, + size_t size, int flags, unsigned int lookup_flags) { struct xattr_name kname; struct xattr_ctx ctx = { @@ -643,33 +642,20 @@ setxattr(struct mnt_idmap *idmap, struct dentry *d, .kname = &kname, .flags = flags, }; + struct path path; int error; error = setxattr_copy(name, &ctx); if (error) return error; - error = do_setxattr(idmap, d, &ctx); - - kvfree(ctx.kvalue); - return error; -} - -static int path_setxattr(const char __user *pathname, - const char __user *name, const void __user *value, - size_t size, int flags, unsigned int lookup_flags) -{ - struct path path; - int error; - retry: error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); if (error) - return error; + goto out; error = mnt_want_write(path.mnt); if (!error) { - error = setxattr(mnt_idmap(path.mnt), path.dentry, name, - value, size, flags); + error = do_setxattr(mnt_idmap(path.mnt), path.dentry, &ctx); mnt_drop_write(path.mnt); } path_put(&path); @@ -677,6 +663,9 @@ retry: lookup_flags |= LOOKUP_REVAL; goto retry; } + +out: + kvfree(ctx.kvalue); return error; } @@ -697,20 +686,32 @@ SYSCALL_DEFINE5(lsetxattr, const char __user *, pathname, SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, const void __user *,value, size_t, size, int, flags) { - struct fd f = fdget(fd); - int error = -EBADF; + struct xattr_name kname; + struct xattr_ctx ctx = { + .cvalue = value, + .kvalue = NULL, + .size = size, + .kname = &kname, + .flags = flags, + }; + int error; + CLASS(fd, f)(fd); if (!f.file) - return error; + return -EBADF; + audit_file(f.file); + error = setxattr_copy(name, &ctx); + if (error) + return error; + error = mnt_want_write_file(f.file); if (!error) { - error = setxattr(file_mnt_idmap(f.file), - f.file->f_path.dentry, name, - value, size, flags); + error = do_setxattr(file_mnt_idmap(f.file), + f.file->f_path.dentry, &ctx); mnt_drop_write_file(f.file); } - fdput(f); + kvfree(ctx.kvalue); return error; } @@ -899,9 +900,17 @@ SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size) * Extended attribute REMOVE operations */ static long -removexattr(struct mnt_idmap *idmap, struct dentry *d, - const char __user *name) +removexattr(struct mnt_idmap *idmap, struct dentry *d, const char *name) { + if (is_posix_acl_xattr(name)) + return vfs_remove_acl(idmap, d, name); + return vfs_removexattr(idmap, d, name); +} + +static int path_removexattr(const char __user *pathname, + const char __user *name, unsigned int lookup_flags) +{ + struct path path; int error; char kname[XATTR_NAME_MAX + 1]; @@ -910,25 +919,13 @@ removexattr(struct mnt_idmap *idmap, struct dentry *d, error = -ERANGE; if (error < 0) return error; - - if (is_posix_acl_xattr(kname)) - return vfs_remove_acl(idmap, d, kname); - - return vfs_removexattr(idmap, d, kname); -} - -static int path_removexattr(const char __user *pathname, - const char __user *name, unsigned int lookup_flags) -{ - struct path path; - int error; retry: error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path); if (error) return error; error = mnt_want_write(path.mnt); if (!error) { - error = removexattr(mnt_idmap(path.mnt), path.dentry, name); + error = removexattr(mnt_idmap(path.mnt), path.dentry, kname); mnt_drop_write(path.mnt); } path_put(&path); @@ -954,15 +951,23 @@ SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname, SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name) { struct fd f = fdget(fd); + char kname[XATTR_NAME_MAX + 1]; int error = -EBADF; if (!f.file) return error; audit_file(f.file); + + error = strncpy_from_user(kname, name, sizeof(kname)); + if (error == 0 || error == sizeof(kname)) + error = -ERANGE; + if (error < 0) + return error; + error = mnt_want_write_file(f.file); if (!error) { error = removexattr(file_mnt_idmap(f.file), - f.file->f_path.dentry, name); + f.file->f_path.dentry, kname); mnt_drop_write_file(f.file); } fdput(f); From f5e5e97c719d289025afce07050effcf1f7373ef Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 24 Jul 2024 10:50:33 +0200 Subject: [PATCH 154/155] inode: clarify what's locked In __wait_on_freeing_inode() we warn in case the inode_hash_lock is held but the inode is unhashed. We then release the inode_lock. So using "locked" as parameter name is confusing. Use is_inode_hash_locked as parameter name instead. Signed-off-by: Christian Brauner --- fs/inode.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 05613745fad6..470b57ef1cf5 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -898,18 +898,18 @@ long prune_icache_sb(struct super_block *sb, struct shrink_control *sc) return freed; } -static void __wait_on_freeing_inode(struct inode *inode, bool locked); +static void __wait_on_freeing_inode(struct inode *inode, bool is_inode_hash_locked); /* * Called with the inode lock held. */ static struct inode *find_inode(struct super_block *sb, struct hlist_head *head, int (*test)(struct inode *, void *), - void *data, bool locked) + void *data, bool is_inode_hash_locked) { struct inode *inode = NULL; - if (locked) + if (is_inode_hash_locked) lockdep_assert_held(&inode_hash_lock); else lockdep_assert_not_held(&inode_hash_lock); @@ -923,7 +923,7 @@ repeat: continue; spin_lock(&inode->i_lock); if (inode->i_state & (I_FREEING|I_WILL_FREE)) { - __wait_on_freeing_inode(inode, locked); + __wait_on_freeing_inode(inode, is_inode_hash_locked); goto repeat; } if (unlikely(inode->i_state & I_CREATING)) { @@ -946,11 +946,11 @@ repeat: */ static struct inode *find_inode_fast(struct super_block *sb, struct hlist_head *head, unsigned long ino, - bool locked) + bool is_inode_hash_locked) { struct inode *inode = NULL; - if (locked) + if (is_inode_hash_locked) lockdep_assert_held(&inode_hash_lock); else lockdep_assert_not_held(&inode_hash_lock); @@ -964,7 +964,7 @@ repeat: continue; spin_lock(&inode->i_lock); if (inode->i_state & (I_FREEING|I_WILL_FREE)) { - __wait_on_freeing_inode(inode, locked); + __wait_on_freeing_inode(inode, is_inode_hash_locked); goto repeat; } if (unlikely(inode->i_state & I_CREATING)) { @@ -2297,7 +2297,7 @@ EXPORT_SYMBOL(inode_needs_sync); * wake_up_bit(&inode->i_state, __I_NEW) after removing from the hash list * will DTRT. */ -static void __wait_on_freeing_inode(struct inode *inode, bool locked) +static void __wait_on_freeing_inode(struct inode *inode, bool is_inode_hash_locked) { wait_queue_head_t *wq; DEFINE_WAIT_BIT(wait, &inode->i_state, __I_NEW); @@ -2306,7 +2306,7 @@ static void __wait_on_freeing_inode(struct inode *inode, bool locked) * Handle racing against evict(), see that routine for more details. */ if (unlikely(inode_unhashed(inode))) { - WARN_ON(locked); + WARN_ON(is_inode_hash_locked); spin_unlock(&inode->i_lock); return; } @@ -2315,11 +2315,11 @@ static void __wait_on_freeing_inode(struct inode *inode, bool locked) prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE); spin_unlock(&inode->i_lock); rcu_read_unlock(); - if (locked) + if (is_inode_hash_locked) spin_unlock(&inode_hash_lock); schedule(); finish_wait(wq, &wait.wq_entry); - if (locked) + if (is_inode_hash_locked) spin_lock(&inode_hash_lock); rcu_read_lock(); } From e44be00289ef3ce8195d915cf354714b14fd30a4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 23 Jul 2024 18:14:54 -0700 Subject: [PATCH 155/155] hostfs: fix folio conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit e3ec0fe944d2 ("hostfs: Convert hostfs_read_folio() to use a folio") simplified hostfs_read_folio(), but in the process of converting to using folios natively also mis-used the folio_zero_tail() function due to the very confusing API of that function. Very arguably it's folio_zero_tail() API itself that is buggy, since it would make more sense (and the documentation kind of implies) that the third argument would be the pointer to the beginning of the folio buffer. But no, the third argument to folio_zero_tail() is where we should start zeroing the tail (even if we already also pass in the offset separately as the second argument). So fix the hostfs caller, and we can leave any folio_zero_tail() sanity cleanup for later. Reported-and-tested-by: Maciej Żenczykowski Fixes: e3ec0fe944d2 ("hostfs: Convert hostfs_read_folio() to use a folio") Link: https://lore.kernel.org/all/CANP3RGceNzwdb7w=vPf5=7BCid5HVQDmz1K5kC9JG42+HVAh_g@mail.gmail.com/ Cc: Matthew Wilcox Cc: Christian Brauner Signed-off-by: Linus Torvalds --- fs/hostfs/hostfs_kern.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 3eb747d26924..2b532670a561 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -455,7 +455,7 @@ static int hostfs_read_folio(struct file *file, struct folio *folio) if (bytes_read < 0) ret = bytes_read; else - buffer = folio_zero_tail(folio, bytes_read, buffer); + buffer = folio_zero_tail(folio, bytes_read, buffer + bytes_read); kunmap_local(buffer); folio_end_read(folio, ret == 0);