Re: [RFC] weird semantics of SG_DXFER_TO_FROM_DEV in BLK_DEV_SKD (drivers/block/skd*)

From: Al Viro
Date: Mon Apr 04 2016 - 13:16:33 EST


On Sun, Apr 03, 2016 at 11:52:20PM -0700, Christoph Hellwig wrote:
> On Mon, Apr 04, 2016 at 04:38:45AM +0100, Al Viro wrote:
> > I've no idea if anything is still using SG_DXFER_TO_FROM_DEV, but this
> > behaviour AFAICS doesn't match that of write() on /dev/sg* (both
> > in and out are done) or normal SG_IO (either both in and out, in case if it
> > hits bio_map_user_iov(), or only out if it hits bio_copy_user_iov()). In
> > all cases the out part is done. Here it is skipped.
> >
> > Not sure who (if anybody) maintains it these days, but that behaviour looks
> > wrong...
>
> The right fix is to kill the duplicate SG_IO implementation and use
> the block layer one. The driver actually is a pretty straight SCSI
> implementation, so making it a block driver has been a mistake from the
> start. I'll see if I can maybe get hold of hardware for the driver - it
> seems pretty much unmaintained unfortunately.

OK... FWIW, that's the last remaining user of iov_shorten(); I have a patch
getting rid of it (preserving the behaviour of that driver), but behaviour
appears to be bogus...

Another fun question: should the normal sg_io() copy the buffer in on
SG_DXFER_TO_FROM_DEV? Right now it doesn't; in !copy case (when it goes
through bio_map_user_iov()) the effect is achieved simply by doing the
read into the pages user has mapped in that area, but bio_copy_user_iov()
doesn't do it:
/*
* success
*/
if (((iter->type & WRITE) && (!map_data || !map_data->null_mapped)) ||
(map_data && map_data->from_user)) {
ret = bio_copy_from_iter(bio, *iter);
if (ret)
goto cleanup;
}
will see NULL map_data; the ->from_user case is sg_start_req() stuff. IOW,
SG_IO behaviour for /dev/sg* is different from the generic one...