The change of block/blk-map.c in kernel 6.1.80:
static int bio_copy_user_iov()
{
......
} else if (map_data && map_data->from_user) {
struct iov_iter iter2 = *iter;
/* This is the copy-in part of SG_DXFER_TO_FROM_DEV. */
iter2.data_source = ITER_SOURCE;
ret = bio_copy_from_iter(bio, &iter2);
if (ret)
goto cleanup;
} else {
......
}
But it forget to update the iter's count and failed to exit the loop after bio_copy_user_iov() and re-copy to the bio in function blk_rq_map_user_iov():
int blk_rq_map_user_iov()
{
......
do {
if (copy)
ret = bio_copy_user_iov(rq, map_data, &i, gfp_mask);
else
ret = bio_map_user_iov(rq, &i, gfp_mask);
if (ret)
goto unmap_rq;
if (!bio)
bio = rq->bio;
} while (iov_iter_count(&i)); /* <-- ALWAYS FALSE HERE */
......
}
In order to complete the iov map, the iter's count should be updated to iter2'count after bio_copy_from_iter():
--- block/blk-map.c.orig 2024-06-21 08:42:35.662927873 -0400
+++ block/blk-map.c 2024-06-21 08:42:42.450776813 -0400
@@ -218,6 +218,7 @@ static int bio_copy_user_iov(struct requ
ret = bio_copy_from_iter(bio, &iter2);
if (ret)
goto cleanup;
+ iov_iter_truncate(iter, iov_iter_count(&iter2));
} else {
if (bmd->is_our_pages)
zero_fill_bio(bio);
or following patch, save iter's data_source before copying, restore iter's data_source later:
--- block/blk-map.c.orig 2024-06-21 08:42:35.662927873 -0400
+++ block/blk-map.c 2024-06-21 09:05:47.423032619 -0400
@@ -211,11 +211,12 @@
if (ret)
goto cleanup;
} else if (map_data && map_data->from_user) {
- struct iov_iter iter2 = *iter;
+ bool data_source = iter->data_source;
/* This is the copy-in part of SG_DXFER_TO_FROM_DEV. */
- iter2.data_source = ITER_SOURCE;
- ret = bio_copy_from_iter(bio, &iter2);
+ iter->data_source = ITER_SOURCE;
+ ret = bio_copy_from_iter(bio, iter);
+ iter->data_source = data_source;
if (ret)
goto cleanup;
} else {
Verified by (modify lib/sg_pt_linux.c to ignore the bidi limitation):
./src/sg_raw --infile=dat.bin --send=96 --request=96 --outfile=out.bin /dev/sg0 12 00 00 00 64 00
Before patching, it failed as -EINVALID.
After patching, it works as expected.