All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "John Groves" <john@jagalactic.com>
Cc: nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org
Subject: Re: [PATCH V12 11/12] famfs: Report device capacity via statfs so df works
Date: Mon, 03 Aug 2026 02:58:40 +0000	[thread overview]
Message-ID: <20260803025840.E72C41F000E9@smtp.kernel.org> (raw)
In-Reply-To: <0100019fc57500f5-06c293c8-393c-44e3-ad42-d4f4de58245d-000000@email.amazonses.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] The `used_capacity` counter is permanently leaked when mapped files are unlinked or their inodes are evicted.
- [High] dax_fsdev_size() accesses the private dev_dax structure without holding the required dax_read_lock(), leading to a use-after-free race condition with concurrent device unbinds.
- [High] Missing read memory barrier when locklessly checking `daxdev->valid` allows reading an uninitialized or stale `daxdev->devp` pointer.
- [Low] famfs_file_init_dax() mixes goto-based error handling with scope-based cleanup helpers, violating the subsystem rules.
--

--- Patch [11]: [PATCH V12 11/12] famfs: Report device capacity via statfs so df works ---
commit 72bb530cad0a65ccec5c1eea93f055e5015f64ec
Author: John Groves <John@Groves.net>

famfs: Report device capacity via statfs so df works

Replace simple_statfs(), which reports zero blocks (so df omits the mount),
with famfs_statfs() reporting real capacity and usage.

Add dax_fsdev_size() in drivers/dax/fsdev.c, returning the size fsdev
caches at probe (dev_dax->cached_size - the sum of the device's ranges,
stable while bound), exported. It lives in fsdev.c because cached_size is
set only by the fsdev driver, and famfs only ever holds fsdev-mode daxdevs
(fs_dax_get() enforces DAXDRV_FSDEV_TYPE); famfs.ko therefore depends on
fsdev_dax.ko.

famfs tracks two byte counters under a new stats_sem:
 - total_capacity: summed in famfs_install_daxdev() from dax_fsdev_size(),
   covering the mount primary and every DAXDEV_OPEN secondary, counted once
   per daxdev (on the valid 0->1 transition).
 - used_capacity: summed in famfs_file_init_dax() from the fmap's mapped
   device bytes (superblock + log + data files).

famfs_statfs() reports total and free (total - used). Free is an
approximation of the userspace allocator's free space (it ignores allocator
gaps and reserved regions), which is adequate for df.

(Side note: I am the maintainer of drivers/dax/fsdev.c)

Signed-off-by: John Groves <john@groves.net>

> diff --git a/drivers/dax/fsdev.c b/drivers/dax/fsdev.c
> index 188b2526bee45..a5b4b2d794286 100644
> --- a/drivers/dax/fsdev.c
> +++ b/drivers/dax/fsdev.c
> @@ -104,6 +104,25 @@ static size_t fsdev_dax_recovery_write(struct dax_device *dax_dev, pgoff_t pgoff
>  	return _copy_from_iter_flushcache(addr, bytes, i);
>  }
>  
> +/**
> + * dax_fsdev_size() - total size in bytes of an fsdev dax device
> + * @dax_dev: the dax device (must be bound to this driver)
> + *
> + * Returns the size cached at probe time (sum of all ranges); it cannot change
> + * while the driver is bound. Only valid for fsdev dax devices - callers
> + * ensure that (e.g. fs_dax_get() enforces DAXDRV_FSDEV_TYPE). Returns 0 if the
> + * device is not alive.
> + */
> +u64 dax_fsdev_size(struct dax_device *dax_dev)
> +{
> +	struct dev_dax *dev_dax = dax_get_private(dax_dev);
> +
> +	if (!dev_dax)
> +		return 0;
> +	return dev_dax->cached_size;
> +}

[Severity: High]
Does dax_get_private() require holding the dax_read_lock() here?

In the DAX subsystem, accessing dev_dax from a dax_device requires holding the
SRCU-based dax_read_lock(), because a concurrent driver unbind can call
kill_dax(), which clears DAXDEV_ALIVE, calls synchronize_srcu(&dax_srcu), and
frees the dev_dax structure.

Since dax_fsdev_size() and its caller famfs_install_daxdev() do not hold
dax_read_lock(), could a concurrent unbind result in dev_dax being freed
before dev_dax->cached_size is dereferenced?

> +EXPORT_SYMBOL_GPL(dax_fsdev_size);
> +
>  static const struct dax_operations dev_dax_ops = {
> diff --git a/fs/famfs/famfs_file.c b/fs/famfs/famfs_file.c
> index abf049b32a4b9..5be39d6770899 100644
> --- a/fs/famfs/famfs_file.c
> +++ b/fs/famfs/famfs_file.c
> @@ -280,6 +280,11 @@ famfs_file_init_dax(struct file *file, void __user *arg)
>  	}
>  	inode_unlock(inode);
>  
> +	/* Account the mapped device bytes for statfs (only on success) */
> +	if (!rc) {
> +		scoped_guard(rwsem_write, &fsi->stats_sem)
> +			fsi->used_capacity += extent_total;

[Severity: High]
Is fsi->used_capacity ever decremented when files are unlinked?

If famfs_file_init_dax() increments fsi->used_capacity here, should there be a
matching decrement path when the mapping is torn down and its private metadata
is freed in famfs_evict_inode()?

If FAMFS_OPT_UNLINK is enabled via ioctl, userspace could repeatedly create,
map, and delete files, causing used_capacity to monotonically grow until it
exceeds total_capacity. Would this permanently break the statfs free space
accounting for the mount?

> +	}
>  out:
>  	kvfree(fmap_buf);
>  	if (meta)
>  		famfs_meta_free(meta);

[Severity: Low]
This isn't a bug, but does this code mix goto-based error handling with
scope-based cleanup helpers?

Using the out label for manual cleanup (freeing fmap_buf and meta) while also
using scoped_guard() in the same function violates the documented cleanup
guidelines, which state that functions must not mix goto-based cleanup labels
and __free()/guard() declarations.

> diff --git a/fs/famfs/famfs_inode.c b/fs/famfs/famfs_inode.c
> index 6cbd7d657fd80..3c0d1094d6532 100644
> --- a/fs/famfs/famfs_inode.c
> +++ b/fs/famfs/famfs_inode.c
> @@ -462,6 +493,15 @@ int famfs_install_daxdev(
>  
>  		wmb(); /* All other fields must be visible before valid */
>  		daxdev->valid = 1;
> +		devp = daxdev->devp;
> +	}

[Severity: High]
Is there a matching read memory barrier on the consumer side?

Here the producer uses wmb() before setting daxdev->valid = 1 to make the new
devp visible.

On the IO/fault path, famfs_daxdev_from_index() calls famfs_dax_err() to
locklessly read dd->valid, and if true, proceeds to read dd->devp:

fs/famfs/famfs_file.c:famfs_dax_err() {
    if (!dd->valid) {
        ...
}

fs/famfs/famfs_file.c:famfs_daxdev_from_index() {
    ...
    *errp = 0;
    return dd->devp;
}

If there is no paired read memory barrier (e.g., smp_rmb() or
smp_load_acquire()) on the consumer side, could the CPU reorder the loads?

A concurrent thread might observe valid == 1 but still read a stale or NULL
value for devp, resulting in a NULL pointer dereference during IO.

> +
> +	/* Freshly installed: add its capacity to the statfs accounting */
> +	if (devp) {
> +		u64 sz = dax_fsdev_size(devp);
> +
> +		scoped_guard(rwsem_write, &fsi->stats_sem)
> +			fsi->total_capacity += sz;
>  	}
>  
>  	return 0;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/0100019fc572ca94-ec363dd7-3a77-484b-b4b7-f2503a0931a6-000000@email.amazonses.com?part=11

  reply	other threads:[~2026-08-03  2:58 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20260803022730.75731-1-john@jagalactic.com>
2026-08-03  2:27 ` [PATCH V12 00/12] famfs: the Fabric-Attached Memory File System (standalone) John Groves
2026-08-03  2:28   ` [PATCH V12 01/12] dax: replace exported dax_dev_get() with non-allocating dax_dev_find() John Groves
2026-08-03  2:43     ` sashiko-bot
2026-08-03 19:13     ` Alison Schofield
2026-08-05 20:21       ` John Groves
2026-08-03  2:28   ` [PATCH V12 02/12] famfs: Module operations, fs_context, and mount John Groves
2026-08-03  2:49     ` sashiko-bot
2026-08-06  4:37     ` Darrick J. Wong
2026-08-06 13:22       ` John Groves
2026-08-03  2:28   ` [PATCH V12 03/12] famfs: Add daxdev table and dax notify_failure support John Groves
2026-08-03  2:45     ` sashiko-bot
2026-08-06  5:05     ` Darrick J. Wong
2026-08-06 13:36       ` John Groves
2026-08-03  2:28   ` [PATCH V12 04/12] famfs: Introduce inode_operations and super_operations John Groves
2026-08-03  2:42     ` sashiko-bot
2026-08-06  5:12     ` Darrick J. Wong
2026-08-06 16:31       ` John Groves
2026-08-03  2:29   ` [PATCH V12 05/12] famfs: Introduce file_operations read/write John Groves
2026-08-03  2:42     ` sashiko-bot
2026-08-06  5:14     ` Darrick J. Wong
2026-08-06 20:03       ` John Groves
2026-08-03  2:29   ` [PATCH V12 06/12] famfs: Introduce mmap and VM fault handling John Groves
2026-08-03  2:46     ` sashiko-bot
2026-08-06  5:16     ` Darrick J. Wong
2026-08-06 20:40       ` John Groves
2026-08-03  2:29   ` [PATCH V12 07/12] famfs: MAP_CREATE ioctl and fmap ingest (ABI 44) John Groves
2026-08-03  2:42     ` sashiko-bot
2026-08-06  5:24     ` Darrick J. Wong
2026-08-06 20:53       ` John Groves
2026-08-07 22:17         ` John Groves
2026-08-03  2:29   ` [PATCH V12 08/12] famfs: iomap_begin and file-to-dax offset resolution John Groves
2026-08-03  2:44     ` sashiko-bot
2026-08-06  5:28     ` Darrick J. Wong
2026-08-06 22:14       ` John Groves
2026-08-03  2:29   ` [PATCH V12 09/12] famfs: Register secondary daxdevs by path (FAMFSIOC_DAXDEV_OPEN) John Groves
2026-08-03  2:42     ` sashiko-bot
2026-08-06  5:29     ` Darrick J. Wong
2026-08-06 22:22       ` John Groves
2026-08-03  2:29   ` [PATCH V12 10/12] famfs: Add runtime operation-permission (opts) framework John Groves
2026-08-03  2:42     ` sashiko-bot
2026-08-06  5:31     ` Darrick J. Wong
2026-08-06 22:30       ` John Groves
2026-08-03  2:30   ` [PATCH V12 11/12] famfs: Report device capacity via statfs so df works John Groves
2026-08-03  2:58     ` sashiko-bot [this message]
2026-08-06  5:33     ` Darrick J. Wong
2026-08-07 13:47       ` John Groves
2026-08-03  2:30   ` [PATCH V12 12/12] famfs: Add documentation John Groves
2026-08-06  5:38     ` Darrick J. Wong
2026-08-07 15:05       ` John Groves
2026-08-03  8:52   ` [PATCH V12 00/12] famfs: the Fabric-Attached Memory File System (standalone) Amir Goldstein
2026-08-06  5:19     ` Matthew Wilcox
2026-08-06  5:34       ` Darrick J. Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260803025840.E72C41F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=john@jagalactic.com \
    --cc=linux-cxl@vger.kernel.org \
    --cc=nvdimm@lists.linux.dev \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.