Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a4517511 authored by Akinobu Mita's avatar Akinobu Mita Committed by Linus Torvalds
Browse files

scsi_debug: fix do_device_access() with wrap around range



do_device_access() is a function that abstracts copying SG list from/to
ramdisk storage (fake_storep).

It must deal with the ranges exceeding actual fake_storep size, because
such ranges are valid if virtual_gb is set greater than zero, and they
should be treated as fake_storep is repeatedly mirrored up to virtual
size.

Unfortunately, it can't deal with the range which wraps around the end of
fake_storep.  A wrap around range is copied by two
sg_copy_{from,to}_buffer() calls, but sg_copy_{from,to}_buffer() can't
copy from/to in the middle of SG list, therefore the second call can't
copy correctly.

This fixes it by using sg_pcopy_{from,to}_buffer() that can copy from/to
the middle of SG list.

This also simplifies the assignment of sdb->resid in
fill_from_dev_buffer().  Because fill_from_dev_buffer() is now only called
once per command execution cycle.  So it is not necessary to take care to
decrease sdb->resid if fill_from_dev_buffer() is called more than once.

Signed-off-by: default avatarAkinobu Mita <akinobu.mita@gmail.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: "James E.J. Bottomley" <JBottomley@parallels.com>
Cc: Douglas Gilbert <dgilbert@interlog.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Horia Geanta <horia.geanta@freescale.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d0525723
Loading
Loading
Loading
Loading
+37 −11
Original line number Diff line number Diff line
@@ -439,9 +439,6 @@ static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,

	act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents,
				      arr, arr_len);
	if (sdb->resid)
		sdb->resid -= act_len;
	else
	sdb->resid = scsi_bufflen(scp) - act_len;

	return 0;
@@ -1693,24 +1690,48 @@ static int check_device_access_params(struct sdebug_dev_info *devi,
	return 0;
}

/* Returns number of bytes copied or -1 if error. */
static int do_device_access(struct scsi_cmnd *scmd,
			    struct sdebug_dev_info *devi,
			    unsigned long long lba, unsigned int num, int write)
{
	int ret;
	unsigned long long block, rest = 0;
	int (*func)(struct scsi_cmnd *, unsigned char *, int);
	struct scsi_data_buffer *sdb;
	enum dma_data_direction dir;
	size_t (*func)(struct scatterlist *, unsigned int, void *, size_t,
		       off_t);

	if (write) {
		sdb = scsi_out(scmd);
		dir = DMA_TO_DEVICE;
		func = sg_pcopy_to_buffer;
	} else {
		sdb = scsi_in(scmd);
		dir = DMA_FROM_DEVICE;
		func = sg_pcopy_from_buffer;
	}

	func = write ? fetch_to_dev_buffer : fill_from_dev_buffer;
	if (!sdb->length)
		return 0;
	if (!(scsi_bidi_cmnd(scmd) || scmd->sc_data_direction == dir))
		return -1;

	block = do_div(lba, sdebug_store_sectors);
	if (block + num > sdebug_store_sectors)
		rest = block + num - sdebug_store_sectors;

	ret = func(scmd, fake_storep + (block * scsi_debug_sector_size),
	ret = func(sdb->table.sgl, sdb->table.nents,
		   fake_storep + (block * scsi_debug_sector_size),
		   (num - rest) * scsi_debug_sector_size, 0);
	if (ret != (num - rest) * scsi_debug_sector_size)
		return ret;

	if (rest) {
		ret += func(sdb->table.sgl, sdb->table.nents,
			    fake_storep, rest * scsi_debug_sector_size,
			    (num - rest) * scsi_debug_sector_size);
	if (!ret && rest)
		ret = func(scmd, fake_storep, rest * scsi_debug_sector_size);
	}

	return ret;
}
@@ -1849,7 +1870,12 @@ static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba,
	read_lock_irqsave(&atomic_rw, iflags);
	ret = do_device_access(SCpnt, devip, lba, num, 0);
	read_unlock_irqrestore(&atomic_rw, iflags);
	return ret;
	if (ret == -1)
		return DID_ERROR << 16;

	scsi_in(SCpnt)->resid = scsi_bufflen(SCpnt) - ret;

	return 0;
}

void dump_sector(unsigned char *buf, int len)