summaryrefslogtreecommitdiff
path: root/drivers/block/drbd/drbd_actlog.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-27 10:42:02 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-27 10:42:02 -0800
commit78d964c47f91c22e9214956316743d2759cd0fd2 (patch)
tree81d6c6fc4397616def29501980024553c8aaf676 /drivers/block/drbd/drbd_actlog.c
parent530b0b61df5a2824250060f2a1ab77d7501a747d (diff)
parent3c4617117a2b7682cf037be5e5533e379707f050 (diff)
downloadlinux-next-78d964c47f91c22e9214956316743d2759cd0fd2.tar.gz
linux-next-78d964c47f91c22e9214956316743d2759cd0fd2.zip
Merge tag 'block-7.0-20260227' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux
Pull block fixes from Jens Axboe: "Two sets of fixes, one for drbd, and one for the zoned loop driver" * tag 'block-7.0-20260227' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux: zloop: check for spurious options passed to remove zloop: advertise a volatile write cache drbd: fix null-pointer dereference on local read error drbd: Replace deprecated strcpy with strscpy drbd: fix "LOGIC BUG" in drbd_al_begin_io_nonblock()
Diffstat (limited to 'drivers/block/drbd/drbd_actlog.c')
-rw-r--r--drivers/block/drbd/drbd_actlog.c53
1 files changed, 23 insertions, 30 deletions
diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c
index 742b2908ff68..b3dbf6c76e98 100644
--- a/drivers/block/drbd/drbd_actlog.c
+++ b/drivers/block/drbd/drbd_actlog.c
@@ -483,38 +483,20 @@ void drbd_al_begin_io(struct drbd_device *device, struct drbd_interval *i)
int drbd_al_begin_io_nonblock(struct drbd_device *device, struct drbd_interval *i)
{
- struct lru_cache *al = device->act_log;
/* for bios crossing activity log extent boundaries,
* we may need to activate two extents in one go */
unsigned first = i->sector >> (AL_EXTENT_SHIFT-9);
unsigned last = i->size == 0 ? first : (i->sector + (i->size >> 9) - 1) >> (AL_EXTENT_SHIFT-9);
- unsigned nr_al_extents;
- unsigned available_update_slots;
unsigned enr;
- D_ASSERT(device, first <= last);
-
- nr_al_extents = 1 + last - first; /* worst case: all touched extends are cold. */
- available_update_slots = min(al->nr_elements - al->used,
- al->max_pending_changes - al->pending_changes);
-
- /* We want all necessary updates for a given request within the same transaction
- * We could first check how many updates are *actually* needed,
- * and use that instead of the worst-case nr_al_extents */
- if (available_update_slots < nr_al_extents) {
- /* Too many activity log extents are currently "hot".
- *
- * If we have accumulated pending changes already,
- * we made progress.
- *
- * If we cannot get even a single pending change through,
- * stop the fast path until we made some progress,
- * or requests to "cold" extents could be starved. */
- if (!al->pending_changes)
- __set_bit(__LC_STARVING, &device->act_log->flags);
- return -ENOBUFS;
+ if (i->partially_in_al_next_enr) {
+ D_ASSERT(device, first < i->partially_in_al_next_enr);
+ D_ASSERT(device, last >= i->partially_in_al_next_enr);
+ first = i->partially_in_al_next_enr;
}
+ D_ASSERT(device, first <= last);
+
/* Is resync active in this area? */
for (enr = first; enr <= last; enr++) {
struct lc_element *tmp;
@@ -529,14 +511,21 @@ int drbd_al_begin_io_nonblock(struct drbd_device *device, struct drbd_interval *
}
}
- /* Checkout the refcounts.
- * Given that we checked for available elements and update slots above,
- * this has to be successful. */
+ /* Try to checkout the refcounts. */
for (enr = first; enr <= last; enr++) {
struct lc_element *al_ext;
al_ext = lc_get_cumulative(device->act_log, enr);
- if (!al_ext)
- drbd_info(device, "LOGIC BUG for enr=%u\n", enr);
+
+ if (!al_ext) {
+ /* Did not work. We may have exhausted the possible
+ * changes per transaction. Or raced with someone
+ * "locking" it against changes.
+ * Remember where to continue from.
+ */
+ if (enr > first)
+ i->partially_in_al_next_enr = enr;
+ return -ENOBUFS;
+ }
}
return 0;
}
@@ -556,7 +545,11 @@ void drbd_al_complete_io(struct drbd_device *device, struct drbd_interval *i)
for (enr = first; enr <= last; enr++) {
extent = lc_find(device->act_log, enr);
- if (!extent) {
+ /* Yes, this masks a bug elsewhere. However, during normal
+ * operation this is harmless, so no need to crash the kernel
+ * by the BUG_ON(refcount == 0) in lc_put().
+ */
+ if (!extent || extent->refcnt == 0) {
drbd_err(device, "al_complete_io() called on inactive extent %u\n", enr);
continue;
}