linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* SG_DXFER_TO_FROM_DEV does not copy user buffer to driver buffer in linux 2.6.28 and later?
@ 2009-06-29  0:50 zhou sf
  2009-06-29 11:41 ` Douglas Gilbert
  0 siblings, 1 reply; 4+ messages in thread
From: zhou sf @ 2009-06-29  0:50 UTC (permalink / raw)
  To: linux-scsi

Test with the following program, and dump the data buf at queuecommand
of the driver, found the data is something like:
"00 00 00 00 ff 53 4d 42 2e 00 00 00 00 80 01 c0 00 00 00 00 00 00 00
00 00 00 00 00 02 20 4a ..."
While before 2.6.28, it is "ec ec ec ec ..." as expected.


#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <scsi/sg.h>

int main(int argc, char* argv[])
{
       int fd;
       sg_io_hdr_t io_hdr;
       int ret;

       unsigned char sensebuf[32], cdb[16], outbuf[256];

       if ((argc!=2) || (fd = open(argv[1], O_RDWR)) < 0) {
               return -1;
       }

       memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
       memset(cdb, 0, sizeof(cdb));
       memset(outbuf, 0xec, sizeof(outbuf));
       memset(sensebuf, 0xec, sizeof(sensebuf));

       io_hdr.interface_id = 'S';

       io_hdr.cmdp = cdb;
       io_hdr.cmd_len = sizeof(cdb);

       io_hdr.sbp = sensebuf;
       io_hdr.mx_sb_len = sizeof(sensebuf);

       io_hdr.dxferp = outbuf;
       io_hdr.dxfer_len = sizeof(outbuf);
       io_hdr.dxfer_direction = SG_DXFER_TO_FROM_DEV;

       io_hdr.timeout = 20000;

       ret = ioctl(fd, SG_IO, &io_hdr);
       return 0;
}

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: SG_DXFER_TO_FROM_DEV does not copy user buffer to driver buffer in  linux 2.6.28 and later?
  2009-06-29  0:50 SG_DXFER_TO_FROM_DEV does not copy user buffer to driver buffer in linux 2.6.28 and later? zhou sf
@ 2009-06-29 11:41 ` Douglas Gilbert
  2009-06-30  6:45   ` FUJITA Tomonori
  0 siblings, 1 reply; 4+ messages in thread
From: Douglas Gilbert @ 2009-06-29 11:41 UTC (permalink / raw)
  To: zhou sf; +Cc: linux-scsi, FUJITA Tomonori

The semantics of SG_DXFER_TO_FROM_DEV were:
   - copy user space buffer to kernel (LLD) buffer
   - do SCSI command which is assumed to be of the DATA_IN
     (data from device) variety. This would overwrite
     some or all of the kernel buffer
   - copy kernel (LLD) buffer back to the user space.

The idea was to detect short reads by filling the original
user space buffer with some marker bytes ("0xec" it would
seem in this report). The "resid" value is a better way
of detecting short reads but that was only added this century
and requires co-operation from the LLD.

There were major plumbing changes in the sg driver around
lk 2.6.28 and it looks like the above nuance was lost.
Perhaps the block layer does support the above "double
shuffle".


Doug Gilbert
cc-ed to Tomo for comments


zhou sf wrote:
> Test with the following program, and dump the data buf at queuecommand
> of the driver, found the data is something like:
> "00 00 00 00 ff 53 4d 42 2e 00 00 00 00 80 01 c0 00 00 00 00 00 00 00
> 00 00 00 00 00 02 20 4a ..."
> While before 2.6.28, it is "ec ec ec ec ..." as expected.
> 
> 
> #include <string.h>
> #include <fcntl.h>
> #include <sys/ioctl.h>
> #include <scsi/sg.h>
> 
> int main(int argc, char* argv[])
> {
>        int fd;
>        sg_io_hdr_t io_hdr;
>        int ret;
> 
>        unsigned char sensebuf[32], cdb[16], outbuf[256];
> 
>        if ((argc!=2) || (fd = open(argv[1], O_RDWR)) < 0) {
>                return -1;
>        }
> 
>        memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
>        memset(cdb, 0, sizeof(cdb));
>        memset(outbuf, 0xec, sizeof(outbuf));
>        memset(sensebuf, 0xec, sizeof(sensebuf));
> 
>        io_hdr.interface_id = 'S';
> 
>        io_hdr.cmdp = cdb;
>        io_hdr.cmd_len = sizeof(cdb);
> 
>        io_hdr.sbp = sensebuf;
>        io_hdr.mx_sb_len = sizeof(sensebuf);
> 
>        io_hdr.dxferp = outbuf;
>        io_hdr.dxfer_len = sizeof(outbuf);
>        io_hdr.dxfer_direction = SG_DXFER_TO_FROM_DEV;
> 
>        io_hdr.timeout = 20000;
> 
>        ret = ioctl(fd, SG_IO, &io_hdr);
>        return 0;
> }
> --
> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: SG_DXFER_TO_FROM_DEV does not copy user buffer to driver buffer in  linux 2.6.28 and later?
  2009-06-29 11:41 ` Douglas Gilbert
@ 2009-06-30  6:45   ` FUJITA Tomonori
  2009-06-30  7:56     ` zhou sf
  0 siblings, 1 reply; 4+ messages in thread
From: FUJITA Tomonori @ 2009-06-30  6:45 UTC (permalink / raw)
  To: dgilbert; +Cc: sxzzsf, linux-scsi, fujita.tomonori

On Mon, 29 Jun 2009 13:41:20 +0200
Douglas Gilbert <dgilbert@interlog.com> wrote:

> The semantics of SG_DXFER_TO_FROM_DEV were:
>    - copy user space buffer to kernel (LLD) buffer
>    - do SCSI command which is assumed to be of the DATA_IN
>      (data from device) variety. This would overwrite
>      some or all of the kernel buffer
>    - copy kernel (LLD) buffer back to the user space.
> 
> The idea was to detect short reads by filling the original
> user space buffer with some marker bytes ("0xec" it would
> seem in this report). The "resid" value is a better way
> of detecting short reads but that was only added this century
> and requires co-operation from the LLD.
> 
> There were major plumbing changes in the sg driver around
> lk 2.6.28 and it looks like the above nuance was lost.
> Perhaps the block layer does support the above "double
> shuffle".

Sorry, I overlooked SG_DXFER_TO_FROM_DEV.

Does this patch work?


diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 8201387..8df3b6a 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -1658,6 +1658,10 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd)
 		md->nr_entries = req_schp->k_use_sg;
 		md->offset = 0;
 		md->null_mapped = hp->dxferp ? 0 : 1;
+		if (dxfer_dir == SG_DXFER_TO_FROM_DEV)
+			md->from_user = 1;
+		else
+			md->from_user = 0;
 	}
 
 	if (iov_count) {
diff --git a/fs/bio.c b/fs/bio.c
index 24c9140..e467dd8 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -705,14 +705,13 @@ static struct bio_map_data *bio_alloc_map_data(int nr_segs, int iov_count,
 }
 
 static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs,
-			  struct sg_iovec *iov, int iov_count, int uncopy,
-			  int do_free_page)
+			  struct sg_iovec *iov, int iov_count,
+			  int to_user, int from_user, int do_free_page)
 {
 	int ret = 0, i;
 	struct bio_vec *bvec;
 	int iov_idx = 0;
 	unsigned int iov_off = 0;
-	int read = bio_data_dir(bio) == READ;
 
 	__bio_for_each_segment(bvec, bio, i, 0) {
 		char *bv_addr = page_address(bvec->bv_page);
@@ -727,13 +726,14 @@ static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs,
 			iov_addr = iov[iov_idx].iov_base + iov_off;
 
 			if (!ret) {
-				if (!read && !uncopy)
-					ret = copy_from_user(bv_addr, iov_addr,
-							     bytes);
-				if (read && uncopy)
+				if (to_user)
 					ret = copy_to_user(iov_addr, bv_addr,
 							   bytes);
 
+				if (from_user)
+					ret = copy_from_user(bv_addr, iov_addr,
+							     bytes);
+
 				if (ret)
 					ret = -EFAULT;
 			}
@@ -770,7 +770,8 @@ int bio_uncopy_user(struct bio *bio)
 
 	if (!bio_flagged(bio, BIO_NULL_MAPPED))
 		ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs,
-				     bmd->nr_sgvecs, 1, bmd->is_our_pages);
+				     bmd->nr_sgvecs, bio_data_dir(bio) == READ,
+				     0, bmd->is_our_pages);
 	bio_free_map_data(bmd);
 	bio_put(bio);
 	return ret;
@@ -875,8 +876,9 @@ struct bio *bio_copy_user_iov(struct request_queue *q,
 	/*
 	 * success
 	 */
-	if (!write_to_vm && (!map_data || !map_data->null_mapped)) {
-		ret = __bio_copy_iov(bio, bio->bi_io_vec, iov, iov_count, 0, 0);
+	if ((!write_to_vm && (!map_data || !map_data->null_mapped)) ||
+	    (map_data && map_data->from_user)) {
+		ret = __bio_copy_iov(bio, bio->bi_io_vec, iov, iov_count, 0, 1, 0);
 		if (ret)
 			goto cleanup;
 	}
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 8963d91..36c5ff8 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -730,6 +730,7 @@ struct rq_map_data {
 	int nr_entries;
 	unsigned long offset;
 	int null_mapped;
+	int from_user;
 };
 
 struct req_iterator {

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: SG_DXFER_TO_FROM_DEV does not copy user buffer to driver buffer in linux 2.6.28 and later?
  2009-06-30  6:45   ` FUJITA Tomonori
@ 2009-06-30  7:56     ` zhou sf
  0 siblings, 0 replies; 4+ messages in thread
From: zhou sf @ 2009-06-30  7:56 UTC (permalink / raw)
  To: FUJITA Tomonori; +Cc: dgilbert, linux-scsi

It works under 2.6.28.

2009/6/30 FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>:
> On Mon, 29 Jun 2009 13:41:20 +0200
> Douglas Gilbert <dgilbert@interlog.com> wrote:
>
>> The semantics of SG_DXFER_TO_FROM_DEV were:
>>    - copy user space buffer to kernel (LLD) buffer
>>    - do SCSI command which is assumed to be of the DATA_IN
>>      (data from device) variety. This would overwrite
>>      some or all of the kernel buffer
>>    - copy kernel (LLD) buffer back to the user space.
>>
>> The idea was to detect short reads by filling the original
>> user space buffer with some marker bytes ("0xec" it would
>> seem in this report). The "resid" value is a better way
>> of detecting short reads but that was only added this century
>> and requires co-operation from the LLD.
>>
>> There were major plumbing changes in the sg driver around
>> lk 2.6.28 and it looks like the above nuance was lost.
>> Perhaps the block layer does support the above "double
>> shuffle".
>
> Sorry, I overlooked SG_DXFER_TO_FROM_DEV.
>
> Does this patch work?
>
>
> diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
> index 8201387..8df3b6a 100644
> --- a/drivers/scsi/sg.c
> +++ b/drivers/scsi/sg.c
> @@ -1658,6 +1658,10 @@ static int sg_start_req(Sg_request *srp, unsigned char *cmd)
>                md->nr_entries = req_schp->k_use_sg;
>                md->offset = 0;
>                md->null_mapped = hp->dxferp ? 0 : 1;
> +               if (dxfer_dir == SG_DXFER_TO_FROM_DEV)
> +                       md->from_user = 1;
> +               else
> +                       md->from_user = 0;
>        }
>
>        if (iov_count) {
> diff --git a/fs/bio.c b/fs/bio.c
> index 24c9140..e467dd8 100644
> --- a/fs/bio.c
> +++ b/fs/bio.c
> @@ -705,14 +705,13 @@ static struct bio_map_data *bio_alloc_map_data(int nr_segs, int iov_count,
>  }
>
>  static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs,
> -                         struct sg_iovec *iov, int iov_count, int uncopy,
> -                         int do_free_page)
> +                         struct sg_iovec *iov, int iov_count,
> +                         int to_user, int from_user, int do_free_page)
>  {
>        int ret = 0, i;
>        struct bio_vec *bvec;
>        int iov_idx = 0;
>        unsigned int iov_off = 0;
> -       int read = bio_data_dir(bio) == READ;
>
>        __bio_for_each_segment(bvec, bio, i, 0) {
>                char *bv_addr = page_address(bvec->bv_page);
> @@ -727,13 +726,14 @@ static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs,
>                        iov_addr = iov[iov_idx].iov_base + iov_off;
>
>                        if (!ret) {
> -                               if (!read && !uncopy)
> -                                       ret = copy_from_user(bv_addr, iov_addr,
> -                                                            bytes);
> -                               if (read && uncopy)
> +                               if (to_user)
>                                        ret = copy_to_user(iov_addr, bv_addr,
>                                                           bytes);
>
> +                               if (from_user)
> +                                       ret = copy_from_user(bv_addr, iov_addr,
> +                                                            bytes);
> +
>                                if (ret)
>                                        ret = -EFAULT;
>                        }
> @@ -770,7 +770,8 @@ int bio_uncopy_user(struct bio *bio)
>
>        if (!bio_flagged(bio, BIO_NULL_MAPPED))
>                ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs,
> -                                    bmd->nr_sgvecs, 1, bmd->is_our_pages);
> +                                    bmd->nr_sgvecs, bio_data_dir(bio) == READ,
> +                                    0, bmd->is_our_pages);
>        bio_free_map_data(bmd);
>        bio_put(bio);
>        return ret;
> @@ -875,8 +876,9 @@ struct bio *bio_copy_user_iov(struct request_queue *q,
>        /*
>         * success
>         */
> -       if (!write_to_vm && (!map_data || !map_data->null_mapped)) {
> -               ret = __bio_copy_iov(bio, bio->bi_io_vec, iov, iov_count, 0, 0);
> +       if ((!write_to_vm && (!map_data || !map_data->null_mapped)) ||
> +           (map_data && map_data->from_user)) {
> +               ret = __bio_copy_iov(bio, bio->bi_io_vec, iov, iov_count, 0, 1, 0);
>                if (ret)
>                        goto cleanup;
>        }
> diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> index 8963d91..36c5ff8 100644
> --- a/include/linux/blkdev.h
> +++ b/include/linux/blkdev.h
> @@ -730,6 +730,7 @@ struct rq_map_data {
>        int nr_entries;
>        unsigned long offset;
>        int null_mapped;
> +       int from_user;
>  };
>
>  struct req_iterator {
>
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2009-06-30  7:56 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-06-29  0:50 SG_DXFER_TO_FROM_DEV does not copy user buffer to driver buffer in linux 2.6.28 and later? zhou sf
2009-06-29 11:41 ` Douglas Gilbert
2009-06-30  6:45   ` FUJITA Tomonori
2009-06-30  7:56     ` zhou sf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).