From: Yuping Luo <Yuping.Luo@xxxxxxx> 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@xxxxxxx> Signed-off-by: YuPing Luo <yuping.luo@xxxxxxx> Signed-off-by: Barry Song <Baohua.Song@xxxxxxx> Acked-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> Acked-by: Michal Nazarewicz <mina86@xxxxxxxxxx> --- -v2: move common->curlun update to step 1 from step 2; add wrapper check_command_size_in_blocks(); drivers/usb/gadget/f_mass_storage.c | 58 +++++++++++++++++++------------- drivers/usb/gadget/file_storage.c | 62 +++++++++++++++++++++++------------ 2 files changed, 76 insertions(+), 44 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 52583a2..2597b01 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -1871,17 +1871,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; /* @@ -1927,6 +1924,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; @@ -2009,9 +2017,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) @@ -2020,9 +2028,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) @@ -2031,9 +2039,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) @@ -2132,9 +2140,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) @@ -2143,9 +2151,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) @@ -2154,9 +2162,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) @@ -2271,6 +2279,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; } diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 3ac4f51..19520f3 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -2296,19 +2296,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 @@ -2350,6 +2348,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) { @@ -2424,26 +2432,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); @@ -2528,26 +2537,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); @@ -2714,7 +2724,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; } -- 1.7.1 Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom More information can be found at www.csr.com. Follow CSR on Twitter at http://twitter.com/CSR_PLC and read our blog at www.csr.com/blog -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html