sg's SG_DXFER_TO_FROM_DEV failed to work after kerne 6.1.80

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.




[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux