From 6a1c4c4688355063c8ead7de328c358bd959297a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 10 Mar 2023 13:48:47 -0500 Subject: [PATCH 01/18] ufs: fix handling of delete_entry and set_link failures similar to minixfs series - make ufs_set_link() report failures, lift folio_release_kmap() into the callers of ufs_set_link() and ufs_delete_entry(), make ufs_rename() handle failures in both. Signed-off-by: Al Viro --- fs/ufs/dir.c | 29 ++++++++++++++--------------- fs/ufs/namei.c | 39 +++++++++++++++++---------------------- fs/ufs/ufs.h | 4 ++-- 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index d6e6a2198971..88d0062cfdb9 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -81,10 +81,9 @@ ino_t ufs_inode_by_name(struct inode *dir, const struct qstr *qstr) } -/* Releases the page */ -void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, - struct folio *folio, struct inode *inode, - bool update_times) +int ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, + struct folio *folio, struct inode *inode, + bool update_times) { loff_t pos = folio_pos(folio) + offset_in_folio(folio, de); unsigned len = fs16_to_cpu(dir->i_sb, de->d_reclen); @@ -92,17 +91,19 @@ void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, folio_lock(folio); err = ufs_prepare_chunk(folio, pos, len); - BUG_ON(err); + if (unlikely(err)) { + folio_unlock(folio); + return err; + } de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino); ufs_set_de_type(dir->i_sb, de, inode->i_mode); ufs_commit_chunk(folio, pos, len); - folio_release_kmap(folio, de); if (update_times) inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); mark_inode_dirty(dir); - ufs_handle_dirsync(dir); + return ufs_handle_dirsync(dir); } static bool ufs_check_folio(struct folio *folio, char *kaddr) @@ -505,8 +506,7 @@ int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir, if (de->d_reclen == 0) { ufs_error(inode->i_sb, __func__, "zero-length directory entry"); - err = -EIO; - goto out; + return -EIO; } pde = de; de = ufs_next_entry(sb, de); @@ -516,18 +516,17 @@ int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir, pos = folio_pos(folio) + from; folio_lock(folio); err = ufs_prepare_chunk(folio, pos, to - from); - BUG_ON(err); + if (unlikely(err)) { + folio_unlock(folio); + return err; + } if (pde) pde->d_reclen = cpu_to_fs16(sb, to - from); dir->d_ino = 0; ufs_commit_chunk(folio, pos, to - from); inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); mark_inode_dirty(inode); - err = ufs_handle_dirsync(inode); -out: - folio_release_kmap(folio, kaddr); - UFSD("EXIT\n"); - return err; + return ufs_handle_dirsync(inode); } int ufs_make_empty(struct inode * inode, struct inode *dir) diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index c8390976ab6a..38a024c8cccd 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -210,20 +210,18 @@ static int ufs_unlink(struct inode *dir, struct dentry *dentry) struct inode * inode = d_inode(dentry); struct ufs_dir_entry *de; struct folio *folio; - int err = -ENOENT; + int err; de = ufs_find_entry(dir, &dentry->d_name, &folio); if (!de) - goto out; + return -ENOENT; err = ufs_delete_entry(dir, de, folio); - if (err) - goto out; - - inode_set_ctime_to_ts(inode, inode_get_ctime(dir)); - inode_dec_link_count(inode); - err = 0; -out: + if (!err) { + inode_set_ctime_to_ts(inode, inode_get_ctime(dir)); + inode_dec_link_count(inode); + } + folio_release_kmap(folio, de); return err; } @@ -253,14 +251,14 @@ static int ufs_rename(struct mnt_idmap *idmap, struct inode *old_dir, struct ufs_dir_entry * dir_de = NULL; struct folio *old_folio; struct ufs_dir_entry *old_de; - int err = -ENOENT; + int err; if (flags & ~RENAME_NOREPLACE) return -EINVAL; old_de = ufs_find_entry(old_dir, &old_dentry->d_name, &old_folio); if (!old_de) - goto out; + return -ENOENT; if (S_ISDIR(old_inode->i_mode)) { err = -EIO; @@ -281,7 +279,10 @@ static int ufs_rename(struct mnt_idmap *idmap, struct inode *old_dir, new_de = ufs_find_entry(new_dir, &new_dentry->d_name, &new_folio); if (!new_de) goto out_dir; - ufs_set_link(new_dir, new_de, new_folio, old_inode, 1); + err = ufs_set_link(new_dir, new_de, new_folio, old_inode, 1); + folio_release_kmap(new_folio, new_de); + if (err) + goto out_dir; inode_set_ctime_current(new_inode); if (dir_de) drop_nlink(new_inode); @@ -299,26 +300,20 @@ static int ufs_rename(struct mnt_idmap *idmap, struct inode *old_dir, * rename. */ inode_set_ctime_current(old_inode); - - ufs_delete_entry(old_dir, old_de, old_folio); mark_inode_dirty(old_inode); - if (dir_de) { + err = ufs_delete_entry(old_dir, old_de, old_folio); + if (!err && dir_de) { if (old_dir != new_dir) - ufs_set_link(old_inode, dir_de, dir_folio, new_dir, 0); - else - folio_release_kmap(dir_folio, dir_de); + err = ufs_set_link(old_inode, dir_de, dir_folio, + new_dir, 0); inode_dec_link_count(old_dir); } - return 0; - - out_dir: if (dir_de) folio_release_kmap(dir_folio, dir_de); out_old: folio_release_kmap(old_folio, old_de); -out: return err; } diff --git a/fs/ufs/ufs.h b/fs/ufs/ufs.h index a2c762cb65a0..c7638e62ffe8 100644 --- a/fs/ufs/ufs.h +++ b/fs/ufs/ufs.h @@ -108,8 +108,8 @@ struct ufs_dir_entry *ufs_find_entry(struct inode *, const struct qstr *, int ufs_delete_entry(struct inode *, struct ufs_dir_entry *, struct folio *); int ufs_empty_dir(struct inode *); struct ufs_dir_entry *ufs_dotdot(struct inode *, struct folio **); -void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, - struct folio *folio, struct inode *inode, bool update_times); +int ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, + struct folio *folio, struct inode *inode, bool update_times); /* file.c */ extern const struct inode_operations ufs_file_inode_operations; From 7f71d6e3462bde7db08058e749667b5d3235fad1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 18 Aug 2024 00:15:05 -0400 Subject: [PATCH 02/18] ufs: missing ->splice_write() normal ->write_iter()-based ->splice_write() works here just fine... Signed-off-by: Al Viro --- fs/ufs/file.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ufs/file.c b/fs/ufs/file.c index 6558882a89ef..487ad1fc2de6 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -42,4 +42,5 @@ const struct file_operations ufs_file_operations = { .open = generic_file_open, .fsync = generic_file_fsync, .splice_read = filemap_splice_read, + .splice_write = iter_file_splice_write, }; From 0bfd3e1078c537cd66a8addb1c01835de4234a4c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 13 Dec 2023 11:20:44 -0500 Subject: [PATCH 03/18] ufs: fix ufs_read_cylinder() failure handling 1) ufs_load_cylinder() should return NULL on ufs_read_cylinder() failures. ufs_error() is not enough. As it is, IO failure on attempt to read a part of cylinder group metadata is likely to end up with an oops. 2) we drop the wrong buffer heads when undoing sb_bread() on IO failure halfway through the read - we need to brelse() what we've got from sb_bread(), TYVM... Signed-off-by: Al Viro --- fs/ufs/cylinder.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c index 1abe5454de47..a2813270c303 100644 --- a/fs/ufs/cylinder.c +++ b/fs/ufs/cylinder.c @@ -26,7 +26,7 @@ * Read cylinder group into cache. The memory space for ufs_cg_private_info * structure is already allocated during ufs_read_super. */ -static void ufs_read_cylinder (struct super_block * sb, +static bool ufs_read_cylinder(struct super_block *sb, unsigned cgno, unsigned bitmap_nr) { struct ufs_sb_info * sbi = UFS_SB(sb); @@ -46,9 +46,11 @@ static void ufs_read_cylinder (struct super_block * sb, * We have already the first fragment of cylinder group block in buffer */ UCPI_UBH(ucpi)->bh[0] = sbi->s_ucg[cgno]; - for (i = 1; i < UCPI_UBH(ucpi)->count; i++) - if (!(UCPI_UBH(ucpi)->bh[i] = sb_bread(sb, UCPI_UBH(ucpi)->fragment + i))) + for (i = 1; i < UCPI_UBH(ucpi)->count; i++) { + UCPI_UBH(ucpi)->bh[i] = sb_bread(sb, UCPI_UBH(ucpi)->fragment + i); + if (!UCPI_UBH(ucpi)->bh[i]) goto failed; + } sbi->s_cgno[bitmap_nr] = cgno; ucpi->c_cgx = fs32_to_cpu(sb, ucg->cg_cgx); @@ -67,13 +69,14 @@ static void ufs_read_cylinder (struct super_block * sb, ucpi->c_clusteroff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clusteroff); ucpi->c_nclusterblks = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_nclusterblks); UFSD("EXIT\n"); - return; + return true; failed: for (j = 1; j < i; j++) - brelse (sbi->s_ucg[j]); + brelse(UCPI_UBH(ucpi)->bh[j]); sbi->s_cgno[bitmap_nr] = UFS_CGNO_EMPTY; ufs_error (sb, "ufs_read_cylinder", "can't read cylinder group block %u", cgno); + return false; } /* @@ -156,15 +159,14 @@ struct ufs_cg_private_info * ufs_load_cylinder ( UFSD("EXIT (FAILED)\n"); return NULL; } - else { - UFSD("EXIT\n"); - return sbi->s_ucpi[cgno]; - } } else { - ufs_read_cylinder (sb, cgno, cgno); - UFSD("EXIT\n"); - return sbi->s_ucpi[cgno]; + if (unlikely(!ufs_read_cylinder (sb, cgno, cgno))) { + UFSD("EXIT (FAILED)\n"); + return NULL; + } } + UFSD("EXIT\n"); + return sbi->s_ucpi[cgno]; } /* * Cylinder group number cg is in cache but it was not last used, @@ -195,7 +197,10 @@ struct ufs_cg_private_info * ufs_load_cylinder ( sbi->s_ucpi[j] = sbi->s_ucpi[j-1]; } sbi->s_ucpi[0] = ucpi; - ufs_read_cylinder (sb, cgno, 0); + if (unlikely(!ufs_read_cylinder (sb, cgno, 0))) { + UFSD("EXIT (FAILED)\n"); + return NULL; + } } UFSD("EXIT\n"); return sbi->s_ucpi[0]; From 65136e46a03f70ee9e5515c401fd89b2de86b66c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 27 Jan 2023 19:23:20 -0500 Subject: [PATCH 04/18] ufs: untangle ubh_...block...() macros, part 1 passing implicit argument to a macro by having it in a variable with special name is Not Nice(tm); just pass it explicitly. kill an unused macro, while we are at it... Signed-off-by: Al Viro --- fs/ufs/balloc.c | 10 +++++----- fs/ufs/util.h | 11 +++-------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index 53c11be2b2c1..e412ddcfda03 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -95,7 +95,7 @@ void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count) * Trying to reassemble free fragments into block */ blkno = ufs_fragstoblks (bbase); - if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) { + if (ubh_isblockset(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) { fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb); uspi->cs_total.cs_nffree -= uspi->s_fpb; fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, uspi->s_fpb); @@ -182,10 +182,10 @@ do_more: for (i = bit; i < end_bit; i += uspi->s_fpb) { blkno = ufs_fragstoblks(i); - if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) { + if (ubh_isblockset(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) { ufs_error(sb, "ufs_free_blocks", "freeing free fragment"); } - ubh_setblock(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno); + ubh_setblock(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, blkno); inode_sub_bytes(inode, uspi->s_fpb << uspi->s_fshift); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, 1); @@ -716,7 +716,7 @@ static u64 ufs_alloccg_block(struct inode *inode, /* * If the requested block is available, use it. */ - if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, ufs_fragstoblks(goal))) { + if (ubh_isblockset(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, ufs_fragstoblks(goal))) { result = goal; goto gotit; } @@ -730,7 +730,7 @@ gotit: if (!try_add_frags(inode, uspi->s_fpb)) return 0; blkno = ufs_fragstoblks(result); - ubh_clrblock (UCPI_UBH(ucpi), ucpi->c_freeoff, blkno); + ubh_clrblock(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, blkno); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, -1); diff --git a/fs/ufs/util.h b/fs/ufs/util.h index bf708b68f150..729bc55398f2 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -455,10 +455,7 @@ static inline unsigned _ubh_find_last_zero_bit_( return (base << uspi->s_bpfshift) + pos - begin; } -#define ubh_isblockclear(ubh,begin,block) (!_ubh_isblockset_(uspi,ubh,begin,block)) - -#define ubh_isblockset(ubh,begin,block) _ubh_isblockset_(uspi,ubh,begin,block) -static inline int _ubh_isblockset_(struct ufs_sb_private_info * uspi, +static inline int ubh_isblockset(struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, unsigned begin, unsigned block) { u8 mask; @@ -478,8 +475,7 @@ static inline int _ubh_isblockset_(struct ufs_sb_private_info * uspi, return 0; } -#define ubh_clrblock(ubh,begin,block) _ubh_clrblock_(uspi,ubh,begin,block) -static inline void _ubh_clrblock_(struct ufs_sb_private_info * uspi, +static inline void ubh_clrblock(struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, unsigned begin, unsigned block) { switch (uspi->s_fpb) { @@ -498,8 +494,7 @@ static inline void _ubh_clrblock_(struct ufs_sb_private_info * uspi, } } -#define ubh_setblock(ubh,begin,block) _ubh_setblock_(uspi,ubh,begin,block) -static inline void _ubh_setblock_(struct ufs_sb_private_info * uspi, +static inline void ubh_setblock(struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, unsigned begin, unsigned block) { switch (uspi->s_fpb) { From 8bec0618a42959b9fecac115c5dbf942675f9776 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 27 Jan 2023 19:36:21 -0500 Subject: [PATCH 05/18] ufs: untangle ubh_...block...(), part 2 pass cylinder group descriptor instead of its buffer head (ubh, always UCPI_UBH(ucpi)) and its ->c_freeoff. Signed-off-by: Al Viro --- fs/ufs/balloc.c | 10 +++++----- fs/ufs/util.h | 16 +++++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index e412ddcfda03..d76c04fbd4fa 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -95,7 +95,7 @@ void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count) * Trying to reassemble free fragments into block */ blkno = ufs_fragstoblks (bbase); - if (ubh_isblockset(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) { + if (ubh_isblockset(uspi, ucpi, blkno)) { fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb); uspi->cs_total.cs_nffree -= uspi->s_fpb; fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, uspi->s_fpb); @@ -182,10 +182,10 @@ do_more: for (i = bit; i < end_bit; i += uspi->s_fpb) { blkno = ufs_fragstoblks(i); - if (ubh_isblockset(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) { + if (ubh_isblockset(uspi, ucpi, blkno)) { ufs_error(sb, "ufs_free_blocks", "freeing free fragment"); } - ubh_setblock(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, blkno); + ubh_setblock(uspi, ucpi, blkno); inode_sub_bytes(inode, uspi->s_fpb << uspi->s_fshift); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, 1); @@ -716,7 +716,7 @@ static u64 ufs_alloccg_block(struct inode *inode, /* * If the requested block is available, use it. */ - if (ubh_isblockset(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, ufs_fragstoblks(goal))) { + if (ubh_isblockset(uspi, ucpi, ufs_fragstoblks(goal))) { result = goal; goto gotit; } @@ -730,7 +730,7 @@ gotit: if (!try_add_frags(inode, uspi->s_fpb)) return 0; blkno = ufs_fragstoblks(result); - ubh_clrblock(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, blkno); + ubh_clrblock(uspi, ucpi, blkno); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, -1); diff --git a/fs/ufs/util.h b/fs/ufs/util.h index 729bc55398f2..c7196a81fb0d 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -455,9 +455,11 @@ static inline unsigned _ubh_find_last_zero_bit_( return (base << uspi->s_bpfshift) + pos - begin; } -static inline int ubh_isblockset(struct ufs_sb_private_info * uspi, - struct ufs_buffer_head * ubh, unsigned begin, unsigned block) +static inline int ubh_isblockset(struct ufs_sb_private_info *uspi, + struct ufs_cg_private_info *ucpi, unsigned block) { + struct ufs_buffer_head *ubh = UCPI_UBH(ucpi); + unsigned begin = ucpi->c_freeoff; u8 mask; switch (uspi->s_fpb) { case 8: @@ -475,9 +477,11 @@ static inline int ubh_isblockset(struct ufs_sb_private_info * uspi, return 0; } -static inline void ubh_clrblock(struct ufs_sb_private_info * uspi, - struct ufs_buffer_head * ubh, unsigned begin, unsigned block) +static inline void ubh_clrblock(struct ufs_sb_private_info *uspi, + struct ufs_cg_private_info *ucpi, unsigned block) { + struct ufs_buffer_head *ubh = UCPI_UBH(ucpi); + unsigned begin = ucpi->c_freeoff; switch (uspi->s_fpb) { case 8: *ubh_get_addr (ubh, begin + block) = 0x00; @@ -495,8 +499,10 @@ static inline void ubh_clrblock(struct ufs_sb_private_info * uspi, } static inline void ubh_setblock(struct ufs_sb_private_info * uspi, - struct ufs_buffer_head * ubh, unsigned begin, unsigned block) + struct ufs_cg_private_info *ucpi, unsigned block) { + struct ufs_buffer_head *ubh = UCPI_UBH(ucpi); + unsigned begin = ucpi->c_freeoff; switch (uspi->s_fpb) { case 8: *ubh_get_addr(ubh, begin + block) = 0xff; From dce3e8d33aa7588fe9bea5f58796e94ee66091c3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 27 Jan 2023 19:56:44 -0500 Subject: [PATCH 06/18] ufs: untangle ubh_...block...(), part 3 Pass fragment number instead of a block one. It's available in all callers and it makes the logics inside those helpers much simpler. The bitmap they operate upon is with bit per fragment, block being an aligned group of 1, 2, 4 or 8 adjacent fragments. We still need a switch by the number of fragments in block (== number of bits to check/set/clear), but finding the byte we need to work with becomes uniform and that makes the things easier to follow. Signed-off-by: Al Viro --- fs/ufs/balloc.c | 10 +++++----- fs/ufs/util.h | 45 ++++++++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index d76c04fbd4fa..7694666fac18 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -95,7 +95,7 @@ void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count) * Trying to reassemble free fragments into block */ blkno = ufs_fragstoblks (bbase); - if (ubh_isblockset(uspi, ucpi, blkno)) { + if (ubh_isblockset(uspi, ucpi, bbase)) { fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb); uspi->cs_total.cs_nffree -= uspi->s_fpb; fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, uspi->s_fpb); @@ -182,10 +182,10 @@ do_more: for (i = bit; i < end_bit; i += uspi->s_fpb) { blkno = ufs_fragstoblks(i); - if (ubh_isblockset(uspi, ucpi, blkno)) { + if (ubh_isblockset(uspi, ucpi, i)) { ufs_error(sb, "ufs_free_blocks", "freeing free fragment"); } - ubh_setblock(uspi, ucpi, blkno); + ubh_setblock(uspi, ucpi, i); inode_sub_bytes(inode, uspi->s_fpb << uspi->s_fshift); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, 1); @@ -716,7 +716,7 @@ static u64 ufs_alloccg_block(struct inode *inode, /* * If the requested block is available, use it. */ - if (ubh_isblockset(uspi, ucpi, ufs_fragstoblks(goal))) { + if (ubh_isblockset(uspi, ucpi, goal)) { result = goal; goto gotit; } @@ -730,7 +730,7 @@ gotit: if (!try_add_frags(inode, uspi->s_fpb)) return 0; blkno = ufs_fragstoblks(result); - ubh_clrblock(uspi, ucpi, blkno); + ubh_clrblock(uspi, ucpi, result); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, -1); diff --git a/fs/ufs/util.h b/fs/ufs/util.h index c7196a81fb0d..fafae166ee55 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -456,65 +456,68 @@ static inline unsigned _ubh_find_last_zero_bit_( } static inline int ubh_isblockset(struct ufs_sb_private_info *uspi, - struct ufs_cg_private_info *ucpi, unsigned block) + struct ufs_cg_private_info *ucpi, unsigned int frag) { struct ufs_buffer_head *ubh = UCPI_UBH(ucpi); - unsigned begin = ucpi->c_freeoff; + u8 *p = ubh_get_addr(ubh, ucpi->c_freeoff + (frag >> 3)); u8 mask; + switch (uspi->s_fpb) { case 8: - return (*ubh_get_addr (ubh, begin + block) == 0xff); + return *p == 0xff; case 4: - mask = 0x0f << ((block & 0x01) << 2); - return (*ubh_get_addr (ubh, begin + (block >> 1)) & mask) == mask; + mask = 0x0f << (frag & 4); + return (*p & mask) == mask; case 2: - mask = 0x03 << ((block & 0x03) << 1); - return (*ubh_get_addr (ubh, begin + (block >> 2)) & mask) == mask; + mask = 0x03 << (frag & 6); + return (*p & mask) == mask; case 1: - mask = 0x01 << (block & 0x07); - return (*ubh_get_addr (ubh, begin + (block >> 3)) & mask) == mask; + mask = 0x01 << (frag & 7); + return (*p & mask) == mask; } return 0; } static inline void ubh_clrblock(struct ufs_sb_private_info *uspi, - struct ufs_cg_private_info *ucpi, unsigned block) + struct ufs_cg_private_info *ucpi, unsigned int frag) { struct ufs_buffer_head *ubh = UCPI_UBH(ucpi); - unsigned begin = ucpi->c_freeoff; + u8 *p = ubh_get_addr(ubh, ucpi->c_freeoff + (frag >> 3)); + switch (uspi->s_fpb) { case 8: - *ubh_get_addr (ubh, begin + block) = 0x00; + *p = 0x00; return; case 4: - *ubh_get_addr (ubh, begin + (block >> 1)) &= ~(0x0f << ((block & 0x01) << 2)); + *p &= ~(0x0f << (frag & 4)); return; case 2: - *ubh_get_addr (ubh, begin + (block >> 2)) &= ~(0x03 << ((block & 0x03) << 1)); + *p &= ~(0x03 << (frag & 6)); return; case 1: - *ubh_get_addr (ubh, begin + (block >> 3)) &= ~(0x01 << ((block & 0x07))); + *p &= ~(0x01 << (frag & 7)); return; } } static inline void ubh_setblock(struct ufs_sb_private_info * uspi, - struct ufs_cg_private_info *ucpi, unsigned block) + struct ufs_cg_private_info *ucpi, unsigned int frag) { struct ufs_buffer_head *ubh = UCPI_UBH(ucpi); - unsigned begin = ucpi->c_freeoff; + u8 *p = ubh_get_addr(ubh, ucpi->c_freeoff + (frag >> 3)); + switch (uspi->s_fpb) { case 8: - *ubh_get_addr(ubh, begin + block) = 0xff; + *p = 0xff; return; case 4: - *ubh_get_addr(ubh, begin + (block >> 1)) |= (0x0f << ((block & 0x01) << 2)); + *p |= 0x0f << (frag & 4); return; case 2: - *ubh_get_addr(ubh, begin + (block >> 2)) |= (0x03 << ((block & 0x03) << 1)); + *p |= 0x03 << (frag & 6); return; case 1: - *ubh_get_addr(ubh, begin + (block >> 3)) |= (0x01 << ((block & 0x07))); + *p |= 0x01 << (frag & 7); return; } } From c5df105f7da3448129b120da7dba765bc64ddb62 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 27 Jan 2023 20:12:07 -0500 Subject: [PATCH 07/18] ufs_clusteracct(): switch to passing fragment number Currently all callers pass it a block number. All of them have it derived from a fragment number (both fragment and block numbers are within a cylinder group, and thus 32bit). Pass it the fragment number instead; none of the callers has other uses for the block number, so that ends up with cleaner code. Signed-off-by: Al Viro --- fs/ufs/balloc.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index 7694666fac18..1793ce48df0a 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -43,7 +43,6 @@ void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count) struct ufs_cg_private_info * ucpi; struct ufs_cylinder_group * ucg; unsigned cgno, bit, end_bit, bbase, blkmap, i; - u64 blkno; sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -94,13 +93,12 @@ void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count) /* * Trying to reassemble free fragments into block */ - blkno = ufs_fragstoblks (bbase); if (ubh_isblockset(uspi, ucpi, bbase)) { fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb); uspi->cs_total.cs_nffree -= uspi->s_fpb; fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, uspi->s_fpb); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) - ufs_clusteracct (sb, ucpi, blkno, 1); + ufs_clusteracct(sb, ucpi, bbase, 1); fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1); uspi->cs_total.cs_nbfree++; fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1); @@ -139,7 +137,6 @@ void ufs_free_blocks(struct inode *inode, u64 fragment, unsigned count) struct ufs_cg_private_info * ucpi; struct ufs_cylinder_group * ucg; unsigned overflow, cgno, bit, end_bit, i; - u64 blkno; sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -181,14 +178,13 @@ do_more: } for (i = bit; i < end_bit; i += uspi->s_fpb) { - blkno = ufs_fragstoblks(i); if (ubh_isblockset(uspi, ucpi, i)) { ufs_error(sb, "ufs_free_blocks", "freeing free fragment"); } ubh_setblock(uspi, ucpi, i); inode_sub_bytes(inode, uspi->s_fpb << uspi->s_fshift); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) - ufs_clusteracct (sb, ucpi, blkno, 1); + ufs_clusteracct(sb, ucpi, i, 1); fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1); uspi->cs_total.cs_nbfree++; @@ -698,7 +694,7 @@ static u64 ufs_alloccg_block(struct inode *inode, struct super_block * sb; struct ufs_sb_private_info * uspi; struct ufs_cylinder_group * ucg; - u64 result, blkno; + u64 result; UFSD("ENTER, goal %llu\n", (unsigned long long)goal); @@ -729,10 +725,9 @@ norot: gotit: if (!try_add_frags(inode, uspi->s_fpb)) return 0; - blkno = ufs_fragstoblks(result); ubh_clrblock(uspi, ucpi, result); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) - ufs_clusteracct (sb, ucpi, blkno, -1); + ufs_clusteracct(sb, ucpi, result, -1); fs32_sub(sb, &ucg->cg_cs.cs_nbfree, 1); uspi->cs_total.cs_nbfree--; @@ -863,12 +858,12 @@ static u64 ufs_bitmap_search(struct super_block *sb, } static void ufs_clusteracct(struct super_block * sb, - struct ufs_cg_private_info * ucpi, unsigned blkno, int cnt) + struct ufs_cg_private_info * ucpi, unsigned frag, int cnt) { - struct ufs_sb_private_info * uspi; + struct ufs_sb_private_info * uspi = UFS_SB(sb)->s_uspi; int i, start, end, forw, back; + unsigned blkno = ufs_fragstoblks(frag); - uspi = UFS_SB(sb)->s_uspi; if (uspi->s_contigsumsize <= 0) return; From 426f07ad3e2c9b51bfd93b870a3a9b04644130d0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 18 Oct 2024 03:25:29 -0400 Subject: [PATCH 08/18] ufs_free_fragments(): fix the braino in sanity check The function expects that all fragments it's been asked to free will be within the same block. And it even has a sanity check verifying that - it takes the fragment number modulo the number of fragments per block, adds the count and checks if that's too high. Unfortunately, it misspells the upper limit - instead of ->s_fpb (fragments per block) it says ->s_fpg (fragments per cylinder group). So "too high" ends up being insanely lenient. Had been that way since 2.1.112, when UFS write support had been added. 27 years to spot a typo... Signed-off-by: Al Viro --- fs/ufs/balloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index 1793ce48df0a..82f1a4a128a2 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -50,7 +50,7 @@ void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count) UFSD("ENTER, fragment %llu, count %u\n", (unsigned long long)fragment, count); - if (ufs_fragnum(fragment) + count > uspi->s_fpg) + if (ufs_fragnum(fragment) + count > uspi->s_fpb) ufs_error (sb, "ufs_free_fragments", "internal error"); mutex_lock(&UFS_SB(sb)->s_lock); From ae79ce9d061bc6cee3b136813b26dd0c735e682d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 27 Jan 2023 20:14:36 -0500 Subject: [PATCH 09/18] ufs_inode_getfrag(): remove junk comment It used to be a stubbed out beginning of ufs2 support, which had been implemented differently quite a while ago. Remove the commented-out (pseudo-)code. Signed-off-by: Al Viro --- fs/ufs/inode.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 5331ae7ebf3e..71d28561200f 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -264,11 +264,6 @@ ufs_inode_getfrag(struct inode *inode, unsigned index, unsigned nfrags = uspi->s_fpb; void *p; - /* TODO : to be done for write support - if ( (flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) - goto ufs2; - */ - p = ufs_get_direct_data_ptr(uspi, ufsi, index); tmp = ufs_data_ptr_to_cpu(sb, p); if (tmp) @@ -303,21 +298,6 @@ ufs_inode_getfrag(struct inode *inode, unsigned index, mark_inode_dirty(inode); out: return tmp + uspi->s_sbbase; - - /* This part : To be implemented .... - Required only for writing, not required for READ-ONLY. -ufs2: - - u2_block = ufs_fragstoblks(fragment); - u2_blockoff = ufs_fragnum(fragment); - p = ufsi->i_u1.u2_i_data + block; - goal = 0; - -repeat2: - tmp = fs32_to_cpu(sb, *p); - lastfrag = ufsi->i_lastfrag; - - */ } /** From db57044217d609d1d60854e958e28f5d3684d5bc Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 10 Mar 2023 20:44:59 -0500 Subject: [PATCH 10/18] ufs: get rid of ubh_{ubhcpymem,memcpyubh}() used only in ufs_read_cylinder_structures()/ufs_put_super_internal() and there we can just as well avoid bothering with ufs_buffer_head and just deal with it fragment-by-fragment. Signed-off-by: Al Viro --- fs/ufs/super.c | 43 ++++++++++++++++--------------------------- fs/ufs/util.c | 46 ---------------------------------------------- fs/ufs/util.h | 5 ----- 3 files changed, 16 insertions(+), 78 deletions(-) diff --git a/fs/ufs/super.c b/fs/ufs/super.c index bc625788589c..e876c60e70ea 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -505,7 +505,6 @@ static int ufs_read_cylinder_structures(struct super_block *sb) { struct ufs_sb_info *sbi = UFS_SB(sb); struct ufs_sb_private_info *uspi = sbi->s_uspi; - struct ufs_buffer_head * ubh; unsigned char * base, * space; unsigned size, blks, i; @@ -521,21 +520,13 @@ static int ufs_read_cylinder_structures(struct super_block *sb) if (!base) goto failed; sbi->s_csp = (struct ufs_csum *)space; - for (i = 0; i < blks; i += uspi->s_fpb) { - size = uspi->s_bsize; - if (i + uspi->s_fpb > blks) - size = (blks - i) * uspi->s_fsize; - - ubh = ubh_bread(sb, uspi->s_csaddr + i, size); - - if (!ubh) + for (i = 0; i < blks; i++) { + struct buffer_head *bh = sb_bread(sb, uspi->s_csaddr + i); + if (!bh) goto failed; - - ubh_ubhcpymem (space, ubh, size); - - space += size; - ubh_brelse (ubh); - ubh = NULL; + memcpy(space, bh->b_data, uspi->s_fsize); + space += uspi->s_fsize; + brelse (bh); } /* @@ -645,7 +636,6 @@ static void ufs_put_super_internal(struct super_block *sb) { struct ufs_sb_info *sbi = UFS_SB(sb); struct ufs_sb_private_info *uspi = sbi->s_uspi; - struct ufs_buffer_head * ubh; unsigned char * base, * space; unsigned blks, size, i; @@ -656,18 +646,17 @@ static void ufs_put_super_internal(struct super_block *sb) size = uspi->s_cssize; blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift; base = space = (char*) sbi->s_csp; - for (i = 0; i < blks; i += uspi->s_fpb) { - size = uspi->s_bsize; - if (i + uspi->s_fpb > blks) - size = (blks - i) * uspi->s_fsize; + for (i = 0; i < blks; i++, space += uspi->s_fsize) { + struct buffer_head *bh = sb_bread(sb, uspi->s_csaddr + i); - ubh = ubh_bread(sb, uspi->s_csaddr + i, size); - - ubh_memcpyubh (ubh, space, size); - space += size; - ubh_mark_buffer_uptodate (ubh, 1); - ubh_mark_buffer_dirty (ubh); - ubh_brelse (ubh); + if (unlikely(!bh)) { // better than an oops... + ufs_panic(sb, __func__, + "can't write part of cylinder group summary"); + continue; + } + memcpy(bh->b_data, space, uspi->s_fsize); + mark_buffer_dirty(bh); + brelse(bh); } for (i = 0; i < sbi->s_cg_loaded; i++) { ufs_put_cylinder (sb, i); diff --git a/fs/ufs/util.c b/fs/ufs/util.c index 2acf191eb89e..f0e906ab4ddd 100644 --- a/fs/ufs/util.c +++ b/fs/ufs/util.c @@ -99,20 +99,6 @@ void ubh_mark_buffer_dirty (struct ufs_buffer_head * ubh) mark_buffer_dirty (ubh->bh[i]); } -void ubh_mark_buffer_uptodate (struct ufs_buffer_head * ubh, int flag) -{ - unsigned i; - if (!ubh) - return; - if (flag) { - for ( i = 0; i < ubh->count; i++ ) - set_buffer_uptodate (ubh->bh[i]); - } else { - for ( i = 0; i < ubh->count; i++ ) - clear_buffer_uptodate (ubh->bh[i]); - } -} - void ubh_sync_block(struct ufs_buffer_head *ubh) { if (ubh) { @@ -146,38 +132,6 @@ int ubh_buffer_dirty (struct ufs_buffer_head * ubh) return result; } -void _ubh_ubhcpymem_(struct ufs_sb_private_info * uspi, - unsigned char * mem, struct ufs_buffer_head * ubh, unsigned size) -{ - unsigned len, bhno; - if (size > (ubh->count << uspi->s_fshift)) - size = ubh->count << uspi->s_fshift; - bhno = 0; - while (size) { - len = min_t(unsigned int, size, uspi->s_fsize); - memcpy (mem, ubh->bh[bhno]->b_data, len); - mem += uspi->s_fsize; - size -= len; - bhno++; - } -} - -void _ubh_memcpyubh_(struct ufs_sb_private_info * uspi, - struct ufs_buffer_head * ubh, unsigned char * mem, unsigned size) -{ - unsigned len, bhno; - if (size > (ubh->count << uspi->s_fshift)) - size = ubh->count << uspi->s_fshift; - bhno = 0; - while (size) { - len = min_t(unsigned int, size, uspi->s_fsize); - memcpy (ubh->bh[bhno]->b_data, mem, len); - mem += uspi->s_fsize; - size -= len; - bhno++; - } -} - dev_t ufs_get_inode_dev(struct super_block *sb, struct ufs_inode_info *ufsi) { diff --git a/fs/ufs/util.h b/fs/ufs/util.h index fafae166ee55..391bb4f11d74 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -263,14 +263,9 @@ extern struct ufs_buffer_head * ubh_bread_uspi(struct ufs_sb_private_info *, str extern void ubh_brelse (struct ufs_buffer_head *); extern void ubh_brelse_uspi (struct ufs_sb_private_info *); extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *); -extern void ubh_mark_buffer_uptodate (struct ufs_buffer_head *, int); extern void ubh_sync_block(struct ufs_buffer_head *); extern void ubh_bforget (struct ufs_buffer_head *); extern int ubh_buffer_dirty (struct ufs_buffer_head *); -#define ubh_ubhcpymem(mem,ubh,size) _ubh_ubhcpymem_(uspi,mem,ubh,size) -extern void _ubh_ubhcpymem_(struct ufs_sb_private_info *, unsigned char *, struct ufs_buffer_head *, unsigned); -#define ubh_memcpyubh(ubh,mem,size) _ubh_memcpyubh_(uspi,ubh,mem,size) -extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head *, unsigned char *, unsigned); /* This functions works with cache pages*/ struct folio *ufs_get_locked_folio(struct address_space *mapping, pgoff_t index); From 64f30e80d653c4d5aa0a7f1390789348af0cf8f2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 13 Mar 2023 12:40:40 -0400 Subject: [PATCH 11/18] clean ufs_trunc_direct() up a bit... For short files (== no indirect blocks needed) UFS allows the last block to be a partial one. That creates some complications for truncation down to "short file" lengths. ufs_trunc_direct() is called when we'd already made sure that new EOF is not in a hole; nothing needs to be done if we are extending the file and in case we are shrinking the file it needs to * shrink or free the old final block. * free all full direct blocks between the new and old EOF. * possibly shrink the new final block. The logics is needlessly complicated by trying to keep all cases handled by the same sequence of operations. if not shrinking nothing to do else if number of full blocks unchanged free the tail of possibly partial last block else free the tail of (currently full) new last block free all present (full) blocks in between free the (possibly partial) old last block is easier to follow than the result of trying to unify these cases. Signed-off-by: Al Viro --- fs/ufs/inode.c | 137 +++++++++++++++++++++++-------------------------- 1 file changed, 65 insertions(+), 72 deletions(-) diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 71d28561200f..a3475afb3c26 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -878,91 +878,84 @@ static inline void free_data(struct to_free *ctx, u64 from, unsigned count) #define DIRECT_FRAGMENT ((inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift) +/* + * used only for truncation down to direct blocks. + */ static void ufs_trunc_direct(struct inode *inode) { struct ufs_inode_info *ufsi = UFS_I(inode); - struct super_block * sb; - struct ufs_sb_private_info * uspi; - void *p; - u64 frag1, frag2, frag3, frag4, block1, block2; + struct super_block *sb = inode->i_sb; + struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; + unsigned int new_frags, old_frags; + unsigned int old_slot, new_slot; + unsigned int old_tail, new_tail; struct to_free ctx = {.inode = inode}; - unsigned i, tmp; UFSD("ENTER: ino %lu\n", inode->i_ino); - sb = inode->i_sb; - uspi = UFS_SB(sb)->s_uspi; + new_frags = DIRECT_FRAGMENT; + // new_frags = first fragment past the new EOF + old_frags = min_t(u64, UFS_NDIR_FRAGMENT, ufsi->i_lastfrag); + // old_frags = first fragment past the old EOF or covered by indirects - frag1 = DIRECT_FRAGMENT; - frag4 = min_t(u64, UFS_NDIR_FRAGMENT, ufsi->i_lastfrag); - frag2 = ((frag1 & uspi->s_fpbmask) ? ((frag1 | uspi->s_fpbmask) + 1) : frag1); - frag3 = frag4 & ~uspi->s_fpbmask; - block1 = block2 = 0; - if (frag2 > frag3) { - frag2 = frag4; - frag3 = frag4 = 0; - } else if (frag2 < frag3) { - block1 = ufs_fragstoblks (frag2); - block2 = ufs_fragstoblks (frag3); - } + if (new_frags >= old_frags) // expanding - nothing to free + goto done; - UFSD("ino %lu, frag1 %llu, frag2 %llu, block1 %llu, block2 %llu," - " frag3 %llu, frag4 %llu\n", inode->i_ino, - (unsigned long long)frag1, (unsigned long long)frag2, - (unsigned long long)block1, (unsigned long long)block2, - (unsigned long long)frag3, (unsigned long long)frag4); + old_tail = ufs_fragnum(old_frags); + old_slot = ufs_fragstoblks(old_frags); + new_tail = ufs_fragnum(new_frags); + new_slot = ufs_fragstoblks(new_frags); - if (frag1 >= frag2) - goto next1; - - /* - * Free first free fragments - */ - p = ufs_get_direct_data_ptr(uspi, ufsi, ufs_fragstoblks(frag1)); - tmp = ufs_data_ptr_to_cpu(sb, p); - if (!tmp ) - ufs_panic (sb, "ufs_trunc_direct", "internal error"); - frag2 -= frag1; - frag1 = ufs_fragnum (frag1); - - ufs_free_fragments(inode, tmp + frag1, frag2); - -next1: - /* - * Free whole blocks - */ - for (i = block1 ; i < block2; i++) { - p = ufs_get_direct_data_ptr(uspi, ufsi, i); - tmp = ufs_data_ptr_to_cpu(sb, p); + if (old_slot == new_slot) { // old_tail > 0 + void *p = ufs_get_direct_data_ptr(uspi, ufsi, old_slot); + u64 tmp = ufs_data_ptr_to_cpu(sb, p); if (!tmp) - continue; - write_seqlock(&ufsi->meta_lock); - ufs_data_ptr_clear(uspi, p); - write_sequnlock(&ufsi->meta_lock); + ufs_panic(sb, __func__, "internal error"); + if (!new_tail) { + write_seqlock(&ufsi->meta_lock); + ufs_data_ptr_clear(uspi, p); + write_sequnlock(&ufsi->meta_lock); + } + ufs_free_fragments(inode, tmp + new_tail, old_tail - new_tail); + } else { + unsigned int slot = new_slot; - free_data(&ctx, tmp, uspi->s_fpb); + if (new_tail) { + void *p = ufs_get_direct_data_ptr(uspi, ufsi, slot++); + u64 tmp = ufs_data_ptr_to_cpu(sb, p); + if (!tmp) + ufs_panic(sb, __func__, "internal error"); + + ufs_free_fragments(inode, tmp + new_tail, + uspi->s_fpb - new_tail); + } + while (slot < old_slot) { + void *p = ufs_get_direct_data_ptr(uspi, ufsi, slot++); + u64 tmp = ufs_data_ptr_to_cpu(sb, p); + if (!tmp) + continue; + write_seqlock(&ufsi->meta_lock); + ufs_data_ptr_clear(uspi, p); + write_sequnlock(&ufsi->meta_lock); + + free_data(&ctx, tmp, uspi->s_fpb); + } + + free_data(&ctx, 0, 0); + + if (old_tail) { + void *p = ufs_get_direct_data_ptr(uspi, ufsi, slot); + u64 tmp = ufs_data_ptr_to_cpu(sb, p); + if (!tmp) + ufs_panic(sb, __func__, "internal error"); + write_seqlock(&ufsi->meta_lock); + ufs_data_ptr_clear(uspi, p); + write_sequnlock(&ufsi->meta_lock); + + ufs_free_fragments(inode, tmp, old_tail); + } } - - free_data(&ctx, 0, 0); - - if (frag3 >= frag4) - goto next3; - - /* - * Free last free fragments - */ - p = ufs_get_direct_data_ptr(uspi, ufsi, ufs_fragstoblks(frag3)); - tmp = ufs_data_ptr_to_cpu(sb, p); - if (!tmp ) - ufs_panic(sb, "ufs_truncate_direct", "internal error"); - frag4 = ufs_fragnum (frag4); - write_seqlock(&ufsi->meta_lock); - ufs_data_ptr_clear(uspi, p); - write_sequnlock(&ufsi->meta_lock); - - ufs_free_fragments (inode, tmp, frag4); - next3: - +done: UFSD("EXIT: ino %lu\n", inode->i_ino); } From 6b103cc0ba58c680ab4e59dfa2fca4ceedfb5495 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 13 Dec 2023 14:23:49 -0500 Subject: [PATCH 12/18] ufs: take the handling of free block counters into a helper There are 3 places where those counters (many and varied...) are adjusted - when we are freeing fragments and get an entire block freed, when we are freeing blocks and (in opposite direction) when we are grabbing a block. The logics is identical (modulo the sign of adjustment) in all three; better take it into a helper - less duplication and less clutter in the callers that way. Signed-off-by: Al Viro --- fs/ufs/balloc.c | 66 +++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index 82f1a4a128a2..c60006c5806c 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -33,6 +33,29 @@ static u64 ufs_bitmap_search (struct super_block *, struct ufs_cg_private_info * static unsigned char ufs_fragtable_8fpb[], ufs_fragtable_other[]; static void ufs_clusteracct(struct super_block *, struct ufs_cg_private_info *, unsigned, int); +static void adjust_free_blocks(struct super_block *sb, + struct ufs_cylinder_group *ucg, + struct ufs_cg_private_info *ucpi, + unsigned fragment, int delta) +{ + struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; + + if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) + ufs_clusteracct(sb, ucpi, fragment, delta); + + fs32_add(sb, &ucg->cg_cs.cs_nbfree, delta); + uspi->cs_total.cs_nbfree += delta; + fs32_add(sb, &UFS_SB(sb)->fs_cs(ucpi->c_cgx).cs_nbfree, delta); + + if (uspi->fs_magic != UFS2_MAGIC) { + unsigned cylno = ufs_cbtocylno(fragment); + + fs16_add(sb, &ubh_cg_blks(ucpi, cylno, + ufs_cbtorpos(fragment)), delta); + fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), delta); + } +} + /* * Free 'count' fragments from fragment number 'fragment' */ @@ -97,18 +120,7 @@ void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count) fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb); uspi->cs_total.cs_nffree -= uspi->s_fpb; fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, uspi->s_fpb); - if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) - ufs_clusteracct(sb, ucpi, bbase, 1); - fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1); - uspi->cs_total.cs_nbfree++; - fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1); - if (uspi->fs_magic != UFS2_MAGIC) { - unsigned cylno = ufs_cbtocylno (bbase); - - fs16_add(sb, &ubh_cg_blks(ucpi, cylno, - ufs_cbtorpos(bbase)), 1); - fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1); - } + adjust_free_blocks(sb, ucg, ucpi, bbase, 1); } ubh_mark_buffer_dirty (USPI_UBH(uspi)); @@ -183,20 +195,7 @@ do_more: } ubh_setblock(uspi, ucpi, i); inode_sub_bytes(inode, uspi->s_fpb << uspi->s_fshift); - if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) - ufs_clusteracct(sb, ucpi, i, 1); - - fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1); - uspi->cs_total.cs_nbfree++; - fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1); - - if (uspi->fs_magic != UFS2_MAGIC) { - unsigned cylno = ufs_cbtocylno(i); - - fs16_add(sb, &ubh_cg_blks(ucpi, cylno, - ufs_cbtorpos(i)), 1); - fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1); - } + adjust_free_blocks(sb, ucg, ucpi, i, 1); } ubh_mark_buffer_dirty (USPI_UBH(uspi)); @@ -726,20 +725,7 @@ gotit: if (!try_add_frags(inode, uspi->s_fpb)) return 0; ubh_clrblock(uspi, ucpi, result); - if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) - ufs_clusteracct(sb, ucpi, result, -1); - - fs32_sub(sb, &ucg->cg_cs.cs_nbfree, 1); - uspi->cs_total.cs_nbfree--; - fs32_sub(sb, &UFS_SB(sb)->fs_cs(ucpi->c_cgx).cs_nbfree, 1); - - if (uspi->fs_magic != UFS2_MAGIC) { - unsigned cylno = ufs_cbtocylno((unsigned)result); - - fs16_sub(sb, &ubh_cg_blks(ucpi, cylno, - ufs_cbtorpos((unsigned)result)), 1); - fs32_sub(sb, &ubh_cg_blktot(ucpi, cylno), 1); - } + adjust_free_blocks(sb, ucg, ucpi, result, -1); UFSD("EXIT, result %llu\n", (unsigned long long)result); From d9036c488c6ed503e0c251236abae2b7ed679847 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Sat, 5 Oct 2024 19:02:04 +0100 Subject: [PATCH 13/18] ufs: Convert ufs_inode_getblock() to take a folio Pass bh->b_folio instead of bh->b_page. They're in a union, so no code change expected. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Al Viro --- fs/ufs/inode.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index a3475afb3c26..912950ee5104 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -309,12 +309,11 @@ out: * (block will hold this fragment and also uspi->s_fpb-1) * @err: see ufs_inode_getfrag() * @new: see ufs_inode_getfrag() - * @locked_page: see ufs_inode_getfrag() + * @locked_folio: see ufs_inode_getfrag() */ -static u64 -ufs_inode_getblock(struct inode *inode, u64 ind_block, - unsigned index, sector_t new_fragment, int *err, - int *new, struct page *locked_page) +static u64 ufs_inode_getblock(struct inode *inode, u64 ind_block, + unsigned index, sector_t new_fragment, int *err, + int *new, struct folio *locked_folio) { struct super_block *sb = inode->i_sb; struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; @@ -349,7 +348,7 @@ ufs_inode_getblock(struct inode *inode, u64 ind_block, else goal = bh->b_blocknr + uspi->s_fpb; tmp = ufs_new_fragments(inode, p, ufs_blknum(new_fragment), goal, - uspi->s_fpb, err, locked_page); + uspi->s_fpb, err, &locked_folio->page); if (!tmp) goto out; @@ -430,7 +429,7 @@ static int ufs_getfrag_block(struct inode *inode, sector_t fragment, struct buff phys64 = ufs_inode_getblock(inode, phys64, offsets[i], fragment, &err, NULL, NULL); phys64 = ufs_inode_getblock(inode, phys64, offsets[depth - 1], - fragment, &err, &new, bh_result->b_page); + fragment, &err, &new, bh_result->b_folio); } out: if (phys64) { From b6250a013d62d100416d4953ae0fcfe8997a8ad4 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Sat, 5 Oct 2024 19:02:05 +0100 Subject: [PATCH 14/18] ufs: Convert ufs_extend_tail() to take a folio Pass bh->b_folio instead of bh->b_page. They're in a union, so no code change expected. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Al Viro --- fs/ufs/inode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 912950ee5104..1d3eb485df41 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -220,7 +220,7 @@ changed: */ static bool ufs_extend_tail(struct inode *inode, u64 writes_to, - int *err, struct page *locked_page) + int *err, struct folio *locked_folio) { struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block *sb = inode->i_sb; @@ -239,7 +239,7 @@ ufs_extend_tail(struct inode *inode, u64 writes_to, p = ufs_get_direct_data_ptr(uspi, ufsi, block); tmp = ufs_new_fragments(inode, p, lastfrag, ufs_data_ptr_to_cpu(sb, p), new_size - (lastfrag & uspi->s_fpbmask), err, - locked_page); + &locked_folio->page); return tmp != 0; } @@ -413,7 +413,7 @@ static int ufs_getfrag_block(struct inode *inode, sector_t fragment, struct buff unsigned tailfrags = lastfrag & uspi->s_fpbmask; if (tailfrags && fragment >= lastfrag) { if (!ufs_extend_tail(inode, fragment, - &err, bh_result->b_page)) + &err, bh_result->b_folio)) goto out; } } From 24a87a0adb2febe62b6cf3e0932a23d99931906f Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Sat, 5 Oct 2024 19:02:06 +0100 Subject: [PATCH 15/18] ufs: Convert ufs_inode_getfrag() to take a folio Pass bh->b_folio instead of bh->b_page. They're in a union, so no code change expected. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Al Viro --- fs/ufs/inode.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 1d3eb485df41..30e5d695d74d 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -252,10 +252,9 @@ ufs_extend_tail(struct inode *inode, u64 writes_to, * @new: we set it if we allocate new block * @locked_page: for ufs_new_fragments() */ -static u64 -ufs_inode_getfrag(struct inode *inode, unsigned index, +static u64 ufs_inode_getfrag(struct inode *inode, unsigned index, sector_t new_fragment, int *err, - int *new, struct page *locked_page) + int *new, struct folio *locked_folio) { struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block *sb = inode->i_sb; @@ -283,7 +282,7 @@ ufs_inode_getfrag(struct inode *inode, unsigned index, goal += uspi->s_fpb; } tmp = ufs_new_fragments(inode, p, ufs_blknum(new_fragment), - goal, nfrags, err, locked_page); + goal, nfrags, err, &locked_folio->page); if (!tmp) { *err = -ENOSPC; @@ -420,7 +419,7 @@ static int ufs_getfrag_block(struct inode *inode, sector_t fragment, struct buff if (depth == 1) { phys64 = ufs_inode_getfrag(inode, offsets[0], fragment, - &err, &new, bh_result->b_page); + &err, &new, bh_result->b_folio); } else { int i; phys64 = ufs_inode_getfrag(inode, offsets[0], fragment, From 14bcb7bb68094f3a8bb7dd8a6a87db910b596f19 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Sat, 5 Oct 2024 19:02:07 +0100 Subject: [PATCH 16/18] ufs: Pass a folio to ufs_new_fragments() All callers now have a folio, pass it to ufs_new_fragments() instead of converting back to a page. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Al Viro --- fs/ufs/balloc.c | 10 +++++----- fs/ufs/inode.c | 8 ++++---- fs/ufs/ufs.h | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index c60006c5806c..e578e429c5d8 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -332,7 +332,7 @@ static void ufs_clear_frags(struct inode *inode, sector_t beg, unsigned int n, u64 ufs_new_fragments(struct inode *inode, void *p, u64 fragment, u64 goal, unsigned count, int *err, - struct page *locked_page) + struct folio *locked_folio) { struct super_block * sb; struct ufs_sb_private_info * uspi; @@ -412,7 +412,7 @@ u64 ufs_new_fragments(struct inode *inode, void *p, u64 fragment, result = ufs_alloc_fragments (inode, cgno, goal, count, err); if (result) { ufs_clear_frags(inode, result + oldcount, - newcount - oldcount, locked_page != NULL); + newcount - oldcount, locked_folio != NULL); *err = 0; write_seqlock(&UFS_I(inode)->meta_lock); ufs_cpu_to_data_ptr(sb, p, result); @@ -436,7 +436,7 @@ u64 ufs_new_fragments(struct inode *inode, void *p, u64 fragment, fragment + count); read_sequnlock_excl(&UFS_I(inode)->meta_lock); ufs_clear_frags(inode, result + oldcount, newcount - oldcount, - locked_page != NULL); + locked_folio != NULL); mutex_unlock(&UFS_SB(sb)->s_lock); UFSD("EXIT, result %llu\n", (unsigned long long)result); return result; @@ -457,11 +457,11 @@ u64 ufs_new_fragments(struct inode *inode, void *p, u64 fragment, result = ufs_alloc_fragments (inode, cgno, goal, request, err); if (result) { ufs_clear_frags(inode, result + oldcount, newcount - oldcount, - locked_page != NULL); + locked_folio != NULL); mutex_unlock(&UFS_SB(sb)->s_lock); ufs_change_blocknr(inode, fragment - oldcount, oldcount, uspi->s_sbbase + tmp, - uspi->s_sbbase + result, locked_page); + uspi->s_sbbase + result, &locked_folio->page); *err = 0; write_seqlock(&UFS_I(inode)->meta_lock); ufs_cpu_to_data_ptr(sb, p, result); diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 30e5d695d74d..7dc38fdef2ea 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -239,7 +239,7 @@ ufs_extend_tail(struct inode *inode, u64 writes_to, p = ufs_get_direct_data_ptr(uspi, ufsi, block); tmp = ufs_new_fragments(inode, p, lastfrag, ufs_data_ptr_to_cpu(sb, p), new_size - (lastfrag & uspi->s_fpbmask), err, - &locked_folio->page); + locked_folio); return tmp != 0; } @@ -250,7 +250,7 @@ ufs_extend_tail(struct inode *inode, u64 writes_to, * @new_fragment: number of new allocated fragment(s) * @err: we set it if something wrong * @new: we set it if we allocate new block - * @locked_page: for ufs_new_fragments() + * @locked_folio: for ufs_new_fragments() */ static u64 ufs_inode_getfrag(struct inode *inode, unsigned index, sector_t new_fragment, int *err, @@ -282,7 +282,7 @@ static u64 ufs_inode_getfrag(struct inode *inode, unsigned index, goal += uspi->s_fpb; } tmp = ufs_new_fragments(inode, p, ufs_blknum(new_fragment), - goal, nfrags, err, &locked_folio->page); + goal, nfrags, err, locked_folio); if (!tmp) { *err = -ENOSPC; @@ -347,7 +347,7 @@ static u64 ufs_inode_getblock(struct inode *inode, u64 ind_block, else goal = bh->b_blocknr + uspi->s_fpb; tmp = ufs_new_fragments(inode, p, ufs_blknum(new_fragment), goal, - uspi->s_fpb, err, &locked_folio->page); + uspi->s_fpb, err, locked_folio); if (!tmp) goto out; diff --git a/fs/ufs/ufs.h b/fs/ufs/ufs.h index c7638e62ffe8..e7df65dd4351 100644 --- a/fs/ufs/ufs.h +++ b/fs/ufs/ufs.h @@ -88,10 +88,10 @@ struct ufs_inode_info { #endif /* balloc.c */ -extern void ufs_free_fragments (struct inode *, u64, unsigned); -extern void ufs_free_blocks (struct inode *, u64, unsigned); -extern u64 ufs_new_fragments(struct inode *, void *, u64, u64, - unsigned, int *, struct page *); +void ufs_free_fragments (struct inode *, u64 fragment, unsigned count); +void ufs_free_blocks (struct inode *, u64 fragment, unsigned count); +u64 ufs_new_fragments(struct inode *, void *, u64 fragment, u64 goal, + unsigned count, int *err, struct folio *); /* cylinder.c */ extern struct ufs_cg_private_info * ufs_load_cylinder (struct super_block *, unsigned); From b57c010e70ff11cce7f6b702d2e3bba1d893580b Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Sat, 5 Oct 2024 19:02:08 +0100 Subject: [PATCH 17/18] ufs: Convert ufs_change_blocknr() to take a folio Now that ufs_new_fragments() has a folio, pass it to ufs_change_blocknr() as a folio instead of converting it from folio to page to folio. This removes the last use of struct page in UFS. Signed-off-by: Matthew Wilcox (Oracle) Signed-off-by: Al Viro --- fs/ufs/balloc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index e578e429c5d8..194ed3ab945e 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -229,13 +229,13 @@ failed: * situated at the end of file. * * We can come here from ufs_writepage or ufs_prepare_write, - * locked_page is argument of these functions, so we already lock it. + * locked_folio is argument of these functions, so we already lock it. */ static void ufs_change_blocknr(struct inode *inode, sector_t beg, unsigned int count, sector_t oldb, - sector_t newb, struct page *locked_page) + sector_t newb, struct folio *locked_folio) { - struct folio *folio, *locked_folio = page_folio(locked_page); + struct folio *folio; const unsigned blks_per_page = 1 << (PAGE_SHIFT - inode->i_blkbits); const unsigned mask = blks_per_page - 1; @@ -461,7 +461,7 @@ u64 ufs_new_fragments(struct inode *inode, void *p, u64 fragment, mutex_unlock(&UFS_SB(sb)->s_lock); ufs_change_blocknr(inode, fragment - oldcount, oldcount, uspi->s_sbbase + tmp, - uspi->s_sbbase + result, &locked_folio->page); + uspi->s_sbbase + result, locked_folio); *err = 0; write_seqlock(&UFS_I(inode)->meta_lock); ufs_cpu_to_data_ptr(sb, p, result); From 6cfe56fbad32c8c5b50e82d9109413566d691569 Mon Sep 17 00:00:00 2001 From: Agathe Porte Date: Tue, 12 Nov 2024 13:18:25 +0100 Subject: [PATCH 18/18] ufs: ufs_sb_private_info: remove unused s_{2,3}apb fields These two fields are populated and stored as a "frequently used value" in ufs_fill_super, but are not used afterwards in the driver. Moreover, one of the shifts triggers UBSAN: shift-out-of-bounds when apbshift is 12 because 12 * 3 = 36 and 1 << 36 does not fit in the 32 bit integer used to store the value. Closes: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2087853 Signed-off-by: Agathe Porte Signed-off-by: Al Viro --- fs/ufs/super.c | 4 ---- fs/ufs/ufs_fs.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/fs/ufs/super.c b/fs/ufs/super.c index e876c60e70ea..762699c1bcf6 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1229,11 +1229,7 @@ magic_found: else uspi->s_apbshift = uspi->s_bshift - 2; - uspi->s_2apbshift = uspi->s_apbshift * 2; - uspi->s_3apbshift = uspi->s_apbshift * 3; uspi->s_apb = 1 << uspi->s_apbshift; - uspi->s_2apb = 1 << uspi->s_2apbshift; - uspi->s_3apb = 1 << uspi->s_3apbshift; uspi->s_apbmask = uspi->s_apb - 1; uspi->s_nspfshift = uspi->s_fshift - UFS_SECTOR_BITS; uspi->s_nspb = uspi->s_nspf << uspi->s_fpbshift; diff --git a/fs/ufs/ufs_fs.h b/fs/ufs/ufs_fs.h index ef9ead44776a..0905f9a16b91 100644 --- a/fs/ufs/ufs_fs.h +++ b/fs/ufs/ufs_fs.h @@ -775,12 +775,8 @@ struct ufs_sb_private_info { __u32 s_fpbmask; /* fragments per block mask */ __u32 s_apb; /* address per block */ - __u32 s_2apb; /* address per block^2 */ - __u32 s_3apb; /* address per block^3 */ __u32 s_apbmask; /* address per block mask */ __u32 s_apbshift; /* address per block shift */ - __u32 s_2apbshift; /* address per block shift * 2 */ - __u32 s_3apbshift; /* address per block shift * 3 */ __u32 s_nspfshift; /* number of sector per fragment shift */ __u32 s_nspb; /* number of sector per block */ __u32 s_inopf; /* inodes per fragment */