Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scsi_debug: add Report supported opcodes+tmfs; Compare and write

The Report supported operation codes command is very closely integrated
into the table driven parser and very useful for testing it. Its cdb
masks form the basis of the 'strict' parameter's checks. The Report
supported TMFs command is a simple extension. The Compare and write
command may even be useful, as it should be atomic due to the read-write
lock that the driver uses on its backing store (ram).

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>

authored by

Douglas Gilbert and committed by
Christoph Hellwig
38d5c833 c2248fc9

+307 -19
+307 -19
drivers/scsi/scsi_debug.c
··· 307 307 #define FF_SA (F_SA_HIGH | F_SA_LOW) 308 308 309 309 struct sdebug_dev_info; 310 - static int scsi_debug_queuecommand(struct scsi_cmnd *scp); 311 310 static int resp_inquiry(struct scsi_cmnd *, struct sdebug_dev_info *); 312 311 static int resp_report_luns(struct scsi_cmnd *, struct sdebug_dev_info *); 313 312 static int resp_requests(struct scsi_cmnd *, struct sdebug_dev_info *); ··· 321 322 static int resp_get_lba_status(struct scsi_cmnd *, struct sdebug_dev_info *); 322 323 static int resp_report_tgtpgs(struct scsi_cmnd *, struct sdebug_dev_info *); 323 324 static int resp_unmap(struct scsi_cmnd *, struct sdebug_dev_info *); 325 + static int resp_rsup_opcodes(struct scsi_cmnd *, struct sdebug_dev_info *); 326 + static int resp_rsup_tmfs(struct scsi_cmnd *, struct sdebug_dev_info *); 324 327 static int resp_write_same_10(struct scsi_cmnd *, struct sdebug_dev_info *); 325 328 static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *); 326 329 static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *); 330 + static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *); 327 331 328 332 struct opcode_info_t { 329 333 u8 num_attached; /* 0 if this is it (i.e. a leaf); use 0xff ··· 385 383 }; 386 384 387 385 static const struct opcode_info_t maint_in_iarr[2] = { 388 - {0, 0xa3, 0xc, F_SA_LOW | F_D_IN, NULL, NULL, 386 + {0, 0xa3, 0xc, F_SA_LOW | F_D_IN, resp_rsup_opcodes, NULL, 389 387 {12, 0xc, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 390 388 0xc7, 0, 0, 0, 0} }, 391 - {0, 0xa3, 0xd, F_SA_LOW | F_D_IN, NULL, NULL, 389 + {0, 0xa3, 0xd, F_SA_LOW | F_D_IN, resp_rsup_tmfs, NULL, 392 390 {12, 0xd, 0x80, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 393 391 0, 0} }, 394 392 }; ··· 489 487 {0, 0x35, 0, F_DELAY_OVERR | FF_DIRECT_IO, NULL, NULL, /* SYNC_CACHE */ 490 488 {10, 0x7, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0, 491 489 0, 0, 0, 0} }, 492 - {0, 0x89, 0, F_D_OUT | FF_DIRECT_IO, NULL, NULL, 490 + {0, 0x89, 0, F_D_OUT | FF_DIRECT_IO, resp_comp_write, NULL, 493 491 {16, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 494 492 0, 0xff, 0x1f, 0xc7} }, /* COMPARE AND WRITE */ 495 493 ··· 1605 1603 return ret; 1606 1604 } 1607 1605 1606 + static int 1607 + resp_rsup_opcodes(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) 1608 + { 1609 + bool rctd; 1610 + u8 reporting_opts, req_opcode, sdeb_i, supp; 1611 + u16 req_sa, u; 1612 + u32 alloc_len, a_len; 1613 + int k, offset, len, errsts, count, bump, na; 1614 + const struct opcode_info_t *oip; 1615 + const struct opcode_info_t *r_oip; 1616 + u8 *arr; 1617 + u8 *cmd = scp->cmnd; 1618 + 1619 + rctd = !!(cmd[2] & 0x80); 1620 + reporting_opts = cmd[2] & 0x7; 1621 + req_opcode = cmd[3]; 1622 + req_sa = get_unaligned_be16(cmd + 4); 1623 + alloc_len = get_unaligned_be32(cmd + 6); 1624 + if (alloc_len < 4 && alloc_len > 0xffff) { 1625 + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 6, -1); 1626 + return check_condition_result; 1627 + } 1628 + if (alloc_len > 8192) 1629 + a_len = 8192; 1630 + else 1631 + a_len = alloc_len; 1632 + arr = kzalloc((a_len < 256) ? 320 : a_len + 64, GFP_KERNEL); 1633 + if (NULL == arr) { 1634 + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, 1635 + INSUFF_RES_ASCQ); 1636 + return check_condition_result; 1637 + } 1638 + switch (reporting_opts) { 1639 + case 0: /* all commands */ 1640 + /* count number of commands */ 1641 + for (count = 0, oip = opcode_info_arr; 1642 + oip->num_attached != 0xff; ++oip) { 1643 + if (F_INV_OP & oip->flags) 1644 + continue; 1645 + count += (oip->num_attached + 1); 1646 + } 1647 + bump = rctd ? 20 : 8; 1648 + put_unaligned_be32(count * bump, arr); 1649 + for (offset = 4, oip = opcode_info_arr; 1650 + oip->num_attached != 0xff && offset < a_len; ++oip) { 1651 + if (F_INV_OP & oip->flags) 1652 + continue; 1653 + na = oip->num_attached; 1654 + arr[offset] = oip->opcode; 1655 + put_unaligned_be16(oip->sa, arr + offset + 2); 1656 + if (rctd) 1657 + arr[offset + 5] |= 0x2; 1658 + if (FF_SA & oip->flags) 1659 + arr[offset + 5] |= 0x1; 1660 + put_unaligned_be16(oip->len_mask[0], arr + offset + 6); 1661 + if (rctd) 1662 + put_unaligned_be16(0xa, arr + offset + 8); 1663 + r_oip = oip; 1664 + for (k = 0, oip = oip->arrp; k < na; ++k, ++oip) { 1665 + if (F_INV_OP & oip->flags) 1666 + continue; 1667 + offset += bump; 1668 + arr[offset] = oip->opcode; 1669 + put_unaligned_be16(oip->sa, arr + offset + 2); 1670 + if (rctd) 1671 + arr[offset + 5] |= 0x2; 1672 + if (FF_SA & oip->flags) 1673 + arr[offset + 5] |= 0x1; 1674 + put_unaligned_be16(oip->len_mask[0], 1675 + arr + offset + 6); 1676 + if (rctd) 1677 + put_unaligned_be16(0xa, 1678 + arr + offset + 8); 1679 + } 1680 + oip = r_oip; 1681 + offset += bump; 1682 + } 1683 + break; 1684 + case 1: /* one command: opcode only */ 1685 + case 2: /* one command: opcode plus service action */ 1686 + case 3: /* one command: if sa==0 then opcode only else opcode+sa */ 1687 + sdeb_i = opcode_ind_arr[req_opcode]; 1688 + oip = &opcode_info_arr[sdeb_i]; 1689 + if (F_INV_OP & oip->flags) { 1690 + supp = 1; 1691 + offset = 4; 1692 + } else { 1693 + if (1 == reporting_opts) { 1694 + if (FF_SA & oip->flags) { 1695 + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1696 + 2, 2); 1697 + kfree(arr); 1698 + return check_condition_result; 1699 + } 1700 + req_sa = 0; 1701 + } else if (2 == reporting_opts && 1702 + 0 == (FF_SA & oip->flags)) { 1703 + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 4, -1); 1704 + kfree(arr); /* point at requested sa */ 1705 + return check_condition_result; 1706 + } 1707 + if (0 == (FF_SA & oip->flags) && 1708 + req_opcode == oip->opcode) 1709 + supp = 3; 1710 + else if (0 == (FF_SA & oip->flags)) { 1711 + na = oip->num_attached; 1712 + for (k = 0, oip = oip->arrp; k < na; 1713 + ++k, ++oip) { 1714 + if (req_opcode == oip->opcode) 1715 + break; 1716 + } 1717 + supp = (k >= na) ? 1 : 3; 1718 + } else if (req_sa != oip->sa) { 1719 + na = oip->num_attached; 1720 + for (k = 0, oip = oip->arrp; k < na; 1721 + ++k, ++oip) { 1722 + if (req_sa == oip->sa) 1723 + break; 1724 + } 1725 + supp = (k >= na) ? 1 : 3; 1726 + } else 1727 + supp = 3; 1728 + if (3 == supp) { 1729 + u = oip->len_mask[0]; 1730 + put_unaligned_be16(u, arr + 2); 1731 + arr[4] = oip->opcode; 1732 + for (k = 1; k < u; ++k) 1733 + arr[4 + k] = (k < 16) ? 1734 + oip->len_mask[k] : 0xff; 1735 + offset = 4 + u; 1736 + } else 1737 + offset = 4; 1738 + } 1739 + arr[1] = (rctd ? 0x80 : 0) | supp; 1740 + if (rctd) { 1741 + put_unaligned_be16(0xa, arr + offset); 1742 + offset += 12; 1743 + } 1744 + break; 1745 + default: 1746 + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 2); 1747 + kfree(arr); 1748 + return check_condition_result; 1749 + } 1750 + offset = (offset < a_len) ? offset : a_len; 1751 + len = (offset < alloc_len) ? offset : alloc_len; 1752 + errsts = fill_from_dev_buffer(scp, arr, len); 1753 + kfree(arr); 1754 + return errsts; 1755 + } 1756 + 1757 + static int 1758 + resp_rsup_tmfs(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) 1759 + { 1760 + bool repd; 1761 + u32 alloc_len, len; 1762 + u8 arr[16]; 1763 + u8 *cmd = scp->cmnd; 1764 + 1765 + memset(arr, 0, sizeof(arr)); 1766 + repd = !!(cmd[2] & 0x80); 1767 + alloc_len = get_unaligned_be32(cmd + 6); 1768 + if (alloc_len < 4) { 1769 + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 6, -1); 1770 + return check_condition_result; 1771 + } 1772 + arr[0] = 0xc8; /* ATS | ATSS | LURS */ 1773 + arr[1] = 0x1; /* ITNRS */ 1774 + if (repd) { 1775 + arr[3] = 0xc; 1776 + len = 16; 1777 + } else 1778 + len = 4; 1779 + 1780 + len = (len < alloc_len) ? len : alloc_len; 1781 + return fill_from_dev_buffer(scp, arr, len); 1782 + } 1783 + 1608 1784 /* <<Following mode page info copied from ST318451LW>> */ 1609 1785 1610 1786 static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target) ··· 2343 2163 } 2344 2164 2345 2165 return ret; 2166 + } 2167 + 2168 + /* If fake_store(lba,num) compares equal to arr(num), then copy top half of 2169 + * arr into fake_store(lba,num) and return true. If comparison fails then 2170 + * return false. */ 2171 + static bool 2172 + comp_write_worker(u64 lba, u32 num, const u8 *arr) 2173 + { 2174 + bool res; 2175 + u64 block, rest = 0; 2176 + u32 store_blks = sdebug_store_sectors; 2177 + u32 lb_size = scsi_debug_sector_size; 2178 + 2179 + block = do_div(lba, store_blks); 2180 + if (block + num > store_blks) 2181 + rest = block + num - store_blks; 2182 + 2183 + res = !memcmp(fake_storep + (block * lb_size), arr, 2184 + (num - rest) * lb_size); 2185 + if (!res) 2186 + return res; 2187 + if (rest) 2188 + res = memcmp(fake_storep, arr + ((num - rest) * lb_size), 2189 + rest * lb_size); 2190 + if (!res) 2191 + return res; 2192 + arr += num * lb_size; 2193 + memcpy(fake_storep + (block * lb_size), arr, (num - rest) * lb_size); 2194 + if (rest) 2195 + memcpy(fake_storep, arr + ((num - rest) * lb_size), 2196 + rest * lb_size); 2197 + return res; 2346 2198 } 2347 2199 2348 2200 static __be16 dif_compute_csum(const void *buf, int len) ··· 3031 2819 return check_condition_result; 3032 2820 } 3033 2821 return resp_write_same(scp, lba, num, ei_lba, unmap, ndob); 2822 + } 2823 + 2824 + static int 2825 + resp_comp_write(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) 2826 + { 2827 + u8 *cmd = scp->cmnd; 2828 + u8 *arr; 2829 + u8 *fake_storep_hold; 2830 + u64 lba; 2831 + u32 dnum; 2832 + u32 lb_size = scsi_debug_sector_size; 2833 + u8 num; 2834 + unsigned long iflags; 2835 + int ret; 2836 + 2837 + lba = get_unaligned_be32(cmd + 2); 2838 + num = cmd[13]; /* 1 to a maximum of 255 logical blocks */ 2839 + if (0 == num) 2840 + return 0; /* degenerate case, not an error */ 2841 + dnum = 2 * num; 2842 + arr = kzalloc(dnum * lb_size, GFP_ATOMIC); 2843 + if (NULL == arr) { 2844 + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, 2845 + INSUFF_RES_ASCQ); 2846 + return check_condition_result; 2847 + } 2848 + if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && 2849 + (cmd[1] & 0xe0)) { 2850 + mk_sense_invalid_opcode(scp); 2851 + return check_condition_result; 2852 + } 2853 + if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION || 2854 + scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) && 2855 + (cmd[1] & 0xe0) == 0) 2856 + sdev_printk(KERN_ERR, scp->device, "Unprotected WR " 2857 + "to DIF device\n"); 2858 + 2859 + /* inline check_device_access_params() */ 2860 + if (lba + num > sdebug_capacity) { 2861 + mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0); 2862 + return check_condition_result; 2863 + } 2864 + /* transfer length excessive (tie in to block limits VPD page) */ 2865 + if (num > sdebug_store_sectors) { 2866 + /* needs work to find which cdb byte 'num' comes from */ 2867 + mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); 2868 + return check_condition_result; 2869 + } 2870 + 2871 + write_lock_irqsave(&atomic_rw, iflags); 2872 + 2873 + /* trick do_device_access() to fetch both compare and write buffers 2874 + * from data-in into arr. Safe (atomic) since write_lock held. */ 2875 + fake_storep_hold = fake_storep; 2876 + fake_storep = arr; 2877 + ret = do_device_access(scp, 0, dnum, true); 2878 + fake_storep = fake_storep_hold; 2879 + if (ret == -1) { 2880 + write_unlock_irqrestore(&atomic_rw, iflags); 2881 + kfree(arr); 2882 + return DID_ERROR << 16; 2883 + } else if ((ret < (dnum * lb_size)) && 2884 + (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) 2885 + sdev_printk(KERN_INFO, scp->device, "%s: compare_write: cdb " 2886 + "indicated=%u, IO sent=%d bytes\n", my_name, 2887 + dnum * lb_size, ret); 2888 + if (!comp_write_worker(lba, num, arr)) { 2889 + write_unlock_irqrestore(&atomic_rw, iflags); 2890 + kfree(arr); 2891 + mk_sense_buffer(scp, MISCOMPARE, MISCOMPARE_VERIFY_ASC, 0); 2892 + return check_condition_result; 2893 + } 2894 + if (scsi_debug_lbp()) 2895 + map_region(lba, num); 2896 + write_unlock_irqrestore(&atomic_rw, iflags); 2897 + return 0; 3034 2898 } 3035 2899 3036 2900 struct unmap_block_desc { ··· 4957 4669 } 4958 4670 4959 4671 static int 4960 - sdebug_queuecommand_lock_or_not(struct Scsi_Host *shost, struct scsi_cmnd *cmd) 4961 - { 4962 - if (scsi_debug_host_lock) { 4963 - unsigned long iflags; 4964 - int rc; 4965 - 4966 - spin_lock_irqsave(shost->host_lock, iflags); 4967 - rc = scsi_debug_queuecommand(cmd); 4968 - spin_unlock_irqrestore(shost->host_lock, iflags); 4969 - return rc; 4970 - } else 4971 - return scsi_debug_queuecommand(cmd); 4972 - } 4973 - 4974 - static int 4975 4672 sdebug_change_qdepth(struct scsi_device *sdev, int qdepth) 4976 4673 { 4977 4674 int num_in_q = 0; ··· 5183 4910 ((F_DELAY_OVERR & flags) ? 0 : scsi_debug_delay)); 5184 4911 check_cond: 5185 4912 return schedule_resp(scp, devip, check_condition_result, 0); 4913 + } 4914 + 4915 + static int 4916 + sdebug_queuecommand_lock_or_not(struct Scsi_Host *shost, struct scsi_cmnd *cmd) 4917 + { 4918 + if (scsi_debug_host_lock) { 4919 + unsigned long iflags; 4920 + int rc; 4921 + 4922 + spin_lock_irqsave(shost->host_lock, iflags); 4923 + rc = scsi_debug_queuecommand(cmd); 4924 + spin_unlock_irqrestore(shost->host_lock, iflags); 4925 + return rc; 4926 + } else 4927 + return scsi_debug_queuecommand(cmd); 5186 4928 } 5187 4929 5188 4930 static struct scsi_host_template sdebug_driver_template = {