Commit 144974e7 authored by Yuping Luo's avatar Yuping Luo Committed by Felipe Balbi

usb: gadget: mass_storage: support multi-luns with different logic block size

With Peiyu's patch "gadget: mass_storage: adapt logic block size to bound block
devices" (http://www.spinics.net/lists/linux-usb/msg50791.html), now mass storage
can adjust logic block size dynamically based on real devices.
Then there is one issue caused by it, if two luns have different logic block size,
mass storage can't work.
Let's check the current software flow:
1. get_next_command(): call received_cbw();
2. received_cbw(): update common->lun = cbw->Lun, but common->curlen is not updated;
3. do_scsi_command(): in READ_X and WRITE_X commands, common->data_size_from_cmnd is
updated by common->curlun->blkbits;
4. check_command(): update common->curlun according to common->lun
As you can see, the step 3 uses wrong common->curlun, then wrong common->curlun->blkbits.
If the two luns have same blkbits, there isn't issue. Otherwise, both will fail.
This patch moves the common->curlun update to step 1, then make sure step 3 gets right
blkbits and data_size_from_cmnd.

Cc: Peiyu Li <peiyu.li@csr.com>
Signed-off-by: default avatarYuPing Luo <yuping.luo@csr.com>
Signed-off-by: default avatarBarry Song <Baohua.Song@csr.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Acked-by: default avatarMichal Nazarewicz <mina86@mina86.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent b4fcea2a
...@@ -1873,17 +1873,14 @@ static int check_command(struct fsg_common *common, int cmnd_size, ...@@ -1873,17 +1873,14 @@ static int check_command(struct fsg_common *common, int cmnd_size,
common->lun, lun); common->lun, lun);
/* Check the LUN */ /* Check the LUN */
if (common->lun < common->nluns) { curlun = common->curlun;
curlun = &common->luns[common->lun]; if (curlun) {
common->curlun = curlun;
if (common->cmnd[0] != REQUEST_SENSE) { if (common->cmnd[0] != REQUEST_SENSE) {
curlun->sense_data = SS_NO_SENSE; curlun->sense_data = SS_NO_SENSE;
curlun->sense_data_info = 0; curlun->sense_data_info = 0;
curlun->info_valid = 0; curlun->info_valid = 0;
} }
} else { } else {
common->curlun = NULL;
curlun = NULL;
common->bad_lun_okay = 0; common->bad_lun_okay = 0;
/* /*
...@@ -1929,6 +1926,17 @@ static int check_command(struct fsg_common *common, int cmnd_size, ...@@ -1929,6 +1926,17 @@ static int check_command(struct fsg_common *common, int cmnd_size,
return 0; return 0;
} }
/* wrapper of check_command for data size in blocks handling */
static int check_command_size_in_blocks(struct fsg_common *common,
int cmnd_size, enum data_direction data_dir,
unsigned int mask, int needs_medium, const char *name)
{
if (common->curlun)
common->data_size_from_cmnd <<= common->curlun->blkbits;
return check_command(common, cmnd_size, data_dir,
mask, needs_medium, name);
}
static int do_scsi_command(struct fsg_common *common) static int do_scsi_command(struct fsg_common *common)
{ {
struct fsg_buffhd *bh; struct fsg_buffhd *bh;
...@@ -2011,9 +2019,9 @@ static int do_scsi_command(struct fsg_common *common) ...@@ -2011,9 +2019,9 @@ static int do_scsi_command(struct fsg_common *common)
case READ_6: case READ_6:
i = common->cmnd[4]; i = common->cmnd[4];
common->data_size_from_cmnd = (i == 0 ? 256 : i) << common->data_size_from_cmnd = (i == 0) ? 256 : i;
common->curlun->blkbits; reply = check_command_size_in_blocks(common, 6,
reply = check_command(common, 6, DATA_DIR_TO_HOST, DATA_DIR_TO_HOST,
(7<<1) | (1<<4), 1, (7<<1) | (1<<4), 1,
"READ(6)"); "READ(6)");
if (reply == 0) if (reply == 0)
...@@ -2022,9 +2030,9 @@ static int do_scsi_command(struct fsg_common *common) ...@@ -2022,9 +2030,9 @@ static int do_scsi_command(struct fsg_common *common)
case READ_10: case READ_10:
common->data_size_from_cmnd = common->data_size_from_cmnd =
get_unaligned_be16(&common->cmnd[7]) << get_unaligned_be16(&common->cmnd[7]);
common->curlun->blkbits; reply = check_command_size_in_blocks(common, 10,
reply = check_command(common, 10, DATA_DIR_TO_HOST, DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1, (1<<1) | (0xf<<2) | (3<<7), 1,
"READ(10)"); "READ(10)");
if (reply == 0) if (reply == 0)
...@@ -2033,9 +2041,9 @@ static int do_scsi_command(struct fsg_common *common) ...@@ -2033,9 +2041,9 @@ static int do_scsi_command(struct fsg_common *common)
case READ_12: case READ_12:
common->data_size_from_cmnd = common->data_size_from_cmnd =
get_unaligned_be32(&common->cmnd[6]) << get_unaligned_be32(&common->cmnd[6]);
common->curlun->blkbits; reply = check_command_size_in_blocks(common, 12,
reply = check_command(common, 12, DATA_DIR_TO_HOST, DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1, (1<<1) | (0xf<<2) | (0xf<<6), 1,
"READ(12)"); "READ(12)");
if (reply == 0) if (reply == 0)
...@@ -2134,9 +2142,9 @@ static int do_scsi_command(struct fsg_common *common) ...@@ -2134,9 +2142,9 @@ static int do_scsi_command(struct fsg_common *common)
case WRITE_6: case WRITE_6:
i = common->cmnd[4]; i = common->cmnd[4];
common->data_size_from_cmnd = (i == 0 ? 256 : i) << common->data_size_from_cmnd = (i == 0) ? 256 : i;
common->curlun->blkbits; reply = check_command_size_in_blocks(common, 6,
reply = check_command(common, 6, DATA_DIR_FROM_HOST, DATA_DIR_FROM_HOST,
(7<<1) | (1<<4), 1, (7<<1) | (1<<4), 1,
"WRITE(6)"); "WRITE(6)");
if (reply == 0) if (reply == 0)
...@@ -2145,9 +2153,9 @@ static int do_scsi_command(struct fsg_common *common) ...@@ -2145,9 +2153,9 @@ static int do_scsi_command(struct fsg_common *common)
case WRITE_10: case WRITE_10:
common->data_size_from_cmnd = common->data_size_from_cmnd =
get_unaligned_be16(&common->cmnd[7]) << get_unaligned_be16(&common->cmnd[7]);
common->curlun->blkbits; reply = check_command_size_in_blocks(common, 10,
reply = check_command(common, 10, DATA_DIR_FROM_HOST, DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1, (1<<1) | (0xf<<2) | (3<<7), 1,
"WRITE(10)"); "WRITE(10)");
if (reply == 0) if (reply == 0)
...@@ -2156,9 +2164,9 @@ static int do_scsi_command(struct fsg_common *common) ...@@ -2156,9 +2164,9 @@ static int do_scsi_command(struct fsg_common *common)
case WRITE_12: case WRITE_12:
common->data_size_from_cmnd = common->data_size_from_cmnd =
get_unaligned_be32(&common->cmnd[6]) << get_unaligned_be32(&common->cmnd[6]);
common->curlun->blkbits; reply = check_command_size_in_blocks(common, 12,
reply = check_command(common, 12, DATA_DIR_FROM_HOST, DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1, (1<<1) | (0xf<<2) | (0xf<<6), 1,
"WRITE(12)"); "WRITE(12)");
if (reply == 0) if (reply == 0)
...@@ -2273,6 +2281,10 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) ...@@ -2273,6 +2281,10 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
if (common->data_size == 0) if (common->data_size == 0)
common->data_dir = DATA_DIR_NONE; common->data_dir = DATA_DIR_NONE;
common->lun = cbw->Lun; common->lun = cbw->Lun;
if (common->lun >= 0 && common->lun < common->nluns)
common->curlun = &common->luns[common->lun];
else
common->curlun = NULL;
common->tag = cbw->Tag; common->tag = cbw->Tag;
return 0; return 0;
} }
......
...@@ -2297,19 +2297,17 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, ...@@ -2297,19 +2297,17 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
DBG(fsg, "using LUN %d from CBW, " DBG(fsg, "using LUN %d from CBW, "
"not LUN %d from CDB\n", "not LUN %d from CDB\n",
fsg->lun, lun); fsg->lun, lun);
} else }
fsg->lun = lun; // Use LUN from the command
/* Check the LUN */ /* Check the LUN */
if (fsg->lun < fsg->nluns) { curlun = fsg->curlun;
fsg->curlun = curlun = &fsg->luns[fsg->lun]; if (curlun) {
if (fsg->cmnd[0] != REQUEST_SENSE) { if (fsg->cmnd[0] != REQUEST_SENSE) {
curlun->sense_data = SS_NO_SENSE; curlun->sense_data = SS_NO_SENSE;
curlun->sense_data_info = 0; curlun->sense_data_info = 0;
curlun->info_valid = 0; curlun->info_valid = 0;
} }
} else { } else {
fsg->curlun = curlun = NULL;
fsg->bad_lun_okay = 0; fsg->bad_lun_okay = 0;
/* INQUIRY and REQUEST SENSE commands are explicitly allowed /* INQUIRY and REQUEST SENSE commands are explicitly allowed
...@@ -2351,6 +2349,16 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, ...@@ -2351,6 +2349,16 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
return 0; return 0;
} }
/* wrapper of check_command for data size in blocks handling */
static int check_command_size_in_blocks(struct fsg_dev *fsg, int cmnd_size,
enum data_direction data_dir, unsigned int mask,
int needs_medium, const char *name)
{
if (fsg->curlun)
fsg->data_size_from_cmnd <<= fsg->curlun->blkbits;
return check_command(fsg, cmnd_size, data_dir,
mask, needs_medium, name);
}
static int do_scsi_command(struct fsg_dev *fsg) static int do_scsi_command(struct fsg_dev *fsg)
{ {
...@@ -2425,26 +2433,27 @@ static int do_scsi_command(struct fsg_dev *fsg) ...@@ -2425,26 +2433,27 @@ static int do_scsi_command(struct fsg_dev *fsg)
case READ_6: case READ_6:
i = fsg->cmnd[4]; i = fsg->cmnd[4];
fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits; fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, if ((reply = check_command_size_in_blocks(fsg, 6,
DATA_DIR_TO_HOST,
(7<<1) | (1<<4), 1, (7<<1) | (1<<4), 1,
"READ(6)")) == 0) "READ(6)")) == 0)
reply = do_read(fsg); reply = do_read(fsg);
break; break;
case READ_10: case READ_10:
fsg->data_size_from_cmnd = fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits; if ((reply = check_command_size_in_blocks(fsg, 10,
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1, (1<<1) | (0xf<<2) | (3<<7), 1,
"READ(10)")) == 0) "READ(10)")) == 0)
reply = do_read(fsg); reply = do_read(fsg);
break; break;
case READ_12: case READ_12:
fsg->data_size_from_cmnd = fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits; if ((reply = check_command_size_in_blocks(fsg, 12,
if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1, (1<<1) | (0xf<<2) | (0xf<<6), 1,
"READ(12)")) == 0) "READ(12)")) == 0)
reply = do_read(fsg); reply = do_read(fsg);
...@@ -2529,26 +2538,27 @@ static int do_scsi_command(struct fsg_dev *fsg) ...@@ -2529,26 +2538,27 @@ static int do_scsi_command(struct fsg_dev *fsg)
case WRITE_6: case WRITE_6:
i = fsg->cmnd[4]; i = fsg->cmnd[4];
fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits; fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, if ((reply = check_command_size_in_blocks(fsg, 6,
DATA_DIR_FROM_HOST,
(7<<1) | (1<<4), 1, (7<<1) | (1<<4), 1,
"WRITE(6)")) == 0) "WRITE(6)")) == 0)
reply = do_write(fsg); reply = do_write(fsg);
break; break;
case WRITE_10: case WRITE_10:
fsg->data_size_from_cmnd = fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits; if ((reply = check_command_size_in_blocks(fsg, 10,
if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1, (1<<1) | (0xf<<2) | (3<<7), 1,
"WRITE(10)")) == 0) "WRITE(10)")) == 0)
reply = do_write(fsg); reply = do_write(fsg);
break; break;
case WRITE_12: case WRITE_12:
fsg->data_size_from_cmnd = fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits; if ((reply = check_command_size_in_blocks(fsg, 12,
if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1, (1<<1) | (0xf<<2) | (0xf<<6), 1,
"WRITE(12)")) == 0) "WRITE(12)")) == 0)
reply = do_write(fsg); reply = do_write(fsg);
...@@ -2715,7 +2725,17 @@ static int get_next_command(struct fsg_dev *fsg) ...@@ -2715,7 +2725,17 @@ static int get_next_command(struct fsg_dev *fsg)
memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size); memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size);
fsg->cbbuf_cmnd_size = 0; fsg->cbbuf_cmnd_size = 0;
spin_unlock_irq(&fsg->lock); spin_unlock_irq(&fsg->lock);
/* Use LUN from the command */
fsg->lun = fsg->cmnd[1] >> 5;
} }
/* Update current lun */
if (fsg->lun >= 0 && fsg->lun < fsg->nluns)
fsg->curlun = &fsg->luns[fsg->lun];
else
fsg->curlun = NULL;
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment