* [PATCH v3 00/20] drbd: DRBD 9 rework @ 2026-11-10 16:26 Christoph Böhmwalder 2026-11-10 16:26 ` [PATCH v3 06/20] drbd: add DAX/PMEM support for metadata access Christoph Böhmwalder 0 siblings, 1 reply; 3+ messages in thread From: Christoph Böhmwalder @ 2026-11-10 16:26 UTC (permalink / raw) To: Jens Axboe Cc: drbd-dev, linux-kernel, Lars Ellenberg, Philipp Reisner, linux-block, Christoph Böhmwalder, Dan Williams, Vishal Verma, Dave Jiang, Alison Schofield, nvdimm, Donald Hunter, Jakub Kicinski As discussed (context: [0]), here is our DRBD 9 rework series, intended for linux-next via for-7.x/drbd. This replays about 10-15 years of active out-of-tree development work [1], depending on your way of counting. The out-of-tree module has severely diverged from the in-tree version over the years, which is what we are aiming to fix now. Hopefully that somewhat excuses (or at least explains) the massive diffs -- we've tried to come up with a way to group the changes by topic, but I realize it's still not exactly trivial to review. We've been polishing this series for a while now, and we have taken great care to make it as "upstream-presentable" as possible. That said, there are still probably imperfections. It's a start -- feedback welcome! The netlink interface has been reworked so that userspace is not broken: the existing "drbd" generic netlink family keeps being served at version 1, and this series does not touch its generated serialization or its UAPI header. Every shipped drbd-utils release therefore keeps working against this driver. DRBD 9's own utilities speak a new family, "drbd2", written as a YNL specification under Documentation/netlink/specs/ and following current genetlink conventions; its generated code is checked in as usual. The driver registers both families and translates each onto the same wire-neutral core, so the multi-peer administration model is only reachable through drbd2, while version-1 clients see the single-peer semantics they expect. The RDMA and load-balancing TCP (lb-tcp) transports are not part of this series; they will be submitted separately once they have had the same treatment. The rdma_ctrl_rcvbuf_size and rdma_ctrl_sndbuf_size net options do remain in the UAPI: they are generic transport options that are interpreted by whichever transport is in use, so there is no reason to remove and later re-add them. The base for these patches is for-7.5/block, which has the preparation patches [2] this series builds on. As discussed, the goal is to get this applied to a for-7.x/drbd branch so it can land in linux-next via block-next. After letting it mature in linux-next for a while, it should go in via the block tree. Thanks, Christoph [0] https://lore.kernel.org/linux-next/899e0337-9642-4ca6-9050-aeab14fa22ef@kernel.dk/ [1] https://github.com/LINBIT/drbd [2] https://lore.kernel.org/linux-block/20260923140608.1116713-1-christoph.boehmwalder@linbit.com/ v2: https://lore.kernel.org/linux-block/20261006154607.3936501-1-christoph.boehmwalder@linbit.com/ Changes since v2: - Rebased onto for-7.5/block, now that the preparation patches are applied there; v2 no longer applied - Kept the in-tree DRBD changes that went into v7.4: the explicit linux/umh.h include and the conversion to sk_set_nospace() - Synced with the current out-of-tree state Christoph Böhmwalder (20): drbd: mark as BROKEN during DRBD 9 rework drbd: extend wire protocol definitions for DRBD 9 drbd: introduce DRBD 9 on-disk metadata format drbd: add transport layer abstraction drbd: add TCP transport implementation drbd: add DAX/PMEM support for metadata access drbd: add optional compatibility layer for DRBD 8.4 drbd: add application/resync IO synchronization documentation drbd: rework sender for DRBD 9 multi-peer drbd: replace per-device state model with multi-peer data structures drbd: rewrite state machine for DRBD 9 multi-peer clusters drbd: rework activity log and bitmap for multi-peer replication drbd: rework request processing for DRBD 9 multi-peer IO drbd: rework module core for DRBD 9 transport and multi-peer drbd: rework receiver for DRBD 9 transport and multi-peer protocol drbd: rework the netlink core for DRBD 9 drbd: serve the "drbd" genl family at version 1 for existing userspace drbd: add the "drbd2" genl family drbd: update monitoring interfaces for multi-peer topology drbd: remove BROKEN for DRBD .../application-resync-synchronization.rst | 208 + .../admin-guide/blockdev/drbd/index.rst | 1 + Documentation/netlink/specs/drbd2.yaml | 2600 +++ drivers/block/drbd/Kconfig | 43 + drivers/block/drbd/Makefile | 9 +- drivers/block/drbd/drbd2_nl_gen.c | 810 + drivers/block/drbd/drbd2_nl_gen.h | 113 + drivers/block/drbd/drbd_actlog.c | 1373 +- drivers/block/drbd/drbd_bitmap.c | 2075 +- drivers/block/drbd/drbd_config.h | 45 +- drivers/block/drbd/drbd_dax_pmem.c | 175 + drivers/block/drbd/drbd_dax_pmem.h | 43 + drivers/block/drbd/drbd_debugfs.c | 1758 +- drivers/block/drbd/drbd_debugfs.h | 2 + drivers/block/drbd/drbd_int.h | 3930 ++-- drivers/block/drbd/drbd_interval.c | 37 +- drivers/block/drbd/drbd_interval.h | 130 +- drivers/block/drbd/drbd_legacy_84.c | 641 + drivers/block/drbd/drbd_legacy_84.h | 116 + drivers/block/drbd/drbd_main.c | 6812 ++++-- drivers/block/drbd/drbd_meta_data.h | 170 + drivers/block/drbd/drbd_nl.c | 10232 ++++++--- drivers/block/drbd/drbd_nl.h | 400 + drivers/block/drbd/drbd_nl_84.c | 3622 ++++ drivers/block/drbd/drbd_nl_defaults.c | 168 + drivers/block/drbd/drbd_nl_drbd2.c | 2589 +++ drivers/block/drbd/drbd_nl_types.h | 402 + drivers/block/drbd/drbd_polymorph_printk.h | 37 +- drivers/block/drbd/drbd_proc.c | 327 +- drivers/block/drbd/drbd_protocol.h | 614 +- drivers/block/drbd/drbd_receiver.c | 17304 ++++++++++++---- drivers/block/drbd/drbd_req.c | 3188 ++- drivers/block/drbd/drbd_req.h | 332 +- drivers/block/drbd/drbd_sender.c | 4445 ++-- drivers/block/drbd/drbd_state.c | 9203 ++++++-- drivers/block/drbd/drbd_state.h | 334 +- drivers/block/drbd/drbd_state_change.h | 80 +- drivers/block/drbd/drbd_strings.c | 103 +- drivers/block/drbd/drbd_strings.h | 4 +- drivers/block/drbd/drbd_transport.c | 545 + drivers/block/drbd/drbd_transport.h | 487 + drivers/block/drbd/drbd_transport_tcp.c | 1805 ++ drivers/block/drbd/drbd_transport_template.c | 164 + include/uapi/linux/drbd.h | 305 +- include/uapi/linux/drbd2.h | 1038 + include/uapi/linux/drbd_limits.h | 138 +- 46 files changed, 61765 insertions(+), 17192 deletions(-) create mode 100644 Documentation/admin-guide/blockdev/drbd/application-resync-synchronization.rst create mode 100644 Documentation/netlink/specs/drbd2.yaml create mode 100644 drivers/block/drbd/drbd2_nl_gen.c create mode 100644 drivers/block/drbd/drbd2_nl_gen.h create mode 100644 drivers/block/drbd/drbd_dax_pmem.c create mode 100644 drivers/block/drbd/drbd_dax_pmem.h create mode 100644 drivers/block/drbd/drbd_legacy_84.c create mode 100644 drivers/block/drbd/drbd_legacy_84.h create mode 100644 drivers/block/drbd/drbd_meta_data.h create mode 100644 drivers/block/drbd/drbd_nl.h create mode 100644 drivers/block/drbd/drbd_nl_84.c create mode 100644 drivers/block/drbd/drbd_nl_defaults.c create mode 100644 drivers/block/drbd/drbd_nl_drbd2.c create mode 100644 drivers/block/drbd/drbd_nl_types.h create mode 100644 drivers/block/drbd/drbd_transport.c create mode 100644 drivers/block/drbd/drbd_transport.h create mode 100644 drivers/block/drbd/drbd_transport_tcp.c create mode 100644 drivers/block/drbd/drbd_transport_template.c create mode 100644 include/uapi/linux/drbd2.h base-commit: aa7014f8d7565cfea117eac36a5de0a05eba65a4 -- 2.56.0 ^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v3 06/20] drbd: add DAX/PMEM support for metadata access 2026-11-10 16:26 [PATCH v3 00/20] drbd: DRBD 9 rework Christoph Böhmwalder @ 2026-11-10 16:26 ` Christoph Böhmwalder 2026-11-10 16:55 ` sashiko-bot 0 siblings, 1 reply; 3+ messages in thread From: Christoph Böhmwalder @ 2026-11-10 16:26 UTC (permalink / raw) To: Jens Axboe Cc: drbd-dev, linux-kernel, Lars Ellenberg, Philipp Reisner, linux-block, Christoph Böhmwalder, Dan Williams, Vishal Verma, Dave Jiang, Alison Schofield, nvdimm When DRBD's metadata device resides on persistent memory (PMEM/NVDIMM), accessing it by reading and writing full blocks is unnecessarily costly. Add a DAX-based metadata path that directly maps the metadata region, enabling byte-granular, IRQ-safe access without having to go through the block layer. The PMEM path also introduces a more efficient activity log layout: instead of writing journal transactions, the in-memory LRU-cache hash table is stored directly in persistent memory and updated in-place. Similarly, the resync bitmap is accessed directly from PMEM rather than being loaded into and flushed from DRAM. This is compiled in only when CONFIG_DEV_DAX_PMEM is enabled. Co-developed-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Christoph Böhmwalder <christoph.boehmwalder@linbit.com> --- drivers/block/drbd/Makefile | 1 + drivers/block/drbd/drbd_dax_pmem.c | 175 +++++++++++++++++++++++++++++ drivers/block/drbd/drbd_dax_pmem.h | 43 +++++++ 3 files changed, 219 insertions(+) create mode 100644 drivers/block/drbd/drbd_dax_pmem.c create mode 100644 drivers/block/drbd/drbd_dax_pmem.h diff --git a/drivers/block/drbd/Makefile b/drivers/block/drbd/Makefile index 9f05f74e619c..8ec1ad360449 100644 --- a/drivers/block/drbd/Makefile +++ b/drivers/block/drbd/Makefile @@ -5,6 +5,7 @@ drbd-y += drbd_main.o drbd_strings.o drbd_nl.o drbd-y += drbd_interval.o drbd_state.o drbd-y += drbd_nl_gen.o drbd-y += drbd_transport.o +drbd-$(CONFIG_DEV_DAX_PMEM) += drbd_dax_pmem.o drbd-$(CONFIG_DEBUG_FS) += drbd_debugfs.o obj-$(CONFIG_BLK_DEV_DRBD) += drbd.o diff --git a/drivers/block/drbd/drbd_dax_pmem.c b/drivers/block/drbd/drbd_dax_pmem.c new file mode 100644 index 000000000000..1fb22e57cf5d --- /dev/null +++ b/drivers/block/drbd/drbd_dax_pmem.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2017, LINBIT HA-Solutions GmbH. + */ + +/* + * In case DRBD's meta-data resides in persistent memory do a few things + * different. + * + * 1 direct access the bitmap in place. Do not load it into DRAM, do not + * write it back from DRAM. + * 2 Use a better fitting format for the on-disk activity log. Instead of + * writing transactions, the unmangled LRU-cache hash table is there. + */ + +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/dax.h> +#include <linux/libnvdimm.h> +#include <linux/blkdev.h> +#include <linux/sizes.h> +#include "drbd_int.h" +#include "drbd_dax_pmem.h" +#include "drbd_meta_data.h" + +/* + * Unit conversions for addressing the metadata area through DAX. + * DRBD keeps metadata offsets in sectors, dax_direct_access() works in pages. + */ +static pgoff_t md_sector_to_pgoff(sector_t sector) +{ + return sector >> (PAGE_SHIFT - SECTOR_SHIFT); +} + +static long md_sectors_to_pages(sector_t nr_sectors) +{ + return nr_sectors >> (PAGE_SHIFT - SECTOR_SHIFT); +} + +/* Signed, since the bitmap offset relative to the superblock may be negative. */ +static long md_sector_offset_to_bytes(long sector_offset) +{ + return sector_offset * SECTOR_SIZE; +} + +static int map_superblock_for_dax(struct drbd_backing_dev *bdev, struct dax_device *dax_dev) +{ + long want = 1; + pgoff_t pgoff = md_sector_to_pgoff(bdev->md.md_offset); + void *kaddr; + long len; + int id; + + id = dax_read_lock(); + len = dax_direct_access(dax_dev, pgoff, want, DAX_ACCESS, &kaddr, NULL); + dax_read_unlock(id); + + if (len < want) + return -EIO; + + bdev->md_on_pmem = kaddr; + + return 0; +} + +/** + * drbd_dax_open() - Open device for dax and map metadata superblock + * @bdev: backing device to be opened + */ +int drbd_dax_open(struct drbd_backing_dev *bdev) +{ + struct dax_device *dax_dev; + int err; + u64 part_off; + + dax_dev = fs_dax_get_by_bdev(bdev->md_bdev, &part_off, NULL, NULL); + if (!dax_dev) + return -ENODEV; + + err = map_superblock_for_dax(bdev, dax_dev); + if (!err) + bdev->dax_dev = dax_dev; + else + put_dax(dax_dev); + + return err; +} + +void drbd_dax_close(struct drbd_backing_dev *bdev) +{ + put_dax(bdev->dax_dev); +} + +/** + * drbd_dax_map() - Map metadata for dax + * @bdev: backing device whose metadata is to be mapped + */ +int drbd_dax_map(struct drbd_backing_dev *bdev) +{ + struct dax_device *dax_dev = bdev->dax_dev; + sector_t first_sector = drbd_md_first_sector(bdev); + sector_t al_sector = bdev->md.md_offset + bdev->md.al_offset; + long want = md_sectors_to_pages(drbd_md_last_sector(bdev) + 1 - first_sector); + pgoff_t pgoff = md_sector_to_pgoff(first_sector); + long md_offset_byte = md_sector_offset_to_bytes(bdev->md.md_offset - first_sector); + long al_offset_byte = md_sector_offset_to_bytes(al_sector - first_sector); + void *kaddr; + long len; + int id; + + id = dax_read_lock(); + len = dax_direct_access(dax_dev, pgoff, want, DAX_ACCESS, &kaddr, NULL); + dax_read_unlock(id); + + if (len < want) + return -EIO; + + bdev->md_on_pmem = kaddr + md_offset_byte; + bdev->al_on_pmem = kaddr + al_offset_byte; + + return 0; +} + +void drbd_dax_al_update(struct drbd_device *device, struct lc_element *al_ext) +{ + struct al_on_pmem *al_on_pmem = device->ldev->al_on_pmem; + __be32 *slot = &al_on_pmem->slots[al_ext->lc_index]; + + *slot = cpu_to_be32(al_ext->lc_new_number); + arch_wb_cache_pmem(slot, sizeof(*slot)); +} + +void drbd_dax_al_begin_io_commit(struct drbd_device *device) +{ + struct lc_element *e; + + spin_lock_irq(&device->al_lock); + + list_for_each_entry(e, &device->act_log->to_be_changed, list) + drbd_dax_al_update(device, e); + + lc_committed(device->act_log); + + spin_unlock_irq(&device->al_lock); +} + +int drbd_dax_al_initialize(struct drbd_device *device) +{ + struct al_on_pmem *al_on_pmem = device->ldev->al_on_pmem; + __be32 *slots = al_on_pmem->slots; + /* The activity log area holds the magic followed by the slots. */ + int i, al_slots = device->ldev->md.al_size_4k * (SZ_4K / sizeof(*slots)) - 1; + + al_on_pmem->magic = cpu_to_be32(DRBD_AL_PMEM_MAGIC); + /* + * Initialize all slots rather than just the configured number in case + * the configuration is later changed. + */ + for (i = 0; i < al_slots; i++) { + unsigned int extent_nr = i < device->act_log->nr_elements ? + lc_element_by_index(device->act_log, i)->lc_number : + LC_FREE; + slots[i] = cpu_to_be32(extent_nr); + } + + return 0; +} + +void *drbd_dax_bitmap(struct drbd_device *device, unsigned long want) +{ + struct drbd_backing_dev *bdev = device->ldev; + unsigned char *md_on_pmem = (unsigned char *)bdev->md_on_pmem; + + return md_on_pmem + md_sector_offset_to_bytes(bdev->md.bm_offset); +} diff --git a/drivers/block/drbd/drbd_dax_pmem.h b/drivers/block/drbd/drbd_dax_pmem.h new file mode 100644 index 000000000000..52b116300122 --- /dev/null +++ b/drivers/block/drbd/drbd_dax_pmem.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2017, LINBIT HA-Solutions GmbH. + */ +#ifndef DRBD_DAX_H +#define DRBD_DAX_H + +#include <linux/kconfig.h> + +#if IS_ENABLED(CONFIG_DEV_DAX_PMEM) + +int drbd_dax_open(struct drbd_backing_dev *bdev); +void drbd_dax_close(struct drbd_backing_dev *bdev); +int drbd_dax_map(struct drbd_backing_dev *bdev); +void drbd_dax_al_update(struct drbd_device *device, struct lc_element *al_ext); +void drbd_dax_al_begin_io_commit(struct drbd_device *device); +int drbd_dax_al_initialize(struct drbd_device *device); +void *drbd_dax_bitmap(struct drbd_device *device, unsigned long want); + +static inline bool drbd_md_dax_active(struct drbd_backing_dev *bdev) +{ + return bdev->dax_dev != NULL; +} +static inline struct meta_data_on_disk_9 *drbd_dax_md_addr(struct drbd_backing_dev *bdev) +{ + return bdev->md_on_pmem; +} +#else + +#define drbd_dax_open(B) do { } while (0) +#define drbd_dax_close(B) do { } while (0) +#define drbd_dax_map(B) (-ENOTSUPP) +#define drbd_dax_al_begin_io_commit(D) do { } while (0) +#define drbd_dax_al_initialize(D) (-EIO) +#define drbd_dax_bitmap(D, L) (NULL) +#define drbd_md_dax_active(B) (false) +#define drbd_dax_md_addr(B) (NULL) + +#define arch_wb_cache_pmem(A, L) do { } while (0) + +#endif /* IS_ENABLED(CONFIG_DEV_DAX_PMEM) */ + +#endif /* DRBD_DAX_H */ -- 2.56.0 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v3 06/20] drbd: add DAX/PMEM support for metadata access 2026-11-10 16:26 ` [PATCH v3 06/20] drbd: add DAX/PMEM support for metadata access Christoph Böhmwalder @ 2026-11-10 16:55 ` sashiko-bot 0 siblings, 0 replies; 3+ messages in thread From: sashiko-bot @ 2026-11-10 16:55 UTC (permalink / raw) To: Christoph Böhmwalder; +Cc: nvdimm Thank you for your contribution! Sashiko AI review found 7 potential issues to consider: - [High] drbd: partition offset ignored in drbd_dax_open() and drbd_dax_map() - [High] drbd: sub-page offset and page-count truncation when PAGE_SIZE > 4096 - [High] drbd: missing cache writeback and ordering in drbd_dax_al_initialize() - [High] drbd: __LC_DIRTY never cleared in drbd_dax_al_begin_io_commit() - [High] drbd: link failure and wrong Kconfig dependency on CONFIG_DEV_DAX_PMEM - [Medium] drbd: signed 32-bit overflow of al_slots in drbd_dax_al_initialize() - [Medium] drbd: uncommitted and unlocked act_log read in drbd_dax_al_initialize() -- commit 78fd73a53d10bb7aa94cbf5a0d1278098930c4df Author: Christoph Böhmwalder <christoph.boehmwalder@linbit.com> drbd: add DAX/PMEM support for metadata access This commit adds DAX/PMEM support for DRBD metadata access, allowing the superblock, activity log, and resync bitmap to be mapped and updated directly in persistent memory. > diff --git a/drivers/block/drbd/Makefile b/drivers/block/drbd/Makefile > index 9f05f74e619c1..8ec1ad3604499 100644 > --- a/drivers/block/drbd/Makefile > +++ b/drivers/block/drbd/Makefile > @@ -5,6 +5,7 @@ drbd-y += drbd_main.o drbd_strings.o drbd_nl.o > drbd-y += drbd_interval.o drbd_state.o > drbd-y += drbd_nl_gen.o > drbd-y += drbd_transport.o > +drbd-$(CONFIG_DEV_DAX_PMEM) += drbd_dax_pmem.o > drbd-$(CONFIG_DEBUG_FS) += drbd_debugfs.o > > obj-$(CONFIG_BLK_DEV_DRBD) += drbd.o [Severity: High] Does the build fail to link when CONFIG_BLK_DEV_DRBD=y and CONFIG_DEV_DAX_PMEM=m? In that configuration, this line expands to drbd-m += drbd_dax_pmem.o, which Kbuild ignores when linking built-in drbd.o, while IS_ENABLED(CONFIG_DEV_DAX_PMEM) in drbd_dax_pmem.h still evaluates to true and declares drbd_dax_*() as external functions instead of stubs. In addition, drbd_dax_open() looks up the DAX device via fs_dax_get_by_bdev(), which is gated on CONFIG_FS_DAX rather than CONFIG_DEV_DAX_PMEM (the /dev/daxX.Y character device driver), so drbd_dax_open() always returns -ENODEV if CONFIG_FS_DAX is disabled. > diff --git a/drivers/block/drbd/drbd_dax_pmem.c b/drivers/block/drbd/drbd_dax_pmem.c > new file mode 100644 > index 0000000000000..1fb22e57cf5d0 > --- /dev/null > +++ b/drivers/block/drbd/drbd_dax_pmem.c [ ... ] > +static pgoff_t md_sector_to_pgoff(sector_t sector) > +{ > + return sector >> (PAGE_SHIFT - SECTOR_SHIFT); > +} > + > +static long md_sectors_to_pages(sector_t nr_sectors) > +{ > + return nr_sectors >> (PAGE_SHIFT - SECTOR_SHIFT); > +} > + > +/* Signed, since the bitmap offset relative to the superblock may be negative. */ > +static long md_sector_offset_to_bytes(long sector_offset) > +{ > + return sector_offset * SECTOR_SIZE; > +} > + > +static int map_superblock_for_dax(struct drbd_backing_dev *bdev, struct dax_device *dax_dev) > +{ > + long want = 1; > + pgoff_t pgoff = md_sector_to_pgoff(bdev->md.md_offset); > + void *kaddr; > + long len; > + int id; > + > + id = dax_read_lock(); > + len = dax_direct_access(dax_dev, pgoff, want, DAX_ACCESS, &kaddr, NULL); > + dax_read_unlock(id); > + > + if (len < want) > + return -EIO; > + > + bdev->md_on_pmem = kaddr; > + > + return 0; > +} > + > +/** > + * drbd_dax_open() - Open device for dax and map metadata superblock > + * @bdev: backing device to be opened > + */ > +int drbd_dax_open(struct drbd_backing_dev *bdev) > +{ > + struct dax_device *dax_dev; > + int err; > + u64 part_off; > + > + dax_dev = fs_dax_get_by_bdev(bdev->md_bdev, &part_off, NULL, NULL); > + if (!dax_dev) > + return -ENODEV; > + > + err = map_superblock_for_dax(bdev, dax_dev); > + if (!err) > + bdev->dax_dev = dax_dev; > + else > + put_dax(dax_dev); > + > + return err; > +} > + > +void drbd_dax_close(struct drbd_backing_dev *bdev) > +{ > + put_dax(bdev->dax_dev); > +} > + > +/** > + * drbd_dax_map() - Map metadata for dax > + * @bdev: backing device whose metadata is to be mapped > + */ > +int drbd_dax_map(struct drbd_backing_dev *bdev) > +{ > + struct dax_device *dax_dev = bdev->dax_dev; > + sector_t first_sector = drbd_md_first_sector(bdev); > + sector_t al_sector = bdev->md.md_offset + bdev->md.al_offset; > + long want = md_sectors_to_pages(drbd_md_last_sector(bdev) + 1 - first_sector); > + pgoff_t pgoff = md_sector_to_pgoff(first_sector); > + long md_offset_byte = md_sector_offset_to_bytes(bdev->md.md_offset - first_sector); > + long al_offset_byte = md_sector_offset_to_bytes(al_sector - first_sector); > + void *kaddr; > + long len; > + int id; > + > + id = dax_read_lock(); > + len = dax_direct_access(dax_dev, pgoff, want, DAX_ACCESS, &kaddr, NULL); > + dax_read_unlock(id); > + > + if (len < want) > + return -EIO; > + > + bdev->md_on_pmem = kaddr + md_offset_byte; > + bdev->al_on_pmem = kaddr + al_offset_byte; > + > + return 0; > +} [Severity: High] Does discarding part_off in drbd_dax_open() cause DRBD to read and write metadata at the wrong offset when bdev->md_bdev is a partition? fs_dax_get_by_bdev() returns the dax_device for the whole disk and writes the partition's starting byte offset into part_off. Because map_superblock_for_dax() and drbd_dax_map() pass partition-relative pgoff values to dax_direct_access() without adding part_off, metadata accesses hit the beginning of the parent PMEM disk instead of the partition, corrupting the partition table or earlier partitions. [Severity: High] Can md_sector_to_pgoff() and md_sectors_to_pages() misalign pointers and truncate the mapping when PAGE_SIZE > 4096 (such as on arm64 or powerpc with 64 KiB pages)? DRBD metadata offsets and sizes are 4 KiB-aligned, not PAGE_SIZE-aligned; for example, internal metadata places the 4 KiB superblock in the last 8 sectors of the device. In map_superblock_for_dax(), md_sector_to_pgoff() shifts off the sub-page bits of bdev->md.md_offset, so kaddr points to the start of the page rather than the superblock. In drbd_dax_map(), kaddr also starts at the page boundary pgoff << PAGE_SHIFT, but md_offset_byte and al_offset_byte are calculated relative to first_sector instead of the start of pgoff, and md_sectors_to_pages() right-shifts the sector count down (yielding want == 0 if the metadata region is smaller than a page). [ ... ] > +void drbd_dax_al_begin_io_commit(struct drbd_device *device) > +{ > + struct lc_element *e; > + > + spin_lock_irq(&device->al_lock); > + > + list_for_each_entry(e, &device->act_log->to_be_changed, list) > + drbd_dax_al_update(device, e); > + > + lc_committed(device->act_log); > + > + spin_unlock_irq(&device->al_lock); > +} [Severity: High] Does leaving __LC_DIRTY set in device->act_log->flags after drbd_dax_al_begin_io_commit() cause later activity log locking to hang? When an activity log lookup misses, __lc_get() sets __LC_DIRTY in lc->flags. lc_committed() clears pending_changes, but __LC_DIRTY is only cleared by lc_unlock(). Unlike drbd_al_begin_io_commit(), this function does not acquire drbd_al_try_lock_for_transaction(), call lc_unlock(device->act_log), or wake device->al_wait. Once __LC_DIRTY is set, lc_try_lock(device->act_log) (which requires lc->flags == 0) always fails, so check_al_extents() hangs forever in wait_event(device->al_wait, drbd_al_try_lock(device)) and drbd_try_suspend_al() always fails. > +int drbd_dax_al_initialize(struct drbd_device *device) > +{ > + struct al_on_pmem *al_on_pmem = device->ldev->al_on_pmem; > + __be32 *slots = al_on_pmem->slots; > + /* The activity log area holds the magic followed by the slots. */ > + int i, al_slots = device->ldev->md.al_size_4k * (SZ_4K / sizeof(*slots)) - 1; > + > + al_on_pmem->magic = cpu_to_be32(DRBD_AL_PMEM_MAGIC); > + /* > + * Initialize all slots rather than just the configured number in case > + * the configuration is later changed. > + */ > + for (i = 0; i < al_slots; i++) { > + unsigned int extent_nr = i < device->act_log->nr_elements ? > + lc_element_by_index(device->act_log, i)->lc_number : > + LC_FREE; > + slots[i] = cpu_to_be32(extent_nr); > + } > + > + return 0; > +} [Severity: High] Does drbd_dax_al_initialize() need to flush the initialized activity log with arch_wb_cache_pmem() and write al_on_pmem->magic only after slots[] are persisted? Unlike drbd_dax_al_update(), drbd_dax_al_initialize() never calls arch_wb_cache_pmem(), and it stores DRBD_AL_PMEM_MAGIC before populating slots[]. If a crash or power failure occurs before the slot cache lines are evicted from volatile CPU caches, the activity log on PMEM can have a valid magic header with uninitialized or stale slots. [Severity: Medium] Can al_slots overflow a signed 32-bit int when al_size_4k is larger than 8 GiB? check_activity_log_stripe_size() allows al_size_4k up to 4,194,304 (16 GiB). Multiplying by SZ_4K / sizeof(*slots) (1024) produces up to 0xffffffff, which becomes negative when stored in int al_slots and causes the loop to be skipped without initializing any slots. [Severity: Medium] Can drbd_dax_al_initialize() write stale extent numbers to PMEM when device->act_log->to_be_changed has uncommitted elements? Unlike the non-DAX drbd_al_initialize() path, which holds device->al_lock, writes lc_new_number for entries on to_be_changed, and calls lc_committed(device->act_log), drbd_dax_al_initialize() reads lc_number without holding device->al_lock or committing to_be_changed. Any pending element on to_be_changed has its old lc_number written to PMEM and remains on to_be_changed after the caller unlocks device->act_log. -- Sashiko AI review · https://sashiko.dev/#/patchset/20261110162646.3028472-1-christoph.boehmwalder@linbit.com?part=6 ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-10 16:55 UTC | newest] Thread overview: 3+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-10 16:26 [PATCH v3 00/20] drbd: DRBD 9 rework Christoph Böhmwalder 2026-11-10 16:26 ` [PATCH v3 06/20] drbd: add DAX/PMEM support for metadata access Christoph Böhmwalder 2026-11-10 16:55 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox