Merge tag 'block-6.19-20251218' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux

Pull block fixes from Jens Axboe:

 - ublk selftests for missing coverage

 - two fixes for the block integrity code

 - fix for the newly added newly added PR read keys ioctl, limiting the
   memory that can be allocated

 - work around for a deadlock that can occur with ublk, where partition
   scanning ends up recursing back into file closure, which needs the
   same mutex grabbed. Not the prettiest thing in the world, but an
   acceptable work-around until we can eliminate the reliance on
   disk->open_mutex for this

 - fix for a race between enabling writeback throttling and new IO
   submissions

 - move a bit of bio flag handling code. No changes, but needed for a
   patchset for a future kernel

 - fix for an init time id leak failure in rnbd

 - loop/zloop state check fix

* tag 'block-6.19-20251218' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux:
  block: validate interval_exp integrity limit
  block: validate pi_offset integrity limit
  block: rnbd-clt: Fix leaked ID in init_dev()
  ublk: fix deadlock when reading partition table
  block: add allocation size check in blkdev_pr_read_keys()
  Documentation: admin-guide: blockdev: replace zone_capacity with zone_capacity_mb when creating devices
  zloop: use READ_ONCE() to read lo->lo_state in queue_rq path
  loop: use READ_ONCE() to read lo->lo_state without locking
  block: fix race between wbt_enable_default and IO submission
  selftests: ublk: add user copy test cases
  selftests: ublk: add support for user copy to kublk
  selftests: ublk: forbid multiple data copy modes
  selftests: ublk: don't share backing files between ublk servers
  selftests: ublk: use auto_zc for PER_IO_DAEMON tests in stress_04
  selftests: ublk: fix fio arguments in run_io_and_recover()
  selftests: ublk: remove unused ios map in seq_io.bt
  selftests: ublk: correct last_rw map type in seq_io.bt
  selftests: ublk: fix overflow in ublk_queue_auto_zc_fallback()
  block: move around bio flagging helpers
This commit is contained in:
Linus Torvalds
2025-12-20 09:48:56 -08:00
35 changed files with 450 additions and 91 deletions

View File

@@ -134,7 +134,7 @@ MB and a zone capacity of 63 MB::
$ modprobe zloop $ modprobe zloop
$ mkdir -p /var/local/zloop/0 $ mkdir -p /var/local/zloop/0
$ echo "add capacity_mb=2048,zone_size_mb=64,zone_capacity=63MB" > /dev/zloop-control $ echo "add capacity_mb=2048,zone_size_mb=64,zone_capacity_mb=63" > /dev/zloop-control
For the device created (/dev/zloop0), the zone backing files are all created For the device created (/dev/zloop0), the zone backing files are all created
under the default base directory (/var/local/zloop):: under the default base directory (/var/local/zloop)::

View File

@@ -7181,7 +7181,7 @@ static void bfq_exit_queue(struct elevator_queue *e)
blk_stat_disable_accounting(bfqd->queue); blk_stat_disable_accounting(bfqd->queue);
blk_queue_flag_clear(QUEUE_FLAG_DISABLE_WBT_DEF, bfqd->queue); blk_queue_flag_clear(QUEUE_FLAG_DISABLE_WBT_DEF, bfqd->queue);
set_bit(ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT, &e->flags); wbt_enable_default(bfqd->queue->disk);
kfree(bfqd); kfree(bfqd);
} }

View File

@@ -161,10 +161,9 @@ static int blk_validate_integrity_limits(struct queue_limits *lim)
return -EINVAL; return -EINVAL;
} }
if (bi->pi_tuple_size > bi->metadata_size) { if (bi->pi_offset + bi->pi_tuple_size > bi->metadata_size) {
pr_warn("pi_tuple_size (%u) exceeds metadata_size (%u)\n", pr_warn("pi_offset (%u) + pi_tuple_size (%u) exceeds metadata_size (%u)\n",
bi->pi_tuple_size, bi->pi_offset, bi->pi_tuple_size, bi->metadata_size);
bi->metadata_size);
return -EINVAL; return -EINVAL;
} }
@@ -194,8 +193,13 @@ static int blk_validate_integrity_limits(struct queue_limits *lim)
break; break;
} }
if (!bi->interval_exp) if (!bi->interval_exp) {
bi->interval_exp = ilog2(lim->logical_block_size); bi->interval_exp = ilog2(lim->logical_block_size);
} else if (bi->interval_exp < SECTOR_SHIFT ||
bi->interval_exp > ilog2(lim->logical_block_size)) {
pr_warn("invalid interval_exp %u\n", bi->interval_exp);
return -EINVAL;
}
/* /*
* The PI generation / validation helpers do not expect intervals to * The PI generation / validation helpers do not expect intervals to

View File

@@ -932,7 +932,7 @@ int blk_register_queue(struct gendisk *disk)
elevator_set_default(q); elevator_set_default(q);
blk_queue_flag_set(QUEUE_FLAG_REGISTERED, q); blk_queue_flag_set(QUEUE_FLAG_REGISTERED, q);
wbt_enable_default(disk); wbt_init_enable_default(disk);
/* Now everything is ready and send out KOBJ_ADD uevent */ /* Now everything is ready and send out KOBJ_ADD uevent */
kobject_uevent(&disk->queue_kobj, KOBJ_ADD); kobject_uevent(&disk->queue_kobj, KOBJ_ADD);

View File

@@ -699,7 +699,7 @@ static void wbt_requeue(struct rq_qos *rqos, struct request *rq)
/* /*
* Enable wbt if defaults are configured that way * Enable wbt if defaults are configured that way
*/ */
void wbt_enable_default(struct gendisk *disk) static bool __wbt_enable_default(struct gendisk *disk)
{ {
struct request_queue *q = disk->queue; struct request_queue *q = disk->queue;
struct rq_qos *rqos; struct rq_qos *rqos;
@@ -716,19 +716,31 @@ void wbt_enable_default(struct gendisk *disk)
if (enable && RQWB(rqos)->enable_state == WBT_STATE_OFF_DEFAULT) if (enable && RQWB(rqos)->enable_state == WBT_STATE_OFF_DEFAULT)
RQWB(rqos)->enable_state = WBT_STATE_ON_DEFAULT; RQWB(rqos)->enable_state = WBT_STATE_ON_DEFAULT;
mutex_unlock(&disk->rqos_state_mutex); mutex_unlock(&disk->rqos_state_mutex);
return; return false;
} }
mutex_unlock(&disk->rqos_state_mutex); mutex_unlock(&disk->rqos_state_mutex);
/* Queue not registered? Maybe shutting down... */ /* Queue not registered? Maybe shutting down... */
if (!blk_queue_registered(q)) if (!blk_queue_registered(q))
return; return false;
if (queue_is_mq(q) && enable) if (queue_is_mq(q) && enable)
wbt_init(disk); return true;
return false;
}
void wbt_enable_default(struct gendisk *disk)
{
__wbt_enable_default(disk);
} }
EXPORT_SYMBOL_GPL(wbt_enable_default); EXPORT_SYMBOL_GPL(wbt_enable_default);
void wbt_init_enable_default(struct gendisk *disk)
{
if (__wbt_enable_default(disk))
WARN_ON_ONCE(wbt_init(disk));
}
u64 wbt_default_latency_nsec(struct request_queue *q) u64 wbt_default_latency_nsec(struct request_queue *q)
{ {
/* /*

View File

@@ -5,6 +5,7 @@
#ifdef CONFIG_BLK_WBT #ifdef CONFIG_BLK_WBT
int wbt_init(struct gendisk *disk); int wbt_init(struct gendisk *disk);
void wbt_init_enable_default(struct gendisk *disk);
void wbt_disable_default(struct gendisk *disk); void wbt_disable_default(struct gendisk *disk);
void wbt_enable_default(struct gendisk *disk); void wbt_enable_default(struct gendisk *disk);
@@ -16,6 +17,10 @@ u64 wbt_default_latency_nsec(struct request_queue *);
#else #else
static inline void wbt_init_enable_default(struct gendisk *disk)
{
}
static inline void wbt_disable_default(struct gendisk *disk) static inline void wbt_disable_default(struct gendisk *disk)
{ {
} }

View File

@@ -633,14 +633,10 @@ static int elevator_change_done(struct request_queue *q,
.et = ctx->old->et, .et = ctx->old->et,
.data = ctx->old->elevator_data .data = ctx->old->elevator_data
}; };
bool enable_wbt = test_bit(ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT,
&ctx->old->flags);
elv_unregister_queue(q, ctx->old); elv_unregister_queue(q, ctx->old);
blk_mq_free_sched_res(&res, ctx->old->type, q->tag_set); blk_mq_free_sched_res(&res, ctx->old->type, q->tag_set);
kobject_put(&ctx->old->kobj); kobject_put(&ctx->old->kobj);
if (enable_wbt)
wbt_enable_default(q->disk);
} }
if (ctx->new) { if (ctx->new) {
ret = elv_register_queue(q, ctx->new, !ctx->no_uevent); ret = elv_register_queue(q, ctx->new, !ctx->no_uevent);

View File

@@ -156,7 +156,6 @@ struct elevator_queue
#define ELEVATOR_FLAG_REGISTERED 0 #define ELEVATOR_FLAG_REGISTERED 0
#define ELEVATOR_FLAG_DYING 1 #define ELEVATOR_FLAG_DYING 1
#define ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT 2
/* /*
* block elevator interface * block elevator interface

View File

@@ -442,11 +442,12 @@ static int blkdev_pr_read_keys(struct block_device *bdev, blk_mode_t mode,
if (copy_from_user(&read_keys, arg, sizeof(read_keys))) if (copy_from_user(&read_keys, arg, sizeof(read_keys)))
return -EFAULT; return -EFAULT;
keys_info_len = struct_size(keys_info, keys, read_keys.num_keys); if (read_keys.num_keys > PR_KEYS_MAX)
if (keys_info_len == SIZE_MAX)
return -EINVAL; return -EINVAL;
keys_info = kzalloc(keys_info_len, GFP_KERNEL); keys_info_len = struct_size(keys_info, keys, read_keys.num_keys);
keys_info = kvzalloc(keys_info_len, GFP_KERNEL);
if (!keys_info) if (!keys_info)
return -ENOMEM; return -ENOMEM;
@@ -473,7 +474,7 @@ static int blkdev_pr_read_keys(struct block_device *bdev, blk_mode_t mode,
if (copy_to_user(arg, &read_keys, sizeof(read_keys))) if (copy_to_user(arg, &read_keys, sizeof(read_keys)))
ret = -EFAULT; ret = -EFAULT;
out: out:
kfree(keys_info); kvfree(keys_info);
return ret; return ret;
} }

View File

@@ -1082,7 +1082,7 @@ static int loop_configure(struct loop_device *lo, blk_mode_t mode,
/* Order wrt reading lo_state in loop_validate_file(). */ /* Order wrt reading lo_state in loop_validate_file(). */
wmb(); wmb();
lo->lo_state = Lo_bound; WRITE_ONCE(lo->lo_state, Lo_bound);
if (part_shift) if (part_shift)
lo->lo_flags |= LO_FLAGS_PARTSCAN; lo->lo_flags |= LO_FLAGS_PARTSCAN;
partscan = lo->lo_flags & LO_FLAGS_PARTSCAN; partscan = lo->lo_flags & LO_FLAGS_PARTSCAN;
@@ -1179,7 +1179,7 @@ static void __loop_clr_fd(struct loop_device *lo)
if (!part_shift) if (!part_shift)
set_bit(GD_SUPPRESS_PART_SCAN, &lo->lo_disk->state); set_bit(GD_SUPPRESS_PART_SCAN, &lo->lo_disk->state);
mutex_lock(&lo->lo_mutex); mutex_lock(&lo->lo_mutex);
lo->lo_state = Lo_unbound; WRITE_ONCE(lo->lo_state, Lo_unbound);
mutex_unlock(&lo->lo_mutex); mutex_unlock(&lo->lo_mutex);
/* /*
@@ -1218,7 +1218,7 @@ static int loop_clr_fd(struct loop_device *lo)
lo->lo_flags |= LO_FLAGS_AUTOCLEAR; lo->lo_flags |= LO_FLAGS_AUTOCLEAR;
if (disk_openers(lo->lo_disk) == 1) if (disk_openers(lo->lo_disk) == 1)
lo->lo_state = Lo_rundown; WRITE_ONCE(lo->lo_state, Lo_rundown);
loop_global_unlock(lo, true); loop_global_unlock(lo, true);
return 0; return 0;
@@ -1743,7 +1743,7 @@ static void lo_release(struct gendisk *disk)
mutex_lock(&lo->lo_mutex); mutex_lock(&lo->lo_mutex);
if (lo->lo_state == Lo_bound && (lo->lo_flags & LO_FLAGS_AUTOCLEAR)) if (lo->lo_state == Lo_bound && (lo->lo_flags & LO_FLAGS_AUTOCLEAR))
lo->lo_state = Lo_rundown; WRITE_ONCE(lo->lo_state, Lo_rundown);
need_clear = (lo->lo_state == Lo_rundown); need_clear = (lo->lo_state == Lo_rundown);
mutex_unlock(&lo->lo_mutex); mutex_unlock(&lo->lo_mutex);
@@ -1858,7 +1858,7 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx,
blk_mq_start_request(rq); blk_mq_start_request(rq);
if (lo->lo_state != Lo_bound) if (data_race(READ_ONCE(lo->lo_state)) != Lo_bound)
return BLK_STS_IOERR; return BLK_STS_IOERR;
switch (req_op(rq)) { switch (req_op(rq)) {
@@ -2016,7 +2016,7 @@ static int loop_add(int i)
lo->worker_tree = RB_ROOT; lo->worker_tree = RB_ROOT;
INIT_LIST_HEAD(&lo->idle_worker_list); INIT_LIST_HEAD(&lo->idle_worker_list);
timer_setup(&lo->timer, loop_free_idle_workers_timer, TIMER_DEFERRABLE); timer_setup(&lo->timer, loop_free_idle_workers_timer, TIMER_DEFERRABLE);
lo->lo_state = Lo_unbound; WRITE_ONCE(lo->lo_state, Lo_unbound);
err = mutex_lock_killable(&loop_ctl_mutex); err = mutex_lock_killable(&loop_ctl_mutex);
if (err) if (err)
@@ -2174,7 +2174,7 @@ static int loop_control_remove(int idx)
goto mark_visible; goto mark_visible;
} }
/* Mark this loop device as no more bound, but not quite unbound yet */ /* Mark this loop device as no more bound, but not quite unbound yet */
lo->lo_state = Lo_deleting; WRITE_ONCE(lo->lo_state, Lo_deleting);
mutex_unlock(&lo->lo_mutex); mutex_unlock(&lo->lo_mutex);
loop_remove(lo); loop_remove(lo);
@@ -2197,8 +2197,12 @@ static int loop_control_get_free(int idx)
if (ret) if (ret)
return ret; return ret;
idr_for_each_entry(&loop_index_idr, lo, id) { idr_for_each_entry(&loop_index_idr, lo, id) {
/* Hitting a race results in creating a new loop device which is harmless. */ /*
if (lo->idr_visible && data_race(lo->lo_state) == Lo_unbound) * Hitting a race results in creating a new loop device
* which is harmless.
*/
if (lo->idr_visible &&
data_race(READ_ONCE(lo->lo_state)) == Lo_unbound)
goto found; goto found;
} }
mutex_unlock(&loop_ctl_mutex); mutex_unlock(&loop_ctl_mutex);

View File

@@ -1423,9 +1423,11 @@ static struct rnbd_clt_dev *init_dev(struct rnbd_clt_session *sess,
goto out_alloc; goto out_alloc;
} }
ret = ida_alloc_max(&index_ida, (1 << (MINORBITS - RNBD_PART_BITS)) - 1, dev->clt_device_id = ida_alloc_max(&index_ida,
GFP_KERNEL); (1 << (MINORBITS - RNBD_PART_BITS)) - 1,
if (ret < 0) { GFP_KERNEL);
if (dev->clt_device_id < 0) {
ret = dev->clt_device_id;
pr_err("Failed to initialize device '%s' from session %s, allocating idr failed, err: %d\n", pr_err("Failed to initialize device '%s' from session %s, allocating idr failed, err: %d\n",
pathname, sess->sessname, ret); pathname, sess->sessname, ret);
goto out_queues; goto out_queues;
@@ -1434,10 +1436,9 @@ static struct rnbd_clt_dev *init_dev(struct rnbd_clt_session *sess,
dev->pathname = kstrdup(pathname, GFP_KERNEL); dev->pathname = kstrdup(pathname, GFP_KERNEL);
if (!dev->pathname) { if (!dev->pathname) {
ret = -ENOMEM; ret = -ENOMEM;
goto out_queues; goto out_ida;
} }
dev->clt_device_id = ret;
dev->sess = sess; dev->sess = sess;
dev->access_mode = access_mode; dev->access_mode = access_mode;
dev->nr_poll_queues = nr_poll_queues; dev->nr_poll_queues = nr_poll_queues;
@@ -1453,6 +1454,8 @@ static struct rnbd_clt_dev *init_dev(struct rnbd_clt_session *sess,
return dev; return dev;
out_ida:
ida_free(&index_ida, dev->clt_device_id);
out_queues: out_queues:
kfree(dev->hw_queues); kfree(dev->hw_queues);
out_alloc: out_alloc:

View File

@@ -1080,12 +1080,20 @@ static inline struct ublk_uring_cmd_pdu *ublk_get_uring_cmd_pdu(
return io_uring_cmd_to_pdu(ioucmd, struct ublk_uring_cmd_pdu); return io_uring_cmd_to_pdu(ioucmd, struct ublk_uring_cmd_pdu);
} }
static void ublk_end_request(struct request *req, blk_status_t error)
{
local_bh_disable();
blk_mq_end_request(req, error);
local_bh_enable();
}
/* todo: handle partial completion */ /* todo: handle partial completion */
static inline void __ublk_complete_rq(struct request *req, struct ublk_io *io, static inline void __ublk_complete_rq(struct request *req, struct ublk_io *io,
bool need_map) bool need_map)
{ {
unsigned int unmapped_bytes; unsigned int unmapped_bytes;
blk_status_t res = BLK_STS_OK; blk_status_t res = BLK_STS_OK;
bool requeue;
/* failed read IO if nothing is read */ /* failed read IO if nothing is read */
if (!io->res && req_op(req) == REQ_OP_READ) if (!io->res && req_op(req) == REQ_OP_READ)
@@ -1117,14 +1125,30 @@ static inline void __ublk_complete_rq(struct request *req, struct ublk_io *io,
if (unlikely(unmapped_bytes < io->res)) if (unlikely(unmapped_bytes < io->res))
io->res = unmapped_bytes; io->res = unmapped_bytes;
if (blk_update_request(req, BLK_STS_OK, io->res)) /*
* Run bio->bi_end_io() with softirqs disabled. If the final fput
* happens off this path, then that will prevent ublk's blkdev_release()
* from being called on current's task work, see fput() implementation.
*
* Otherwise, ublk server may not provide forward progress in case of
* reading the partition table from bdev_open() with disk->open_mutex
* held, and causes dead lock as we could already be holding
* disk->open_mutex here.
*
* Preferably we would not be doing IO with a mutex held that is also
* used for release, but this work-around will suffice for now.
*/
local_bh_disable();
requeue = blk_update_request(req, BLK_STS_OK, io->res);
local_bh_enable();
if (requeue)
blk_mq_requeue_request(req, true); blk_mq_requeue_request(req, true);
else if (likely(!blk_should_fake_timeout(req->q))) else if (likely(!blk_should_fake_timeout(req->q)))
__blk_mq_end_request(req, BLK_STS_OK); __blk_mq_end_request(req, BLK_STS_OK);
return; return;
exit: exit:
blk_mq_end_request(req, res); ublk_end_request(req, res);
} }
static struct io_uring_cmd *__ublk_prep_compl_io_cmd(struct ublk_io *io, static struct io_uring_cmd *__ublk_prep_compl_io_cmd(struct ublk_io *io,
@@ -1164,7 +1188,7 @@ static inline void __ublk_abort_rq(struct ublk_queue *ubq,
if (ublk_nosrv_dev_should_queue_io(ubq->dev)) if (ublk_nosrv_dev_should_queue_io(ubq->dev))
blk_mq_requeue_request(rq, false); blk_mq_requeue_request(rq, false);
else else
blk_mq_end_request(rq, BLK_STS_IOERR); ublk_end_request(rq, BLK_STS_IOERR);
} }
static void static void
@@ -1209,7 +1233,7 @@ __ublk_do_auto_buf_reg(const struct ublk_queue *ubq, struct request *req,
ublk_auto_buf_reg_fallback(ubq, req->tag); ublk_auto_buf_reg_fallback(ubq, req->tag);
return AUTO_BUF_REG_FALLBACK; return AUTO_BUF_REG_FALLBACK;
} }
blk_mq_end_request(req, BLK_STS_IOERR); ublk_end_request(req, BLK_STS_IOERR);
return AUTO_BUF_REG_FAIL; return AUTO_BUF_REG_FAIL;
} }

View File

@@ -697,7 +697,7 @@ static blk_status_t zloop_queue_rq(struct blk_mq_hw_ctx *hctx,
struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq); struct zloop_cmd *cmd = blk_mq_rq_to_pdu(rq);
struct zloop_device *zlo = rq->q->queuedata; struct zloop_device *zlo = rq->q->queuedata;
if (zlo->state == Zlo_deleting) if (data_race(READ_ONCE(zlo->state)) == Zlo_deleting)
return BLK_STS_IOERR; return BLK_STS_IOERR;
/* /*
@@ -1002,7 +1002,7 @@ static int zloop_ctl_add(struct zloop_options *opts)
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
zlo->state = Zlo_creating; WRITE_ONCE(zlo->state, Zlo_creating);
ret = mutex_lock_killable(&zloop_ctl_mutex); ret = mutex_lock_killable(&zloop_ctl_mutex);
if (ret) if (ret)
@@ -1113,7 +1113,7 @@ static int zloop_ctl_add(struct zloop_options *opts)
} }
mutex_lock(&zloop_ctl_mutex); mutex_lock(&zloop_ctl_mutex);
zlo->state = Zlo_live; WRITE_ONCE(zlo->state, Zlo_live);
mutex_unlock(&zloop_ctl_mutex); mutex_unlock(&zloop_ctl_mutex);
pr_info("zloop: device %d, %u zones of %llu MiB, %u B block size\n", pr_info("zloop: device %d, %u zones of %llu MiB, %u B block size\n",
@@ -1177,7 +1177,7 @@ static int zloop_ctl_remove(struct zloop_options *opts)
ret = -EINVAL; ret = -EINVAL;
} else { } else {
idr_remove(&zloop_index_idr, zlo->id); idr_remove(&zloop_index_idr, zlo->id);
zlo->state = Zlo_deleting; WRITE_ONCE(zlo->state, Zlo_deleting);
} }
mutex_unlock(&zloop_ctl_mutex); mutex_unlock(&zloop_ctl_mutex);

View File

@@ -46,6 +46,21 @@ static inline unsigned int bio_max_segs(unsigned int nr_segs)
#define bio_data_dir(bio) \ #define bio_data_dir(bio) \
(op_is_write(bio_op(bio)) ? WRITE : READ) (op_is_write(bio_op(bio)) ? WRITE : READ)
static inline bool bio_flagged(const struct bio *bio, unsigned int bit)
{
return bio->bi_flags & (1U << bit);
}
static inline void bio_set_flag(struct bio *bio, unsigned int bit)
{
bio->bi_flags |= (1U << bit);
}
static inline void bio_clear_flag(struct bio *bio, unsigned int bit)
{
bio->bi_flags &= ~(1U << bit);
}
/* /*
* Check whether this bio carries any data or not. A NULL bio is allowed. * Check whether this bio carries any data or not. A NULL bio is allowed.
*/ */
@@ -225,21 +240,6 @@ static inline void bio_cnt_set(struct bio *bio, unsigned int count)
atomic_set(&bio->__bi_cnt, count); atomic_set(&bio->__bi_cnt, count);
} }
static inline bool bio_flagged(struct bio *bio, unsigned int bit)
{
return bio->bi_flags & (1U << bit);
}
static inline void bio_set_flag(struct bio *bio, unsigned int bit)
{
bio->bi_flags |= (1U << bit);
}
static inline void bio_clear_flag(struct bio *bio, unsigned int bit)
{
bio->bi_flags &= ~(1U << bit);
}
static inline struct bio_vec *bio_first_bvec_all(struct bio *bio) static inline struct bio_vec *bio_first_bvec_all(struct bio *bio)
{ {
WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)); WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED));

View File

@@ -79,4 +79,6 @@ struct pr_read_reservation {
#define IOC_PR_READ_KEYS _IOWR('p', 206, struct pr_read_keys) #define IOC_PR_READ_KEYS _IOWR('p', 206, struct pr_read_keys)
#define IOC_PR_READ_RESERVATION _IOR('p', 207, struct pr_read_reservation) #define IOC_PR_READ_RESERVATION _IOR('p', 207, struct pr_read_reservation)
#define PR_KEYS_MAX (1u << 16)
#endif /* _UAPI_PR_H */ #endif /* _UAPI_PR_H */

View File

@@ -21,24 +21,32 @@ TEST_PROGS += test_generic_10.sh
TEST_PROGS += test_generic_11.sh TEST_PROGS += test_generic_11.sh
TEST_PROGS += test_generic_12.sh TEST_PROGS += test_generic_12.sh
TEST_PROGS += test_generic_13.sh TEST_PROGS += test_generic_13.sh
TEST_PROGS += test_generic_14.sh
TEST_PROGS += test_null_01.sh TEST_PROGS += test_null_01.sh
TEST_PROGS += test_null_02.sh TEST_PROGS += test_null_02.sh
TEST_PROGS += test_null_03.sh
TEST_PROGS += test_loop_01.sh TEST_PROGS += test_loop_01.sh
TEST_PROGS += test_loop_02.sh TEST_PROGS += test_loop_02.sh
TEST_PROGS += test_loop_03.sh TEST_PROGS += test_loop_03.sh
TEST_PROGS += test_loop_04.sh TEST_PROGS += test_loop_04.sh
TEST_PROGS += test_loop_05.sh TEST_PROGS += test_loop_05.sh
TEST_PROGS += test_loop_06.sh
TEST_PROGS += test_loop_07.sh
TEST_PROGS += test_stripe_01.sh TEST_PROGS += test_stripe_01.sh
TEST_PROGS += test_stripe_02.sh TEST_PROGS += test_stripe_02.sh
TEST_PROGS += test_stripe_03.sh TEST_PROGS += test_stripe_03.sh
TEST_PROGS += test_stripe_04.sh TEST_PROGS += test_stripe_04.sh
TEST_PROGS += test_stripe_05.sh
TEST_PROGS += test_stripe_06.sh
TEST_PROGS += test_stress_01.sh TEST_PROGS += test_stress_01.sh
TEST_PROGS += test_stress_02.sh TEST_PROGS += test_stress_02.sh
TEST_PROGS += test_stress_03.sh TEST_PROGS += test_stress_03.sh
TEST_PROGS += test_stress_04.sh TEST_PROGS += test_stress_04.sh
TEST_PROGS += test_stress_05.sh TEST_PROGS += test_stress_05.sh
TEST_PROGS += test_stress_06.sh
TEST_PROGS += test_stress_07.sh
TEST_GEN_PROGS_EXTENDED = kublk TEST_GEN_PROGS_EXTENDED = kublk

View File

@@ -34,8 +34,9 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
unsigned zc = ublk_queue_use_zc(q); unsigned zc = ublk_queue_use_zc(q);
unsigned auto_zc = ublk_queue_use_auto_zc(q); unsigned auto_zc = ublk_queue_use_auto_zc(q);
enum io_uring_op op = ublk_to_uring_op(iod, zc | auto_zc); enum io_uring_op op = ublk_to_uring_op(iod, zc | auto_zc);
struct ublk_io *io = ublk_get_io(q, tag);
struct io_uring_sqe *sqe[3]; struct io_uring_sqe *sqe[3];
void *addr = (zc | auto_zc) ? NULL : (void *)iod->addr; void *addr = io->buf_addr;
if (!zc || auto_zc) { if (!zc || auto_zc) {
ublk_io_alloc_sqes(t, sqe, 1); ublk_io_alloc_sqes(t, sqe, 1);
@@ -56,7 +57,7 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
ublk_io_alloc_sqes(t, sqe, 3); ublk_io_alloc_sqes(t, sqe, 3);
io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index); io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, io->buf_index);
sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK; sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK;
sqe[0]->user_data = build_user_data(tag, sqe[0]->user_data = build_user_data(tag,
ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1); ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1);
@@ -68,7 +69,7 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
sqe[1]->flags |= IOSQE_FIXED_FILE | IOSQE_IO_HARDLINK; sqe[1]->flags |= IOSQE_FIXED_FILE | IOSQE_IO_HARDLINK;
sqe[1]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1); sqe[1]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1);
io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index); io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, io->buf_index);
sqe[2]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op), 0, q->q_id, 1); sqe[2]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op), 0, q->q_id, 1);
return 2; return 2;

View File

@@ -596,6 +596,38 @@ static void ublk_set_auto_buf_reg(const struct ublk_queue *q,
sqe->addr = ublk_auto_buf_reg_to_sqe_addr(&buf); sqe->addr = ublk_auto_buf_reg_to_sqe_addr(&buf);
} }
/* Copy in pieces to test the buffer offset logic */
#define UBLK_USER_COPY_LEN 2048
static void ublk_user_copy(const struct ublk_io *io, __u8 match_ublk_op)
{
const struct ublk_queue *q = ublk_io_to_queue(io);
const struct ublksrv_io_desc *iod = ublk_get_iod(q, io->tag);
__u64 off = ublk_user_copy_offset(q->q_id, io->tag);
__u8 ublk_op = ublksrv_get_op(iod);
__u32 len = iod->nr_sectors << 9;
void *addr = io->buf_addr;
if (ublk_op != match_ublk_op)
return;
while (len) {
__u32 copy_len = min(len, UBLK_USER_COPY_LEN);
ssize_t copied;
if (ublk_op == UBLK_IO_OP_WRITE)
copied = pread(q->ublk_fd, addr, copy_len, off);
else if (ublk_op == UBLK_IO_OP_READ)
copied = pwrite(q->ublk_fd, addr, copy_len, off);
else
assert(0);
assert(copied == (ssize_t)copy_len);
addr += copy_len;
off += copy_len;
len -= copy_len;
}
}
int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io) int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io)
{ {
struct ublk_queue *q = ublk_io_to_queue(io); struct ublk_queue *q = ublk_io_to_queue(io);
@@ -618,9 +650,12 @@ int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io)
if (io->flags & UBLKS_IO_NEED_GET_DATA) if (io->flags & UBLKS_IO_NEED_GET_DATA)
cmd_op = UBLK_U_IO_NEED_GET_DATA; cmd_op = UBLK_U_IO_NEED_GET_DATA;
else if (io->flags & UBLKS_IO_NEED_COMMIT_RQ_COMP) else if (io->flags & UBLKS_IO_NEED_COMMIT_RQ_COMP) {
if (ublk_queue_use_user_copy(q))
ublk_user_copy(io, UBLK_IO_OP_READ);
cmd_op = UBLK_U_IO_COMMIT_AND_FETCH_REQ; cmd_op = UBLK_U_IO_COMMIT_AND_FETCH_REQ;
else if (io->flags & UBLKS_IO_NEED_FETCH_RQ) } else if (io->flags & UBLKS_IO_NEED_FETCH_RQ)
cmd_op = UBLK_U_IO_FETCH_REQ; cmd_op = UBLK_U_IO_FETCH_REQ;
if (io_uring_sq_space_left(&t->ring) < 1) if (io_uring_sq_space_left(&t->ring) < 1)
@@ -649,7 +684,7 @@ int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io)
sqe[0]->rw_flags = 0; sqe[0]->rw_flags = 0;
cmd->tag = io->tag; cmd->tag = io->tag;
cmd->q_id = q->q_id; cmd->q_id = q->q_id;
if (!ublk_queue_no_buf(q)) if (!ublk_queue_no_buf(q) && !ublk_queue_use_user_copy(q))
cmd->addr = (__u64) (uintptr_t) io->buf_addr; cmd->addr = (__u64) (uintptr_t) io->buf_addr;
else else
cmd->addr = 0; cmd->addr = 0;
@@ -751,6 +786,10 @@ static void ublk_handle_uring_cmd(struct ublk_thread *t,
if (cqe->res == UBLK_IO_RES_OK) { if (cqe->res == UBLK_IO_RES_OK) {
assert(tag < q->q_depth); assert(tag < q->q_depth);
if (ublk_queue_use_user_copy(q))
ublk_user_copy(io, UBLK_IO_OP_WRITE);
if (q->tgt_ops->queue_io) if (q->tgt_ops->queue_io)
q->tgt_ops->queue_io(t, q, tag); q->tgt_ops->queue_io(t, q, tag);
} else if (cqe->res == UBLK_IO_RES_NEED_GET_DATA) { } else if (cqe->res == UBLK_IO_RES_NEED_GET_DATA) {
@@ -1507,7 +1546,7 @@ static void __cmd_create_help(char *exe, bool recovery)
printf("%s %s -t [null|loop|stripe|fault_inject] [-q nr_queues] [-d depth] [-n dev_id]\n", printf("%s %s -t [null|loop|stripe|fault_inject] [-q nr_queues] [-d depth] [-n dev_id]\n",
exe, recovery ? "recover" : "add"); exe, recovery ? "recover" : "add");
printf("\t[--foreground] [--quiet] [-z] [--auto_zc] [--auto_zc_fallback] [--debug_mask mask] [-r 0|1 ] [-g]\n"); printf("\t[--foreground] [--quiet] [-z] [--auto_zc] [--auto_zc_fallback] [--debug_mask mask] [-r 0|1] [-g] [-u]\n");
printf("\t[-e 0|1 ] [-i 0|1] [--no_ublk_fixed_fd]\n"); printf("\t[-e 0|1 ] [-i 0|1] [--no_ublk_fixed_fd]\n");
printf("\t[--nthreads threads] [--per_io_tasks]\n"); printf("\t[--nthreads threads] [--per_io_tasks]\n");
printf("\t[target options] [backfile1] [backfile2] ...\n"); printf("\t[target options] [backfile1] [backfile2] ...\n");
@@ -1568,6 +1607,7 @@ int main(int argc, char *argv[])
{ "get_data", 1, NULL, 'g'}, { "get_data", 1, NULL, 'g'},
{ "auto_zc", 0, NULL, 0 }, { "auto_zc", 0, NULL, 0 },
{ "auto_zc_fallback", 0, NULL, 0 }, { "auto_zc_fallback", 0, NULL, 0 },
{ "user_copy", 0, NULL, 'u'},
{ "size", 1, NULL, 's'}, { "size", 1, NULL, 's'},
{ "nthreads", 1, NULL, 0 }, { "nthreads", 1, NULL, 0 },
{ "per_io_tasks", 0, NULL, 0 }, { "per_io_tasks", 0, NULL, 0 },
@@ -1593,7 +1633,7 @@ int main(int argc, char *argv[])
opterr = 0; opterr = 0;
optind = 2; optind = 2;
while ((opt = getopt_long(argc, argv, "t:n:d:q:r:e:i:s:gaz", while ((opt = getopt_long(argc, argv, "t:n:d:q:r:e:i:s:gazu",
longopts, &option_idx)) != -1) { longopts, &option_idx)) != -1) {
switch (opt) { switch (opt) {
case 'a': case 'a':
@@ -1613,7 +1653,7 @@ int main(int argc, char *argv[])
ctx.queue_depth = strtol(optarg, NULL, 10); ctx.queue_depth = strtol(optarg, NULL, 10);
break; break;
case 'z': case 'z':
ctx.flags |= UBLK_F_SUPPORT_ZERO_COPY | UBLK_F_USER_COPY; ctx.flags |= UBLK_F_SUPPORT_ZERO_COPY;
break; break;
case 'r': case 'r':
value = strtol(optarg, NULL, 10); value = strtol(optarg, NULL, 10);
@@ -1633,6 +1673,9 @@ int main(int argc, char *argv[])
case 'g': case 'g':
ctx.flags |= UBLK_F_NEED_GET_DATA; ctx.flags |= UBLK_F_NEED_GET_DATA;
break; break;
case 'u':
ctx.flags |= UBLK_F_USER_COPY;
break;
case 's': case 's':
ctx.size = strtoull(optarg, NULL, 10); ctx.size = strtoull(optarg, NULL, 10);
break; break;
@@ -1686,6 +1729,15 @@ int main(int argc, char *argv[])
return -EINVAL; return -EINVAL;
} }
if (!!(ctx.flags & UBLK_F_NEED_GET_DATA) +
!!(ctx.flags & UBLK_F_USER_COPY) +
(ctx.flags & UBLK_F_SUPPORT_ZERO_COPY && !ctx.auto_zc_fallback) +
(ctx.flags & UBLK_F_AUTO_BUF_REG && !ctx.auto_zc_fallback) +
ctx.auto_zc_fallback > 1) {
fprintf(stderr, "too many data copy modes specified\n");
return -EINVAL;
}
i = optind; i = optind;
while (i < argc && ctx.nr_files < MAX_BACK_FILES) { while (i < argc && ctx.nr_files < MAX_BACK_FILES) {
ctx.files[ctx.nr_files++] = argv[i++]; ctx.files[ctx.nr_files++] = argv[i++];

View File

@@ -208,6 +208,12 @@ static inline int ublk_io_auto_zc_fallback(const struct ublksrv_io_desc *iod)
return !!(iod->op_flags & UBLK_IO_F_NEED_REG_BUF); return !!(iod->op_flags & UBLK_IO_F_NEED_REG_BUF);
} }
static inline __u64 ublk_user_copy_offset(unsigned q_id, unsigned tag)
{
return UBLKSRV_IO_BUF_OFFSET +
((__u64)q_id << UBLK_QID_OFF | (__u64)tag << UBLK_TAG_OFF);
}
static inline int is_target_io(__u64 user_data) static inline int is_target_io(__u64 user_data)
{ {
return (user_data & (1ULL << 63)) != 0; return (user_data & (1ULL << 63)) != 0;
@@ -390,19 +396,24 @@ static inline int ublk_completed_tgt_io(struct ublk_thread *t,
return --io->tgt_ios == 0; return --io->tgt_ios == 0;
} }
static inline int ublk_queue_use_zc(const struct ublk_queue *q) static inline bool ublk_queue_use_zc(const struct ublk_queue *q)
{ {
return q->flags & UBLK_F_SUPPORT_ZERO_COPY; return !!(q->flags & UBLK_F_SUPPORT_ZERO_COPY);
} }
static inline int ublk_queue_use_auto_zc(const struct ublk_queue *q) static inline bool ublk_queue_use_auto_zc(const struct ublk_queue *q)
{ {
return q->flags & UBLK_F_AUTO_BUF_REG; return !!(q->flags & UBLK_F_AUTO_BUF_REG);
} }
static inline int ublk_queue_auto_zc_fallback(const struct ublk_queue *q) static inline bool ublk_queue_auto_zc_fallback(const struct ublk_queue *q)
{ {
return q->flags & UBLKS_Q_AUTO_BUF_REG_FALLBACK; return !!(q->flags & UBLKS_Q_AUTO_BUF_REG_FALLBACK);
}
static inline bool ublk_queue_use_user_copy(const struct ublk_queue *q)
{
return !!(q->flags & UBLK_F_USER_COPY);
} }
static inline int ublk_queue_no_buf(const struct ublk_queue *q) static inline int ublk_queue_no_buf(const struct ublk_queue *q)

View File

@@ -134,7 +134,7 @@ static int stripe_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
struct stripe_array *s = alloc_stripe_array(conf, iod); struct stripe_array *s = alloc_stripe_array(conf, iod);
struct ublk_io *io = ublk_get_io(q, tag); struct ublk_io *io = ublk_get_io(q, tag);
int i, extra = zc ? 2 : 0; int i, extra = zc ? 2 : 0;
void *base = (zc | auto_zc) ? NULL : (void *)iod->addr; void *base = io->buf_addr;
io->private_data = s; io->private_data = s;
calculate_stripe_array(conf, iod, s, base); calculate_stripe_array(conf, iod, s, base);

View File

@@ -333,11 +333,12 @@ run_io_and_kill_daemon()
run_io_and_recover() run_io_and_recover()
{ {
local action=$1 local size=$1
local action=$2
local state local state
local dev_id local dev_id
shift 1 shift 2
dev_id=$(_add_ublk_dev "$@") dev_id=$(_add_ublk_dev "$@")
_check_add_dev "$TID" $? _check_add_dev "$TID" $?

View File

@@ -8,7 +8,7 @@ ERR_CODE=0
ublk_run_recover_test() ublk_run_recover_test()
{ {
run_io_and_recover "kill_daemon" "$@" run_io_and_recover 256M "kill_daemon" "$@"
ERR_CODE=$? ERR_CODE=$?
if [ ${ERR_CODE} -ne 0 ]; then if [ ${ERR_CODE} -ne 0 ]; then
echo "$TID failure: $*" echo "$TID failure: $*"

View File

@@ -8,7 +8,7 @@ ERR_CODE=0
ublk_run_recover_test() ublk_run_recover_test()
{ {
run_io_and_recover "kill_daemon" "$@" run_io_and_recover 256M "kill_daemon" "$@"
ERR_CODE=$? ERR_CODE=$?
if [ ${ERR_CODE} -ne 0 ]; then if [ ${ERR_CODE} -ne 0 ]; then
echo "$TID failure: $*" echo "$TID failure: $*"

View File

@@ -8,7 +8,7 @@ ERR_CODE=0
ublk_run_quiesce_recover() ublk_run_quiesce_recover()
{ {
run_io_and_recover "quiesce_dev" "$@" run_io_and_recover 256M "quiesce_dev" "$@"
ERR_CODE=$? ERR_CODE=$?
if [ ${ERR_CODE} -ne 0 ]; then if [ ${ERR_CODE} -ne 0 ]; then
echo "$TID failure: $*" echo "$TID failure: $*"

View File

@@ -0,0 +1,40 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
TID="generic_14"
ERR_CODE=0
ublk_run_recover_test()
{
run_io_and_recover 256M "kill_daemon" "$@"
ERR_CODE=$?
if [ ${ERR_CODE} -ne 0 ]; then
echo "$TID failure: $*"
_show_result $TID $ERR_CODE
fi
}
if ! _have_program fio; then
exit "$UBLK_SKIP_CODE"
fi
_prep_test "recover" "basic recover function verification (user copy)"
_create_backfile 0 256M
_create_backfile 1 128M
_create_backfile 2 128M
ublk_run_recover_test -t null -q 2 -r 1 -u &
ublk_run_recover_test -t loop -q 2 -r 1 -u "${UBLK_BACKFILES[0]}" &
ublk_run_recover_test -t stripe -q 2 -r 1 -u "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
ublk_run_recover_test -t null -q 2 -r 1 -u -i 1 &
ublk_run_recover_test -t loop -q 2 -r 1 -u -i 1 "${UBLK_BACKFILES[0]}" &
ublk_run_recover_test -t stripe -q 2 -r 1 -u -i 1 "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
_cleanup_test "recover"
_show_result $TID $ERR_CODE

View File

@@ -0,0 +1,25 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
TID="loop_06"
ERR_CODE=0
if ! _have_program fio; then
exit "$UBLK_SKIP_CODE"
fi
_prep_test "loop" "write and verify over user copy"
_create_backfile 0 256M
dev_id=$(_add_ublk_dev -t loop -u "${UBLK_BACKFILES[0]}")
_check_add_dev $TID $?
# run fio over the ublk disk
_run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=256M
ERR_CODE=$?
_cleanup_test "loop"
_show_result $TID $ERR_CODE

View File

@@ -0,0 +1,21 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
TID="loop_07"
ERR_CODE=0
_prep_test "loop" "mkfs & mount & umount with user copy"
_create_backfile 0 256M
dev_id=$(_add_ublk_dev -t loop -u "${UBLK_BACKFILES[0]}")
_check_add_dev $TID $?
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
_cleanup_test "loop"
_show_result $TID $ERR_CODE

View File

@@ -0,0 +1,24 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
TID="null_03"
ERR_CODE=0
if ! _have_program fio; then
exit "$UBLK_SKIP_CODE"
fi
_prep_test "null" "basic IO test with user copy"
dev_id=$(_add_ublk_dev -t null -u)
_check_add_dev $TID $?
# run fio over the two disks
fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1
ERR_CODE=$?
_cleanup_test "null"
_show_result $TID $ERR_CODE

View File

@@ -31,21 +31,23 @@ _create_backfile 2 128M
ublk_io_and_kill_daemon 8G -t null -q 4 -z --no_ublk_fixed_fd & ublk_io_and_kill_daemon 8G -t null -q 4 -z --no_ublk_fixed_fd &
ublk_io_and_kill_daemon 256M -t loop -q 4 -z --no_ublk_fixed_fd "${UBLK_BACKFILES[0]}" & ublk_io_and_kill_daemon 256M -t loop -q 4 -z --no_ublk_fixed_fd "${UBLK_BACKFILES[0]}" &
ublk_io_and_kill_daemon 256M -t stripe -q 4 -z "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & ublk_io_and_kill_daemon 256M -t stripe -q 4 -z "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
if _have_feature "AUTO_BUF_REG"; then if _have_feature "AUTO_BUF_REG"; then
ublk_io_and_kill_daemon 8G -t null -q 4 --auto_zc & ublk_io_and_kill_daemon 8G -t null -q 4 --auto_zc &
ublk_io_and_kill_daemon 256M -t loop -q 4 --auto_zc "${UBLK_BACKFILES[0]}" & ublk_io_and_kill_daemon 256M -t loop -q 4 --auto_zc "${UBLK_BACKFILES[0]}" &
ublk_io_and_kill_daemon 256M -t stripe -q 4 --auto_zc --no_ublk_fixed_fd "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & ublk_io_and_kill_daemon 256M -t stripe -q 4 --auto_zc --no_ublk_fixed_fd "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
ublk_io_and_kill_daemon 8G -t null -q 4 -z --auto_zc --auto_zc_fallback & ublk_io_and_kill_daemon 8G -t null -q 4 -z --auto_zc --auto_zc_fallback &
wait
fi fi
if _have_feature "PER_IO_DAEMON"; then if _have_feature "PER_IO_DAEMON"; then
ublk_io_and_kill_daemon 8G -t null -q 4 --nthreads 8 --per_io_tasks & ublk_io_and_kill_daemon 8G -t null -q 4 --auto_zc --nthreads 8 --per_io_tasks &
ublk_io_and_kill_daemon 256M -t loop -q 4 --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[0]}" & ublk_io_and_kill_daemon 256M -t loop -q 4 --auto_zc --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[0]}" &
ublk_io_and_kill_daemon 256M -t stripe -q 4 --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & ublk_io_and_kill_daemon 256M -t stripe -q 4 --auto_zc --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
ublk_io_and_kill_daemon 8G -t null -q 4 --nthreads 8 --per_io_tasks & ublk_io_and_kill_daemon 8G -t null -q 4 -z --auto_zc --auto_zc_fallback --nthreads 8 --per_io_tasks &
wait
fi fi
wait
_cleanup_test "stress" _cleanup_test "stress"
_show_result $TID $ERR_CODE _show_result $TID $ERR_CODE

View File

@@ -58,17 +58,17 @@ done
if _have_feature "ZERO_COPY"; then if _have_feature "ZERO_COPY"; then
for reissue in $(seq 0 1); do for reissue in $(seq 0 1); do
ublk_io_and_remove 8G -t null -q 4 -g -z -r 1 -i "$reissue" & ublk_io_and_remove 8G -t null -q 4 -z -r 1 -i "$reissue" &
ublk_io_and_remove 256M -t loop -q 4 -g -z -r 1 -i "$reissue" "${UBLK_BACKFILES[1]}" & ublk_io_and_remove 256M -t loop -q 4 -z -r 1 -i "$reissue" "${UBLK_BACKFILES[1]}" &
wait wait
done done
fi fi
if _have_feature "AUTO_BUF_REG"; then if _have_feature "AUTO_BUF_REG"; then
for reissue in $(seq 0 1); do for reissue in $(seq 0 1); do
ublk_io_and_remove 8G -t null -q 4 -g --auto_zc -r 1 -i "$reissue" & ublk_io_and_remove 8G -t null -q 4 --auto_zc -r 1 -i "$reissue" &
ublk_io_and_remove 256M -t loop -q 4 -g --auto_zc -r 1 -i "$reissue" "${UBLK_BACKFILES[1]}" & ublk_io_and_remove 256M -t loop -q 4 --auto_zc -r 1 -i "$reissue" "${UBLK_BACKFILES[1]}" &
ublk_io_and_remove 8G -t null -q 4 -g -z --auto_zc --auto_zc_fallback -r 1 -i "$reissue" & ublk_io_and_remove 8G -t null -q 4 -z --auto_zc --auto_zc_fallback -r 1 -i "$reissue" &
wait wait
done done
fi fi

View File

@@ -0,0 +1,39 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
TID="stress_06"
ERR_CODE=0
ublk_io_and_remove()
{
run_io_and_remove "$@"
ERR_CODE=$?
if [ ${ERR_CODE} -ne 0 ]; then
echo "$TID failure: $*"
_show_result $TID $ERR_CODE
fi
}
if ! _have_program fio; then
exit "$UBLK_SKIP_CODE"
fi
_prep_test "stress" "run IO and remove device (user copy)"
_create_backfile 0 256M
_create_backfile 1 128M
_create_backfile 2 128M
ublk_io_and_remove 8G -t null -q 4 -u &
ublk_io_and_remove 256M -t loop -q 4 -u "${UBLK_BACKFILES[0]}" &
ublk_io_and_remove 256M -t stripe -q 4 -u "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
ublk_io_and_remove 8G -t null -q 4 -u --nthreads 8 --per_io_tasks &
ublk_io_and_remove 256M -t loop -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[0]}" &
ublk_io_and_remove 256M -t stripe -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
_cleanup_test "stress"
_show_result $TID $ERR_CODE

View File

@@ -0,0 +1,39 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
TID="stress_07"
ERR_CODE=0
ublk_io_and_kill_daemon()
{
run_io_and_kill_daemon "$@"
ERR_CODE=$?
if [ ${ERR_CODE} -ne 0 ]; then
echo "$TID failure: $*"
_show_result $TID $ERR_CODE
fi
}
if ! _have_program fio; then
exit "$UBLK_SKIP_CODE"
fi
_prep_test "stress" "run IO and kill ublk server (user copy)"
_create_backfile 0 256M
_create_backfile 1 128M
_create_backfile 2 128M
ublk_io_and_kill_daemon 8G -t null -q 4 -u --no_ublk_fixed_fd &
ublk_io_and_kill_daemon 256M -t loop -q 4 -u --no_ublk_fixed_fd "${UBLK_BACKFILES[0]}" &
ublk_io_and_kill_daemon 256M -t stripe -q 4 -u "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
ublk_io_and_kill_daemon 8G -t null -q 4 -u --nthreads 8 --per_io_tasks &
ublk_io_and_kill_daemon 256M -t loop -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[0]}" &
ublk_io_and_kill_daemon 256M -t stripe -q 4 -u --nthreads 8 --per_io_tasks "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" &
wait
_cleanup_test "stress"
_show_result $TID $ERR_CODE

View File

@@ -0,0 +1,26 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
TID="stripe_05"
ERR_CODE=0
if ! _have_program fio; then
exit "$UBLK_SKIP_CODE"
fi
_prep_test "stripe" "write and verify test on user copy"
_create_backfile 0 256M
_create_backfile 1 256M
dev_id=$(_add_ublk_dev -t stripe -q 2 -u "${UBLK_BACKFILES[0]}" "${UBLK_BACKFILES[1]}")
_check_add_dev $TID $?
# run fio over the ublk disk
_run_fio_verify_io --filename=/dev/ublkb"${dev_id}" --size=512M
ERR_CODE=$?
_cleanup_test "stripe"
_show_result $TID $ERR_CODE

View File

@@ -0,0 +1,21 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
. "$(cd "$(dirname "$0")" && pwd)"/test_common.sh
TID="stripe_06"
ERR_CODE=0
_prep_test "stripe" "mkfs & mount & umount on user copy"
_create_backfile 0 256M
_create_backfile 1 256M
dev_id=$(_add_ublk_dev -t stripe -u -q 2 "${UBLK_BACKFILES[0]}" "${UBLK_BACKFILES[1]}")
_check_add_dev $TID $?
_mkfs_mount_test /dev/ublkb"${dev_id}"
ERR_CODE=$?
_cleanup_test "stripe"
_show_result $TID $ERR_CODE

View File

@@ -4,7 +4,7 @@
$3: strlen($2) $3: strlen($2)
*/ */
BEGIN { BEGIN {
@last_rw[$1, str($2)] = 0; @last_rw[$1, str($2)] = (uint64)0;
} }
tracepoint:block:block_rq_complete tracepoint:block:block_rq_complete
{ {
@@ -17,7 +17,6 @@ tracepoint:block:block_rq_complete
} }
@last_rw[$dev, str($2)] = (args.sector + args.nr_sector); @last_rw[$dev, str($2)] = (args.sector + args.nr_sector);
} }
@ios = count();
} }
END { END {