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: YuPing Luo <yuping.luo@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
Yuping Luo 2011-10-25 19:13:10 -07:00 committed by Felipe Balbi
parent b4fcea2a71
commit 144974e7f9
2 changed files with 76 additions and 44 deletions

View File

@ -1873,17 +1873,14 @@ static int check_command(struct fsg_common *common, int cmnd_size,
common->lun, lun);
/* Check the LUN */
if (common->lun < common->nluns) {
curlun = &common->luns[common->lun];
common->curlun = curlun;
curlun = common->curlun;
if (curlun) {
if (common->cmnd[0] != REQUEST_SENSE) {
curlun->sense_data = SS_NO_SENSE;
curlun->sense_data_info = 0;
curlun->info_valid = 0;
}
} else {
common->curlun = NULL;
curlun = NULL;
common->bad_lun_okay = 0;
/*
@ -1929,6 +1926,17 @@ static int check_command(struct fsg_common *common, int cmnd_size,
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)
{
struct fsg_buffhd *bh;
@ -2011,9 +2019,9 @@ static int do_scsi_command(struct fsg_common *common)
case READ_6:
i = common->cmnd[4];
common->data_size_from_cmnd = (i == 0 ? 256 : i) <<
common->curlun->blkbits;
reply = check_command(common, 6, DATA_DIR_TO_HOST,
common->data_size_from_cmnd = (i == 0) ? 256 : i;
reply = check_command_size_in_blocks(common, 6,
DATA_DIR_TO_HOST,
(7<<1) | (1<<4), 1,
"READ(6)");
if (reply == 0)
@ -2022,9 +2030,9 @@ static int do_scsi_command(struct fsg_common *common)
case READ_10:
common->data_size_from_cmnd =
get_unaligned_be16(&common->cmnd[7]) <<
common->curlun->blkbits;
reply = check_command(common, 10, DATA_DIR_TO_HOST,
get_unaligned_be16(&common->cmnd[7]);
reply = check_command_size_in_blocks(common, 10,
DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1,
"READ(10)");
if (reply == 0)
@ -2033,9 +2041,9 @@ static int do_scsi_command(struct fsg_common *common)
case READ_12:
common->data_size_from_cmnd =
get_unaligned_be32(&common->cmnd[6]) <<
common->curlun->blkbits;
reply = check_command(common, 12, DATA_DIR_TO_HOST,
get_unaligned_be32(&common->cmnd[6]);
reply = check_command_size_in_blocks(common, 12,
DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1,
"READ(12)");
if (reply == 0)
@ -2134,9 +2142,9 @@ static int do_scsi_command(struct fsg_common *common)
case WRITE_6:
i = common->cmnd[4];
common->data_size_from_cmnd = (i == 0 ? 256 : i) <<
common->curlun->blkbits;
reply = check_command(common, 6, DATA_DIR_FROM_HOST,
common->data_size_from_cmnd = (i == 0) ? 256 : i;
reply = check_command_size_in_blocks(common, 6,
DATA_DIR_FROM_HOST,
(7<<1) | (1<<4), 1,
"WRITE(6)");
if (reply == 0)
@ -2145,9 +2153,9 @@ static int do_scsi_command(struct fsg_common *common)
case WRITE_10:
common->data_size_from_cmnd =
get_unaligned_be16(&common->cmnd[7]) <<
common->curlun->blkbits;
reply = check_command(common, 10, DATA_DIR_FROM_HOST,
get_unaligned_be16(&common->cmnd[7]);
reply = check_command_size_in_blocks(common, 10,
DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1,
"WRITE(10)");
if (reply == 0)
@ -2156,9 +2164,9 @@ static int do_scsi_command(struct fsg_common *common)
case WRITE_12:
common->data_size_from_cmnd =
get_unaligned_be32(&common->cmnd[6]) <<
common->curlun->blkbits;
reply = check_command(common, 12, DATA_DIR_FROM_HOST,
get_unaligned_be32(&common->cmnd[6]);
reply = check_command_size_in_blocks(common, 12,
DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1,
"WRITE(12)");
if (reply == 0)
@ -2273,6 +2281,10 @@ static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
if (common->data_size == 0)
common->data_dir = DATA_DIR_NONE;
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;
return 0;
}

View File

@ -2297,19 +2297,17 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
DBG(fsg, "using LUN %d from CBW, "
"not LUN %d from CDB\n",
fsg->lun, lun);
} else
fsg->lun = lun; // Use LUN from the command
}
/* Check the LUN */
if (fsg->lun < fsg->nluns) {
fsg->curlun = curlun = &fsg->luns[fsg->lun];
curlun = fsg->curlun;
if (curlun) {
if (fsg->cmnd[0] != REQUEST_SENSE) {
curlun->sense_data = SS_NO_SENSE;
curlun->sense_data_info = 0;
curlun->info_valid = 0;
}
} else {
fsg->curlun = curlun = NULL;
fsg->bad_lun_okay = 0;
/* INQUIRY and REQUEST SENSE commands are explicitly allowed
@ -2351,6 +2349,16 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
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)
{
@ -2425,26 +2433,27 @@ static int do_scsi_command(struct fsg_dev *fsg)
case READ_6:
i = fsg->cmnd[4];
fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
if ((reply = check_command_size_in_blocks(fsg, 6,
DATA_DIR_TO_HOST,
(7<<1) | (1<<4), 1,
"READ(6)")) == 0)
reply = do_read(fsg);
break;
case READ_10:
fsg->data_size_from_cmnd =
get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
if ((reply = check_command_size_in_blocks(fsg, 10,
DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1,
"READ(10)")) == 0)
reply = do_read(fsg);
break;
case READ_12:
fsg->data_size_from_cmnd =
get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST,
fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
if ((reply = check_command_size_in_blocks(fsg, 12,
DATA_DIR_TO_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1,
"READ(12)")) == 0)
reply = do_read(fsg);
@ -2529,26 +2538,27 @@ static int do_scsi_command(struct fsg_dev *fsg)
case WRITE_6:
i = fsg->cmnd[4];
fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST,
fsg->data_size_from_cmnd = (i == 0) ? 256 : i;
if ((reply = check_command_size_in_blocks(fsg, 6,
DATA_DIR_FROM_HOST,
(7<<1) | (1<<4), 1,
"WRITE(6)")) == 0)
reply = do_write(fsg);
break;
case WRITE_10:
fsg->data_size_from_cmnd =
get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
fsg->data_size_from_cmnd = get_unaligned_be16(&fsg->cmnd[7]);
if ((reply = check_command_size_in_blocks(fsg, 10,
DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (3<<7), 1,
"WRITE(10)")) == 0)
reply = do_write(fsg);
break;
case WRITE_12:
fsg->data_size_from_cmnd =
get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits;
if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST,
fsg->data_size_from_cmnd = get_unaligned_be32(&fsg->cmnd[6]);
if ((reply = check_command_size_in_blocks(fsg, 12,
DATA_DIR_FROM_HOST,
(1<<1) | (0xf<<2) | (0xf<<6), 1,
"WRITE(12)")) == 0)
reply = do_write(fsg);
@ -2715,7 +2725,17 @@ static int get_next_command(struct fsg_dev *fsg)
memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size);
fsg->cbbuf_cmnd_size = 0;
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;
}