mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
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()
This commit is contained in:
@@ -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)
|
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,
|
/* for bios crossing activity log extent boundaries,
|
||||||
* we may need to activate two extents in one go */
|
* we may need to activate two extents in one go */
|
||||||
unsigned first = i->sector >> (AL_EXTENT_SHIFT-9);
|
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 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;
|
unsigned enr;
|
||||||
|
|
||||||
D_ASSERT(device, first <= last);
|
if (i->partially_in_al_next_enr) {
|
||||||
|
D_ASSERT(device, first < i->partially_in_al_next_enr);
|
||||||
nr_al_extents = 1 + last - first; /* worst case: all touched extends are cold. */
|
D_ASSERT(device, last >= i->partially_in_al_next_enr);
|
||||||
available_update_slots = min(al->nr_elements - al->used,
|
first = i->partially_in_al_next_enr;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
D_ASSERT(device, first <= last);
|
||||||
|
|
||||||
/* Is resync active in this area? */
|
/* Is resync active in this area? */
|
||||||
for (enr = first; enr <= last; enr++) {
|
for (enr = first; enr <= last; enr++) {
|
||||||
struct lc_element *tmp;
|
struct lc_element *tmp;
|
||||||
@@ -529,14 +511,21 @@ int drbd_al_begin_io_nonblock(struct drbd_device *device, struct drbd_interval *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Checkout the refcounts.
|
/* Try to checkout the refcounts. */
|
||||||
* Given that we checked for available elements and update slots above,
|
|
||||||
* this has to be successful. */
|
|
||||||
for (enr = first; enr <= last; enr++) {
|
for (enr = first; enr <= last; enr++) {
|
||||||
struct lc_element *al_ext;
|
struct lc_element *al_ext;
|
||||||
al_ext = lc_get_cumulative(device->act_log, enr);
|
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;
|
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++) {
|
for (enr = first; enr <= last; enr++) {
|
||||||
extent = lc_find(device->act_log, 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);
|
drbd_err(device, "al_complete_io() called on inactive extent %u\n", enr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,15 @@
|
|||||||
struct drbd_interval {
|
struct drbd_interval {
|
||||||
struct rb_node rb;
|
struct rb_node rb;
|
||||||
sector_t sector; /* start sector of the interval */
|
sector_t sector; /* start sector of the interval */
|
||||||
unsigned int size; /* size in bytes */
|
|
||||||
sector_t end; /* highest interval end in subtree */
|
sector_t end; /* highest interval end in subtree */
|
||||||
|
unsigned int size; /* size in bytes */
|
||||||
unsigned int local:1 /* local or remote request? */;
|
unsigned int local:1 /* local or remote request? */;
|
||||||
unsigned int waiting:1; /* someone is waiting for completion */
|
unsigned int waiting:1; /* someone is waiting for completion */
|
||||||
unsigned int completed:1; /* this has been completed already;
|
unsigned int completed:1; /* this has been completed already;
|
||||||
* ignore for conflict detection */
|
* 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)
|
static inline void drbd_clear_interval(struct drbd_interval *i)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <linux/memcontrol.h>
|
#include <linux/memcontrol.h>
|
||||||
#include <linux/mm_inline.h>
|
#include <linux/mm_inline.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
#include <linux/reboot.h>
|
#include <linux/reboot.h>
|
||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
@@ -732,9 +733,9 @@ int drbd_send_sync_param(struct drbd_peer_device *peer_device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (apv >= 88)
|
if (apv >= 88)
|
||||||
strcpy(p->verify_alg, nc->verify_alg);
|
strscpy(p->verify_alg, nc->verify_alg);
|
||||||
if (apv >= 89)
|
if (apv >= 89)
|
||||||
strcpy(p->csums_alg, nc->csums_alg);
|
strscpy(p->csums_alg, nc->csums_alg);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
return drbd_send_command(peer_device, sock, cmd, size, NULL, 0);
|
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 drbd_socket *sock;
|
||||||
struct p_protocol *p;
|
struct p_protocol *p;
|
||||||
struct net_conf *nc;
|
struct net_conf *nc;
|
||||||
|
size_t integrity_alg_len;
|
||||||
int size, cf;
|
int size, cf;
|
||||||
|
|
||||||
sock = &connection->data;
|
sock = &connection->data;
|
||||||
@@ -762,8 +764,10 @@ int __drbd_send_protocol(struct drbd_connection *connection, enum drbd_packet cm
|
|||||||
}
|
}
|
||||||
|
|
||||||
size = sizeof(*p);
|
size = sizeof(*p);
|
||||||
if (connection->agreed_pro_version >= 87)
|
if (connection->agreed_pro_version >= 87) {
|
||||||
size += strlen(nc->integrity_alg) + 1;
|
integrity_alg_len = strlen(nc->integrity_alg) + 1;
|
||||||
|
size += integrity_alg_len;
|
||||||
|
}
|
||||||
|
|
||||||
p->protocol = cpu_to_be32(nc->wire_protocol);
|
p->protocol = cpu_to_be32(nc->wire_protocol);
|
||||||
p->after_sb_0p = cpu_to_be32(nc->after_sb_0p);
|
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);
|
p->conn_flags = cpu_to_be32(cf);
|
||||||
|
|
||||||
if (connection->agreed_pro_version >= 87)
|
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();
|
rcu_read_unlock();
|
||||||
|
|
||||||
return __conn_send_command(connection, sock, cmd, size, NULL, 0);
|
return __conn_send_command(connection, sock, cmd, size, NULL, 0);
|
||||||
|
|||||||
@@ -3801,14 +3801,14 @@ static int receive_SyncParam(struct drbd_connection *connection, struct packet_i
|
|||||||
*new_net_conf = *old_net_conf;
|
*new_net_conf = *old_net_conf;
|
||||||
|
|
||||||
if (verify_tfm) {
|
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;
|
new_net_conf->verify_alg_len = strlen(p->verify_alg) + 1;
|
||||||
crypto_free_shash(peer_device->connection->verify_tfm);
|
crypto_free_shash(peer_device->connection->verify_tfm);
|
||||||
peer_device->connection->verify_tfm = verify_tfm;
|
peer_device->connection->verify_tfm = verify_tfm;
|
||||||
drbd_info(device, "using verify-alg: \"%s\"\n", p->verify_alg);
|
drbd_info(device, "using verify-alg: \"%s\"\n", p->verify_alg);
|
||||||
}
|
}
|
||||||
if (csums_tfm) {
|
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;
|
new_net_conf->csums_alg_len = strlen(p->csums_alg) + 1;
|
||||||
crypto_free_shash(peer_device->connection->csums_tfm);
|
crypto_free_shash(peer_device->connection->csums_tfm);
|
||||||
peer_device->connection->csums_tfm = csums_tfm;
|
peer_device->connection->csums_tfm = csums_tfm;
|
||||||
|
|||||||
@@ -621,7 +621,8 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case READ_COMPLETED_WITH_ERROR:
|
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_report_io_error(device, req);
|
||||||
__drbd_chk_io_error(device, DRBD_READ_ERROR);
|
__drbd_chk_io_error(device, DRBD_READ_ERROR);
|
||||||
fallthrough;
|
fallthrough;
|
||||||
|
|||||||
@@ -542,6 +542,21 @@ out:
|
|||||||
zloop_put_cmd(cmd);
|
zloop_put_cmd(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sync the entire FS containing the zone files instead of walking all files.
|
||||||
|
*/
|
||||||
|
static int zloop_flush(struct zloop_device *zlo)
|
||||||
|
{
|
||||||
|
struct super_block *sb = file_inode(zlo->data_dir)->i_sb;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
down_read(&sb->s_umount);
|
||||||
|
ret = sync_filesystem(sb);
|
||||||
|
up_read(&sb->s_umount);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void zloop_handle_cmd(struct zloop_cmd *cmd)
|
static void zloop_handle_cmd(struct zloop_cmd *cmd)
|
||||||
{
|
{
|
||||||
struct request *rq = blk_mq_rq_from_pdu(cmd);
|
struct request *rq = blk_mq_rq_from_pdu(cmd);
|
||||||
@@ -562,11 +577,7 @@ static void zloop_handle_cmd(struct zloop_cmd *cmd)
|
|||||||
zloop_rw(cmd);
|
zloop_rw(cmd);
|
||||||
return;
|
return;
|
||||||
case REQ_OP_FLUSH:
|
case REQ_OP_FLUSH:
|
||||||
/*
|
cmd->ret = zloop_flush(zlo);
|
||||||
* Sync the entire FS containing the zone files instead of
|
|
||||||
* walking all files
|
|
||||||
*/
|
|
||||||
cmd->ret = sync_filesystem(file_inode(zlo->data_dir)->i_sb);
|
|
||||||
break;
|
break;
|
||||||
case REQ_OP_ZONE_RESET:
|
case REQ_OP_ZONE_RESET:
|
||||||
cmd->ret = zloop_reset_zone(zlo, rq_zone_no(rq));
|
cmd->ret = zloop_reset_zone(zlo, rq_zone_no(rq));
|
||||||
@@ -981,7 +992,8 @@ static int zloop_ctl_add(struct zloop_options *opts)
|
|||||||
struct queue_limits lim = {
|
struct queue_limits lim = {
|
||||||
.max_hw_sectors = SZ_1M >> SECTOR_SHIFT,
|
.max_hw_sectors = SZ_1M >> SECTOR_SHIFT,
|
||||||
.chunk_sectors = opts->zone_size,
|
.chunk_sectors = opts->zone_size,
|
||||||
.features = BLK_FEAT_ZONED,
|
.features = BLK_FEAT_ZONED | BLK_FEAT_WRITE_CACHE,
|
||||||
|
|
||||||
};
|
};
|
||||||
unsigned int nr_zones, i, j;
|
unsigned int nr_zones, i, j;
|
||||||
struct zloop_device *zlo;
|
struct zloop_device *zlo;
|
||||||
@@ -1162,7 +1174,12 @@ static int zloop_ctl_remove(struct zloop_options *opts)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!(opts->mask & ZLOOP_OPT_ID)) {
|
if (!(opts->mask & ZLOOP_OPT_ID)) {
|
||||||
pr_err("No ID specified\n");
|
pr_err("No ID specified for remove\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->mask & ~ZLOOP_OPT_ID) {
|
||||||
|
pr_err("Invalid option specified for remove\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user