summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-06 14:53:25 +0100
committerMark Brown <broonie@kernel.org>2026-07-06 14:53:27 +0100
commitd997703d9c51b7e86a7cce99b3df2f1060a13d56 (patch)
tree0db4e48c5dfb468d223224bc5752d88c3bf78e52
parentda291a8c59ba7e61408d1c3d7109bdf325e0b977 (diff)
parent39dffa2bee1ba8ed84b6befed3678a1040764ff2 (diff)
downloadlinux-next-d997703d9c51b7e86a7cce99b3df2f1060a13d56.tar.gz
linux-next-d997703d9c51b7e86a7cce99b3df2f1060a13d56.zip
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git
-rw-r--r--Documentation/block/biovecs.rst1
-rw-r--r--block/blk-mq.c3
-rw-r--r--block/blk-wbt.c21
-rw-r--r--block/blk-zoned.c42
-rw-r--r--block/blk.h4
-rw-r--r--block/genhd.c2
-rw-r--r--drivers/block/ublk_drv.c13
-rw-r--r--include/linux/bio.h6
-rw-r--r--include/linux/io_uring_types.h2
-rw-r--r--io_uring/io-wq.c5
-rw-r--r--io_uring/io_uring.c2
-rw-r--r--io_uring/memmap.c2
-rw-r--r--io_uring/msg_ring.c34
-rw-r--r--io_uring/sqpoll.c7
-rw-r--r--io_uring/uring_cmd.c4
15 files changed, 117 insertions, 31 deletions
diff --git a/Documentation/block/biovecs.rst b/Documentation/block/biovecs.rst
index 11126ed6f40f..b9dc0c9dbee4 100644
--- a/Documentation/block/biovecs.rst
+++ b/Documentation/block/biovecs.rst
@@ -135,6 +135,7 @@ Usage of helpers:
bio_first_bvec_all()
bio_first_page_all()
bio_first_folio_all()
+ bio_last_bvec_all()
* The following helpers iterate over single-page segment. The passed 'struct
bio_vec' will contain a single-page IO vector during the iteration::
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 88cb5acc4f39..2c850330a32b 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -5218,6 +5218,7 @@ static int blk_hctx_poll(struct request_queue *q, struct blk_mq_hw_ctx *hctx,
struct io_comp_batch *iob, unsigned int flags)
{
int ret;
+ unsigned long timeout = jiffies + 2;
do {
ret = q->mq_ops->poll(hctx, iob);
@@ -5228,7 +5229,7 @@ static int blk_hctx_poll(struct request_queue *q, struct blk_mq_hw_ctx *hctx,
if (ret < 0 || (flags & BLK_POLL_ONESHOT))
break;
cpu_relax();
- } while (!need_resched());
+ } while (!need_resched() && time_before(jiffies, timeout));
return 0;
}
diff --git a/block/blk-wbt.c b/block/blk-wbt.c
index dcc2438ca16d..953d400fd013 100644
--- a/block/blk-wbt.c
+++ b/block/blk-wbt.c
@@ -813,6 +813,21 @@ static void wbt_queue_depth_changed(struct rq_qos *rqos)
wbt_update_limits(RQWB(rqos));
}
+static bool wbt_set_lat_changed(struct request_queue *q, u64 val)
+{
+ struct rq_qos *rqos = wbt_rq_qos(q);
+ struct rq_wb *rwb;
+
+ if (!rqos)
+ return true;
+
+ rwb = RQWB(rqos);
+ if (rwb->min_lat_nsec != val)
+ return true;
+
+ return rwb_enabled(rwb) != !!val;
+}
+
static void wbt_exit(struct rq_qos *rqos)
{
struct rq_wb *rwb = RQWB(rqos);
@@ -1005,8 +1020,12 @@ int wbt_set_lat(struct gendisk *disk, s64 val)
else if (val >= 0)
val *= 1000ULL;
- if (wbt_get_min_lat(q) == val)
+ mutex_lock(&disk->rqos_state_mutex);
+ if (!wbt_set_lat_changed(q, val)) {
+ mutex_unlock(&disk->rqos_state_mutex);
goto out;
+ }
+ mutex_unlock(&disk->rqos_state_mutex);
blk_mq_quiesce_queue(q);
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index bea817f3de56..ca30caec838e 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -1923,11 +1923,20 @@ static int disk_alloc_zone_resources(struct gendisk *disk,
if (!disk->zone_wplugs_pool)
goto free_hash;
- disk->zone_wplugs_wq =
- alloc_workqueue("%s_zwplugs", WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
- pool_size, disk->disk_name);
- if (!disk->zone_wplugs_wq)
- goto destroy_pool;
+ /*
+ * We may already have a zone write plug workqueue as this function may
+ * be called after disk_free_zone_resources(), which does not destroy
+ * the workqueue (the zone write plugs workqueue is destroyed at
+ * disk_release() time).
+ */
+ if (!disk->zone_wplugs_wq) {
+ disk->zone_wplugs_wq =
+ alloc_workqueue("%s_zwplugs",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
+ pool_size, disk->disk_name);
+ if (!disk->zone_wplugs_wq)
+ goto destroy_pool;
+ }
disk->zone_wplugs_worker =
kthread_create(disk_zone_wplugs_worker, disk,
@@ -1935,15 +1944,12 @@ static int disk_alloc_zone_resources(struct gendisk *disk,
if (IS_ERR(disk->zone_wplugs_worker)) {
ret = PTR_ERR(disk->zone_wplugs_worker);
disk->zone_wplugs_worker = NULL;
- goto destroy_wq;
+ goto destroy_pool;
}
wake_up_process(disk->zone_wplugs_worker);
return 0;
-destroy_wq:
- destroy_workqueue(disk->zone_wplugs_wq);
- disk->zone_wplugs_wq = NULL;
destroy_pool:
mempool_destroy(disk->zone_wplugs_pool);
disk->zone_wplugs_pool = NULL;
@@ -1999,7 +2005,7 @@ static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond)
kfree_rcu_mightsleep(zones_cond);
}
-void disk_free_zone_resources(struct gendisk *disk)
+static void disk_free_zone_resources(struct gendisk *disk)
{
if (disk->zone_wplugs_worker) {
kthread_stop(disk->zone_wplugs_worker);
@@ -2007,10 +2013,8 @@ void disk_free_zone_resources(struct gendisk *disk)
}
WARN_ON_ONCE(!list_empty(&disk->zone_wplugs_list));
- if (disk->zone_wplugs_wq) {
- destroy_workqueue(disk->zone_wplugs_wq);
- disk->zone_wplugs_wq = NULL;
- }
+ if (disk->zone_wplugs_wq)
+ drain_workqueue(disk->zone_wplugs_wq);
disk_destroy_zone_wplugs_hash_table(disk);
@@ -2020,6 +2024,16 @@ void disk_free_zone_resources(struct gendisk *disk)
disk->nr_zones = 0;
}
+void disk_release_zone_resources(struct gendisk *disk)
+{
+ if (disk->zone_wplugs_wq) {
+ destroy_workqueue(disk->zone_wplugs_wq);
+ disk->zone_wplugs_wq = NULL;
+ }
+
+ disk_free_zone_resources(disk);
+}
+
struct blk_revalidate_zone_args {
struct gendisk *disk;
u8 *zones_cond;
diff --git a/block/blk.h b/block/blk.h
index 25af8ac5ef0f..fb95d3c58950 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -528,7 +528,7 @@ static inline void ioc_clear_queue(struct request_queue *q)
#ifdef CONFIG_BLK_DEV_ZONED
void disk_init_zone_resources(struct gendisk *disk);
-void disk_free_zone_resources(struct gendisk *disk);
+void disk_release_zone_resources(struct gendisk *disk);
static inline bool bio_zone_write_plugging(struct bio *bio)
{
return bio_flagged(bio, BIO_ZONE_WRITE_PLUGGING);
@@ -581,7 +581,7 @@ int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,
static inline void disk_init_zone_resources(struct gendisk *disk)
{
}
-static inline void disk_free_zone_resources(struct gendisk *disk)
+static inline void disk_release_zone_resources(struct gendisk *disk)
{
}
static inline bool bio_zone_write_plugging(struct bio *bio)
diff --git a/block/genhd.c b/block/genhd.c
index f84b6a355b57..30ac0ffe6517 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1300,7 +1300,7 @@ static void disk_release(struct device *dev)
disk_release_events(disk);
kfree(disk->random);
- disk_free_zone_resources(disk);
+ disk_release_zone_resources(disk);
xa_destroy(&disk->part_tbl);
kobject_put(&disk->queue_kobj);
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 4f6d9e652187..c2c11f2a01e7 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -3584,6 +3584,7 @@ ublk_batch_auto_buf_reg(const struct ublk_batch_io *uc,
#define UBLK_CMD_BATCH_TMP_BUF_SZ (48 * 10)
struct ublk_batch_io_iter {
void __user *uaddr;
+ const u8 *kaddr;
unsigned done, total;
unsigned char elem_bytes;
/* copy to this buffer from user space */
@@ -3632,7 +3633,10 @@ static int ublk_walk_cmd_buf(struct ublk_batch_io_iter *iter,
while (iter->done < iter->total) {
unsigned int len = min(sizeof(iter->buf), iter->total - iter->done);
- if (copy_from_user(iter->buf, iter->uaddr + iter->done, len)) {
+ if (iter->kaddr) {
+ memcpy(iter->buf, iter->kaddr + iter->done, len);
+ } else if (copy_from_user(iter->buf, iter->uaddr + iter->done,
+ len)) {
pr_warn("ublk%d: read batch cmd buffer failed\n",
data->ub->dev_info.dev_id);
return -EFAULT;
@@ -3723,14 +3727,21 @@ static int ublk_handle_batch_prep_cmd(const struct ublk_batch_io_data *data)
.total = uc->nr_elem * uc->elem_bytes,
.elem_bytes = uc->elem_bytes,
};
+ void *cmd_buf;
int ret;
+ cmd_buf = vmemdup_user(iter.uaddr, iter.total);
+ if (IS_ERR(cmd_buf))
+ return PTR_ERR(cmd_buf);
+ iter.kaddr = cmd_buf;
+
mutex_lock(&data->ub->mutex);
ret = ublk_walk_cmd_buf(&iter, data, ublk_batch_prep_io);
if (ret && iter.done)
ublk_batch_revert_prep_cmd(&iter, data);
mutex_unlock(&data->ub->mutex);
+ kvfree(cmd_buf);
return ret;
}
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 8f33f717b14f..3a0e9cb6c2f6 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -256,6 +256,12 @@ static inline struct folio *bio_first_folio_all(struct bio *bio)
return page_folio(bio_first_page_all(bio));
}
+static inline struct bio_vec *bio_last_bvec_all(struct bio *bio)
+{
+ WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED));
+ return &bio->bi_io_vec[bio->bi_vcnt - 1];
+}
+
/**
* struct folio_iter - State for iterating all folios in a bio.
* @folio: The current folio we're iterating. NULL after the last folio.
diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h
index 87151a5b62c1..a2c623a67a25 100644
--- a/include/linux/io_uring_types.h
+++ b/include/linux/io_uring_types.h
@@ -534,6 +534,8 @@ struct io_ring_ctx {
struct io_mapped_region ring_region;
/* used for optimised request parameter and wait argument passing */
struct io_mapped_region param_region;
+
+ struct kcov_common_handle_id kcov_handle;
};
/*
diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
index 2e14880eef92..be8d75731d24 100644
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -19,6 +19,7 @@
#include <linux/mmu_context.h>
#include <linux/sched/sysctl.h>
#include <uapi/linux/io_uring.h>
+#include <linux/kcov.h>
#include "io-wq.h"
#include "slist.h"
@@ -643,13 +644,17 @@ static void io_worker_handle_work(struct io_wq_acct *acct,
unsigned int hash = __io_wq_is_hashed(work_flags)
? __io_get_work_hash(work_flags)
: -1U;
+ struct io_kiocb *req;
next_hashed = wq_next_work(work);
if (do_kill &&
(work_flags & IO_WQ_WORK_UNBOUND))
atomic_or(IO_WQ_WORK_CANCEL, &work->flags);
+ req = container_of(work, struct io_kiocb, work);
+ kcov_remote_start_common(req->ctx->kcov_handle);
io_wq_submit_work(work);
+ kcov_remote_stop();
io_assign_current_work(worker, NULL);
linked = io_wq_free_work(work);
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 1ea2fca34a36..1279e27c2c6d 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -59,6 +59,7 @@
#include <linux/audit.h>
#include <linux/security.h>
#include <linux/jump_label.h>
+#include <linux/kcov.h>
#define CREATE_TRACE_POINTS
#include <trace/events/io_uring.h>
@@ -293,6 +294,7 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
INIT_HLIST_HEAD(&ctx->cancelable_uring_cmd);
io_napi_init(ctx);
mutex_init(&ctx->mmap_lock);
+ ctx->kcov_handle = kcov_common_handle();
return ctx;
diff --git a/io_uring/memmap.c b/io_uring/memmap.c
index da1f6c5d07f8..23e8a85111bc 100644
--- a/io_uring/memmap.c
+++ b/io_uring/memmap.c
@@ -337,7 +337,7 @@ unsigned long io_uring_get_unmapped_area(struct file *filp, unsigned long addr,
ptr = io_uring_validate_mmap_request(filp, pgoff);
if (IS_ERR(ptr))
- return -ENOMEM;
+ return PTR_ERR(ptr);
/*
* Some architectures have strong cache aliasing requirements.
diff --git a/io_uring/msg_ring.c b/io_uring/msg_ring.c
index 3ff9098573db..3067c9343991 100644
--- a/io_uring/msg_ring.c
+++ b/io_uring/msg_ring.c
@@ -93,19 +93,38 @@ static void io_msg_remote_post(struct io_ring_ctx *ctx, struct io_kiocb *req,
io_req_task_work_add_remote(req, IOU_F_TWQ_LAZY_WAKE);
}
+static int io_msg_ring_cqe_flags(struct io_ring_ctx *target_ctx,
+ const struct io_msg *msg, u32 *flags)
+{
+ *flags = 0;
+
+ if (!(msg->flags & IORING_MSG_RING_FLAGS_PASS))
+ return 0;
+
+ *flags = msg->cqe_flags;
+ if ((*flags & IORING_CQE_F_32) &&
+ !(target_ctx->flags & (IORING_SETUP_CQE32 |
+ IORING_SETUP_CQE_MIXED)))
+ return -EINVAL;
+
+ return 0;
+}
+
static int io_msg_data_remote(struct io_ring_ctx *target_ctx,
struct io_msg *msg)
{
struct io_kiocb *target;
- u32 flags = 0;
+ u32 flags;
+ int ret;
- target = kmem_cache_alloc(req_cachep, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO) ;
+ ret = io_msg_ring_cqe_flags(target_ctx, msg, &flags);
+ if (ret)
+ return ret;
+
+ target = kmem_cache_alloc(req_cachep, GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO);
if (unlikely(!target))
return -ENOMEM;
- if (msg->flags & IORING_MSG_RING_FLAGS_PASS)
- flags = msg->cqe_flags;
-
io_msg_remote_post(target_ctx, target, msg->len, flags, msg->user_data);
return 0;
}
@@ -130,8 +149,9 @@ static int __io_msg_ring_data(struct io_ring_ctx *target_ctx,
if (io_msg_need_remote(target_ctx))
return io_msg_data_remote(target_ctx, msg);
- if (msg->flags & IORING_MSG_RING_FLAGS_PASS)
- flags = msg->cqe_flags;
+ ret = io_msg_ring_cqe_flags(target_ctx, msg, &flags);
+ if (ret)
+ return ret;
ret = -EOVERFLOW;
if (target_ctx->flags & IORING_SETUP_IOPOLL) {
diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c
index 2460bd605266..ad42e8eb1002 100644
--- a/io_uring/sqpoll.c
+++ b/io_uring/sqpoll.c
@@ -13,6 +13,7 @@
#include <linux/cpuset.h>
#include <linux/sched/cputime.h>
#include <linux/io_uring.h>
+#include <linux/kcov.h>
#include <uapi/linux/io_uring.h>
@@ -332,10 +333,14 @@ static int io_sq_thread(void *data)
cap_entries = !list_is_singular(&sqd->ctx_list);
list_for_each_entry(ctx, &sqd->ctx_list, sqd_list) {
- int ret = __io_sq_thread(ctx, sqd, cap_entries, &ist);
+ int ret;
+
+ kcov_remote_start_common(ctx->kcov_handle);
+ ret = __io_sq_thread(ctx, sqd, cap_entries, &ist);
if (!sqt_spin && (ret > 0 || !list_empty(&ctx->iopoll_list)))
sqt_spin = true;
+ kcov_remote_stop();
}
if (io_sq_tw(IORING_TW_CAP_ENTRIES_VALUE))
sqt_spin = true;
diff --git a/io_uring/uring_cmd.c b/io_uring/uring_cmd.c
index 7b25dcd9d05f..c14c22cff49e 100644
--- a/io_uring/uring_cmd.c
+++ b/io_uring/uring_cmd.c
@@ -90,7 +90,7 @@ static void io_uring_cmd_del_cancelable(struct io_uring_cmd *cmd,
}
/*
- * Mark this command as concelable, then io_uring_try_cancel_uring_cmd()
+ * Mark this command as cancelable, then io_uring_try_cancel_uring_cmd()
* will try to cancel this issued command by sending ->uring_cmd() with
* issue_flags of IO_URING_F_CANCEL.
*
@@ -168,7 +168,7 @@ void __io_uring_cmd_done(struct io_uring_cmd *ioucmd, s32 ret, u64 res2,
}
io_req_uring_cleanup(req, issue_flags);
if (req->flags & REQ_F_IOPOLL) {
- /* order with io_iopoll_req_issued() checking ->iopoll_complete */
+ /* order with io_do_iopoll() checking ->iopoll_completed */
smp_store_release(&req->iopoll_completed, 1);
} else if (issue_flags & IO_URING_F_COMPLETE_DEFER) {
if (WARN_ON_ONCE(issue_flags & IO_URING_F_UNLOCKED))