diff mbox series

sg's SG_DXFER_TO_FROM_DEV failed to work after kerne 6.1.80

Message ID 8bb45399-de8e-4f2f-afd1-d1098dcdc9d0@gmail.com (mailing list archive)
State New, archived
Headers show
Series sg's SG_DXFER_TO_FROM_DEV failed to work after kerne 6.1.80 | expand

Commit Message

sxzzsf June 21, 2024, 1:17 p.m. UTC
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():


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.
diff mbox series

Patch

--- 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 {