Message ID | 1474320454-5264-9-git-send-email-damien.lemoal@hgst.com (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
Hi Hannes, [auto build test WARNING on linus/master] [also build test WARNING on v4.8-rc7] [cannot apply to block/for-next next-20160919] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] [Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on] [Check https://git-scm.com/docs/git-format-patch for more information] url: https://github.com/0day-ci/linux/commits/Damien-Le-Moal/ZBC-Zoned-block-device-support/20160920-062608 config: i386-allmodconfig (attached as .config) compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901 reproduce: # save the attached .config to linux build tree make ARCH=i386 All warnings (new ones prefixed by >>): In file included from include/linux/kernel.h:13:0, from include/linux/sched.h:17, from include/linux/blkdev.h:4, from drivers/scsi/sd_zbc.c:24: drivers/scsi/sd_zbc.c: In function 'sd_zbc_report_zones': >> drivers/scsi/sd_zbc.c:61:11: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] pr_debug("%s %s [%s]: " fmt, \ ^ include/linux/printk.h:260:21: note: in definition of macro 'pr_fmt' #define pr_fmt(fmt) fmt ^~~ include/linux/printk.h:308:2: note: in expansion of macro 'dynamic_pr_debug' dynamic_pr_debug(fmt, ##__VA_ARGS__) ^~~~~~~~~~~~~~~~ >> drivers/scsi/sd_zbc.c:61:2: note: in expansion of macro 'pr_debug' pr_debug("%s %s [%s]: " fmt, \ ^~~~~~~~ >> drivers/scsi/sd_zbc.c:221:2: note: in expansion of macro 'sd_zbc_debug' sd_zbc_debug(sdkp, "REPORT ZONES lba %zu len %d\n", ^~~~~~~~~~~~ In file included from include/linux/printk.h:6:0, from include/linux/kernel.h:13, from include/linux/sched.h:17, from include/linux/blkdev.h:4, from drivers/scsi/sd_zbc.c:24: >> include/linux/kern_levels.h:4:18: warning: format '%zu' expects argument of type 'size_t', but argument 5 has type 'sector_t {aka long long unsigned int}' [-Wformat=] #define KERN_SOH "\001" /* ASCII Start Of Header */ ^ include/linux/kern_levels.h:10:18: note: in expansion of macro 'KERN_SOH' #define KERN_ERR KERN_SOH "3" /* error conditions */ ^~~~~~~~ include/linux/printk.h:276:9: note: in expansion of macro 'KERN_ERR' printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) ^~~~~~~~ >> drivers/scsi/sd_zbc.c:73:2: note: in expansion of macro 'pr_err' pr_err("%s %s [%s]: " fmt, \ ^~~~~~ >> drivers/scsi/sd_zbc.c:237:3: note: in expansion of macro 'sd_zbc_err' sd_zbc_err(sdkp, ^~~~~~~~~~ In file included from include/linux/kernel.h:13:0, from include/linux/sched.h:17, from include/linux/blkdev.h:4, from drivers/scsi/sd_zbc.c:24: drivers/scsi/sd_zbc.c: In function 'sd_zbc_setup_reset_cmnd': >> drivers/scsi/sd_zbc.c:61:11: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] pr_debug("%s %s [%s]: " fmt, \ ^ include/linux/printk.h:260:21: note: in definition of macro 'pr_fmt' #define pr_fmt(fmt) fmt ^~~ include/linux/printk.h:308:2: note: in expansion of macro 'dynamic_pr_debug' dynamic_pr_debug(fmt, ##__VA_ARGS__) ^~~~~~~~~~~~~~~~ >> drivers/scsi/sd_zbc.c:61:2: note: in expansion of macro 'pr_debug' pr_debug("%s %s [%s]: " fmt, \ ^~~~~~~~ drivers/scsi/sd_zbc.c:489:4: note: in expansion of macro 'sd_zbc_debug' sd_zbc_debug(sdkp, ^~~~~~~~~~~~ In file included from drivers/scsi/sd_zbc.c:37:0: drivers/scsi/sd_zbc.c:517:7: warning: format '%zu' expects argument of type 'size_t', but argument 5 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned reset wp request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:517:7: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned reset wp request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:517:7: warning: format '%zu' expects argument of type 'size_t', but argument 7 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned reset wp request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:517:7: warning: format '%zu' expects argument of type 'size_t', but argument 8 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned reset wp request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ In file included from include/scsi/scsi_cmnd.h:10:0, from drivers/scsi/sd_zbc.c:30: drivers/scsi/sd_zbc.c:517:7: warning: format '%zu' expects argument of type 'size_t', but argument 5 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned reset wp request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ >> drivers/scsi/sd_zbc.c:516:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:517:7: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned reset wp request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ >> drivers/scsi/sd_zbc.c:516:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:517:7: warning: format '%zu' expects argument of type 'size_t', but argument 7 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned reset wp request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ >> drivers/scsi/sd_zbc.c:516:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:517:7: warning: format '%zu' expects argument of type 'size_t', but argument 8 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned reset wp request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ >> drivers/scsi/sd_zbc.c:516:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ In file included from include/linux/kernel.h:13:0, from include/linux/sched.h:17, from include/linux/blkdev.h:4, from drivers/scsi/sd_zbc.c:24: drivers/scsi/sd_zbc.c: In function 'sd_zbc_setup_open_cmnd': >> drivers/scsi/sd_zbc.c:61:11: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] pr_debug("%s %s [%s]: " fmt, \ ^ include/linux/printk.h:260:21: note: in definition of macro 'pr_fmt' #define pr_fmt(fmt) fmt ^~~ include/linux/printk.h:308:2: note: in expansion of macro 'dynamic_pr_debug' dynamic_pr_debug(fmt, ##__VA_ARGS__) ^~~~~~~~~~~~~~~~ >> drivers/scsi/sd_zbc.c:61:2: note: in expansion of macro 'pr_debug' pr_debug("%s %s [%s]: " fmt, \ ^~~~~~~~ drivers/scsi/sd_zbc.c:573:4: note: in expansion of macro 'sd_zbc_debug' sd_zbc_debug(sdkp, ^~~~~~~~~~~~ In file included from drivers/scsi/sd_zbc.c:37:0: drivers/scsi/sd_zbc.c:594:7: warning: format '%zu' expects argument of type 'size_t', but argument 5 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned open zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:594:7: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned open zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:594:7: warning: format '%zu' expects argument of type 'size_t', but argument 7 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned open zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:594:7: warning: format '%zu' expects argument of type 'size_t', but argument 8 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned open zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ In file included from include/scsi/scsi_cmnd.h:10:0, from drivers/scsi/sd_zbc.c:30: drivers/scsi/sd_zbc.c:594:7: warning: format '%zu' expects argument of type 'size_t', but argument 5 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned open zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:593:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:594:7: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned open zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:593:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:594:7: warning: format '%zu' expects argument of type 'size_t', but argument 7 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned open zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:593:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:594:7: warning: format '%zu' expects argument of type 'size_t', but argument 8 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned open zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:593:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ In file included from include/linux/kernel.h:13:0, from include/linux/sched.h:17, from include/linux/blkdev.h:4, from drivers/scsi/sd_zbc.c:24: drivers/scsi/sd_zbc.c: In function 'sd_zbc_setup_close_cmnd': >> drivers/scsi/sd_zbc.c:61:11: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] pr_debug("%s %s [%s]: " fmt, \ ^ include/linux/printk.h:260:21: note: in definition of macro 'pr_fmt' #define pr_fmt(fmt) fmt ^~~ include/linux/printk.h:308:2: note: in expansion of macro 'dynamic_pr_debug' dynamic_pr_debug(fmt, ##__VA_ARGS__) ^~~~~~~~~~~~~~~~ >> drivers/scsi/sd_zbc.c:61:2: note: in expansion of macro 'pr_debug' pr_debug("%s %s [%s]: " fmt, \ ^~~~~~~~ drivers/scsi/sd_zbc.c:645:4: note: in expansion of macro 'sd_zbc_debug' sd_zbc_debug(sdkp, ^~~~~~~~~~~~ In file included from drivers/scsi/sd_zbc.c:37:0: drivers/scsi/sd_zbc.c:666:7: warning: format '%zu' expects argument of type 'size_t', but argument 5 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned close zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:666:7: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned close zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:666:7: warning: format '%zu' expects argument of type 'size_t', but argument 7 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned close zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:666:7: warning: format '%zu' expects argument of type 'size_t', but argument 8 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned close zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ In file included from include/scsi/scsi_cmnd.h:10:0, from drivers/scsi/sd_zbc.c:30: drivers/scsi/sd_zbc.c:666:7: warning: format '%zu' expects argument of type 'size_t', but argument 5 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned close zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:665:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:666:7: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned close zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:665:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:666:7: warning: format '%zu' expects argument of type 'size_t', but argument 7 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned close zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:665:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:666:7: warning: format '%zu' expects argument of type 'size_t', but argument 8 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned close zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:665:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ In file included from include/linux/kernel.h:13:0, from include/linux/sched.h:17, from include/linux/blkdev.h:4, from drivers/scsi/sd_zbc.c:24: drivers/scsi/sd_zbc.c: In function 'sd_zbc_setup_finish_cmnd': >> drivers/scsi/sd_zbc.c:61:11: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] pr_debug("%s %s [%s]: " fmt, \ ^ include/linux/printk.h:260:21: note: in definition of macro 'pr_fmt' #define pr_fmt(fmt) fmt ^~~ include/linux/printk.h:308:2: note: in expansion of macro 'dynamic_pr_debug' dynamic_pr_debug(fmt, ##__VA_ARGS__) ^~~~~~~~~~~~~~~~ >> drivers/scsi/sd_zbc.c:61:2: note: in expansion of macro 'pr_debug' pr_debug("%s %s [%s]: " fmt, \ ^~~~~~~~ drivers/scsi/sd_zbc.c:717:4: note: in expansion of macro 'sd_zbc_debug' sd_zbc_debug(sdkp, ^~~~~~~~~~~~ In file included from drivers/scsi/sd_zbc.c:37:0: drivers/scsi/sd_zbc.c:734:7: warning: format '%zu' expects argument of type 'size_t', but argument 5 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned finish zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:734:7: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned finish zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:734:7: warning: format '%zu' expects argument of type 'size_t', but argument 7 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned finish zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ drivers/scsi/sd_zbc.c:734:7: warning: format '%zu' expects argument of type 'size_t', but argument 8 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned finish zone request, start %zu/%zu" ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ In file included from include/scsi/scsi_cmnd.h:10:0, from drivers/scsi/sd_zbc.c:30: drivers/scsi/sd_zbc.c:734:7: warning: format '%zu' expects argument of type 'size_t', but argument 5 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned finish zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:733:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:734:7: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned finish zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:733:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:734:7: warning: format '%zu' expects argument of type 'size_t', but argument 7 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned finish zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:733:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ drivers/scsi/sd_zbc.c:734:7: warning: format '%zu' expects argument of type 'size_t', but argument 8 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Unaligned finish zone request, start %zu/%zu" ^ include/scsi/scsi_device.h:233:36: note: in definition of macro 'sdev_printk' sdev_prefix_printk(l, sdev, NULL, fmt, ##a) ^~~ drivers/scsi/sd_zbc.c:733:4: note: in expansion of macro 'sd_printk' sd_printk(KERN_ERR, sdkp, ^~~~~~~~~ In file included from include/linux/kernel.h:13:0, from include/linux/sched.h:17, from include/linux/blkdev.h:4, from drivers/scsi/sd_zbc.c:24: drivers/scsi/sd_zbc.c: In function 'sd_zbc_setup_read_write': >> drivers/scsi/sd_zbc.c:61:11: warning: format '%zu' expects argument of type 'size_t', but argument 6 has type 'sector_t {aka long long unsigned int}' [-Wformat=] pr_debug("%s %s [%s]: " fmt, \ ^ include/linux/printk.h:260:21: note: in definition of macro 'pr_fmt' #define pr_fmt(fmt) fmt ^~~ include/linux/printk.h:308:2: note: in expansion of macro 'dynamic_pr_debug' dynamic_pr_debug(fmt, ##__VA_ARGS__) ^~~~~~~~~~~~~~~~ >> drivers/scsi/sd_zbc.c:61:2: note: in expansion of macro 'pr_debug' pr_debug("%s %s [%s]: " fmt, \ ^~~~~~~~ drivers/scsi/sd_zbc.c:783:3: note: in expansion of macro 'sd_zbc_debug' sd_zbc_debug(sdkp, ^~~~~~~~~~~~ In file included from drivers/scsi/sd_zbc.c:37:0: drivers/scsi/sd_zbc.c: In function 'sd_zbc_read_zones': drivers/scsi/sd_zbc.c:985:8: warning: format '%zu' expects argument of type 'size_t', but argument 5 has type 'sector_t {aka long long unsigned int}' [-Wformat=] "Changing capacity from %zu " ^ drivers/scsi/sd.h:116:31: note: in definition of macro 'sd_printk' (sdsk)->disk->disk_name, fmt, ##a) : \ ^~~ vim +61 drivers/scsi/sd_zbc.c 18 * along with this program; see the file COPYING. If not, write to 19 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, 20 * USA. 21 * 22 */ 23 > 24 #include <linux/blkdev.h> 25 #include <linux/rbtree.h> 26 27 #include <asm/unaligned.h> 28 29 #include <scsi/scsi.h> > 30 #include <scsi/scsi_cmnd.h> 31 #include <scsi/scsi_dbg.h> 32 #include <scsi/scsi_device.h> 33 #include <scsi/scsi_driver.h> 34 #include <scsi/scsi_host.h> 35 #include <scsi/scsi_eh.h> 36 > 37 #include "sd.h" 38 #include "scsi_priv.h" 39 40 enum zbc_zone_type { 41 ZBC_ZONE_TYPE_CONV = 0x1, 42 ZBC_ZONE_TYPE_SEQWRITE_REQ, 43 ZBC_ZONE_TYPE_SEQWRITE_PREF, 44 ZBC_ZONE_TYPE_RESERVED, 45 }; 46 47 enum zbc_zone_cond { 48 ZBC_ZONE_COND_NO_WP, 49 ZBC_ZONE_COND_EMPTY, 50 ZBC_ZONE_COND_IMP_OPEN, 51 ZBC_ZONE_COND_EXP_OPEN, 52 ZBC_ZONE_COND_CLOSED, 53 ZBC_ZONE_COND_READONLY = 0xd, 54 ZBC_ZONE_COND_FULL, 55 ZBC_ZONE_COND_OFFLINE, 56 }; 57 58 #define SD_ZBC_BUF_SIZE 131072 59 60 #define sd_zbc_debug(sdkp, fmt, args...) \ > 61 pr_debug("%s %s [%s]: " fmt, \ 62 dev_driver_string(&(sdkp)->device->sdev_gendev), \ 63 dev_name(&(sdkp)->device->sdev_gendev), \ 64 (sdkp)->disk->disk_name, ## args) 65 66 #define sd_zbc_debug_ratelimit(sdkp, fmt, args...) \ 67 do { \ 68 if (printk_ratelimit()) \ 69 sd_zbc_debug(sdkp, fmt, ## args); \ 70 } while( 0 ) 71 72 #define sd_zbc_err(sdkp, fmt, args...) \ > 73 pr_err("%s %s [%s]: " fmt, \ 74 dev_driver_string(&(sdkp)->device->sdev_gendev), \ 75 dev_name(&(sdkp)->device->sdev_gendev), \ 76 (sdkp)->disk->disk_name, ## args) 77 78 struct zbc_zone_work { 79 struct work_struct zone_work; 80 struct scsi_disk *sdkp; 81 sector_t sector; 82 sector_t nr_sects; 83 bool init; 84 unsigned int nr_zones; 85 }; 86 87 struct blk_zone *zbc_desc_to_zone(struct scsi_disk *sdkp, unsigned char *rec) 88 { 89 struct blk_zone *zone; 90 91 zone = kzalloc(sizeof(struct blk_zone), GFP_KERNEL); 92 if (!zone) 93 return NULL; 94 95 /* Zone type */ 96 switch(rec[0] & 0x0f) { 97 case ZBC_ZONE_TYPE_CONV: 98 case ZBC_ZONE_TYPE_SEQWRITE_REQ: 99 case ZBC_ZONE_TYPE_SEQWRITE_PREF: 100 zone->type = rec[0] & 0x0f; 101 break; 102 default: 103 zone->type = BLK_ZONE_TYPE_UNKNOWN; 104 break; 105 } 106 107 /* Zone condition */ 108 zone->cond = (rec[1] >> 4) & 0xf; 109 if (rec[1] & 0x01) 110 zone->reset = 1; 111 if (rec[1] & 0x02) 112 zone->non_seq = 1; 113 114 /* Zone start sector and length */ 115 zone->len = logical_to_sectors(sdkp->device, 116 get_unaligned_be64(&rec[8])); 117 zone->start = logical_to_sectors(sdkp->device, 118 get_unaligned_be64(&rec[16])); 119 120 /* Zone write pointer */ 121 if (blk_zone_is_empty(zone) && 122 zone->wp != zone->start) 123 zone->wp = zone->start; 124 else if (blk_zone_is_full(zone)) 125 zone->wp = zone->start + zone->len; 126 else if (blk_zone_is_seq(zone)) 127 zone->wp = logical_to_sectors(sdkp->device, 128 get_unaligned_be64(&rec[24])); 129 else 130 zone->wp = (sector_t)-1; 131 132 return zone; 133 } 134 135 static int zbc_parse_zones(struct scsi_disk *sdkp, unsigned char *buf, 136 unsigned int buf_len, sector_t *next_sector) 137 { 138 struct request_queue *q = sdkp->disk->queue; 139 sector_t capacity = logical_to_sectors(sdkp->device, sdkp->capacity); 140 unsigned char *rec = buf; 141 unsigned int zone_len, list_length; 142 143 /* Parse REPORT ZONES header */ 144 list_length = get_unaligned_be32(&buf[0]); 145 rec = buf + 64; 146 list_length += 64; 147 148 if (list_length < buf_len) 149 buf_len = list_length; 150 151 /* Parse REPORT ZONES zone descriptors */ 152 *next_sector = capacity; 153 while (rec < buf + buf_len) { 154 155 struct blk_zone *new, *old; 156 157 new = zbc_desc_to_zone(sdkp, rec); 158 if (!new) 159 return -ENOMEM; 160 161 zone_len = new->len; 162 *next_sector = new->start + zone_len; 163 164 old = blk_insert_zone(q, new); 165 if (old) { 166 blk_lock_zone(old); 167 168 /* 169 * Always update the zone state flags and the zone 170 * offline and read-only condition as the drive may 171 * change those independently of the commands being 172 * executed 173 */ 174 old->reset = new->reset; 175 old->non_seq = new->non_seq; 176 if (blk_zone_is_offline(new) || 177 blk_zone_is_readonly(new)) 178 old->cond = new->cond; 179 180 if (blk_zone_in_update(old)) { 181 old->cond = new->cond; 182 old->wp = new->wp; 183 blk_clear_zone_update(old); 184 } 185 186 blk_unlock_zone(old); 187 188 kfree(new); 189 } 190 191 rec += 64; 192 193 } 194 195 return 0; 196 } 197 198 /** 199 * sd_zbc_report_zones - Issue a REPORT ZONES scsi command 200 * @sdkp: SCSI disk to which the command should be send 201 * @buffer: response buffer 202 * @bufflen: length of @buffer 203 * @start_sector: logical sector for the zone information should be reported 204 * @option: reporting option to be used 205 * @partial: flag to set the 'partial' bit for report zones command 206 */ 207 int sd_zbc_report_zones(struct scsi_disk *sdkp, unsigned char *buffer, 208 int bufflen, sector_t start_sector, 209 enum zbc_zone_reporting_options option, bool partial) 210 { 211 struct scsi_device *sdp = sdkp->device; 212 const int timeout = sdp->request_queue->rq_timeout; 213 struct scsi_sense_hdr sshdr; 214 sector_t start_lba = sectors_to_logical(sdkp->device, start_sector); 215 unsigned char cmd[16]; 216 int result; 217 218 if (!scsi_device_online(sdp)) 219 return -ENODEV; 220 > 221 sd_zbc_debug(sdkp, "REPORT ZONES lba %zu len %d\n", 222 start_lba, bufflen); 223 224 memset(cmd, 0, 16); 225 cmd[0] = ZBC_IN; 226 cmd[1] = ZI_REPORT_ZONES; 227 put_unaligned_be64(start_lba, &cmd[2]); 228 put_unaligned_be32(bufflen, &cmd[10]); 229 cmd[14] = (partial ? ZBC_REPORT_ZONE_PARTIAL : 0) | option; 230 memset(buffer, 0, bufflen); 231 232 result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE, 233 buffer, bufflen, &sshdr, 234 timeout, SD_MAX_RETRIES, NULL); 235 236 if (result) { > 237 sd_zbc_err(sdkp, 238 "REPORT ZONES lba %zu failed with %d/%d\n", 239 start_lba, host_byte(result), driver_byte(result)); 240 return -EIO; --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Mon, Sep 19, 2016 at 4:27 PM, Damien Le Moal <damien.lemoal@hgst.com> wrote: > From: Hannes Reinecke <hare@suse.com> > > Implement ZBC support functions to setup zoned disks and fill the > block device zone information tree during the device scan. The > zone information tree is also always updated on disk revalidation. > This adds support for the REQ_OP_ZONE* operations and also implements > the new RESET_WP provisioning mode so that discard requests can be > mapped to the RESET WRITE POINTER command for devices with a constant > zone size. > > The capacity read of the device triggers the zone information read > for zoned block devices. As this needs the device zone model, the > the call to sd_read_capacity is moved after the call to > sd_read_block_characteristics so that host-aware devices are > properlly initialized. The call to sd_zbc_read_zones in > sd_read_capacity may change the device capacity obtained with > the sd_read_capacity_16 function for devices reporting only the > capacity of conventional zones at the beginning of the LBA range > (i.e. devices with rc_basis et to 0). > > Signed-off-by: Hannes Reinecke <hare@suse.de> > Signed-off-by: Damien Le Moal <damien.lemoal@hgst.com> > --- > drivers/scsi/Makefile | 1 + > drivers/scsi/sd.c | 147 ++++-- > drivers/scsi/sd.h | 68 +++ > drivers/scsi/sd_zbc.c | 1097 +++++++++++++++++++++++++++++++++++++++++++++ > include/scsi/scsi_proto.h | 17 + > 5 files changed, 1304 insertions(+), 26 deletions(-) > create mode 100644 drivers/scsi/sd_zbc.c > > diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile > index d539798..fabcb6d 100644 > --- a/drivers/scsi/Makefile > +++ b/drivers/scsi/Makefile > @@ -179,6 +179,7 @@ hv_storvsc-y := storvsc_drv.o > > sd_mod-objs := sd.o > sd_mod-$(CONFIG_BLK_DEV_INTEGRITY) += sd_dif.o > +sd_mod-$(CONFIG_BLK_DEV_ZONED) += sd_zbc.o > > sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o > ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ > diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c > index d3e852a..46b8b78 100644 > --- a/drivers/scsi/sd.c > +++ b/drivers/scsi/sd.c > @@ -92,6 +92,7 @@ MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK15_MAJOR); > MODULE_ALIAS_SCSI_DEVICE(TYPE_DISK); > MODULE_ALIAS_SCSI_DEVICE(TYPE_MOD); > MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); > +MODULE_ALIAS_SCSI_DEVICE(TYPE_ZBC); > > #if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) > #define SD_MINORS 16 > @@ -99,7 +100,6 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); > #define SD_MINORS 0 > #endif > > -static void sd_config_discard(struct scsi_disk *, unsigned int); > static void sd_config_write_same(struct scsi_disk *); > static int sd_revalidate_disk(struct gendisk *); > static void sd_unlock_native_capacity(struct gendisk *disk); > @@ -162,7 +162,7 @@ cache_type_store(struct device *dev, struct device_attribute *attr, > static const char temp[] = "temporary "; > int len; > > - if (sdp->type != TYPE_DISK) > + if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC) > /* no cache control on RBC devices; theoretically they > * can do it, but there's probably so many exceptions > * it's not worth the risk */ > @@ -261,7 +261,7 @@ allow_restart_store(struct device *dev, struct device_attribute *attr, > if (!capable(CAP_SYS_ADMIN)) > return -EACCES; > > - if (sdp->type != TYPE_DISK) > + if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC) > return -EINVAL; > > sdp->allow_restart = simple_strtoul(buf, NULL, 10); > @@ -369,6 +369,7 @@ static const char *lbp_mode[] = { > [SD_LBP_WS16] = "writesame_16", > [SD_LBP_WS10] = "writesame_10", > [SD_LBP_ZERO] = "writesame_zero", > + [SD_ZBC_RESET_WP] = "reset_wp", > [SD_LBP_DISABLE] = "disabled", > }; > > @@ -391,6 +392,13 @@ provisioning_mode_store(struct device *dev, struct device_attribute *attr, > if (!capable(CAP_SYS_ADMIN)) > return -EACCES; > > + if (sdkp->zoned == 1 || sdp->type == TYPE_ZBC) { > + if (!strncmp(buf, lbp_mode[SD_ZBC_RESET_WP], 20)) { > + sd_config_discard(sdkp, SD_ZBC_RESET_WP); > + return count; > + } > + return -EINVAL; > + } > if (sdp->type != TYPE_DISK) > return -EINVAL; > > @@ -458,7 +466,7 @@ max_write_same_blocks_store(struct device *dev, struct device_attribute *attr, > if (!capable(CAP_SYS_ADMIN)) > return -EACCES; > > - if (sdp->type != TYPE_DISK) > + if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC) > return -EINVAL; > > err = kstrtoul(buf, 10, &max); > @@ -631,7 +639,7 @@ static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd, > return protect; > } > > -static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) > +void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) > { > struct request_queue *q = sdkp->disk->queue; > unsigned int logical_block_size = sdkp->device->sector_size; > @@ -683,6 +691,11 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) > q->limits.discard_zeroes_data = sdkp->lbprz; > break; > > + case SD_ZBC_RESET_WP: > + max_blocks = min_not_zero(sdkp->max_unmap_blocks, > + (u32)SD_MAX_WS16_BLOCKS); > + break; > + > case SD_LBP_ZERO: > max_blocks = min_not_zero(sdkp->max_ws_blocks, > (u32)SD_MAX_WS10_BLOCKS); > @@ -711,16 +724,20 @@ static int sd_setup_discard_cmnd(struct scsi_cmnd *cmd) > unsigned int nr_sectors = blk_rq_sectors(rq); > unsigned int nr_bytes = blk_rq_bytes(rq); > unsigned int len; > - int ret; > + int ret = BLKPREP_OK; > char *buf; > - struct page *page; > + struct page *page = NULL; > > sector >>= ilog2(sdp->sector_size) - 9; > nr_sectors >>= ilog2(sdp->sector_size) - 9; > > - page = alloc_page(GFP_ATOMIC | __GFP_ZERO); > - if (!page) > - return BLKPREP_DEFER; > + if (sdkp->provisioning_mode != SD_ZBC_RESET_WP) { > + page = alloc_page(GFP_ATOMIC | __GFP_ZERO); > + if (!page) > + return BLKPREP_DEFER; > + } > + > + rq->completion_data = page; > > switch (sdkp->provisioning_mode) { > case SD_LBP_UNMAP: > @@ -760,12 +777,19 @@ static int sd_setup_discard_cmnd(struct scsi_cmnd *cmd) > len = sdkp->device->sector_size; > break; > > + case SD_ZBC_RESET_WP: > + ret = sd_zbc_setup_reset_cmnd(cmd); > + if (ret != BLKPREP_OK) > + goto out; > + /* Reset Write Pointer doesn't have a payload */ > + len = 0; > + break; > + > default: > ret = BLKPREP_INVALID; > goto out; > } > > - rq->completion_data = page; > rq->timeout = SD_TIMEOUT; > > cmd->transfersize = len; > @@ -779,13 +803,17 @@ static int sd_setup_discard_cmnd(struct scsi_cmnd *cmd) > * discarded on disk. This allows us to report completion on the full > * amount of blocks described by the request. > */ > - blk_add_request_payload(rq, page, 0, len); > - ret = scsi_init_io(cmd); > + if (len) { > + blk_add_request_payload(rq, page, 0, len); > + ret = scsi_init_io(cmd); > + } > rq->__data_len = nr_bytes; > > out: > - if (ret != BLKPREP_OK) > + if (page && ret != BLKPREP_OK) { > + rq->completion_data = NULL; > __free_page(page); > + } > return ret; > } > > @@ -843,6 +871,13 @@ static int sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) > > BUG_ON(bio_offset(bio) || bio_iovec(bio).bv_len != sdp->sector_size); > > + if (sdkp->zoned == 1 || sdp->type == TYPE_ZBC) { > + /* sd_zbc_setup_read_write uses block layer sector units */ > + ret = sd_zbc_setup_read_write(sdkp, rq, sector, &nr_sectors); > + if (ret != BLKPREP_OK) > + return ret; > + } > + > sector >>= ilog2(sdp->sector_size) - 9; > nr_sectors >>= ilog2(sdp->sector_size) - 9; > > @@ -962,6 +997,13 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) > SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n", > (unsigned long long)block)); > > + if (sdkp->zoned == 1 || sdp->type == TYPE_ZBC) { > + /* sd_zbc_setup_read_write uses block layer sector units */ > + ret = sd_zbc_setup_read_write(sdkp, rq, block, &this_count); > + if (ret != BLKPREP_OK) > + goto out; > + } > + > /* > * If we have a 1K hardware sectorsize, prevent access to single > * 512 byte sectors. In theory we could handle this - in fact > @@ -1148,6 +1190,16 @@ static int sd_init_command(struct scsi_cmnd *cmd) > case REQ_OP_READ: > case REQ_OP_WRITE: > return sd_setup_read_write_cmnd(cmd); > + case REQ_OP_ZONE_REPORT: > + return sd_zbc_setup_report_cmnd(cmd); > + case REQ_OP_ZONE_RESET: > + return sd_zbc_setup_reset_cmnd(cmd); > + case REQ_OP_ZONE_OPEN: > + return sd_zbc_setup_open_cmnd(cmd); > + case REQ_OP_ZONE_CLOSE: > + return sd_zbc_setup_close_cmnd(cmd); > + case REQ_OP_ZONE_FINISH: > + return sd_zbc_setup_finish_cmnd(cmd); > default: > BUG(); > } > @@ -1157,7 +1209,8 @@ static void sd_uninit_command(struct scsi_cmnd *SCpnt) > { > struct request *rq = SCpnt->request; > > - if (req_op(rq) == REQ_OP_DISCARD) > + if (req_op(rq) == REQ_OP_DISCARD && > + rq->completion_data) > __free_page(rq->completion_data); > > if (SCpnt->cmnd != rq->cmd) { > @@ -1778,8 +1831,16 @@ static int sd_done(struct scsi_cmnd *SCpnt) > int sense_deferred = 0; > unsigned char op = SCpnt->cmnd[0]; > unsigned char unmap = SCpnt->cmnd[1] & 8; > + unsigned char sa = SCpnt->cmnd[1] & 0xf; > > - if (req_op(req) == REQ_OP_DISCARD || req_op(req) == REQ_OP_WRITE_SAME) { > + switch(req_op(req)) { > + case REQ_OP_DISCARD: > + case REQ_OP_WRITE_SAME: > + case REQ_OP_ZONE_REPORT: > + case REQ_OP_ZONE_RESET: > + case REQ_OP_ZONE_OPEN: > + case REQ_OP_ZONE_CLOSE: > + case REQ_OP_ZONE_FINISH: > if (!result) { > good_bytes = blk_rq_bytes(req); > scsi_set_resid(SCpnt, 0); > @@ -1787,6 +1848,7 @@ static int sd_done(struct scsi_cmnd *SCpnt) > good_bytes = 0; > scsi_set_resid(SCpnt, blk_rq_bytes(req)); > } > + break; > } > > if (result) { > @@ -1829,6 +1891,10 @@ static int sd_done(struct scsi_cmnd *SCpnt) > case UNMAP: > sd_config_discard(sdkp, SD_LBP_DISABLE); > break; > + case ZBC_OUT: > + if (sa == ZO_RESET_WRITE_POINTER) > + sd_config_discard(sdkp, SD_LBP_DISABLE); > + break; > case WRITE_SAME_16: > case WRITE_SAME: > if (unmap) > @@ -1847,7 +1913,11 @@ static int sd_done(struct scsi_cmnd *SCpnt) > default: > break; > } > + > out: > + if (sdkp->zoned == 1 || sdkp->device->type == TYPE_ZBC) > + sd_zbc_done(SCpnt, &sshdr); > + > SCSI_LOG_HLCOMPLETE(1, scmd_printk(KERN_INFO, SCpnt, > "sd_done: completed %d of %d bytes\n", > good_bytes, scsi_bufflen(SCpnt))); > @@ -1982,7 +2052,6 @@ sd_spinup_disk(struct scsi_disk *sdkp) > } > } > > - > /* > * Determine whether disk supports Data Integrity Field. > */ > @@ -2132,6 +2201,9 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, > /* Logical blocks per physical block exponent */ > sdkp->physical_block_size = (1 << (buffer[13] & 0xf)) * sector_size; > > + /* RC basis */ > + sdkp->rc_basis = (buffer[12] >> 4) & 0x3; > + > /* Lowest aligned logical block */ > alignment = ((buffer[14] & 0x3f) << 8 | buffer[15]) * sector_size; > blk_queue_alignment_offset(sdp->request_queue, alignment); > @@ -2322,6 +2394,11 @@ got_data: > sector_size = 512; > } > blk_queue_logical_block_size(sdp->request_queue, sector_size); > + blk_queue_physical_block_size(sdp->request_queue, > + sdkp->physical_block_size); > + sdkp->device->sector_size = sector_size; > + > + sd_zbc_read_zones(sdkp, buffer); > > { > char cap_str_2[10], cap_str_10[10]; > @@ -2348,9 +2425,6 @@ got_data: > if (sdkp->capacity > 0xffffffff) > sdp->use_16_for_rw = 1; > > - blk_queue_physical_block_size(sdp->request_queue, > - sdkp->physical_block_size); > - sdkp->device->sector_size = sector_size; > } > > /* called with buffer of length 512 */ > @@ -2612,7 +2686,7 @@ static void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer) > struct scsi_mode_data data; > struct scsi_sense_hdr sshdr; > > - if (sdp->type != TYPE_DISK) > + if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC) > return; > > if (sdkp->protection_type == 0) > @@ -2719,6 +2793,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) > */ > static void sd_read_block_characteristics(struct scsi_disk *sdkp) > { > + struct request_queue *q = sdkp->disk->queue; > unsigned char *buffer; > u16 rot; > const int vpd_len = 64; > @@ -2733,10 +2808,21 @@ static void sd_read_block_characteristics(struct scsi_disk *sdkp) > rot = get_unaligned_be16(&buffer[4]); > > if (rot == 1) { > - queue_flag_set_unlocked(QUEUE_FLAG_NONROT, sdkp->disk->queue); > - queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, sdkp->disk->queue); > + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q); > + queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q); > } > > + sdkp->zoned = (buffer[8] >> 4) & 3; > + if (sdkp->zoned == 1) > + q->limits.zoned = BLK_ZONED_HA; > + else if (sdkp->device->type == TYPE_ZBC) > + q->limits.zoned = BLK_ZONED_HM; > + else > + q->limits.zoned = BLK_ZONED_NONE; > + if (blk_queue_zoned(q) && sdkp->first_scan) > + sd_printk(KERN_NOTICE, sdkp, "Host-%s zoned block device\n", > + q->limits.zoned == BLK_ZONED_HM ? "managed" : "aware"); > + > out: > kfree(buffer); > } > @@ -2835,14 +2921,14 @@ static int sd_revalidate_disk(struct gendisk *disk) > * react badly if we do. > */ > if (sdkp->media_present) { > - sd_read_capacity(sdkp, buffer); > - > if (scsi_device_supports_vpd(sdp)) { > sd_read_block_provisioning(sdkp); > sd_read_block_limits(sdkp); > sd_read_block_characteristics(sdkp); > } > > + sd_read_capacity(sdkp, buffer); > + > sd_read_write_protect_flag(sdkp, buffer); > sd_read_cache_type(sdkp, buffer); > sd_read_app_tag_own(sdkp, buffer); > @@ -3040,9 +3126,16 @@ static int sd_probe(struct device *dev) > > scsi_autopm_get_device(sdp); > error = -ENODEV; > - if (sdp->type != TYPE_DISK && sdp->type != TYPE_MOD && sdp->type != TYPE_RBC) > + if (sdp->type != TYPE_DISK && > + sdp->type != TYPE_ZBC && > + sdp->type != TYPE_MOD && > + sdp->type != TYPE_RBC) > goto out; > > +#ifndef CONFIG_BLK_DEV_ZONED > + if (sdp->type == TYPE_ZBC) > + goto out; > +#endif > SCSI_LOG_HLQUEUE(3, sdev_printk(KERN_INFO, sdp, > "sd_probe\n")); > > @@ -3146,6 +3239,8 @@ static int sd_remove(struct device *dev) > del_gendisk(sdkp->disk); > sd_shutdown(dev); > > + sd_zbc_remove(sdkp); > + > blk_register_region(devt, SD_MINORS, NULL, > sd_default_probe, NULL, NULL); > > diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h > index 765a6f1..3452871 100644 > --- a/drivers/scsi/sd.h > +++ b/drivers/scsi/sd.h > @@ -56,6 +56,7 @@ enum { > SD_LBP_WS16, /* Use WRITE SAME(16) with UNMAP bit */ > SD_LBP_WS10, /* Use WRITE SAME(10) with UNMAP bit */ > SD_LBP_ZERO, /* Use WRITE SAME(10) with zero payload */ > + SD_ZBC_RESET_WP, /* Use RESET WRITE POINTER */ > SD_LBP_DISABLE, /* Discard disabled due to failed cmd */ > }; > Can we have adding SD_ZBC_RESET_WP as a separate patch? > @@ -64,6 +65,11 @@ struct scsi_disk { > struct scsi_device *device; > struct device dev; > struct gendisk *disk; > +#ifdef CONFIG_BLK_DEV_ZONED > + struct workqueue_struct *zone_work_q; > + sector_t zone_sectors; > + unsigned int nr_zones; > +#endif > atomic_t openers; > sector_t capacity; /* size in logical blocks */ > u32 max_xfer_blocks; > @@ -94,6 +100,8 @@ struct scsi_disk { > unsigned lbpvpd : 1; > unsigned ws10 : 1; > unsigned ws16 : 1; > + unsigned rc_basis: 2; > + unsigned zoned: 2; > }; > #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev) > > @@ -156,6 +164,13 @@ static inline unsigned int logical_to_bytes(struct scsi_device *sdev, sector_t b > return blocks * sdev->sector_size; > } > > +static inline sector_t sectors_to_logical(struct scsi_device *sdev, sector_t sector) > +{ > + return sector >> (ilog2(sdev->sector_size) - 9); > +} > + > +extern void sd_config_discard(struct scsi_disk *, unsigned int); > + > /* > * A DIF-capable target device can be formatted with different > * protection schemes. Currently 0 through 3 are defined: > @@ -269,4 +284,57 @@ static inline void sd_dif_complete(struct scsi_cmnd *cmd, unsigned int a) > > #endif /* CONFIG_BLK_DEV_INTEGRITY */ > > +#ifdef CONFIG_BLK_DEV_ZONED > + > +extern void sd_zbc_read_zones(struct scsi_disk *, char *); > +extern void sd_zbc_remove(struct scsi_disk *); > +extern int sd_zbc_setup_read_write(struct scsi_disk *, struct request *, > + sector_t, unsigned int *); > +extern int sd_zbc_setup_report_cmnd(struct scsi_cmnd *); > +extern int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *); > +extern int sd_zbc_setup_open_cmnd(struct scsi_cmnd *); > +extern int sd_zbc_setup_close_cmnd(struct scsi_cmnd *); > +extern int sd_zbc_setup_finish_cmnd(struct scsi_cmnd *); > +extern void sd_zbc_done(struct scsi_cmnd *, struct scsi_sense_hdr *); > + > +#else /* CONFIG_BLK_DEV_ZONED */ > + > +static inline void sd_zbc_read_zones(struct scsi_disk *sdkp, > + unsigned char *buf) {} > +static inline void sd_zbc_remove(struct scsi_disk *sdkp) {} > + > +static inline int sd_zbc_setup_read_write(struct scsi_disk *sdkp, > + struct request *rq, sector_t sector, > + unsigned int *num_sectors) > +{ > + /* Let the drive fail requests */ > + return BLKPREP_OK; > +} > + > +static inline int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd) > +{ > + return BLKPREP_KILL; > +} > +static inline int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd) > +{ > + return BLKPREP_KILL; > +} > +static inline int sd_zbc_setup_open_cmnd(struct scsi_cmnd *cmd) > +{ > + return BLKPREP_KILL; > +} > +static inline int sd_zbc_setup_close_cmnd(struct scsi_cmnd *cmd) > +{ > + return BLKPREP_KILL; > +} > +static inline int sd_zbc_setup_finish_cmnd(struct scsi_cmnd *cmd) > +{ > + return BLKPREP_KILL; > +} > + > +static inline void sd_zbc_done(struct scsi_cmnd *cmd, > + struct scsi_sense_hdr *sshdr) {} > + > +#endif /* CONFIG_BLK_DEV_ZONED */ > + > #endif /* _SCSI_DISK_H */ > diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c > new file mode 100644 > index 0000000..ec9c3fc > --- /dev/null > +++ b/drivers/scsi/sd_zbc.c > @@ -0,0 +1,1097 @@ > +/* > + * SCSI Zoned Block commands > + * > + * Copyright (C) 2014-2015 SUSE Linux GmbH > + * Written by: Hannes Reinecke <hare@suse.de> > + * Modified by: Damien Le Moal <damien.lemoal@hgst.com> > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License version > + * 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; see the file COPYING. If not, write to > + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, > + * USA. > + * > + */ > + > +#include <linux/blkdev.h> > +#include <linux/rbtree.h> > + > +#include <asm/unaligned.h> > + > +#include <scsi/scsi.h> > +#include <scsi/scsi_cmnd.h> > +#include <scsi/scsi_dbg.h> > +#include <scsi/scsi_device.h> > +#include <scsi/scsi_driver.h> > +#include <scsi/scsi_host.h> > +#include <scsi/scsi_eh.h> > + > +#include "sd.h" > +#include "scsi_priv.h" > + > +enum zbc_zone_type { > + ZBC_ZONE_TYPE_CONV = 0x1, > + ZBC_ZONE_TYPE_SEQWRITE_REQ, > + ZBC_ZONE_TYPE_SEQWRITE_PREF, > + ZBC_ZONE_TYPE_RESERVED, > +}; > + > +enum zbc_zone_cond { > + ZBC_ZONE_COND_NO_WP, > + ZBC_ZONE_COND_EMPTY, > + ZBC_ZONE_COND_IMP_OPEN, > + ZBC_ZONE_COND_EXP_OPEN, > + ZBC_ZONE_COND_CLOSED, > + ZBC_ZONE_COND_READONLY = 0xd, > + ZBC_ZONE_COND_FULL, > + ZBC_ZONE_COND_OFFLINE, > +}; > + > +#define SD_ZBC_BUF_SIZE 131072 > + > +#define sd_zbc_debug(sdkp, fmt, args...) \ > + pr_debug("%s %s [%s]: " fmt, \ > + dev_driver_string(&(sdkp)->device->sdev_gendev), \ > + dev_name(&(sdkp)->device->sdev_gendev), \ > + (sdkp)->disk->disk_name, ## args) > + > +#define sd_zbc_debug_ratelimit(sdkp, fmt, args...) \ > + do { \ > + if (printk_ratelimit()) \ > + sd_zbc_debug(sdkp, fmt, ## args); \ > + } while( 0 ) > + > +#define sd_zbc_err(sdkp, fmt, args...) \ > + pr_err("%s %s [%s]: " fmt, \ > + dev_driver_string(&(sdkp)->device->sdev_gendev), \ > + dev_name(&(sdkp)->device->sdev_gendev), \ > + (sdkp)->disk->disk_name, ## args) > + > +struct zbc_zone_work { > + struct work_struct zone_work; > + struct scsi_disk *sdkp; > + sector_t sector; > + sector_t nr_sects; > + bool init; > + unsigned int nr_zones; > +}; > + > +struct blk_zone *zbc_desc_to_zone(struct scsi_disk *sdkp, unsigned char *rec) > +{ > + struct blk_zone *zone; > + > + zone = kzalloc(sizeof(struct blk_zone), GFP_KERNEL); > + if (!zone) > + return NULL; > + > + /* Zone type */ > + switch(rec[0] & 0x0f) { > + case ZBC_ZONE_TYPE_CONV: > + case ZBC_ZONE_TYPE_SEQWRITE_REQ: > + case ZBC_ZONE_TYPE_SEQWRITE_PREF: > + zone->type = rec[0] & 0x0f; > + break; > + default: > + zone->type = BLK_ZONE_TYPE_UNKNOWN; > + break; > + } > + > + /* Zone condition */ > + zone->cond = (rec[1] >> 4) & 0xf; > + if (rec[1] & 0x01) > + zone->reset = 1; > + if (rec[1] & 0x02) > + zone->non_seq = 1; > + > + /* Zone start sector and length */ > + zone->len = logical_to_sectors(sdkp->device, > + get_unaligned_be64(&rec[8])); > + zone->start = logical_to_sectors(sdkp->device, > + get_unaligned_be64(&rec[16])); > + > + /* Zone write pointer */ > + if (blk_zone_is_empty(zone) && > + zone->wp != zone->start) > + zone->wp = zone->start; > + else if (blk_zone_is_full(zone)) > + zone->wp = zone->start + zone->len; > + else if (blk_zone_is_seq(zone)) > + zone->wp = logical_to_sectors(sdkp->device, > + get_unaligned_be64(&rec[24])); > + else > + zone->wp = (sector_t)-1; > + > + return zone; > +} > + > +static int zbc_parse_zones(struct scsi_disk *sdkp, unsigned char *buf, > + unsigned int buf_len, sector_t *next_sector) > +{ > + struct request_queue *q = sdkp->disk->queue; > + sector_t capacity = logical_to_sectors(sdkp->device, sdkp->capacity); > + unsigned char *rec = buf; > + unsigned int zone_len, list_length; > + > + /* Parse REPORT ZONES header */ > + list_length = get_unaligned_be32(&buf[0]); > + rec = buf + 64; > + list_length += 64; > + > + if (list_length < buf_len) > + buf_len = list_length; > + > + /* Parse REPORT ZONES zone descriptors */ > + *next_sector = capacity; > + while (rec < buf + buf_len) { > + > + struct blk_zone *new, *old; > + > + new = zbc_desc_to_zone(sdkp, rec); > + if (!new) > + return -ENOMEM; > + > + zone_len = new->len; > + *next_sector = new->start + zone_len; > + > + old = blk_insert_zone(q, new); > + if (old) { > + blk_lock_zone(old); > + > + /* > + * Always update the zone state flags and the zone > + * offline and read-only condition as the drive may > + * change those independently of the commands being > + * executed > + */ > + old->reset = new->reset; > + old->non_seq = new->non_seq; > + if (blk_zone_is_offline(new) || > + blk_zone_is_readonly(new)) > + old->cond = new->cond; > + > + if (blk_zone_in_update(old)) { > + old->cond = new->cond; > + old->wp = new->wp; > + blk_clear_zone_update(old); > + } > + > + blk_unlock_zone(old); > + > + kfree(new); > + } > + > + rec += 64; > + > + } > + > + return 0; > +} > + > +/** > + * sd_zbc_report_zones - Issue a REPORT ZONES scsi command > + * @sdkp: SCSI disk to which the command should be send > + * @buffer: response buffer > + * @bufflen: length of @buffer > + * @start_sector: logical sector for the zone information should be reported > + * @option: reporting option to be used > + * @partial: flag to set the 'partial' bit for report zones command > + */ > +int sd_zbc_report_zones(struct scsi_disk *sdkp, unsigned char *buffer, > + int bufflen, sector_t start_sector, > + enum zbc_zone_reporting_options option, bool partial) > +{ > + struct scsi_device *sdp = sdkp->device; > + const int timeout = sdp->request_queue->rq_timeout; > + struct scsi_sense_hdr sshdr; > + sector_t start_lba = sectors_to_logical(sdkp->device, start_sector); > + unsigned char cmd[16]; > + int result; > + > + if (!scsi_device_online(sdp)) > + return -ENODEV; > + > + sd_zbc_debug(sdkp, "REPORT ZONES lba %zu len %d\n", > + start_lba, bufflen); > + > + memset(cmd, 0, 16); > + cmd[0] = ZBC_IN; > + cmd[1] = ZI_REPORT_ZONES; > + put_unaligned_be64(start_lba, &cmd[2]); > + put_unaligned_be32(bufflen, &cmd[10]); > + cmd[14] = (partial ? ZBC_REPORT_ZONE_PARTIAL : 0) | option; > + memset(buffer, 0, bufflen); > + > + result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE, > + buffer, bufflen, &sshdr, > + timeout, SD_MAX_RETRIES, NULL); > + > + if (result) { > + sd_zbc_err(sdkp, > + "REPORT ZONES lba %zu failed with %d/%d\n", > + start_lba, host_byte(result), driver_byte(result)); > + return -EIO; > + } > + > + return 0; > +} > + > +/** > + * Set or clear the update flag of all zones contained > + * in the range sector..sector+nr_sects. > + * Return the number of zones marked/cleared. > + */ > +static int __sd_zbc_zones_updating(struct scsi_disk *sdkp, > + sector_t sector, sector_t nr_sects, > + bool set) > +{ > + struct request_queue *q = sdkp->disk->queue; > + struct blk_zone *zone; > + struct rb_node *node; > + unsigned long flags; > + int nr_zones = 0; > + > + if (!nr_sects) { > + /* All zones */ > + sector = 0; > + nr_sects = logical_to_sectors(sdkp->device, sdkp->capacity); > + } > + > + spin_lock_irqsave(&q->zones_lock, flags); > + for (node = rb_first(&q->zones); node && nr_sects; node = rb_next(node)) { > + zone = rb_entry(node, struct blk_zone, node); > + if (sector < zone->start || sector >= (zone->start + zone->len)) > + continue; > + if (set) { > + if (!test_and_set_bit_lock(BLK_ZONE_IN_UPDATE, &zone->flags)) > + nr_zones++; > + } else if (test_and_clear_bit(BLK_ZONE_IN_UPDATE, &zone->flags)) { > + wake_up_bit(&zone->flags, BLK_ZONE_IN_UPDATE); > + nr_zones++; > + } > + sector = zone->start + zone->len; > + if (nr_sects <= zone->len) > + nr_sects = 0; > + else > + nr_sects -= zone->len; > + } > + spin_unlock_irqrestore(&q->zones_lock, flags); > + > + return nr_zones; > +} > + > +static inline int sd_zbc_set_zones_updating(struct scsi_disk *sdkp, > + sector_t sector, sector_t nr_sects) > +{ > + return __sd_zbc_zones_updating(sdkp, sector, nr_sects, true); > +} > + > +static inline int sd_zbc_clear_zones_updating(struct scsi_disk *sdkp, > + sector_t sector, sector_t nr_sects) > +{ > + return __sd_zbc_zones_updating(sdkp, sector, nr_sects, false); > +} > + > +static void sd_zbc_start_queue(struct request_queue *q) > +{ > + unsigned long flags; > + > + if (q->mq_ops) { > + blk_mq_start_hw_queues(q); > + } else { > + spin_lock_irqsave(q->queue_lock, flags); > + blk_start_queue(q); > + spin_unlock_irqrestore(q->queue_lock, flags); > + } > +} > + > +static void sd_zbc_update_zone_work(struct work_struct *work) > +{ > + struct zbc_zone_work *zwork = > + container_of(work, struct zbc_zone_work, zone_work); > + struct scsi_disk *sdkp = zwork->sdkp; > + sector_t capacity = logical_to_sectors(sdkp->device, sdkp->capacity); > + struct request_queue *q = sdkp->disk->queue; > + sector_t end_sector, sector = zwork->sector; > + unsigned int bufsize; > + unsigned char *buf; > + int ret = -ENOMEM; > + > + /* Get a buffer */ > + if (!zwork->nr_zones) { > + bufsize = SD_ZBC_BUF_SIZE; > + } else { > + bufsize = (zwork->nr_zones + 1) * 64; > + if (bufsize < 512) > + bufsize = 512; > + else if (bufsize > SD_ZBC_BUF_SIZE) > + bufsize = SD_ZBC_BUF_SIZE; > + else > + bufsize = (bufsize + 511) & ~511; > + } > + buf = kmalloc(bufsize, GFP_KERNEL | GFP_DMA); > + if (!buf) { > + sd_zbc_err(sdkp, "Failed to allocate zone report buffer\n"); > + goto done_free; > + } > + > + /* Process sector range */ > + end_sector = zwork->sector + zwork->nr_sects; > + while(sector < min(end_sector, capacity)) { > + > + /* Get zone report */ > + ret = sd_zbc_report_zones(sdkp, buf, bufsize, sector, > + ZBC_ZONE_REPORTING_OPTION_ALL, true); > + if (ret) > + break; > + > + ret = zbc_parse_zones(sdkp, buf, bufsize, §or); > + if (ret) > + break; > + > + /* Kick start the queue to allow requests waiting */ > + /* for the zones just updated to run */ > + sd_zbc_start_queue(q); > + > + } > + > +done_free: > + if (ret) > + sd_zbc_clear_zones_updating(sdkp, zwork->sector, zwork->nr_sects); > + if (buf) > + kfree(buf); > + kfree(zwork); > +} > + > +/** > + * sd_zbc_update_zones - Update zone information for zones starting > + * from @start_sector. If not in init mode, the update is done only > + * for zones marked with update flag. > + * @sdkp: SCSI disk for which the zone information needs to be updated > + * @start_sector: First sector of the first zone to be updated > + * @bufsize: buffersize to be allocated for report zones > + */ > +static int sd_zbc_update_zones(struct scsi_disk *sdkp, > + sector_t sector, sector_t nr_sects, > + gfp_t gfpflags, bool init) > +{ > + struct zbc_zone_work *zwork; > + > + zwork = kzalloc(sizeof(struct zbc_zone_work), gfpflags); > + if (!zwork) { > + sd_zbc_err(sdkp, "Failed to allocate zone work\n"); > + return -ENOMEM; > + } > + > + if (!nr_sects) { > + /* All zones */ > + sector = 0; > + nr_sects = logical_to_sectors(sdkp->device, sdkp->capacity); > + } > + > + INIT_WORK(&zwork->zone_work, sd_zbc_update_zone_work); > + zwork->sdkp = sdkp; > + zwork->sector = sector; > + zwork->nr_sects = nr_sects; > + zwork->init = init; > + > + if (!init) > + /* Mark the zones falling in the report as updating */ > + zwork->nr_zones = sd_zbc_set_zones_updating(sdkp, sector, nr_sects); > + > + if (init || zwork->nr_zones) > + queue_work(sdkp->zone_work_q, &zwork->zone_work); > + else > + kfree(zwork); > + > + return 0; > +} > + > +int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd) > +{ > + struct request *rq = cmd->request; > + struct gendisk *disk = rq->rq_disk; > + struct scsi_disk *sdkp = scsi_disk(disk); > + int ret; > + > + if (!sdkp->zone_work_q) > + return BLKPREP_KILL; > + > + ret = sd_zbc_update_zones(sdkp, blk_rq_pos(rq), blk_rq_sectors(rq), > + GFP_ATOMIC, false); > + if (unlikely(ret)) > + return BLKPREP_DEFER; > + > + return BLKPREP_DONE; > +} > + > +static void sd_zbc_setup_action_cmnd(struct scsi_cmnd *cmd, > + u8 action, > + bool all) > +{ > + struct request *rq = cmd->request; > + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); > + sector_t lba; > + > + cmd->cmd_len = 16; > + cmd->cmnd[0] = ZBC_OUT; > + cmd->cmnd[1] = action; > + if (all) { > + cmd->cmnd[14] |= 0x01; > + } else { > + lba = sectors_to_logical(sdkp->device, blk_rq_pos(rq)); > + put_unaligned_be64(lba, &cmd->cmnd[2]); > + } > + > + rq->completion_data = NULL; > + rq->timeout = SD_TIMEOUT; > + rq->__data_len = blk_rq_bytes(rq); > + > + /* Don't retry */ > + cmd->allowed = 0; > + cmd->transfersize = 0; > + cmd->sc_data_direction = DMA_NONE; > +} > + > +int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd) > +{ > + struct request *rq = cmd->request; > + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); > + sector_t sector = blk_rq_pos(rq); > + sector_t nr_sects = blk_rq_sectors(rq); > + struct blk_zone *zone = NULL; > + int ret = BLKPREP_OK; > + > + if (nr_sects) { > + zone = blk_lookup_zone(rq->q, sector); > + if (!zone) > + return BLKPREP_KILL; > + } > + > + if (zone) { > + > + blk_lock_zone(zone); > + > + /* If the zone is being updated, wait */ > + if (blk_zone_in_update(zone)) { > + ret = BLKPREP_DEFER; > + goto out; > + } > + > + if (zone->type == BLK_ZONE_TYPE_UNKNOWN) { > + sd_zbc_debug(sdkp, > + "Discarding unknown zone %zu\n", > + zone->start); > + ret = BLKPREP_KILL; > + goto out; > + } > + > + /* Nothing to do for conventional sequential zones */ > + if (blk_zone_is_conv(zone)) { > + ret = BLKPREP_DONE; > + goto out; > + } > + > + if (!blk_try_write_lock_zone(zone)) { > + ret = BLKPREP_DEFER; > + goto out; > + } > + > + /* Nothing to do if the zone is already empty */ > + if (blk_zone_is_empty(zone)) { > + blk_write_unlock_zone(zone); > + ret = BLKPREP_DONE; > + goto out; > + } > + > + if (sector != zone->start || > + (nr_sects != zone->len)) { > + sd_printk(KERN_ERR, sdkp, > + "Unaligned reset wp request, start %zu/%zu" > + " len %zu/%zu\n", > + zone->start, sector, zone->len, nr_sects); > + blk_write_unlock_zone(zone); > + ret = BLKPREP_KILL; > + goto out; > + } > + > + } > + > + sd_zbc_setup_action_cmnd(cmd, ZO_RESET_WRITE_POINTER, !zone); > + > +out: > + if (zone) { > + if (ret == BLKPREP_OK) { > + /* > + * Opportunistic update. Will be fixed up > + * with zone update if the command fails, > + */ > + zone->wp = zone->start; > + zone->cond = BLK_ZONE_COND_EMPTY; > + zone->reset = 0; > + zone->non_seq = 0; > + } > + blk_unlock_zone(zone); > + } > + > + return ret; > +} > + > +int sd_zbc_setup_open_cmnd(struct scsi_cmnd *cmd) > +{ > + struct request *rq = cmd->request; > + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); > + sector_t sector = blk_rq_pos(rq); > + sector_t nr_sects = blk_rq_sectors(rq); > + struct blk_zone *zone = NULL; > + int ret = BLKPREP_OK; > + > + if (nr_sects) { > + zone = blk_lookup_zone(rq->q, sector); > + if (!zone) > + return BLKPREP_KILL; > + } > + > + if (zone) { > + > + blk_lock_zone(zone); > + > + /* If the zone is being updated, wait */ > + if (blk_zone_in_update(zone)) { > + ret = BLKPREP_DEFER; > + goto out; > + } > + > + if (zone->type == BLK_ZONE_TYPE_UNKNOWN) { > + sd_zbc_debug(sdkp, > + "Opening unknown zone %zu\n", > + zone->start); > + ret = BLKPREP_KILL; > + goto out; > + } > + > + /* > + * Nothing to do for conventional zones, > + * zones already open or full zones. > + */ > + if (blk_zone_is_conv(zone) || > + blk_zone_is_open(zone) || > + blk_zone_is_full(zone)) { > + ret = BLKPREP_DONE; > + goto out; > + } > + > + if (sector != zone->start || > + (nr_sects != zone->len)) { > + sd_printk(KERN_ERR, sdkp, > + "Unaligned open zone request, start %zu/%zu" > + " len %zu/%zu\n", > + zone->start, sector, zone->len, nr_sects); > + ret = BLKPREP_KILL; > + goto out; > + } > + > + } > + > + sd_zbc_setup_action_cmnd(cmd, ZO_OPEN_ZONE, !zone); > + > +out: > + if (zone) { > + if (ret == BLKPREP_OK) > + /* > + * Opportunistic update. Will be fixed up > + * with zone update if the command fails. > + */ > + zone->cond = BLK_ZONE_COND_EXP_OPEN; > + blk_unlock_zone(zone); > + } > + > + return ret; > +} > + > +int sd_zbc_setup_close_cmnd(struct scsi_cmnd *cmd) > +{ > + struct request *rq = cmd->request; > + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); > + sector_t sector = blk_rq_pos(rq); > + sector_t nr_sects = blk_rq_sectors(rq); > + struct blk_zone *zone = NULL; > + int ret = BLKPREP_OK; > + > + if (nr_sects) { > + zone = blk_lookup_zone(rq->q, sector); > + if (!zone) > + return BLKPREP_KILL; > + } > + > + if (zone) { > + > + blk_lock_zone(zone); > + > + /* If the zone is being updated, wait */ > + if (blk_zone_in_update(zone)) { > + ret = BLKPREP_DEFER; > + goto out; > + } > + > + if (zone->type == BLK_ZONE_TYPE_UNKNOWN) { > + sd_zbc_debug(sdkp, > + "Closing unknown zone %zu\n", > + zone->start); > + ret = BLKPREP_KILL; > + goto out; > + } > + > + /* > + * Nothing to do for conventional zones, > + * full zones or empty zones. > + */ > + if (blk_zone_is_conv(zone) || > + blk_zone_is_full(zone) || > + blk_zone_is_empty(zone)) { > + ret = BLKPREP_DONE; > + goto out; > + } > + > + if (sector != zone->start || > + (nr_sects != zone->len)) { > + sd_printk(KERN_ERR, sdkp, > + "Unaligned close zone request, start %zu/%zu" > + " len %zu/%zu\n", > + zone->start, sector, zone->len, nr_sects); > + ret = BLKPREP_KILL; > + goto out; > + } > + > + } > + > + sd_zbc_setup_action_cmnd(cmd, ZO_CLOSE_ZONE, !zone); > + > +out: > + if (zone) { > + if (ret == BLKPREP_OK) > + /* > + * Opportunistic update. Will be fixed up > + * with zone update if the command fails. > + */ > + zone->cond = BLK_ZONE_COND_CLOSED; > + blk_unlock_zone(zone); > + } > + > + return ret; > +} > + > +int sd_zbc_setup_finish_cmnd(struct scsi_cmnd *cmd) > +{ > + struct request *rq = cmd->request; > + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); > + sector_t sector = blk_rq_pos(rq); > + sector_t nr_sects = blk_rq_sectors(rq); > + struct blk_zone *zone = NULL; > + int ret = BLKPREP_OK; > + > + if (nr_sects) { > + zone = blk_lookup_zone(rq->q, sector); > + if (!zone) > + return BLKPREP_KILL; > + } > + > + if (zone) { > + > + blk_lock_zone(zone); > + > + /* If the zone is being updated, wait */ > + if (blk_zone_in_update(zone)) { > + ret = BLKPREP_DEFER; > + goto out; > + } > + > + if (zone->type == BLK_ZONE_TYPE_UNKNOWN) { > + sd_zbc_debug(sdkp, > + "Finishing unknown zone %zu\n", > + zone->start); > + ret = BLKPREP_KILL; > + goto out; > + } > + > + /* Nothing to do for conventional zones and full zones */ > + if (blk_zone_is_conv(zone) || > + blk_zone_is_full(zone)) { > + ret = BLKPREP_DONE; > + goto out; > + } > + > + if (sector != zone->start || > + (nr_sects != zone->len)) { > + sd_printk(KERN_ERR, sdkp, > + "Unaligned finish zone request, start %zu/%zu" > + " len %zu/%zu\n", > + zone->start, sector, zone->len, nr_sects); > + ret = BLKPREP_KILL; > + goto out; > + } > + > + } > + > + sd_zbc_setup_action_cmnd(cmd, ZO_FINISH_ZONE, !zone); > + > +out: > + if (zone) { > + if (ret == BLKPREP_OK) { > + /* > + * Opportunistic update. Will be fixed up > + * with zone update if the command fails. > + */ > + zone->cond = BLK_ZONE_COND_FULL; > + if (blk_zone_is_seq(zone)) > + zone->wp = zone->start + zone->len; > + } > + blk_unlock_zone(zone); > + } > + > + return ret; > +} > + Would be nice to have open/close/finish/reset share a little more code. > +int sd_zbc_setup_read_write(struct scsi_disk *sdkp, struct request *rq, > + sector_t sector, unsigned int *num_sectors) > +{ > + struct blk_zone *zone; > + unsigned int sectors = *num_sectors; > + int ret = BLKPREP_OK; > + > + zone = blk_lookup_zone(rq->q, sector); > + if (!zone) > + /* Let the drive handle the request */ > + return BLKPREP_OK; > + > + blk_lock_zone(zone); > + > + /* If the zone is being updated, wait */ > + if (blk_zone_in_update(zone)) { > + ret = BLKPREP_DEFER; > + goto out; > + } > + > + if (zone->type == BLK_ZONE_TYPE_UNKNOWN) { > + sd_zbc_debug(sdkp, > + "Unknown zone %zu\n", > + zone->start); > + ret = BLKPREP_KILL; > + goto out; > + } > + > + /* For offline and read-only zones, let the drive fail the command */ > + if (blk_zone_is_offline(zone) || > + blk_zone_is_readonly(zone)) > + goto out; > + > + /* Do not allow zone boundaries crossing */ > + if (sector + sectors > zone->start + zone->len) { > + ret = BLKPREP_KILL; > + goto out; > + } > + > + /* For conventional zones, no checks */ > + if (blk_zone_is_conv(zone)) > + goto out; > + > + if (req_op(rq) == REQ_OP_WRITE || > + req_op(rq) == REQ_OP_WRITE_SAME) { > + > + /* > + * Write requests may change the write pointer and > + * transition the zone condition to full. Changes > + * are oportunistic here. If the request fails, a > + * zone update will fix the zone information. > + */ > + if (blk_zone_is_seq_req(zone)) { > + > + /* > + * Do not issue more than one write at a time per > + * zone. This solves write ordering problems due to > + * the unlocking of the request queue in the dispatch > + * path in the non scsi-mq case. For scsi-mq, this > + * also avoids potential write reordering when multiple > + * threads running on different CPUs write to the same > + * zone (with a synchronized sequential pattern). > + */ > + if (!blk_try_write_lock_zone(zone)) { > + ret = BLKPREP_DEFER; > + goto out; > + } > + > + /* For host-managed drives, writes are allowed */ > + /* only at the write pointer position. */ > + if (zone->wp != sector) { > + blk_write_unlock_zone(zone); > + ret = BLKPREP_KILL; > + goto out; > + } > + > + zone->wp += sectors; > + if (zone->wp >= zone->start + zone->len) { > + zone->cond = BLK_ZONE_COND_FULL; > + zone->wp = zone->start + zone->len; > + } > + > + } else { > + > + /* For host-aware drives, writes are allowed */ > + /* anywhere in the zone, but wp can only go */ > + /* forward. */ > + sector_t end_sector = sector + sectors; > + if (sector == zone->wp && > + end_sector >= zone->start + zone->len) { > + zone->cond = BLK_ZONE_COND_FULL; > + zone->wp = zone->start + zone->len; > + } else if (end_sector > zone->wp) { > + zone->wp = end_sector; > + } > + > + } > + > + } else { > + If the drive does not have restricted reads the just goto out here. Not all HM drives will have restricted reads and no HA drives have restricted reads. > + /* Check read after write pointer */ > + if (sector + sectors <= zone->wp) > + goto out; > + > + if (zone->wp <= sector) { > + /* Read beyond WP: clear request buffer */ > + struct req_iterator iter; > + struct bio_vec bvec; > + unsigned long flags; > + void *buf; > + rq_for_each_segment(bvec, rq, iter) { > + buf = bvec_kmap_irq(&bvec, &flags); > + memset(buf, 0, bvec.bv_len); > + flush_dcache_page(bvec.bv_page); > + bvec_kunmap_irq(buf, &flags); > + } > + ret = BLKPREP_DONE; > + goto out; > + } > + > + /* Read straddle WP position: limit request size */ > + *num_sectors = zone->wp - sector; > + > + } > + > +out: > + blk_unlock_zone(zone); > + > + return ret; > +} > + > +void sd_zbc_done(struct scsi_cmnd *cmd, > + struct scsi_sense_hdr *sshdr) > +{ > + int result = cmd->result; > + struct request *rq = cmd->request; > + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); > + struct request_queue *q = sdkp->disk->queue; > + sector_t pos = blk_rq_pos(rq); > + struct blk_zone *zone = NULL; > + bool write_unlock = false; > + > + /* > + * Get the target zone of commands of interest. Some may > + * apply to all zones so check the request sectors first. > + */ > + switch (req_op(rq)) { > + case REQ_OP_DISCARD: > + case REQ_OP_WRITE: > + case REQ_OP_WRITE_SAME: > + case REQ_OP_ZONE_RESET: > + write_unlock = true; > + /* fallthru */ > + case REQ_OP_ZONE_OPEN: > + case REQ_OP_ZONE_CLOSE: > + case REQ_OP_ZONE_FINISH: > + if (blk_rq_sectors(rq)) > + zone = blk_lookup_zone(q, pos); > + break; > + } > + > + if (zone && write_unlock) > + blk_write_unlock_zone(zone); > + > + if (!result) > + return; > + > + if (sshdr->sense_key == ILLEGAL_REQUEST && > + sshdr->asc == 0x21) > + /* > + * It is unlikely that retrying requests failed with any > + * kind of alignement error will result in success. So don't > + * try. Report the error back to the user quickly so that > + * corrective actions can be taken after obtaining updated > + * zone information. > + */ > + cmd->allowed = 0; > + > + /* On error, force an update unless this is a failed report */ > + if (req_op(rq) == REQ_OP_ZONE_REPORT) > + sd_zbc_clear_zones_updating(sdkp, pos, blk_rq_sectors(rq)); > + else if (zone) > + sd_zbc_update_zones(sdkp, zone->start, zone->len, > + GFP_ATOMIC, false); > +} > + > +void sd_zbc_read_zones(struct scsi_disk *sdkp, char *buf) > +{ > + struct request_queue *q = sdkp->disk->queue; > + struct blk_zone *zone; > + sector_t capacity; > + sector_t sector; > + bool init = false; > + u32 rep_len; > + int ret = 0; > + > + if (sdkp->zoned != 1 && sdkp->device->type != TYPE_ZBC) > + /* > + * Device managed or normal SCSI disk, > + * no special handling required > + */ > + return; > + > + /* Do a report zone to get the maximum LBA to check capacity */ > + ret = sd_zbc_report_zones(sdkp, buf, SD_BUF_SIZE, > + 0, ZBC_ZONE_REPORTING_OPTION_ALL, false); > + if (ret < 0) > + return; > + > + rep_len = get_unaligned_be32(&buf[0]); > + if (rep_len < 64) { > + sd_printk(KERN_WARNING, sdkp, > + "REPORT ZONES report invalid length %u\n", > + rep_len); > + return; > + } > + > + if (sdkp->rc_basis == 0) { > + /* The max_lba field is the capacity of this device */ > + sector_t lba = get_unaligned_be64(&buf[8]); > + if (lba + 1 > sdkp->capacity) { > + if (sdkp->first_scan) > + sd_printk(KERN_WARNING, sdkp, > + "Changing capacity from %zu " > + "to max LBA+1 %zu\n", > + sdkp->capacity, > + (sector_t) lba + 1); > + sdkp->capacity = lba + 1; > + } > + } > + > + /* Setup the zone work queue */ > + if (! sdkp->zone_work_q) { > + sdkp->zone_work_q = > + alloc_ordered_workqueue("zbc_wq_%s", WQ_MEM_RECLAIM, > + sdkp->disk->disk_name); > + if (!sdkp->zone_work_q) { > + sdev_printk(KERN_WARNING, sdkp->device, > + "Create zoned disk workqueue failed\n"); > + return; > + } > + init = true; > + } > + > + /* > + * Parse what we already got. If all zones are not parsed yet, > + * kick start an update to get the remaining. > + */ > + capacity = logical_to_sectors(sdkp->device, sdkp->capacity); > + ret = zbc_parse_zones(sdkp, buf, SD_BUF_SIZE, §or); > + if (ret == 0 && sector < capacity) { > + sd_zbc_update_zones(sdkp, sector, capacity - sector, > + GFP_KERNEL, init); > + drain_workqueue(sdkp->zone_work_q); > + } > + if (ret) > + return; > + > + /* > + * Analyze the zones layout: if all zones are the same size and > + * the size is a power of 2, chunk the device and map discard to > + * reset write pointer command. Otherwise, disable discard. > + */ > + sdkp->zone_sectors = 0; > + sdkp->nr_zones = 0; > + sector = 0; > + while(sector < capacity) { > + > + zone = blk_lookup_zone(q, sector); > + if (!zone) { > + sdkp->zone_sectors = 0; > + sdkp->nr_zones = 0; > + break; > + } > + > + sector += zone->len; > + > + if (sdkp->zone_sectors == 0) { > + sdkp->zone_sectors = zone->len; > + } else if (sector != capacity && > + zone->len != sdkp->zone_sectors) { > + sdkp->zone_sectors = 0; > + sdkp->nr_zones = 0; > + break; > + } > + > + sdkp->nr_zones++; > + > + } > + > + if (!sdkp->zone_sectors || > + !is_power_of_2(sdkp->zone_sectors)) { > + sd_config_discard(sdkp, SD_LBP_DISABLE); > + if (sdkp->first_scan) > + sd_printk(KERN_NOTICE, sdkp, > + "%u zones (non constant zone size)\n", > + sdkp->nr_zones); > + return; > + } > + > + /* Setup discard granularity to the zone size */ > + blk_queue_chunk_sectors(sdkp->disk->queue, sdkp->zone_sectors); > + sdkp->max_unmap_blocks = sdkp->zone_sectors; > + sdkp->unmap_alignment = sectors_to_logical(sdkp->device, > + sdkp->zone_sectors); > + sdkp->unmap_granularity = sdkp->unmap_alignment; > + sd_config_discard(sdkp, SD_ZBC_RESET_WP); > + > + if (sdkp->first_scan) { > + if (sdkp->nr_zones * sdkp->zone_sectors == capacity) > + sd_printk(KERN_NOTICE, sdkp, > + "%u zones of %zu sectors\n", > + sdkp->nr_zones, > + sdkp->zone_sectors); > + else > + sd_printk(KERN_NOTICE, sdkp, > + "%u zones of %zu sectors " > + "+ 1 runt zone\n", > + sdkp->nr_zones - 1, > + sdkp->zone_sectors); > + } > +} > + > +void sd_zbc_remove(struct scsi_disk *sdkp) > +{ > + > + sd_config_discard(sdkp, SD_LBP_DISABLE); > + > + if (sdkp->zone_work_q) { > + drain_workqueue(sdkp->zone_work_q); > + destroy_workqueue(sdkp->zone_work_q); > + sdkp->zone_work_q = NULL; > + blk_drop_zones(sdkp->disk->queue); > + } > +} > + > diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h > index d1defd1..6ba66e0 100644 > --- a/include/scsi/scsi_proto.h > +++ b/include/scsi/scsi_proto.h > @@ -299,4 +299,21 @@ struct scsi_lun { > #define SCSI_ACCESS_STATE_MASK 0x0f > #define SCSI_ACCESS_STATE_PREFERRED 0x80 > > +/* Reporting options for REPORT ZONES */ > +enum zbc_zone_reporting_options { > + ZBC_ZONE_REPORTING_OPTION_ALL = 0, > + ZBC_ZONE_REPORTING_OPTION_EMPTY, > + ZBC_ZONE_REPORTING_OPTION_IMPLICIT_OPEN, > + ZBC_ZONE_REPORTING_OPTION_EXPLICIT_OPEN, > + ZBC_ZONE_REPORTING_OPTION_CLOSED, > + ZBC_ZONE_REPORTING_OPTION_FULL, > + ZBC_ZONE_REPORTING_OPTION_READONLY, > + ZBC_ZONE_REPORTING_OPTION_OFFLINE, > + ZBC_ZONE_REPORTING_OPTION_NEED_RESET_WP = 0x10, > + ZBC_ZONE_REPORTING_OPTION_NON_SEQWRITE, > + ZBC_ZONE_REPORTING_OPTION_NON_WP = 0x3f, > +}; > + > +#define ZBC_REPORT_ZONE_PARTIAL 0x80 > + Why don't we expose these enums via uapi? > #endif /* _SCSI_PROTO_H_ */ > -- > 2.7.4 > > Western Digital Corporation (and its subsidiaries) E-mail Confidentiality Notice & Disclaimer: > > This e-mail and any files transmitted with it may contain confidential or legally privileged information of WDC and/or its affiliates, and are intended solely for the use of the individual or entity to which they are addressed. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited. If you have received this e-mail in error, please notify the sender immediately and delete the e-mail in its entirety from your system. >
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index d539798..fabcb6d 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -179,6 +179,7 @@ hv_storvsc-y := storvsc_drv.o sd_mod-objs := sd.o sd_mod-$(CONFIG_BLK_DEV_INTEGRITY) += sd_dif.o +sd_mod-$(CONFIG_BLK_DEV_ZONED) += sd_zbc.o sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index d3e852a..46b8b78 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -92,6 +92,7 @@ MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK15_MAJOR); MODULE_ALIAS_SCSI_DEVICE(TYPE_DISK); MODULE_ALIAS_SCSI_DEVICE(TYPE_MOD); MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); +MODULE_ALIAS_SCSI_DEVICE(TYPE_ZBC); #if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) #define SD_MINORS 16 @@ -99,7 +100,6 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC); #define SD_MINORS 0 #endif -static void sd_config_discard(struct scsi_disk *, unsigned int); static void sd_config_write_same(struct scsi_disk *); static int sd_revalidate_disk(struct gendisk *); static void sd_unlock_native_capacity(struct gendisk *disk); @@ -162,7 +162,7 @@ cache_type_store(struct device *dev, struct device_attribute *attr, static const char temp[] = "temporary "; int len; - if (sdp->type != TYPE_DISK) + if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC) /* no cache control on RBC devices; theoretically they * can do it, but there's probably so many exceptions * it's not worth the risk */ @@ -261,7 +261,7 @@ allow_restart_store(struct device *dev, struct device_attribute *attr, if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (sdp->type != TYPE_DISK) + if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC) return -EINVAL; sdp->allow_restart = simple_strtoul(buf, NULL, 10); @@ -369,6 +369,7 @@ static const char *lbp_mode[] = { [SD_LBP_WS16] = "writesame_16", [SD_LBP_WS10] = "writesame_10", [SD_LBP_ZERO] = "writesame_zero", + [SD_ZBC_RESET_WP] = "reset_wp", [SD_LBP_DISABLE] = "disabled", }; @@ -391,6 +392,13 @@ provisioning_mode_store(struct device *dev, struct device_attribute *attr, if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (sdkp->zoned == 1 || sdp->type == TYPE_ZBC) { + if (!strncmp(buf, lbp_mode[SD_ZBC_RESET_WP], 20)) { + sd_config_discard(sdkp, SD_ZBC_RESET_WP); + return count; + } + return -EINVAL; + } if (sdp->type != TYPE_DISK) return -EINVAL; @@ -458,7 +466,7 @@ max_write_same_blocks_store(struct device *dev, struct device_attribute *attr, if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (sdp->type != TYPE_DISK) + if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC) return -EINVAL; err = kstrtoul(buf, 10, &max); @@ -631,7 +639,7 @@ static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd, return protect; } -static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) +void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) { struct request_queue *q = sdkp->disk->queue; unsigned int logical_block_size = sdkp->device->sector_size; @@ -683,6 +691,11 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode) q->limits.discard_zeroes_data = sdkp->lbprz; break; + case SD_ZBC_RESET_WP: + max_blocks = min_not_zero(sdkp->max_unmap_blocks, + (u32)SD_MAX_WS16_BLOCKS); + break; + case SD_LBP_ZERO: max_blocks = min_not_zero(sdkp->max_ws_blocks, (u32)SD_MAX_WS10_BLOCKS); @@ -711,16 +724,20 @@ static int sd_setup_discard_cmnd(struct scsi_cmnd *cmd) unsigned int nr_sectors = blk_rq_sectors(rq); unsigned int nr_bytes = blk_rq_bytes(rq); unsigned int len; - int ret; + int ret = BLKPREP_OK; char *buf; - struct page *page; + struct page *page = NULL; sector >>= ilog2(sdp->sector_size) - 9; nr_sectors >>= ilog2(sdp->sector_size) - 9; - page = alloc_page(GFP_ATOMIC | __GFP_ZERO); - if (!page) - return BLKPREP_DEFER; + if (sdkp->provisioning_mode != SD_ZBC_RESET_WP) { + page = alloc_page(GFP_ATOMIC | __GFP_ZERO); + if (!page) + return BLKPREP_DEFER; + } + + rq->completion_data = page; switch (sdkp->provisioning_mode) { case SD_LBP_UNMAP: @@ -760,12 +777,19 @@ static int sd_setup_discard_cmnd(struct scsi_cmnd *cmd) len = sdkp->device->sector_size; break; + case SD_ZBC_RESET_WP: + ret = sd_zbc_setup_reset_cmnd(cmd); + if (ret != BLKPREP_OK) + goto out; + /* Reset Write Pointer doesn't have a payload */ + len = 0; + break; + default: ret = BLKPREP_INVALID; goto out; } - rq->completion_data = page; rq->timeout = SD_TIMEOUT; cmd->transfersize = len; @@ -779,13 +803,17 @@ static int sd_setup_discard_cmnd(struct scsi_cmnd *cmd) * discarded on disk. This allows us to report completion on the full * amount of blocks described by the request. */ - blk_add_request_payload(rq, page, 0, len); - ret = scsi_init_io(cmd); + if (len) { + blk_add_request_payload(rq, page, 0, len); + ret = scsi_init_io(cmd); + } rq->__data_len = nr_bytes; out: - if (ret != BLKPREP_OK) + if (page && ret != BLKPREP_OK) { + rq->completion_data = NULL; __free_page(page); + } return ret; } @@ -843,6 +871,13 @@ static int sd_setup_write_same_cmnd(struct scsi_cmnd *cmd) BUG_ON(bio_offset(bio) || bio_iovec(bio).bv_len != sdp->sector_size); + if (sdkp->zoned == 1 || sdp->type == TYPE_ZBC) { + /* sd_zbc_setup_read_write uses block layer sector units */ + ret = sd_zbc_setup_read_write(sdkp, rq, sector, &nr_sectors); + if (ret != BLKPREP_OK) + return ret; + } + sector >>= ilog2(sdp->sector_size) - 9; nr_sectors >>= ilog2(sdp->sector_size) - 9; @@ -962,6 +997,13 @@ static int sd_setup_read_write_cmnd(struct scsi_cmnd *SCpnt) SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n", (unsigned long long)block)); + if (sdkp->zoned == 1 || sdp->type == TYPE_ZBC) { + /* sd_zbc_setup_read_write uses block layer sector units */ + ret = sd_zbc_setup_read_write(sdkp, rq, block, &this_count); + if (ret != BLKPREP_OK) + goto out; + } + /* * If we have a 1K hardware sectorsize, prevent access to single * 512 byte sectors. In theory we could handle this - in fact @@ -1148,6 +1190,16 @@ static int sd_init_command(struct scsi_cmnd *cmd) case REQ_OP_READ: case REQ_OP_WRITE: return sd_setup_read_write_cmnd(cmd); + case REQ_OP_ZONE_REPORT: + return sd_zbc_setup_report_cmnd(cmd); + case REQ_OP_ZONE_RESET: + return sd_zbc_setup_reset_cmnd(cmd); + case REQ_OP_ZONE_OPEN: + return sd_zbc_setup_open_cmnd(cmd); + case REQ_OP_ZONE_CLOSE: + return sd_zbc_setup_close_cmnd(cmd); + case REQ_OP_ZONE_FINISH: + return sd_zbc_setup_finish_cmnd(cmd); default: BUG(); } @@ -1157,7 +1209,8 @@ static void sd_uninit_command(struct scsi_cmnd *SCpnt) { struct request *rq = SCpnt->request; - if (req_op(rq) == REQ_OP_DISCARD) + if (req_op(rq) == REQ_OP_DISCARD && + rq->completion_data) __free_page(rq->completion_data); if (SCpnt->cmnd != rq->cmd) { @@ -1778,8 +1831,16 @@ static int sd_done(struct scsi_cmnd *SCpnt) int sense_deferred = 0; unsigned char op = SCpnt->cmnd[0]; unsigned char unmap = SCpnt->cmnd[1] & 8; + unsigned char sa = SCpnt->cmnd[1] & 0xf; - if (req_op(req) == REQ_OP_DISCARD || req_op(req) == REQ_OP_WRITE_SAME) { + switch(req_op(req)) { + case REQ_OP_DISCARD: + case REQ_OP_WRITE_SAME: + case REQ_OP_ZONE_REPORT: + case REQ_OP_ZONE_RESET: + case REQ_OP_ZONE_OPEN: + case REQ_OP_ZONE_CLOSE: + case REQ_OP_ZONE_FINISH: if (!result) { good_bytes = blk_rq_bytes(req); scsi_set_resid(SCpnt, 0); @@ -1787,6 +1848,7 @@ static int sd_done(struct scsi_cmnd *SCpnt) good_bytes = 0; scsi_set_resid(SCpnt, blk_rq_bytes(req)); } + break; } if (result) { @@ -1829,6 +1891,10 @@ static int sd_done(struct scsi_cmnd *SCpnt) case UNMAP: sd_config_discard(sdkp, SD_LBP_DISABLE); break; + case ZBC_OUT: + if (sa == ZO_RESET_WRITE_POINTER) + sd_config_discard(sdkp, SD_LBP_DISABLE); + break; case WRITE_SAME_16: case WRITE_SAME: if (unmap) @@ -1847,7 +1913,11 @@ static int sd_done(struct scsi_cmnd *SCpnt) default: break; } + out: + if (sdkp->zoned == 1 || sdkp->device->type == TYPE_ZBC) + sd_zbc_done(SCpnt, &sshdr); + SCSI_LOG_HLCOMPLETE(1, scmd_printk(KERN_INFO, SCpnt, "sd_done: completed %d of %d bytes\n", good_bytes, scsi_bufflen(SCpnt))); @@ -1982,7 +2052,6 @@ sd_spinup_disk(struct scsi_disk *sdkp) } } - /* * Determine whether disk supports Data Integrity Field. */ @@ -2132,6 +2201,9 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, /* Logical blocks per physical block exponent */ sdkp->physical_block_size = (1 << (buffer[13] & 0xf)) * sector_size; + /* RC basis */ + sdkp->rc_basis = (buffer[12] >> 4) & 0x3; + /* Lowest aligned logical block */ alignment = ((buffer[14] & 0x3f) << 8 | buffer[15]) * sector_size; blk_queue_alignment_offset(sdp->request_queue, alignment); @@ -2322,6 +2394,11 @@ got_data: sector_size = 512; } blk_queue_logical_block_size(sdp->request_queue, sector_size); + blk_queue_physical_block_size(sdp->request_queue, + sdkp->physical_block_size); + sdkp->device->sector_size = sector_size; + + sd_zbc_read_zones(sdkp, buffer); { char cap_str_2[10], cap_str_10[10]; @@ -2348,9 +2425,6 @@ got_data: if (sdkp->capacity > 0xffffffff) sdp->use_16_for_rw = 1; - blk_queue_physical_block_size(sdp->request_queue, - sdkp->physical_block_size); - sdkp->device->sector_size = sector_size; } /* called with buffer of length 512 */ @@ -2612,7 +2686,7 @@ static void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer) struct scsi_mode_data data; struct scsi_sense_hdr sshdr; - if (sdp->type != TYPE_DISK) + if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC) return; if (sdkp->protection_type == 0) @@ -2719,6 +2793,7 @@ static void sd_read_block_limits(struct scsi_disk *sdkp) */ static void sd_read_block_characteristics(struct scsi_disk *sdkp) { + struct request_queue *q = sdkp->disk->queue; unsigned char *buffer; u16 rot; const int vpd_len = 64; @@ -2733,10 +2808,21 @@ static void sd_read_block_characteristics(struct scsi_disk *sdkp) rot = get_unaligned_be16(&buffer[4]); if (rot == 1) { - queue_flag_set_unlocked(QUEUE_FLAG_NONROT, sdkp->disk->queue); - queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, sdkp->disk->queue); + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q); + queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q); } + sdkp->zoned = (buffer[8] >> 4) & 3; + if (sdkp->zoned == 1) + q->limits.zoned = BLK_ZONED_HA; + else if (sdkp->device->type == TYPE_ZBC) + q->limits.zoned = BLK_ZONED_HM; + else + q->limits.zoned = BLK_ZONED_NONE; + if (blk_queue_zoned(q) && sdkp->first_scan) + sd_printk(KERN_NOTICE, sdkp, "Host-%s zoned block device\n", + q->limits.zoned == BLK_ZONED_HM ? "managed" : "aware"); + out: kfree(buffer); } @@ -2835,14 +2921,14 @@ static int sd_revalidate_disk(struct gendisk *disk) * react badly if we do. */ if (sdkp->media_present) { - sd_read_capacity(sdkp, buffer); - if (scsi_device_supports_vpd(sdp)) { sd_read_block_provisioning(sdkp); sd_read_block_limits(sdkp); sd_read_block_characteristics(sdkp); } + sd_read_capacity(sdkp, buffer); + sd_read_write_protect_flag(sdkp, buffer); sd_read_cache_type(sdkp, buffer); sd_read_app_tag_own(sdkp, buffer); @@ -3040,9 +3126,16 @@ static int sd_probe(struct device *dev) scsi_autopm_get_device(sdp); error = -ENODEV; - if (sdp->type != TYPE_DISK && sdp->type != TYPE_MOD && sdp->type != TYPE_RBC) + if (sdp->type != TYPE_DISK && + sdp->type != TYPE_ZBC && + sdp->type != TYPE_MOD && + sdp->type != TYPE_RBC) goto out; +#ifndef CONFIG_BLK_DEV_ZONED + if (sdp->type == TYPE_ZBC) + goto out; +#endif SCSI_LOG_HLQUEUE(3, sdev_printk(KERN_INFO, sdp, "sd_probe\n")); @@ -3146,6 +3239,8 @@ static int sd_remove(struct device *dev) del_gendisk(sdkp->disk); sd_shutdown(dev); + sd_zbc_remove(sdkp); + blk_register_region(devt, SD_MINORS, NULL, sd_default_probe, NULL, NULL); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 765a6f1..3452871 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -56,6 +56,7 @@ enum { SD_LBP_WS16, /* Use WRITE SAME(16) with UNMAP bit */ SD_LBP_WS10, /* Use WRITE SAME(10) with UNMAP bit */ SD_LBP_ZERO, /* Use WRITE SAME(10) with zero payload */ + SD_ZBC_RESET_WP, /* Use RESET WRITE POINTER */ SD_LBP_DISABLE, /* Discard disabled due to failed cmd */ }; @@ -64,6 +65,11 @@ struct scsi_disk { struct scsi_device *device; struct device dev; struct gendisk *disk; +#ifdef CONFIG_BLK_DEV_ZONED + struct workqueue_struct *zone_work_q; + sector_t zone_sectors; + unsigned int nr_zones; +#endif atomic_t openers; sector_t capacity; /* size in logical blocks */ u32 max_xfer_blocks; @@ -94,6 +100,8 @@ struct scsi_disk { unsigned lbpvpd : 1; unsigned ws10 : 1; unsigned ws16 : 1; + unsigned rc_basis: 2; + unsigned zoned: 2; }; #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev) @@ -156,6 +164,13 @@ static inline unsigned int logical_to_bytes(struct scsi_device *sdev, sector_t b return blocks * sdev->sector_size; } +static inline sector_t sectors_to_logical(struct scsi_device *sdev, sector_t sector) +{ + return sector >> (ilog2(sdev->sector_size) - 9); +} + +extern void sd_config_discard(struct scsi_disk *, unsigned int); + /* * A DIF-capable target device can be formatted with different * protection schemes. Currently 0 through 3 are defined: @@ -269,4 +284,57 @@ static inline void sd_dif_complete(struct scsi_cmnd *cmd, unsigned int a) #endif /* CONFIG_BLK_DEV_INTEGRITY */ +#ifdef CONFIG_BLK_DEV_ZONED + +extern void sd_zbc_read_zones(struct scsi_disk *, char *); +extern void sd_zbc_remove(struct scsi_disk *); +extern int sd_zbc_setup_read_write(struct scsi_disk *, struct request *, + sector_t, unsigned int *); +extern int sd_zbc_setup_report_cmnd(struct scsi_cmnd *); +extern int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *); +extern int sd_zbc_setup_open_cmnd(struct scsi_cmnd *); +extern int sd_zbc_setup_close_cmnd(struct scsi_cmnd *); +extern int sd_zbc_setup_finish_cmnd(struct scsi_cmnd *); +extern void sd_zbc_done(struct scsi_cmnd *, struct scsi_sense_hdr *); + +#else /* CONFIG_BLK_DEV_ZONED */ + +static inline void sd_zbc_read_zones(struct scsi_disk *sdkp, + unsigned char *buf) {} +static inline void sd_zbc_remove(struct scsi_disk *sdkp) {} + +static inline int sd_zbc_setup_read_write(struct scsi_disk *sdkp, + struct request *rq, sector_t sector, + unsigned int *num_sectors) +{ + /* Let the drive fail requests */ + return BLKPREP_OK; +} + +static inline int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd) +{ + return BLKPREP_KILL; +} +static inline int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd) +{ + return BLKPREP_KILL; +} +static inline int sd_zbc_setup_open_cmnd(struct scsi_cmnd *cmd) +{ + return BLKPREP_KILL; +} +static inline int sd_zbc_setup_close_cmnd(struct scsi_cmnd *cmd) +{ + return BLKPREP_KILL; +} +static inline int sd_zbc_setup_finish_cmnd(struct scsi_cmnd *cmd) +{ + return BLKPREP_KILL; +} + +static inline void sd_zbc_done(struct scsi_cmnd *cmd, + struct scsi_sense_hdr *sshdr) {} + +#endif /* CONFIG_BLK_DEV_ZONED */ + #endif /* _SCSI_DISK_H */ diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c new file mode 100644 index 0000000..ec9c3fc --- /dev/null +++ b/drivers/scsi/sd_zbc.c @@ -0,0 +1,1097 @@ +/* + * SCSI Zoned Block commands + * + * Copyright (C) 2014-2015 SUSE Linux GmbH + * Written by: Hannes Reinecke <hare@suse.de> + * Modified by: Damien Le Moal <damien.lemoal@hgst.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + */ + +#include <linux/blkdev.h> +#include <linux/rbtree.h> + +#include <asm/unaligned.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_dbg.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_driver.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_eh.h> + +#include "sd.h" +#include "scsi_priv.h" + +enum zbc_zone_type { + ZBC_ZONE_TYPE_CONV = 0x1, + ZBC_ZONE_TYPE_SEQWRITE_REQ, + ZBC_ZONE_TYPE_SEQWRITE_PREF, + ZBC_ZONE_TYPE_RESERVED, +}; + +enum zbc_zone_cond { + ZBC_ZONE_COND_NO_WP, + ZBC_ZONE_COND_EMPTY, + ZBC_ZONE_COND_IMP_OPEN, + ZBC_ZONE_COND_EXP_OPEN, + ZBC_ZONE_COND_CLOSED, + ZBC_ZONE_COND_READONLY = 0xd, + ZBC_ZONE_COND_FULL, + ZBC_ZONE_COND_OFFLINE, +}; + +#define SD_ZBC_BUF_SIZE 131072 + +#define sd_zbc_debug(sdkp, fmt, args...) \ + pr_debug("%s %s [%s]: " fmt, \ + dev_driver_string(&(sdkp)->device->sdev_gendev), \ + dev_name(&(sdkp)->device->sdev_gendev), \ + (sdkp)->disk->disk_name, ## args) + +#define sd_zbc_debug_ratelimit(sdkp, fmt, args...) \ + do { \ + if (printk_ratelimit()) \ + sd_zbc_debug(sdkp, fmt, ## args); \ + } while( 0 ) + +#define sd_zbc_err(sdkp, fmt, args...) \ + pr_err("%s %s [%s]: " fmt, \ + dev_driver_string(&(sdkp)->device->sdev_gendev), \ + dev_name(&(sdkp)->device->sdev_gendev), \ + (sdkp)->disk->disk_name, ## args) + +struct zbc_zone_work { + struct work_struct zone_work; + struct scsi_disk *sdkp; + sector_t sector; + sector_t nr_sects; + bool init; + unsigned int nr_zones; +}; + +struct blk_zone *zbc_desc_to_zone(struct scsi_disk *sdkp, unsigned char *rec) +{ + struct blk_zone *zone; + + zone = kzalloc(sizeof(struct blk_zone), GFP_KERNEL); + if (!zone) + return NULL; + + /* Zone type */ + switch(rec[0] & 0x0f) { + case ZBC_ZONE_TYPE_CONV: + case ZBC_ZONE_TYPE_SEQWRITE_REQ: + case ZBC_ZONE_TYPE_SEQWRITE_PREF: + zone->type = rec[0] & 0x0f; + break; + default: + zone->type = BLK_ZONE_TYPE_UNKNOWN; + break; + } + + /* Zone condition */ + zone->cond = (rec[1] >> 4) & 0xf; + if (rec[1] & 0x01) + zone->reset = 1; + if (rec[1] & 0x02) + zone->non_seq = 1; + + /* Zone start sector and length */ + zone->len = logical_to_sectors(sdkp->device, + get_unaligned_be64(&rec[8])); + zone->start = logical_to_sectors(sdkp->device, + get_unaligned_be64(&rec[16])); + + /* Zone write pointer */ + if (blk_zone_is_empty(zone) && + zone->wp != zone->start) + zone->wp = zone->start; + else if (blk_zone_is_full(zone)) + zone->wp = zone->start + zone->len; + else if (blk_zone_is_seq(zone)) + zone->wp = logical_to_sectors(sdkp->device, + get_unaligned_be64(&rec[24])); + else + zone->wp = (sector_t)-1; + + return zone; +} + +static int zbc_parse_zones(struct scsi_disk *sdkp, unsigned char *buf, + unsigned int buf_len, sector_t *next_sector) +{ + struct request_queue *q = sdkp->disk->queue; + sector_t capacity = logical_to_sectors(sdkp->device, sdkp->capacity); + unsigned char *rec = buf; + unsigned int zone_len, list_length; + + /* Parse REPORT ZONES header */ + list_length = get_unaligned_be32(&buf[0]); + rec = buf + 64; + list_length += 64; + + if (list_length < buf_len) + buf_len = list_length; + + /* Parse REPORT ZONES zone descriptors */ + *next_sector = capacity; + while (rec < buf + buf_len) { + + struct blk_zone *new, *old; + + new = zbc_desc_to_zone(sdkp, rec); + if (!new) + return -ENOMEM; + + zone_len = new->len; + *next_sector = new->start + zone_len; + + old = blk_insert_zone(q, new); + if (old) { + blk_lock_zone(old); + + /* + * Always update the zone state flags and the zone + * offline and read-only condition as the drive may + * change those independently of the commands being + * executed + */ + old->reset = new->reset; + old->non_seq = new->non_seq; + if (blk_zone_is_offline(new) || + blk_zone_is_readonly(new)) + old->cond = new->cond; + + if (blk_zone_in_update(old)) { + old->cond = new->cond; + old->wp = new->wp; + blk_clear_zone_update(old); + } + + blk_unlock_zone(old); + + kfree(new); + } + + rec += 64; + + } + + return 0; +} + +/** + * sd_zbc_report_zones - Issue a REPORT ZONES scsi command + * @sdkp: SCSI disk to which the command should be send + * @buffer: response buffer + * @bufflen: length of @buffer + * @start_sector: logical sector for the zone information should be reported + * @option: reporting option to be used + * @partial: flag to set the 'partial' bit for report zones command + */ +int sd_zbc_report_zones(struct scsi_disk *sdkp, unsigned char *buffer, + int bufflen, sector_t start_sector, + enum zbc_zone_reporting_options option, bool partial) +{ + struct scsi_device *sdp = sdkp->device; + const int timeout = sdp->request_queue->rq_timeout; + struct scsi_sense_hdr sshdr; + sector_t start_lba = sectors_to_logical(sdkp->device, start_sector); + unsigned char cmd[16]; + int result; + + if (!scsi_device_online(sdp)) + return -ENODEV; + + sd_zbc_debug(sdkp, "REPORT ZONES lba %zu len %d\n", + start_lba, bufflen); + + memset(cmd, 0, 16); + cmd[0] = ZBC_IN; + cmd[1] = ZI_REPORT_ZONES; + put_unaligned_be64(start_lba, &cmd[2]); + put_unaligned_be32(bufflen, &cmd[10]); + cmd[14] = (partial ? ZBC_REPORT_ZONE_PARTIAL : 0) | option; + memset(buffer, 0, bufflen); + + result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE, + buffer, bufflen, &sshdr, + timeout, SD_MAX_RETRIES, NULL); + + if (result) { + sd_zbc_err(sdkp, + "REPORT ZONES lba %zu failed with %d/%d\n", + start_lba, host_byte(result), driver_byte(result)); + return -EIO; + } + + return 0; +} + +/** + * Set or clear the update flag of all zones contained + * in the range sector..sector+nr_sects. + * Return the number of zones marked/cleared. + */ +static int __sd_zbc_zones_updating(struct scsi_disk *sdkp, + sector_t sector, sector_t nr_sects, + bool set) +{ + struct request_queue *q = sdkp->disk->queue; + struct blk_zone *zone; + struct rb_node *node; + unsigned long flags; + int nr_zones = 0; + + if (!nr_sects) { + /* All zones */ + sector = 0; + nr_sects = logical_to_sectors(sdkp->device, sdkp->capacity); + } + + spin_lock_irqsave(&q->zones_lock, flags); + for (node = rb_first(&q->zones); node && nr_sects; node = rb_next(node)) { + zone = rb_entry(node, struct blk_zone, node); + if (sector < zone->start || sector >= (zone->start + zone->len)) + continue; + if (set) { + if (!test_and_set_bit_lock(BLK_ZONE_IN_UPDATE, &zone->flags)) + nr_zones++; + } else if (test_and_clear_bit(BLK_ZONE_IN_UPDATE, &zone->flags)) { + wake_up_bit(&zone->flags, BLK_ZONE_IN_UPDATE); + nr_zones++; + } + sector = zone->start + zone->len; + if (nr_sects <= zone->len) + nr_sects = 0; + else + nr_sects -= zone->len; + } + spin_unlock_irqrestore(&q->zones_lock, flags); + + return nr_zones; +} + +static inline int sd_zbc_set_zones_updating(struct scsi_disk *sdkp, + sector_t sector, sector_t nr_sects) +{ + return __sd_zbc_zones_updating(sdkp, sector, nr_sects, true); +} + +static inline int sd_zbc_clear_zones_updating(struct scsi_disk *sdkp, + sector_t sector, sector_t nr_sects) +{ + return __sd_zbc_zones_updating(sdkp, sector, nr_sects, false); +} + +static void sd_zbc_start_queue(struct request_queue *q) +{ + unsigned long flags; + + if (q->mq_ops) { + blk_mq_start_hw_queues(q); + } else { + spin_lock_irqsave(q->queue_lock, flags); + blk_start_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + } +} + +static void sd_zbc_update_zone_work(struct work_struct *work) +{ + struct zbc_zone_work *zwork = + container_of(work, struct zbc_zone_work, zone_work); + struct scsi_disk *sdkp = zwork->sdkp; + sector_t capacity = logical_to_sectors(sdkp->device, sdkp->capacity); + struct request_queue *q = sdkp->disk->queue; + sector_t end_sector, sector = zwork->sector; + unsigned int bufsize; + unsigned char *buf; + int ret = -ENOMEM; + + /* Get a buffer */ + if (!zwork->nr_zones) { + bufsize = SD_ZBC_BUF_SIZE; + } else { + bufsize = (zwork->nr_zones + 1) * 64; + if (bufsize < 512) + bufsize = 512; + else if (bufsize > SD_ZBC_BUF_SIZE) + bufsize = SD_ZBC_BUF_SIZE; + else + bufsize = (bufsize + 511) & ~511; + } + buf = kmalloc(bufsize, GFP_KERNEL | GFP_DMA); + if (!buf) { + sd_zbc_err(sdkp, "Failed to allocate zone report buffer\n"); + goto done_free; + } + + /* Process sector range */ + end_sector = zwork->sector + zwork->nr_sects; + while(sector < min(end_sector, capacity)) { + + /* Get zone report */ + ret = sd_zbc_report_zones(sdkp, buf, bufsize, sector, + ZBC_ZONE_REPORTING_OPTION_ALL, true); + if (ret) + break; + + ret = zbc_parse_zones(sdkp, buf, bufsize, §or); + if (ret) + break; + + /* Kick start the queue to allow requests waiting */ + /* for the zones just updated to run */ + sd_zbc_start_queue(q); + + } + +done_free: + if (ret) + sd_zbc_clear_zones_updating(sdkp, zwork->sector, zwork->nr_sects); + if (buf) + kfree(buf); + kfree(zwork); +} + +/** + * sd_zbc_update_zones - Update zone information for zones starting + * from @start_sector. If not in init mode, the update is done only + * for zones marked with update flag. + * @sdkp: SCSI disk for which the zone information needs to be updated + * @start_sector: First sector of the first zone to be updated + * @bufsize: buffersize to be allocated for report zones + */ +static int sd_zbc_update_zones(struct scsi_disk *sdkp, + sector_t sector, sector_t nr_sects, + gfp_t gfpflags, bool init) +{ + struct zbc_zone_work *zwork; + + zwork = kzalloc(sizeof(struct zbc_zone_work), gfpflags); + if (!zwork) { + sd_zbc_err(sdkp, "Failed to allocate zone work\n"); + return -ENOMEM; + } + + if (!nr_sects) { + /* All zones */ + sector = 0; + nr_sects = logical_to_sectors(sdkp->device, sdkp->capacity); + } + + INIT_WORK(&zwork->zone_work, sd_zbc_update_zone_work); + zwork->sdkp = sdkp; + zwork->sector = sector; + zwork->nr_sects = nr_sects; + zwork->init = init; + + if (!init) + /* Mark the zones falling in the report as updating */ + zwork->nr_zones = sd_zbc_set_zones_updating(sdkp, sector, nr_sects); + + if (init || zwork->nr_zones) + queue_work(sdkp->zone_work_q, &zwork->zone_work); + else + kfree(zwork); + + return 0; +} + +int sd_zbc_setup_report_cmnd(struct scsi_cmnd *cmd) +{ + struct request *rq = cmd->request; + struct gendisk *disk = rq->rq_disk; + struct scsi_disk *sdkp = scsi_disk(disk); + int ret; + + if (!sdkp->zone_work_q) + return BLKPREP_KILL; + + ret = sd_zbc_update_zones(sdkp, blk_rq_pos(rq), blk_rq_sectors(rq), + GFP_ATOMIC, false); + if (unlikely(ret)) + return BLKPREP_DEFER; + + return BLKPREP_DONE; +} + +static void sd_zbc_setup_action_cmnd(struct scsi_cmnd *cmd, + u8 action, + bool all) +{ + struct request *rq = cmd->request; + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); + sector_t lba; + + cmd->cmd_len = 16; + cmd->cmnd[0] = ZBC_OUT; + cmd->cmnd[1] = action; + if (all) { + cmd->cmnd[14] |= 0x01; + } else { + lba = sectors_to_logical(sdkp->device, blk_rq_pos(rq)); + put_unaligned_be64(lba, &cmd->cmnd[2]); + } + + rq->completion_data = NULL; + rq->timeout = SD_TIMEOUT; + rq->__data_len = blk_rq_bytes(rq); + + /* Don't retry */ + cmd->allowed = 0; + cmd->transfersize = 0; + cmd->sc_data_direction = DMA_NONE; +} + +int sd_zbc_setup_reset_cmnd(struct scsi_cmnd *cmd) +{ + struct request *rq = cmd->request; + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); + sector_t sector = blk_rq_pos(rq); + sector_t nr_sects = blk_rq_sectors(rq); + struct blk_zone *zone = NULL; + int ret = BLKPREP_OK; + + if (nr_sects) { + zone = blk_lookup_zone(rq->q, sector); + if (!zone) + return BLKPREP_KILL; + } + + if (zone) { + + blk_lock_zone(zone); + + /* If the zone is being updated, wait */ + if (blk_zone_in_update(zone)) { + ret = BLKPREP_DEFER; + goto out; + } + + if (zone->type == BLK_ZONE_TYPE_UNKNOWN) { + sd_zbc_debug(sdkp, + "Discarding unknown zone %zu\n", + zone->start); + ret = BLKPREP_KILL; + goto out; + } + + /* Nothing to do for conventional sequential zones */ + if (blk_zone_is_conv(zone)) { + ret = BLKPREP_DONE; + goto out; + } + + if (!blk_try_write_lock_zone(zone)) { + ret = BLKPREP_DEFER; + goto out; + } + + /* Nothing to do if the zone is already empty */ + if (blk_zone_is_empty(zone)) { + blk_write_unlock_zone(zone); + ret = BLKPREP_DONE; + goto out; + } + + if (sector != zone->start || + (nr_sects != zone->len)) { + sd_printk(KERN_ERR, sdkp, + "Unaligned reset wp request, start %zu/%zu" + " len %zu/%zu\n", + zone->start, sector, zone->len, nr_sects); + blk_write_unlock_zone(zone); + ret = BLKPREP_KILL; + goto out; + } + + } + + sd_zbc_setup_action_cmnd(cmd, ZO_RESET_WRITE_POINTER, !zone); + +out: + if (zone) { + if (ret == BLKPREP_OK) { + /* + * Opportunistic update. Will be fixed up + * with zone update if the command fails, + */ + zone->wp = zone->start; + zone->cond = BLK_ZONE_COND_EMPTY; + zone->reset = 0; + zone->non_seq = 0; + } + blk_unlock_zone(zone); + } + + return ret; +} + +int sd_zbc_setup_open_cmnd(struct scsi_cmnd *cmd) +{ + struct request *rq = cmd->request; + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); + sector_t sector = blk_rq_pos(rq); + sector_t nr_sects = blk_rq_sectors(rq); + struct blk_zone *zone = NULL; + int ret = BLKPREP_OK; + + if (nr_sects) { + zone = blk_lookup_zone(rq->q, sector); + if (!zone) + return BLKPREP_KILL; + } + + if (zone) { + + blk_lock_zone(zone); + + /* If the zone is being updated, wait */ + if (blk_zone_in_update(zone)) { + ret = BLKPREP_DEFER; + goto out; + } + + if (zone->type == BLK_ZONE_TYPE_UNKNOWN) { + sd_zbc_debug(sdkp, + "Opening unknown zone %zu\n", + zone->start); + ret = BLKPREP_KILL; + goto out; + } + + /* + * Nothing to do for conventional zones, + * zones already open or full zones. + */ + if (blk_zone_is_conv(zone) || + blk_zone_is_open(zone) || + blk_zone_is_full(zone)) { + ret = BLKPREP_DONE; + goto out; + } + + if (sector != zone->start || + (nr_sects != zone->len)) { + sd_printk(KERN_ERR, sdkp, + "Unaligned open zone request, start %zu/%zu" + " len %zu/%zu\n", + zone->start, sector, zone->len, nr_sects); + ret = BLKPREP_KILL; + goto out; + } + + } + + sd_zbc_setup_action_cmnd(cmd, ZO_OPEN_ZONE, !zone); + +out: + if (zone) { + if (ret == BLKPREP_OK) + /* + * Opportunistic update. Will be fixed up + * with zone update if the command fails. + */ + zone->cond = BLK_ZONE_COND_EXP_OPEN; + blk_unlock_zone(zone); + } + + return ret; +} + +int sd_zbc_setup_close_cmnd(struct scsi_cmnd *cmd) +{ + struct request *rq = cmd->request; + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); + sector_t sector = blk_rq_pos(rq); + sector_t nr_sects = blk_rq_sectors(rq); + struct blk_zone *zone = NULL; + int ret = BLKPREP_OK; + + if (nr_sects) { + zone = blk_lookup_zone(rq->q, sector); + if (!zone) + return BLKPREP_KILL; + } + + if (zone) { + + blk_lock_zone(zone); + + /* If the zone is being updated, wait */ + if (blk_zone_in_update(zone)) { + ret = BLKPREP_DEFER; + goto out; + } + + if (zone->type == BLK_ZONE_TYPE_UNKNOWN) { + sd_zbc_debug(sdkp, + "Closing unknown zone %zu\n", + zone->start); + ret = BLKPREP_KILL; + goto out; + } + + /* + * Nothing to do for conventional zones, + * full zones or empty zones. + */ + if (blk_zone_is_conv(zone) || + blk_zone_is_full(zone) || + blk_zone_is_empty(zone)) { + ret = BLKPREP_DONE; + goto out; + } + + if (sector != zone->start || + (nr_sects != zone->len)) { + sd_printk(KERN_ERR, sdkp, + "Unaligned close zone request, start %zu/%zu" + " len %zu/%zu\n", + zone->start, sector, zone->len, nr_sects); + ret = BLKPREP_KILL; + goto out; + } + + } + + sd_zbc_setup_action_cmnd(cmd, ZO_CLOSE_ZONE, !zone); + +out: + if (zone) { + if (ret == BLKPREP_OK) + /* + * Opportunistic update. Will be fixed up + * with zone update if the command fails. + */ + zone->cond = BLK_ZONE_COND_CLOSED; + blk_unlock_zone(zone); + } + + return ret; +} + +int sd_zbc_setup_finish_cmnd(struct scsi_cmnd *cmd) +{ + struct request *rq = cmd->request; + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); + sector_t sector = blk_rq_pos(rq); + sector_t nr_sects = blk_rq_sectors(rq); + struct blk_zone *zone = NULL; + int ret = BLKPREP_OK; + + if (nr_sects) { + zone = blk_lookup_zone(rq->q, sector); + if (!zone) + return BLKPREP_KILL; + } + + if (zone) { + + blk_lock_zone(zone); + + /* If the zone is being updated, wait */ + if (blk_zone_in_update(zone)) { + ret = BLKPREP_DEFER; + goto out; + } + + if (zone->type == BLK_ZONE_TYPE_UNKNOWN) { + sd_zbc_debug(sdkp, + "Finishing unknown zone %zu\n", + zone->start); + ret = BLKPREP_KILL; + goto out; + } + + /* Nothing to do for conventional zones and full zones */ + if (blk_zone_is_conv(zone) || + blk_zone_is_full(zone)) { + ret = BLKPREP_DONE; + goto out; + } + + if (sector != zone->start || + (nr_sects != zone->len)) { + sd_printk(KERN_ERR, sdkp, + "Unaligned finish zone request, start %zu/%zu" + " len %zu/%zu\n", + zone->start, sector, zone->len, nr_sects); + ret = BLKPREP_KILL; + goto out; + } + + } + + sd_zbc_setup_action_cmnd(cmd, ZO_FINISH_ZONE, !zone); + +out: + if (zone) { + if (ret == BLKPREP_OK) { + /* + * Opportunistic update. Will be fixed up + * with zone update if the command fails. + */ + zone->cond = BLK_ZONE_COND_FULL; + if (blk_zone_is_seq(zone)) + zone->wp = zone->start + zone->len; + } + blk_unlock_zone(zone); + } + + return ret; +} + +int sd_zbc_setup_read_write(struct scsi_disk *sdkp, struct request *rq, + sector_t sector, unsigned int *num_sectors) +{ + struct blk_zone *zone; + unsigned int sectors = *num_sectors; + int ret = BLKPREP_OK; + + zone = blk_lookup_zone(rq->q, sector); + if (!zone) + /* Let the drive handle the request */ + return BLKPREP_OK; + + blk_lock_zone(zone); + + /* If the zone is being updated, wait */ + if (blk_zone_in_update(zone)) { + ret = BLKPREP_DEFER; + goto out; + } + + if (zone->type == BLK_ZONE_TYPE_UNKNOWN) { + sd_zbc_debug(sdkp, + "Unknown zone %zu\n", + zone->start); + ret = BLKPREP_KILL; + goto out; + } + + /* For offline and read-only zones, let the drive fail the command */ + if (blk_zone_is_offline(zone) || + blk_zone_is_readonly(zone)) + goto out; + + /* Do not allow zone boundaries crossing */ + if (sector + sectors > zone->start + zone->len) { + ret = BLKPREP_KILL; + goto out; + } + + /* For conventional zones, no checks */ + if (blk_zone_is_conv(zone)) + goto out; + + if (req_op(rq) == REQ_OP_WRITE || + req_op(rq) == REQ_OP_WRITE_SAME) { + + /* + * Write requests may change the write pointer and + * transition the zone condition to full. Changes + * are oportunistic here. If the request fails, a + * zone update will fix the zone information. + */ + if (blk_zone_is_seq_req(zone)) { + + /* + * Do not issue more than one write at a time per + * zone. This solves write ordering problems due to + * the unlocking of the request queue in the dispatch + * path in the non scsi-mq case. For scsi-mq, this + * also avoids potential write reordering when multiple + * threads running on different CPUs write to the same + * zone (with a synchronized sequential pattern). + */ + if (!blk_try_write_lock_zone(zone)) { + ret = BLKPREP_DEFER; + goto out; + } + + /* For host-managed drives, writes are allowed */ + /* only at the write pointer position. */ + if (zone->wp != sector) { + blk_write_unlock_zone(zone); + ret = BLKPREP_KILL; + goto out; + } + + zone->wp += sectors; + if (zone->wp >= zone->start + zone->len) { + zone->cond = BLK_ZONE_COND_FULL; + zone->wp = zone->start + zone->len; + } + + } else { + + /* For host-aware drives, writes are allowed */ + /* anywhere in the zone, but wp can only go */ + /* forward. */ + sector_t end_sector = sector + sectors; + if (sector == zone->wp && + end_sector >= zone->start + zone->len) { + zone->cond = BLK_ZONE_COND_FULL; + zone->wp = zone->start + zone->len; + } else if (end_sector > zone->wp) { + zone->wp = end_sector; + } + + } + + } else { + + /* Check read after write pointer */ + if (sector + sectors <= zone->wp) + goto out; + + if (zone->wp <= sector) { + /* Read beyond WP: clear request buffer */ + struct req_iterator iter; + struct bio_vec bvec; + unsigned long flags; + void *buf; + rq_for_each_segment(bvec, rq, iter) { + buf = bvec_kmap_irq(&bvec, &flags); + memset(buf, 0, bvec.bv_len); + flush_dcache_page(bvec.bv_page); + bvec_kunmap_irq(buf, &flags); + } + ret = BLKPREP_DONE; + goto out; + } + + /* Read straddle WP position: limit request size */ + *num_sectors = zone->wp - sector; + + } + +out: + blk_unlock_zone(zone); + + return ret; +} + +void sd_zbc_done(struct scsi_cmnd *cmd, + struct scsi_sense_hdr *sshdr) +{ + int result = cmd->result; + struct request *rq = cmd->request; + struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); + struct request_queue *q = sdkp->disk->queue; + sector_t pos = blk_rq_pos(rq); + struct blk_zone *zone = NULL; + bool write_unlock = false; + + /* + * Get the target zone of commands of interest. Some may + * apply to all zones so check the request sectors first. + */ + switch (req_op(rq)) { + case REQ_OP_DISCARD: + case REQ_OP_WRITE: + case REQ_OP_WRITE_SAME: + case REQ_OP_ZONE_RESET: + write_unlock = true; + /* fallthru */ + case REQ_OP_ZONE_OPEN: + case REQ_OP_ZONE_CLOSE: + case REQ_OP_ZONE_FINISH: + if (blk_rq_sectors(rq)) + zone = blk_lookup_zone(q, pos); + break; + } + + if (zone && write_unlock) + blk_write_unlock_zone(zone); + + if (!result) + return; + + if (sshdr->sense_key == ILLEGAL_REQUEST && + sshdr->asc == 0x21) + /* + * It is unlikely that retrying requests failed with any + * kind of alignement error will result in success. So don't + * try. Report the error back to the user quickly so that + * corrective actions can be taken after obtaining updated + * zone information. + */ + cmd->allowed = 0; + + /* On error, force an update unless this is a failed report */ + if (req_op(rq) == REQ_OP_ZONE_REPORT) + sd_zbc_clear_zones_updating(sdkp, pos, blk_rq_sectors(rq)); + else if (zone) + sd_zbc_update_zones(sdkp, zone->start, zone->len, + GFP_ATOMIC, false); +} + +void sd_zbc_read_zones(struct scsi_disk *sdkp, char *buf) +{ + struct request_queue *q = sdkp->disk->queue; + struct blk_zone *zone; + sector_t capacity; + sector_t sector; + bool init = false; + u32 rep_len; + int ret = 0; + + if (sdkp->zoned != 1 && sdkp->device->type != TYPE_ZBC) + /* + * Device managed or normal SCSI disk, + * no special handling required + */ + return; + + /* Do a report zone to get the maximum LBA to check capacity */ + ret = sd_zbc_report_zones(sdkp, buf, SD_BUF_SIZE, + 0, ZBC_ZONE_REPORTING_OPTION_ALL, false); + if (ret < 0) + return; + + rep_len = get_unaligned_be32(&buf[0]); + if (rep_len < 64) { + sd_printk(KERN_WARNING, sdkp, + "REPORT ZONES report invalid length %u\n", + rep_len); + return; + } + + if (sdkp->rc_basis == 0) { + /* The max_lba field is the capacity of this device */ + sector_t lba = get_unaligned_be64(&buf[8]); + if (lba + 1 > sdkp->capacity) { + if (sdkp->first_scan) + sd_printk(KERN_WARNING, sdkp, + "Changing capacity from %zu " + "to max LBA+1 %zu\n", + sdkp->capacity, + (sector_t) lba + 1); + sdkp->capacity = lba + 1; + } + } + + /* Setup the zone work queue */ + if (! sdkp->zone_work_q) { + sdkp->zone_work_q = + alloc_ordered_workqueue("zbc_wq_%s", WQ_MEM_RECLAIM, + sdkp->disk->disk_name); + if (!sdkp->zone_work_q) { + sdev_printk(KERN_WARNING, sdkp->device, + "Create zoned disk workqueue failed\n"); + return; + } + init = true; + } + + /* + * Parse what we already got. If all zones are not parsed yet, + * kick start an update to get the remaining. + */ + capacity = logical_to_sectors(sdkp->device, sdkp->capacity); + ret = zbc_parse_zones(sdkp, buf, SD_BUF_SIZE, §or); + if (ret == 0 && sector < capacity) { + sd_zbc_update_zones(sdkp, sector, capacity - sector, + GFP_KERNEL, init); + drain_workqueue(sdkp->zone_work_q); + } + if (ret) + return; + + /* + * Analyze the zones layout: if all zones are the same size and + * the size is a power of 2, chunk the device and map discard to + * reset write pointer command. Otherwise, disable discard. + */ + sdkp->zone_sectors = 0; + sdkp->nr_zones = 0; + sector = 0; + while(sector < capacity) { + + zone = blk_lookup_zone(q, sector); + if (!zone) { + sdkp->zone_sectors = 0; + sdkp->nr_zones = 0; + break; + } + + sector += zone->len; + + if (sdkp->zone_sectors == 0) { + sdkp->zone_sectors = zone->len; + } else if (sector != capacity && + zone->len != sdkp->zone_sectors) { + sdkp->zone_sectors = 0; + sdkp->nr_zones = 0; + break; + } + + sdkp->nr_zones++; + + } + + if (!sdkp->zone_sectors || + !is_power_of_2(sdkp->zone_sectors)) { + sd_config_discard(sdkp, SD_LBP_DISABLE); + if (sdkp->first_scan) + sd_printk(KERN_NOTICE, sdkp, + "%u zones (non constant zone size)\n", + sdkp->nr_zones); + return; + } + + /* Setup discard granularity to the zone size */ + blk_queue_chunk_sectors(sdkp->disk->queue, sdkp->zone_sectors); + sdkp->max_unmap_blocks = sdkp->zone_sectors; + sdkp->unmap_alignment = sectors_to_logical(sdkp->device, + sdkp->zone_sectors); + sdkp->unmap_granularity = sdkp->unmap_alignment; + sd_config_discard(sdkp, SD_ZBC_RESET_WP); + + if (sdkp->first_scan) { + if (sdkp->nr_zones * sdkp->zone_sectors == capacity) + sd_printk(KERN_NOTICE, sdkp, + "%u zones of %zu sectors\n", + sdkp->nr_zones, + sdkp->zone_sectors); + else + sd_printk(KERN_NOTICE, sdkp, + "%u zones of %zu sectors " + "+ 1 runt zone\n", + sdkp->nr_zones - 1, + sdkp->zone_sectors); + } +} + +void sd_zbc_remove(struct scsi_disk *sdkp) +{ + + sd_config_discard(sdkp, SD_LBP_DISABLE); + + if (sdkp->zone_work_q) { + drain_workqueue(sdkp->zone_work_q); + destroy_workqueue(sdkp->zone_work_q); + sdkp->zone_work_q = NULL; + blk_drop_zones(sdkp->disk->queue); + } +} + diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index d1defd1..6ba66e0 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -299,4 +299,21 @@ struct scsi_lun { #define SCSI_ACCESS_STATE_MASK 0x0f #define SCSI_ACCESS_STATE_PREFERRED 0x80 +/* Reporting options for REPORT ZONES */ +enum zbc_zone_reporting_options { + ZBC_ZONE_REPORTING_OPTION_ALL = 0, + ZBC_ZONE_REPORTING_OPTION_EMPTY, + ZBC_ZONE_REPORTING_OPTION_IMPLICIT_OPEN, + ZBC_ZONE_REPORTING_OPTION_EXPLICIT_OPEN, + ZBC_ZONE_REPORTING_OPTION_CLOSED, + ZBC_ZONE_REPORTING_OPTION_FULL, + ZBC_ZONE_REPORTING_OPTION_READONLY, + ZBC_ZONE_REPORTING_OPTION_OFFLINE, + ZBC_ZONE_REPORTING_OPTION_NEED_RESET_WP = 0x10, + ZBC_ZONE_REPORTING_OPTION_NON_SEQWRITE, + ZBC_ZONE_REPORTING_OPTION_NON_WP = 0x3f, +}; + +#define ZBC_REPORT_ZONE_PARTIAL 0x80 + #endif /* _SCSI_PROTO_H_ */