Commit 78d964c4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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()
parents 530b0b61 3c461711
Loading
Loading
Loading
Loading
+23 −30
Original line number Diff line number Diff line
@@ -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;
		}
+4 −1
Original line number Diff line number Diff line
@@ -8,12 +8,15 @@
struct drbd_interval {
	struct rb_node rb;
	sector_t sector;		/* start sector of the interval */
	unsigned int size;		/* size in bytes */
	sector_t end;			/* highest interval end in subtree */
	unsigned int size;		/* size in bytes */
	unsigned int local:1		/* local or remote request? */;
	unsigned int waiting:1;		/* someone is waiting for completion */
	unsigned int completed:1;	/* this has been completed already;
					 * ignore for conflict detection */

	/* to resume a partially successful drbd_al_begin_io_nonblock(); */
	unsigned int partially_in_al_next_enr;
};

static inline void drbd_clear_interval(struct drbd_interval *i)
+9 −5
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <linux/memcontrol.h>
#include <linux/mm_inline.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/random.h>
#include <linux/reboot.h>
#include <linux/notifier.h>
@@ -732,9 +733,9 @@ int drbd_send_sync_param(struct drbd_peer_device *peer_device)
	}

	if (apv >= 88)
		strcpy(p->verify_alg, nc->verify_alg);
		strscpy(p->verify_alg, nc->verify_alg);
	if (apv >= 89)
		strcpy(p->csums_alg, nc->csums_alg);
		strscpy(p->csums_alg, nc->csums_alg);
	rcu_read_unlock();

	return drbd_send_command(peer_device, sock, cmd, size, NULL, 0);
@@ -745,6 +746,7 @@ int __drbd_send_protocol(struct drbd_connection *connection, enum drbd_packet cm
	struct drbd_socket *sock;
	struct p_protocol *p;
	struct net_conf *nc;
	size_t integrity_alg_len;
	int size, cf;

	sock = &connection->data;
@@ -762,8 +764,10 @@ int __drbd_send_protocol(struct drbd_connection *connection, enum drbd_packet cm
	}

	size = sizeof(*p);
	if (connection->agreed_pro_version >= 87)
		size += strlen(nc->integrity_alg) + 1;
	if (connection->agreed_pro_version >= 87) {
		integrity_alg_len = strlen(nc->integrity_alg) + 1;
		size += integrity_alg_len;
	}

	p->protocol      = cpu_to_be32(nc->wire_protocol);
	p->after_sb_0p   = cpu_to_be32(nc->after_sb_0p);
@@ -778,7 +782,7 @@ int __drbd_send_protocol(struct drbd_connection *connection, enum drbd_packet cm
	p->conn_flags    = cpu_to_be32(cf);

	if (connection->agreed_pro_version >= 87)
		strcpy(p->integrity_alg, nc->integrity_alg);
		strscpy(p->integrity_alg, nc->integrity_alg, integrity_alg_len);
	rcu_read_unlock();

	return __conn_send_command(connection, sock, cmd, size, NULL, 0);
+2 −2
Original line number Diff line number Diff line
@@ -3801,14 +3801,14 @@ static int receive_SyncParam(struct drbd_connection *connection, struct packet_i
			*new_net_conf = *old_net_conf;

			if (verify_tfm) {
				strcpy(new_net_conf->verify_alg, p->verify_alg);
				strscpy(new_net_conf->verify_alg, p->verify_alg);
				new_net_conf->verify_alg_len = strlen(p->verify_alg) + 1;
				crypto_free_shash(peer_device->connection->verify_tfm);
				peer_device->connection->verify_tfm = verify_tfm;
				drbd_info(device, "using verify-alg: \"%s\"\n", p->verify_alg);
			}
			if (csums_tfm) {
				strcpy(new_net_conf->csums_alg, p->csums_alg);
				strscpy(new_net_conf->csums_alg, p->csums_alg);
				new_net_conf->csums_alg_len = strlen(p->csums_alg) + 1;
				crypto_free_shash(peer_device->connection->csums_tfm);
				peer_device->connection->csums_tfm = csums_tfm;
+2 −1
Original line number Diff line number Diff line
@@ -621,7 +621,8 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
		break;

	case READ_COMPLETED_WITH_ERROR:
		drbd_set_out_of_sync(peer_device, req->i.sector, req->i.size);
		drbd_set_out_of_sync(first_peer_device(device),
				req->i.sector, req->i.size);
		drbd_report_io_error(device, req);
		__drbd_chk_io_error(device, DRBD_READ_ERROR);
		fallthrough;
Loading