diff mbox series

btrfs: enhance ordered extent double freeing detection

Message ID 53b793f2e7a7788f89cda97de565cfc1577cbf75.1733890357.git.wqu@suse.com (mailing list archive)
State New
Headers show
Series btrfs: enhance ordered extent double freeing detection | expand

Commit Message

Qu Wenruo Dec. 11, 2024, 4:13 a.m. UTC
With recent bugs exposed through run_delalloc_range() failure, the
importance of detecting double accounting is obvious.

Currently the way to detect such errors is to just check if we underflow
the btrfs_ordered_extent::bytes_left member.

That's fine but that only shows the length we're trying to decrease, not
enough to show the problem.

Here we enhance the situation by:

- Introduce btrfs_ordered_extent::finished_bitmap
  This is a new bitmap to indicate which blocks are already finished.
  This bitmap will be initialized at alloc_ordered_extent() and release
  when the ordered extent is freed.

- Detect any already finished block during can_finish_ordered_extent()
  If double accounting detected, show the full range we're trying and the bitmap.

- Make sure the bitmap is all set when the OE is finished

- Show the full range we're finishing for the existing double accounting
  detection
  This is to enhance the code to work with the new run_delalloc_range()
  error messages.

This will have extra memory and runtime cost, now an ordered extent can
have as large as 4K memory just for the finished_bitmap, and extra
operations to detect such double accounting.

Thus this double accounting detection is only enabled for
CONFIG_BTRFS_DEBUG build for developers.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/misc.h         | 28 ++++++++++++++++++++++
 fs/btrfs/ordered-data.c | 53 +++++++++++++++++++++++++++++++++++++++--
 fs/btrfs/ordered-data.h |  9 +++++++
 3 files changed, 88 insertions(+), 2 deletions(-)

Comments

kernel test robot Dec. 11, 2024, 6:11 p.m. UTC | #1
Hi Qu,

kernel test robot noticed the following build errors:

[auto build test ERROR on kdave/for-next]
[also build test ERROR on linus/master v6.13-rc2 next-20241211]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Qu-Wenruo/btrfs-enhance-ordered-extent-double-freeing-detection/20241211-121647
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
patch link:    https://lore.kernel.org/r/53b793f2e7a7788f89cda97de565cfc1577cbf75.1733890357.git.wqu%40suse.com
patch subject: [PATCH] btrfs: enhance ordered extent double freeing detection
config: i386-buildonly-randconfig-003-20241211 (https://download.01.org/0day-ci/archive/20241212/202412120136.6OwbbtZf-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241212/202412120136.6OwbbtZf-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202412120136.6OwbbtZf-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from fs/btrfs/ordered-data.c:7:
   In file included from include/linux/blkdev.h:9:
   In file included from include/linux/blk_types.h:10:
   In file included from include/linux/bvec.h:10:
   In file included from include/linux/highmem.h:8:
   In file included from include/linux/cacheflush.h:5:
   In file included from arch/x86/include/asm/cacheflush.h:5:
   In file included from include/linux/mm.h:2223:
   include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     518 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
>> fs/btrfs/ordered-data.c:200:10: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
     200 |                 entry->finished_bitmap = bitmap_zalloc(
         |                 ~~~~~  ^
   fs/btrfs/ordered-data.c:202:15: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
     202 |                 if (!entry->finished_bitmap) {
         |                      ~~~~~  ^
   fs/btrfs/ordered-data.c:380:38: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
     380 |                 nr_set = bitmap_count_set(ordered->finished_bitmap, start_bit, nbits);
         |                                           ~~~~~~~  ^
   fs/btrfs/ordered-data.c:388:17: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
     388 |                                    ordered->finished_bitmap);
         |                                    ~~~~~~~  ^
   fs/btrfs/messages.h:44:41: note: expanded from macro 'btrfs_crit'
      44 |         btrfs_printk(fs_info, KERN_CRIT fmt, ##args)
         |                                                ^~~~
   fs/btrfs/messages.h:27:32: note: expanded from macro 'btrfs_printk'
      27 |         _btrfs_printk(fs_info, fmt, ##args)
         |                                       ^~~~
   fs/btrfs/ordered-data.c:390:23: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
     390 |                 bitmap_set(ordered->finished_bitmap, start_bit, nbits);
         |                            ~~~~~~~  ^
   fs/btrfs/ordered-data.c:418:37: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
     418 |                 if (WARN_ON(!bitmap_full(ordered->finished_bitmap,
         |                                          ~~~~~~~  ^
   include/asm-generic/bug.h:123:25: note: expanded from macro 'WARN_ON'
     123 |         int __ret_warn_on = !!(condition);                              \
         |                                ^~~~~~~~~
   fs/btrfs/ordered-data.c:426:14: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
     426 |                                 ordered->finished_bitmap);
         |                                 ~~~~~~~  ^
   fs/btrfs/messages.h:44:41: note: expanded from macro 'btrfs_crit'
      44 |         btrfs_printk(fs_info, KERN_CRIT fmt, ##args)
         |                                                ^~~~
   fs/btrfs/messages.h:27:32: note: expanded from macro 'btrfs_printk'
      27 |         _btrfs_printk(fs_info, fmt, ##args)
         |                                       ^~~~
   fs/btrfs/ordered-data.c:675:23: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
     675 |                         bitmap_free(entry->finished_bitmap);
         |                                     ~~~~~  ^
   1 warning and 8 errors generated.


vim +200 fs/btrfs/ordered-data.c

   147	
   148	static struct btrfs_ordered_extent *alloc_ordered_extent(
   149				struct btrfs_inode *inode, u64 file_offset, u64 num_bytes,
   150				u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes,
   151				u64 offset, unsigned long flags, int compress_type)
   152	{
   153		struct btrfs_ordered_extent *entry;
   154		int ret;
   155		u64 qgroup_rsv = 0;
   156	
   157		if (flags &
   158		    ((1 << BTRFS_ORDERED_NOCOW) | (1 << BTRFS_ORDERED_PREALLOC))) {
   159			/* For nocow write, we can release the qgroup rsv right now */
   160			ret = btrfs_qgroup_free_data(inode, NULL, file_offset, num_bytes, &qgroup_rsv);
   161			if (ret < 0)
   162				return ERR_PTR(ret);
   163		} else {
   164			/*
   165			 * The ordered extent has reserved qgroup space, release now
   166			 * and pass the reserved number for qgroup_record to free.
   167			 */
   168			ret = btrfs_qgroup_release_data(inode, file_offset, num_bytes, &qgroup_rsv);
   169			if (ret < 0)
   170				return ERR_PTR(ret);
   171		}
   172		entry = kmem_cache_zalloc(btrfs_ordered_extent_cache, GFP_NOFS);
   173		if (!entry)
   174			return ERR_PTR(-ENOMEM);
   175	
   176		entry->file_offset = file_offset;
   177		entry->num_bytes = num_bytes;
   178		entry->ram_bytes = ram_bytes;
   179		entry->disk_bytenr = disk_bytenr;
   180		entry->disk_num_bytes = disk_num_bytes;
   181		entry->offset = offset;
   182		entry->bytes_left = num_bytes;
   183		entry->inode = BTRFS_I(igrab(&inode->vfs_inode));
   184		entry->compress_type = compress_type;
   185		entry->truncated_len = (u64)-1;
   186		entry->qgroup_rsv = qgroup_rsv;
   187		entry->flags = flags;
   188		refcount_set(&entry->refs, 1);
   189		init_waitqueue_head(&entry->wait);
   190		INIT_LIST_HEAD(&entry->list);
   191		INIT_LIST_HEAD(&entry->log_list);
   192		INIT_LIST_HEAD(&entry->root_extent_list);
   193		INIT_LIST_HEAD(&entry->work_list);
   194		INIT_LIST_HEAD(&entry->bioc_list);
   195		init_completion(&entry->completion);
   196	
   197		if (IS_ENABLED(CONFIG_BTRFS_DEBUG)) {
   198			struct btrfs_fs_info *fs_info = inode->root->fs_info;
   199	
 > 200			entry->finished_bitmap = bitmap_zalloc(
   201				num_bytes >> fs_info->sectorsize_bits, GFP_NOFS);
   202			if (!entry->finished_bitmap) {
   203				kmem_cache_free(btrfs_ordered_extent_cache, entry);
   204				return ERR_PTR(-ENOMEM);
   205			}
   206		}
   207		/*
   208		 * We don't need the count_max_extents here, we can assume that all of
   209		 * that work has been done at higher layers, so this is truly the
   210		 * smallest the extent is going to get.
   211		 */
   212		spin_lock(&inode->lock);
   213		btrfs_mod_outstanding_extents(inode, 1);
   214		spin_unlock(&inode->lock);
   215	
   216		return entry;
   217	}
   218
kernel test robot Dec. 11, 2024, 6:12 p.m. UTC | #2
Hi Qu,

kernel test robot noticed the following build errors:

[auto build test ERROR on kdave/for-next]
[also build test ERROR on linus/master v6.13-rc2 next-20241211]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Qu-Wenruo/btrfs-enhance-ordered-extent-double-freeing-detection/20241211-121647
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
patch link:    https://lore.kernel.org/r/53b793f2e7a7788f89cda97de565cfc1577cbf75.1733890357.git.wqu%40suse.com
patch subject: [PATCH] btrfs: enhance ordered extent double freeing detection
config: i386-buildonly-randconfig-005-20241211 (https://download.01.org/0day-ci/archive/20241212/202412120129.tEVBFsR6-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241212/202412120129.tEVBFsR6-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202412120129.tEVBFsR6-lkp@intel.com/

All errors (new ones prefixed by >>):

   fs/btrfs/ordered-data.c: In function 'alloc_ordered_extent':
>> fs/btrfs/ordered-data.c:200:22: error: 'struct btrfs_ordered_extent' has no member named 'finished_bitmap'
     200 |                 entry->finished_bitmap = bitmap_zalloc(
         |                      ^~
   fs/btrfs/ordered-data.c:202:27: error: 'struct btrfs_ordered_extent' has no member named 'finished_bitmap'
     202 |                 if (!entry->finished_bitmap) {
         |                           ^~
   fs/btrfs/ordered-data.c: In function 'can_finish_ordered_extent':
   fs/btrfs/ordered-data.c:380:50: error: 'struct btrfs_ordered_extent' has no member named 'finished_bitmap'
     380 |                 nr_set = bitmap_count_set(ordered->finished_bitmap, start_bit, nbits);
         |                                                  ^~
   In file included from fs/btrfs/ordered-data.c:10:
   fs/btrfs/ordered-data.c:388:43: error: 'struct btrfs_ordered_extent' has no member named 'finished_bitmap'
     388 |                                    ordered->finished_bitmap);
         |                                           ^~
   fs/btrfs/messages.h:36:41: note: in definition of macro 'btrfs_printk'
      36 |         btrfs_no_printk(fs_info, fmt, ##args)
         |                                         ^~~~
   fs/btrfs/ordered-data.c:382:25: note: in expansion of macro 'btrfs_crit'
     382 |                         btrfs_crit(fs_info,
         |                         ^~~~~~~~~~
   fs/btrfs/ordered-data.c:390:35: error: 'struct btrfs_ordered_extent' has no member named 'finished_bitmap'
     390 |                 bitmap_set(ordered->finished_bitmap, start_bit, nbits);
         |                                   ^~
   In file included from arch/x86/include/asm/bug.h:99,
                    from include/linux/bug.h:5,
                    from include/linux/thread_info.h:13,
                    from include/linux/spinlock.h:60,
                    from include/linux/mmzone.h:8,
                    from include/linux/gfp.h:7,
                    from include/linux/slab.h:16,
                    from fs/btrfs/ordered-data.c:6:
   fs/btrfs/ordered-data.c:418:49: error: 'struct btrfs_ordered_extent' has no member named 'finished_bitmap'
     418 |                 if (WARN_ON(!bitmap_full(ordered->finished_bitmap,
         |                                                 ^~
   include/asm-generic/bug.h:171:32: note: in definition of macro 'WARN_ON'
     171 |         int __ret_warn_on = !!(condition);                              \
         |                                ^~~~~~~~~
   fs/btrfs/ordered-data.c:426:40: error: 'struct btrfs_ordered_extent' has no member named 'finished_bitmap'
     426 |                                 ordered->finished_bitmap);
         |                                        ^~
   fs/btrfs/messages.h:36:41: note: in definition of macro 'btrfs_printk'
      36 |         btrfs_no_printk(fs_info, fmt, ##args)
         |                                         ^~~~
   fs/btrfs/ordered-data.c:420:25: note: in expansion of macro 'btrfs_crit'
     420 |                         btrfs_crit(fs_info,
         |                         ^~~~~~~~~~
   fs/btrfs/ordered-data.c: In function 'btrfs_put_ordered_extent':
   fs/btrfs/ordered-data.c:675:42: error: 'struct btrfs_ordered_extent' has no member named 'finished_bitmap'
     675 |                         bitmap_free(entry->finished_bitmap);
         |                                          ^~


vim +200 fs/btrfs/ordered-data.c

   147	
   148	static struct btrfs_ordered_extent *alloc_ordered_extent(
   149				struct btrfs_inode *inode, u64 file_offset, u64 num_bytes,
   150				u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes,
   151				u64 offset, unsigned long flags, int compress_type)
   152	{
   153		struct btrfs_ordered_extent *entry;
   154		int ret;
   155		u64 qgroup_rsv = 0;
   156	
   157		if (flags &
   158		    ((1 << BTRFS_ORDERED_NOCOW) | (1 << BTRFS_ORDERED_PREALLOC))) {
   159			/* For nocow write, we can release the qgroup rsv right now */
   160			ret = btrfs_qgroup_free_data(inode, NULL, file_offset, num_bytes, &qgroup_rsv);
   161			if (ret < 0)
   162				return ERR_PTR(ret);
   163		} else {
   164			/*
   165			 * The ordered extent has reserved qgroup space, release now
   166			 * and pass the reserved number for qgroup_record to free.
   167			 */
   168			ret = btrfs_qgroup_release_data(inode, file_offset, num_bytes, &qgroup_rsv);
   169			if (ret < 0)
   170				return ERR_PTR(ret);
   171		}
   172		entry = kmem_cache_zalloc(btrfs_ordered_extent_cache, GFP_NOFS);
   173		if (!entry)
   174			return ERR_PTR(-ENOMEM);
   175	
   176		entry->file_offset = file_offset;
   177		entry->num_bytes = num_bytes;
   178		entry->ram_bytes = ram_bytes;
   179		entry->disk_bytenr = disk_bytenr;
   180		entry->disk_num_bytes = disk_num_bytes;
   181		entry->offset = offset;
   182		entry->bytes_left = num_bytes;
   183		entry->inode = BTRFS_I(igrab(&inode->vfs_inode));
   184		entry->compress_type = compress_type;
   185		entry->truncated_len = (u64)-1;
   186		entry->qgroup_rsv = qgroup_rsv;
   187		entry->flags = flags;
   188		refcount_set(&entry->refs, 1);
   189		init_waitqueue_head(&entry->wait);
   190		INIT_LIST_HEAD(&entry->list);
   191		INIT_LIST_HEAD(&entry->log_list);
   192		INIT_LIST_HEAD(&entry->root_extent_list);
   193		INIT_LIST_HEAD(&entry->work_list);
   194		INIT_LIST_HEAD(&entry->bioc_list);
   195		init_completion(&entry->completion);
   196	
   197		if (IS_ENABLED(CONFIG_BTRFS_DEBUG)) {
   198			struct btrfs_fs_info *fs_info = inode->root->fs_info;
   199	
 > 200			entry->finished_bitmap = bitmap_zalloc(
   201				num_bytes >> fs_info->sectorsize_bits, GFP_NOFS);
   202			if (!entry->finished_bitmap) {
   203				kmem_cache_free(btrfs_ordered_extent_cache, entry);
   204				return ERR_PTR(-ENOMEM);
   205			}
   206		}
   207		/*
   208		 * We don't need the count_max_extents here, we can assume that all of
   209		 * that work has been done at higher layers, so this is truly the
   210		 * smallest the extent is going to get.
   211		 */
   212		spin_lock(&inode->lock);
   213		btrfs_mod_outstanding_extents(inode, 1);
   214		spin_unlock(&inode->lock);
   215	
   216		return entry;
   217	}
   218
Qu Wenruo Dec. 11, 2024, 7:47 p.m. UTC | #3
在 2024/12/12 04:41, kernel test robot 写道:
> Hi Qu,
>
> kernel test robot noticed the following build errors:
>
> [auto build test ERROR on kdave/for-next]
> [also build test ERROR on linus/master v6.13-rc2 next-20241211]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url:    https://github.com/intel-lab-lkp/linux/commits/Qu-Wenruo/btrfs-enhance-ordered-extent-double-freeing-detection/20241211-121647
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
> patch link:    https://lore.kernel.org/r/53b793f2e7a7788f89cda97de565cfc1577cbf75.1733890357.git.wqu%40suse.com
> patch subject: [PATCH] btrfs: enhance ordered extent double freeing detection
> config: i386-buildonly-randconfig-003-20241211 (https://download.01.org/0day-ci/archive/20241212/202412120136.6OwbbtZf-lkp@intel.com/config)
> compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241212/202412120136.6OwbbtZf-lkp@intel.com/reproduce)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202412120136.6OwbbtZf-lkp@intel.com/
>
> All errors (new ones prefixed by >>):
>
>     In file included from fs/btrfs/ordered-data.c:7:
>     In file included from include/linux/blkdev.h:9:
>     In file included from include/linux/blk_types.h:10:
>     In file included from include/linux/bvec.h:10:
>     In file included from include/linux/highmem.h:8:
>     In file included from include/linux/cacheflush.h:5:
>     In file included from arch/x86/include/asm/cacheflush.h:5:
>     In file included from include/linux/mm.h:2223:
>     include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
>       518 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
>           |                               ~~~~~~~~~~~ ^ ~~~
>>> fs/btrfs/ordered-data.c:200:10: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
>       200 |                 entry->finished_bitmap = bitmap_zalloc(
>           |                 ~~~~~  ^

I don't know why but it looks like IS_ENABLED() is not exactly doing the
#ifdef #endif, thus those checks are not fully skipped in this case.

Anyway I'll change it to the more traditional #ifdef #endif instead.

Thanks for the report.
Qu

>     fs/btrfs/ordered-data.c:202:15: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
>       202 |                 if (!entry->finished_bitmap) {
>           |                      ~~~~~  ^
>     fs/btrfs/ordered-data.c:380:38: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
>       380 |                 nr_set = bitmap_count_set(ordered->finished_bitmap, start_bit, nbits);
>           |                                           ~~~~~~~  ^
>     fs/btrfs/ordered-data.c:388:17: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
>       388 |                                    ordered->finished_bitmap);
>           |                                    ~~~~~~~  ^
>     fs/btrfs/messages.h:44:41: note: expanded from macro 'btrfs_crit'
>        44 |         btrfs_printk(fs_info, KERN_CRIT fmt, ##args)
>           |                                                ^~~~
>     fs/btrfs/messages.h:27:32: note: expanded from macro 'btrfs_printk'
>        27 |         _btrfs_printk(fs_info, fmt, ##args)
>           |                                       ^~~~
>     fs/btrfs/ordered-data.c:390:23: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
>       390 |                 bitmap_set(ordered->finished_bitmap, start_bit, nbits);
>           |                            ~~~~~~~  ^
>     fs/btrfs/ordered-data.c:418:37: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
>       418 |                 if (WARN_ON(!bitmap_full(ordered->finished_bitmap,
>           |                                          ~~~~~~~  ^
>     include/asm-generic/bug.h:123:25: note: expanded from macro 'WARN_ON'
>       123 |         int __ret_warn_on = !!(condition);                              \
>           |                                ^~~~~~~~~
>     fs/btrfs/ordered-data.c:426:14: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
>       426 |                                 ordered->finished_bitmap);
>           |                                 ~~~~~~~  ^
>     fs/btrfs/messages.h:44:41: note: expanded from macro 'btrfs_crit'
>        44 |         btrfs_printk(fs_info, KERN_CRIT fmt, ##args)
>           |                                                ^~~~
>     fs/btrfs/messages.h:27:32: note: expanded from macro 'btrfs_printk'
>        27 |         _btrfs_printk(fs_info, fmt, ##args)
>           |                                       ^~~~
>     fs/btrfs/ordered-data.c:675:23: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
>       675 |                         bitmap_free(entry->finished_bitmap);
>           |                                     ~~~~~  ^
>     1 warning and 8 errors generated.
>
>
> vim +200 fs/btrfs/ordered-data.c
>
>     147
>     148	static struct btrfs_ordered_extent *alloc_ordered_extent(
>     149				struct btrfs_inode *inode, u64 file_offset, u64 num_bytes,
>     150				u64 ram_bytes, u64 disk_bytenr, u64 disk_num_bytes,
>     151				u64 offset, unsigned long flags, int compress_type)
>     152	{
>     153		struct btrfs_ordered_extent *entry;
>     154		int ret;
>     155		u64 qgroup_rsv = 0;
>     156
>     157		if (flags &
>     158		    ((1 << BTRFS_ORDERED_NOCOW) | (1 << BTRFS_ORDERED_PREALLOC))) {
>     159			/* For nocow write, we can release the qgroup rsv right now */
>     160			ret = btrfs_qgroup_free_data(inode, NULL, file_offset, num_bytes, &qgroup_rsv);
>     161			if (ret < 0)
>     162				return ERR_PTR(ret);
>     163		} else {
>     164			/*
>     165			 * The ordered extent has reserved qgroup space, release now
>     166			 * and pass the reserved number for qgroup_record to free.
>     167			 */
>     168			ret = btrfs_qgroup_release_data(inode, file_offset, num_bytes, &qgroup_rsv);
>     169			if (ret < 0)
>     170				return ERR_PTR(ret);
>     171		}
>     172		entry = kmem_cache_zalloc(btrfs_ordered_extent_cache, GFP_NOFS);
>     173		if (!entry)
>     174			return ERR_PTR(-ENOMEM);
>     175
>     176		entry->file_offset = file_offset;
>     177		entry->num_bytes = num_bytes;
>     178		entry->ram_bytes = ram_bytes;
>     179		entry->disk_bytenr = disk_bytenr;
>     180		entry->disk_num_bytes = disk_num_bytes;
>     181		entry->offset = offset;
>     182		entry->bytes_left = num_bytes;
>     183		entry->inode = BTRFS_I(igrab(&inode->vfs_inode));
>     184		entry->compress_type = compress_type;
>     185		entry->truncated_len = (u64)-1;
>     186		entry->qgroup_rsv = qgroup_rsv;
>     187		entry->flags = flags;
>     188		refcount_set(&entry->refs, 1);
>     189		init_waitqueue_head(&entry->wait);
>     190		INIT_LIST_HEAD(&entry->list);
>     191		INIT_LIST_HEAD(&entry->log_list);
>     192		INIT_LIST_HEAD(&entry->root_extent_list);
>     193		INIT_LIST_HEAD(&entry->work_list);
>     194		INIT_LIST_HEAD(&entry->bioc_list);
>     195		init_completion(&entry->completion);
>     196
>     197		if (IS_ENABLED(CONFIG_BTRFS_DEBUG)) {
>     198			struct btrfs_fs_info *fs_info = inode->root->fs_info;
>     199
>   > 200			entry->finished_bitmap = bitmap_zalloc(
>     201				num_bytes >> fs_info->sectorsize_bits, GFP_NOFS);
>     202			if (!entry->finished_bitmap) {
>     203				kmem_cache_free(btrfs_ordered_extent_cache, entry);
>     204				return ERR_PTR(-ENOMEM);
>     205			}
>     206		}
>     207		/*
>     208		 * We don't need the count_max_extents here, we can assume that all of
>     209		 * that work has been done at higher layers, so this is truly the
>     210		 * smallest the extent is going to get.
>     211		 */
>     212		spin_lock(&inode->lock);
>     213		btrfs_mod_outstanding_extents(inode, 1);
>     214		spin_unlock(&inode->lock);
>     215
>     216		return entry;
>     217	}
>     218
>
David Sterba Dec. 11, 2024, 9:26 p.m. UTC | #4
On Thu, Dec 12, 2024 at 06:17:37AM +1030, Qu Wenruo wrote:
> >>> fs/btrfs/ordered-data.c:200:10: error: no member named 'finished_bitmap' in 'struct btrfs_ordered_extent'
> >       200 |                 entry->finished_bitmap = bitmap_zalloc(
> >           |                 ~~~~~  ^
> 
> I don't know why but it looks like IS_ENABLED() is not exactly doing the
> #ifdef #endif, thus those checks are not fully skipped in this case.

It's never been equvalent to ifdef/endif, it evaluates to 0 or 1 if the
config option is enabled or not but otherwise the source code is still
compiled. The compiler then finds out it's dead code and eliminates it.
diff mbox series

Patch

diff --git a/fs/btrfs/misc.h b/fs/btrfs/misc.h
index 0d599fd847c9..438887a1ca32 100644
--- a/fs/btrfs/misc.h
+++ b/fs/btrfs/misc.h
@@ -163,4 +163,32 @@  static inline bool bitmap_test_range_all_zero(const unsigned long *addr,
 	return (found_set == start + nbits);
 }
 
+/*
+ * Count how many bits are set in the bitmap.
+ *
+ * Similar to bitmap_weight() but accepts a subrange of the bitmap.
+ */
+static inline unsigned int bitmap_count_set(const unsigned long *addr,
+					    unsigned long start,
+					    unsigned long nbits)
+{
+	const unsigned long bitmap_nbits = start + nbits;
+	unsigned long cur = start;
+	unsigned long total_set = 0;
+
+	while (cur < bitmap_nbits) {
+		unsigned long found_zero;
+		unsigned long found_set;
+
+		found_zero = find_next_zero_bit(addr, bitmap_nbits, cur);
+		total_set += found_zero - cur;
+
+		cur = found_zero;
+		if (cur >= bitmap_nbits)
+			break;
+		found_set = find_next_bit(addr, bitmap_nbits, cur);
+		cur = found_set;
+	}
+	return total_set;
+}
 #endif
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index 30eceaf829a7..1b1f14aa13d5 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -194,6 +194,16 @@  static struct btrfs_ordered_extent *alloc_ordered_extent(
 	INIT_LIST_HEAD(&entry->bioc_list);
 	init_completion(&entry->completion);
 
+	if (IS_ENABLED(CONFIG_BTRFS_DEBUG)) {
+		struct btrfs_fs_info *fs_info = inode->root->fs_info;
+
+		entry->finished_bitmap = bitmap_zalloc(
+			num_bytes >> fs_info->sectorsize_bits, GFP_NOFS);
+		if (!entry->finished_bitmap) {
+			kmem_cache_free(btrfs_ordered_extent_cache, entry);
+			return ERR_PTR(-ENOMEM);
+		}
+	}
 	/*
 	 * We don't need the count_max_extents here, we can assume that all of
 	 * that work has been done at higher layers, so this is truly the
@@ -356,13 +366,38 @@  static bool can_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
 		btrfs_folio_clear_ordered(fs_info, folio, file_offset, len);
 	}
 
+	if (IS_ENABLED(CONFIG_BTRFS_DEBUG)) {
+		unsigned long start_bit;
+		unsigned long nbits;
+		unsigned long nr_set;
+
+		ASSERT(file_offset >= ordered->file_offset);
+		ASSERT(file_offset + len <= ordered->file_offset  + ordered->num_bytes);
+
+		start_bit = (file_offset - ordered->file_offset) >> fs_info->sectorsize_bits;
+		nbits = len >> fs_info->sectorsize_bits;
+
+		nr_set = bitmap_count_set(ordered->finished_bitmap, start_bit, nbits);
+		if (WARN_ON(nr_set)) {
+			btrfs_crit(fs_info,
+"double ordered extent accounting, root=%llu ino=%llu OE offset=%llu OE len=%llu range offset=%llu range len=%llu already finished len=%lu finish_bitmap=%*pbl",
+				   btrfs_root_id(inode->root), btrfs_ino(inode),
+				   ordered->file_offset, ordered->num_bytes,
+				   file_offset, len, nr_set << fs_info->sectorsize_bits,
+				   (int)(ordered->num_bytes >> fs_info->sectorsize_bits),
+				   ordered->finished_bitmap);
+		}
+		bitmap_set(ordered->finished_bitmap, start_bit, nbits);
+		len -= (nr_set << fs_info->sectorsize_bits);
+	}
+
 	/* Now we're fine to update the accounting. */
 	if (WARN_ON_ONCE(len > ordered->bytes_left)) {
 		btrfs_crit(fs_info,
-"bad ordered extent accounting, root=%llu ino=%llu OE offset=%llu OE len=%llu to_dec=%llu left=%llu",
+"bad ordered extent accounting, root=%llu ino=%llu OE offset=%llu OE len=%llu range start=%llu range len=%llu left=%llu",
 			   btrfs_root_id(inode->root), btrfs_ino(inode),
 			   ordered->file_offset, ordered->num_bytes,
-			   len, ordered->bytes_left);
+			   file_offset, len, ordered->bytes_left);
 		ordered->bytes_left = 0;
 	} else {
 		ordered->bytes_left -= len;
@@ -379,6 +414,18 @@  static bool can_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
 	 * the finish_func to be executed.
 	 */
 	set_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags);
+	if (IS_ENABLED(CONFIG_BTRFS_DEBUG)) {
+		if (WARN_ON(!bitmap_full(ordered->finished_bitmap,
+				 ordered->num_bytes >> fs_info->sectorsize_bits))) {
+			btrfs_crit(fs_info,
+"ordered extent finished bitmap desync, root=%llu ino=%llu OE offset=%llu OE len=%llu bytes_left=%llu bitmap=%*pbl",
+				btrfs_root_id(inode->root), btrfs_ino(inode),
+				ordered->file_offset, ordered->num_bytes,
+				ordered->bytes_left,
+				(int)(ordered->num_bytes >> fs_info->sectorsize_bits),
+				ordered->finished_bitmap);
+		}
+	}
 	cond_wake_up(&ordered->wait);
 	refcount_inc(&ordered->refs);
 	trace_btrfs_ordered_extent_mark_finished(inode, ordered);
@@ -624,6 +671,8 @@  void btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)
 			list_del(&sum->list);
 			kvfree(sum);
 		}
+		if (IS_ENABLED(CONFIG_BTRFS_DEBUG))
+			bitmap_free(entry->finished_bitmap);
 		kmem_cache_free(btrfs_ordered_extent_cache, entry);
 	}
 }
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
index 4e152736d06c..f04a2f7a593a 100644
--- a/fs/btrfs/ordered-data.h
+++ b/fs/btrfs/ordered-data.h
@@ -154,6 +154,15 @@  struct btrfs_ordered_extent {
 	struct list_head work_list;
 
 	struct list_head bioc_list;
+
+#ifdef CONFIG_BTRFS_DEBUG
+	/*
+	 * Set if one block has finished.
+	 *
+	 * To catch double freeing with more accuracy.
+	 */
+	unsigned long *finished_bitmap;
+#endif
 };
 
 int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent);