diff --git a/scst/README b/scst/README index 735a6f803..dbff71ad0 100644 --- a/scst/README +++ b/scst/README @@ -576,6 +576,9 @@ subdirectories "vdisk" and "vcdrom". They have similar layout: * "close NAME" - closes device "NAME". + * "resync_size NAME" - refreshes size of device "NAME". Intended to be + used after device resize. + * "change NAME [PATH]" - changes a virtual CD in the VDISK CDROM. By default, if neither BLOCKIO, nor NULLIO option is supplied, FILEIO diff --git a/scst/README_in-tree b/scst/README_in-tree index a7fceb4bc..22048234c 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -515,6 +515,9 @@ subdirectories "vdisk" and "vcdrom". They have similar layout: * "close NAME" - closes device "NAME". + * "resync_size NAME" - refreshes size of device "NAME". Intended to be + used after device resize. + * "change NAME [PATH]" - changes a virtual CD in the VDISK CDROM. By default, if neither BLOCKIO, nor NULLIO option is supplied, FILEIO diff --git a/scst/include/scst.h b/scst/include/scst.h index eb207e859..d527a6752 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -269,8 +269,36 @@ enum scst_exec_context { #define SCST_PREPROCESS_STATUS_NEED_THREAD 4 /************************************************************* - ** Allowed return codes for xmit_response(), rdy_to_xfer(), - ** report_aen() + ** Values for AEN functions + *************************************************************/ + +/* + * SCSI Asynchronous Event. Parameter contains SCSI sense + * (Unit Attention). AENs generated only for 2 the following UAs: + * CAPACITY DATA HAS CHANGED and REPORTED LUNS DATA HAS CHANGED. + * Other UAs reported regularly as CHECK CONDITION status, + * because it doesn't look safe to report them using AENs, since + * reporting using AENs opens delivery race windows even in case of + * untagged commands. + */ +#define SCST_AEN_SCSI 0 + +/************************************************************* + ** Allowed return/status codes for report_aen() callback and + ** scst_set_aen_delivery_status() function + *************************************************************/ + +/* Success */ +#define SCST_AEN_RES_SUCCESS 0 + +/* Not supported */ +#define SCST_AEN_RES_NOT_SUPPORTED -1 + +/* Failure */ +#define SCST_AEN_RES_FAILED -2 + +/************************************************************* + ** Allowed return codes for xmit_response(), rdy_to_xfer() *************************************************************/ /* Success */ @@ -408,6 +436,21 @@ enum scst_exec_context { (__flags), NULL, NULL) #endif +/************************************************************* + ** Vlaid_mask constants for scst_analyze_sense() + *************************************************************/ + +#define SCST_SENSE_KEY_VALID 1 +#define SCST_SENSE_ASC_VALID 2 +#define SCST_SENSE_ASCQ_VALID 4 + +#define SCST_SENSE_ASCx_VALID (SCST_SENSE_ASC_VALID | \ + SCST_SENSE_ASCQ_VALID) + +#define SCST_SENSE_ALL_VALID (SCST_SENSE_KEY_VALID | \ + SCST_SENSE_ASC_VALID | \ + SCST_SENSE_ASCQ_VALID) + /************************************************************* * TYPES *************************************************************/ @@ -422,6 +465,7 @@ struct scst_dev_type; struct scst_acg; struct scst_acg_dev; struct scst_acn; +struct scst_aen; /* * SCST uses 64-bit numbers to represent LUN's internally. The value @@ -665,17 +709,17 @@ struct scst_tgt_template { int (*release) (struct scst_tgt *tgt); /* - * This function is used for Asynchronous Event Notification. - * It is the responsibility of the driver to notify any/all - * initiators about the Asynchronous Event reported. - * Returns one of the SCST_TGT_RES_* constants. + * This function is used for Asynchronous Event Notifications. + * + * Returns one of the SCST_AEN_RES_* constants. + * After AEN is sent, target driver must call scst_aen_done() and, + * optionally, scst_set_aen_delivery_status(). + * * This command is expected to be NON-BLOCKING, but can sleep. * - * MUST HAVE if low-level protocol supports AEN - * - * ToDo + * MUST HAVE, if low-level protocol supports AENs. */ - int (*report_aen) (int mgmt_fn, const uint8_t *lun, int lun_len); + int (*report_aen) (struct scst_aen *aen); /* * Those functions can be used to export the driver's statistics and @@ -1598,10 +1642,33 @@ struct scst_acn { struct scst_tgt_dev_UA { /* List entry in tgt_dev->UA_list */ struct list_head UA_list_entry; + + /* Set if UA is global for session */ + unsigned int global_UA:1; + /* Unit Attention sense */ uint8_t UA_sense_buffer[SCST_SENSE_BUFFERSIZE]; }; +/* Used to deliver AENs */ +struct scst_aen { + int event_fn; /* AEN fn */ + + struct scst_session *sess; /* corresponding session */ + uint64_t lun; /* corresponding LUN in SCSI form */ + + union { + /* SCSI AEN data */ + struct { + int aen_sense_len; + uint8_t aen_sense[SCST_STANDARD_SENSE_LEN]; + }; + }; + + /* Keeps status of AEN's delivery to remote initiator */ + int delivery_status; +}; + #ifndef smp_mb__after_set_bit /* There is no smp_mb__after_set_bit() in the kernel */ #define smp_mb__after_set_bit() smp_mb() @@ -1922,6 +1989,11 @@ void scst_set_busy(struct scst_cmd *cmd); */ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq); +/* + * Notifies SCST core that dev changed its capacity + */ +void scst_capacity_data_changed(struct scst_device *dev); + /* * Finds a command based on the supplied tag comparing it with one * that previously set by scst_cmd_set_tag(). @@ -2340,8 +2412,8 @@ static inline void scst_clear_may_need_dma_sync(struct scst_cmd *cmd) } /* - * Get/clear functions for cmd's delivery_status. It is one of - * SCST_CMD_DELIVERY_* constants, it specifies the status of the + * Get/set functions for cmd's delivery_status. It is one of + * SCST_CMD_DELIVERY_* constants. It specifies the status of the * command's delivery to initiator. */ static inline int scst_get_delivery_status(struct scst_cmd *cmd) @@ -2393,6 +2465,60 @@ void scst_prepare_async_mcmd(struct scst_mgmt_cmd *mcmd); */ void scst_async_mcmd_completed(struct scst_mgmt_cmd *mcmd, int status); +/* Returns AEN's fn */ +static inline int scst_aen_get_event_fn(struct scst_aen *aen) +{ + return aen->event_fn; +} + +/* Returns AEN's session */ +static inline struct scst_session *scst_aen_get_sess(struct scst_aen *aen) +{ + return aen->sess; +} + +/* Returns AEN's LUN */ +static inline uint64_t scst_aen_get_lun(struct scst_aen *aen) +{ + return aen->lun; +} + +/* Returns SCSI AEN's sense */ +static inline const uint8_t *scst_aen_get_sense(struct scst_aen *aen) +{ + return aen->aen_sense; +} + +/* Returns SCSI AEN's sense length */ +static inline int scst_aen_get_sense_len(struct scst_aen *aen) +{ + return aen->aen_sense_len; +} + +/* + * Get/set functions for AEN's delivery_status. It is one of + * SCST_AEN_RES_* constants. It specifies the status of the + * command's delivery to initiator. + */ +static inline int scst_get_aen_delivery_status(struct scst_aen *aen) +{ + return aen->delivery_status; +} + +static inline void scst_set_aen_delivery_status(struct scst_aen *aen, + int status) +{ + aen->delivery_status = status; +} + +/* + * Notifies SCST that the driver has sent the AEN and it + * can be freed now. Don't forget to set the delivery status, if it + * isn't success, using scst_set_aen_delivery_status() before calling + * this function. + */ +void scst_aen_done(struct scst_aen *aen); + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) static inline struct page *sg_page(struct scatterlist *sg) @@ -2410,6 +2536,13 @@ static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) memset(sgl, 0, sizeof(*sgl) * nents); } +static inline void sg_init_one(struct scatterlist *sg, const void *buf, + unsigned int buflen) +{ + sg_init_table(sg, 1); + sg_set_buf(sg, buf, buflen); +} + static inline void sg_assign_page(struct scatterlist *sg, struct page *page) { sg->page = page; @@ -2612,8 +2745,15 @@ int scst_alloc_sense(struct scst_cmd *cmd, int atomic); int scst_alloc_set_sense(struct scst_cmd *cmd, int atomic, const uint8_t *sense, unsigned int len); -void scst_set_sense(uint8_t *buffer, int len, int key, - int asc, int ascq); +void scst_set_sense(uint8_t *buffer, int len, int key, int asc, int ascq); + +/* + * Returnes true if sense matches to (key, asc, ascq) and false otherwise. + * Valid_mask is one or several SCST_SENSE_*_VALID constants setting valid + * (key, asc, ascq) values. + */ +bool scst_analyze_sense(const uint8_t *sense, int len, unsigned int valid_mask, + int key, int asc, int ascq); /* * Returnes a pseudo-random number for debugging purposes. Available only in diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index ad71965e5..23aeac7e8 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -29,6 +29,12 @@ /* Max size of CDB */ #define SCST_MAX_CDB_SIZE 16 +/* + * Size of sense sufficient to carry standard sense data. + * Warning! It's allocated on stack! + */ +#define SCST_STANDARD_SENSE_LEN 17 + /* Max size of sense */ #define SCST_SENSE_BUFFERSIZE 96 @@ -156,8 +162,8 @@ static inline int scst_is_ua_sense(const uint8_t *sense) #define scst_sense_not_ready NOT_READY, 0x04, 0x10 #define scst_sense_invalid_message ILLEGAL_REQUEST, 0x49, 0 #define scst_sense_cleared_by_another_ini_UA UNIT_ATTENTION, 0x2F, 0 - -#define SCST_STANDARD_SENSE_LEN 14 +#define scst_sense_capacity_data_changed UNIT_ATTENTION, 0x2A, 0x9 +#define scst_sense_reported_luns_data_changed UNIT_ATTENTION, 0x3F, 0xE /************************************************************* * SCSI opcodes not listed anywhere else diff --git a/scst/include/scst_user.h b/scst/include/scst_user.h index edc612bad..8d4210a28 100644 --- a/scst/include/scst_user.h +++ b/scst/include/scst_user.h @@ -242,6 +242,7 @@ struct scst_user_reply_cmd { #define SCST_USER_REPLY_AND_GET_CMD _IOWR('u', 5, struct scst_user_get_cmd) #define SCST_USER_REPLY_CMD _IOW('u', 6, struct scst_user_reply_cmd) #define SCST_USER_FLUSH_CACHE _IO('u', 7) +#define SCST_USER_DEVICE_CAPACITY_CHANGED _IO('u', 8) /* Values for scst_user_get_cmd.subcode */ #define SCST_USER_ATTACH_SESS \ diff --git a/scst/src/dev_handlers/scst_cdrom.c b/scst/src/dev_handlers/scst_cdrom.c index 356dbf329..c3043b984 100644 --- a/scst/src/dev_handlers/scst_cdrom.c +++ b/scst/src/dev_handlers/scst_cdrom.c @@ -68,9 +68,8 @@ static int cdrom_attach(struct scst_device *dev) const int buffer_size = 512; uint8_t *buffer = NULL; int retries; - unsigned char sense_buffer[SCST_SENSE_BUFFERSIZE]; + unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; enum dma_data_direction data_dir; - unsigned char *sbuff; struct cdrom_params *params; TRACE_ENTRY(); @@ -106,16 +105,15 @@ static int cdrom_attach(struct scst_device *dev) while (1) { memset(buffer, 0, buffer_size); data_dir = SCST_DATA_READ; - sbuff = sense_buffer; TRACE_DBG("%s", "Doing READ_CAPACITY"); res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer, - buffer_size, sbuff, + buffer_size, sense_buffer, SCST_GENERIC_CDROM_REG_TIMEOUT, 3, 0); TRACE_DBG("READ_CAPACITY done: %x", res); - if ((res == 0) || (sbuff[2] != UNIT_ATTENTION)) + if ((res == 0) || (sense_buffer[2] != UNIT_ATTENTION)) break; if (!--retries) { @@ -137,7 +135,7 @@ static int cdrom_attach(struct scst_device *dev) TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)", sector_size, dev->scsi_dev->scsi_level, SCSI_2); } else { - TRACE_BUFFER("Sense set", sbuff, SCST_SENSE_BUFFERSIZE); + TRACE_BUFFER("Sense set", sense_buffer, sizeof(sense_buffer)); params->block_shift = CDROM_DEF_BLOCK_SHIFT; } diff --git a/scst/src/dev_handlers/scst_disk.c b/scst/src/dev_handlers/scst_disk.c index e7c5fd662..3228def31 100644 --- a/scst/src/dev_handlers/scst_disk.c +++ b/scst/src/dev_handlers/scst_disk.c @@ -144,9 +144,8 @@ static int disk_attach(struct scst_device *dev) const int buffer_size = 512; uint8_t *buffer = NULL; int retries; - unsigned char sense_buffer[SCST_SENSE_BUFFERSIZE]; + unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; enum dma_data_direction data_dir; - unsigned char *sbuff; struct disk_params *params; TRACE_ENTRY(); @@ -182,16 +181,16 @@ static int disk_attach(struct scst_device *dev) while (1) { memset(buffer, 0, buffer_size); data_dir = SCST_DATA_READ; - sbuff = sense_buffer; TRACE_DBG("%s", "Doing READ_CAPACITY"); res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer, - buffer_size, sbuff, + buffer_size, sense_buffer, SCST_GENERIC_DISK_REG_TIMEOUT, 3, 0); TRACE_DBG("READ_CAPACITY done: %x", res); - if (!res || (sbuff[12] != 0x28 && sbuff[12] != 0x29)) + if (!res || (sense_buffer[12] != 0x28 && + sense_buffer[12] != 0x29)) break; if (!--retries) { PRINT_ERROR("UA not clear after %d retries", @@ -209,7 +208,7 @@ static int disk_attach(struct scst_device *dev) params->block_shift = scst_calc_block_shift(sector_size); } else { - TRACE_BUFFER("Sense set", sbuff, SCST_SENSE_BUFFERSIZE); + TRACE_BUFFER("Sense set", sense_buffer, sizeof(sense_buffer)); res = -ENODEV; goto out_free_buf; } diff --git a/scst/src/dev_handlers/scst_modisk.c b/scst/src/dev_handlers/scst_modisk.c index 0a24cdc7a..80d17f03d 100644 --- a/scst/src/dev_handlers/scst_modisk.c +++ b/scst/src/dev_handlers/scst_modisk.c @@ -144,9 +144,8 @@ static int modisk_attach(struct scst_device *dev) const int buffer_size = 512; uint8_t *buffer = NULL; int retries; - unsigned char sense_buffer[SCST_SENSE_BUFFERSIZE]; + unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; enum dma_data_direction data_dir; - unsigned char *sbuff; struct modisk_params *params; TRACE_ENTRY(); @@ -196,16 +195,15 @@ static int modisk_attach(struct scst_device *dev) while (1) { memset(buffer, 0, buffer_size); data_dir = SCST_DATA_READ; - sbuff = sense_buffer; TRACE_DBG("%s", "Doing READ_CAPACITY"); res = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer, - buffer_size, sbuff, + buffer_size, sense_buffer, SCST_GENERIC_MODISK_REG_TIMEOUT, 3, 0); TRACE_DBG("READ_CAPACITY done: %x", res); - if (!res || (sbuff[2] != UNIT_ATTENTION)) + if (!res || (sense_buffer[2] != UNIT_ATTENTION)) break; if (!--retries) { @@ -226,9 +224,9 @@ static int modisk_attach(struct scst_device *dev) TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)", sector_size, dev->scsi_dev->scsi_level, SCSI_2); } else { - TRACE_BUFFER("Sense set", sbuff, SCST_SENSE_BUFFERSIZE); + TRACE_BUFFER("Sense set", sense_buffer, sizeof(sense_buffer)); - if (sbuff[2] != NOT_READY) { + if (sense_buffer[2] != NOT_READY) { res = -ENODEV; goto out_free_buf; } diff --git a/scst/src/dev_handlers/scst_tape.c b/scst/src/dev_handlers/scst_tape.c index 0430c906b..2ec33677d 100644 --- a/scst/src/dev_handlers/scst_tape.c +++ b/scst/src/dev_handlers/scst_tape.c @@ -336,7 +336,17 @@ static int tape_done(struct scst_cmd *cmd) else if ((status == SAM_STAT_CHECK_CONDITION) && SCST_SENSE_VALID(cmd->sense)) { struct tape_params *params; - TRACE_DBG("%s", "Extended sense"); + + TRACE_DBG("Extended sense %x", cmd->sense[0] & 0x7F); + + if ((cmd->sense[0] & 0x7F) != 0x70) { + PRINT_ERROR("Sense format 0x%x is not supported", + cmd->sense[0] & 0x7F); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_hardw_error)); + goto out; + } + if (opcode == READ_6 && !(cmd->cdb[1] & SILI_BIT) && (cmd->sense[2] & 0xe0)) { /* EOF, EOM, or ILI */ @@ -374,6 +384,7 @@ static int tape_done(struct scst_cmd *cmd) } } +out: TRACE_DBG("cmd->is_send_status=%x, cmd->resp_data_len=%d, " "res=%d", cmd->is_send_status, cmd->resp_data_len, res); diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index 2dda43e4c..3b198aa18 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -186,6 +186,7 @@ static int dev_user_register_dev(struct file *file, const struct scst_user_dev_desc *dev_desc); static int dev_user_unregister_dev(struct file *file); static int dev_user_flush_cache(struct file *file); +static int dev_user_capacity_changed(struct file *file); static int __dev_user_set_opt(struct scst_user_dev *dev, const struct scst_user_opt *opt); static int dev_user_set_opt(struct file *file, const struct scst_user_opt *opt); @@ -1862,6 +1863,11 @@ static long dev_user_ioctl(struct file *file, unsigned int cmd, res = dev_user_get_opt(file, (void __user *)arg); break; + case SCST_USER_DEVICE_CAPACITY_CHANGED: + TRACE_DBG("%s", "CAPACITY_CHANGED"); + res = dev_user_capacity_changed(file); + break; + default: PRINT_ERROR("Invalid ioctl cmd %x", cmd); res = -EINVAL; @@ -2765,6 +2771,33 @@ out: return res; } +static int dev_user_capacity_changed(struct file *file) +{ + int res; + struct scst_user_dev *dev; + + TRACE_ENTRY(); + + mutex_lock(&dev_priv_mutex); + dev = (struct scst_user_dev *)file->private_data; + res = dev_user_check_reg(dev); + if (res != 0) { + mutex_unlock(&dev_priv_mutex); + goto out; + } + down_read(&dev->dev_rwsem); + mutex_unlock(&dev_priv_mutex); + + scst_capacity_data_changed(dev->sdev); + + up_read(&dev->dev_rwsem); + +out: + TRACE_EXIT_RES(res); + return res; +} + + static int __dev_user_set_opt(struct scst_user_dev *dev, const struct scst_user_opt *opt) { diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index d7e581329..c8155585f 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -93,6 +93,8 @@ static struct scst_proc_log vdisk_proc_local_trace_tbl[] = #define VDISK_NAME "vdisk" #define VCDROM_NAME "vcdrom" +#define VDISK_NULLIO_SIZE 3LL*1024*1024*1024*1024/2 + #define DEF_TST SCST_CONTR_MODE_SEP_TASK_SETS /* * Since we can't control backstorage device's reordering, we have to always @@ -361,9 +363,9 @@ static struct scst_dev_type vcdrom_devtype = VCDROM_TYPE; static struct scst_vdisk_thr nullio_thr_data; static char *vdisk_proc_help_string = - "echo \"open|close NAME [FILE_NAME [BLOCK_SIZE] [WRITE_THROUGH " - "READ_ONLY O_DIRECT NULLIO NV_CACHE BLOCKIO]]\" >/proc/scsi_tgt/" - VDISK_NAME "/" VDISK_NAME "\n"; + "echo \"open|close|resync_size NAME [FILE_NAME [BLOCK_SIZE] " + "[WRITE_THROUGH READ_ONLY O_DIRECT NULLIO NV_CACHE BLOCKIO]]\" " + ">/proc/scsi_tgt/" VDISK_NAME "/" VDISK_NAME "\n"; static char *vcdrom_proc_help_string = "echo \"open|change|close NAME [FILE_NAME]\" " @@ -407,6 +409,76 @@ static struct file *vdisk_open(const struct scst_vdisk_dev *virt_dev) return fd; } +/************************************************************** + * Function: vdisk_get_file_size + * + * Argument: + * + * Returns : 0 on success and file size in *file_size, + * error code otherwise + * + * Description: + *************************************************************/ +static int vdisk_get_check_file_size(const char *file_name, bool blockio, + loff_t *file_size) +{ + struct inode *inode; + int res = 0; + struct file *fd; + + TRACE_ENTRY(); + + *file_size = 0; + + fd = filp_open(file_name, O_LARGEFILE | O_RDONLY, 0600); + if (IS_ERR(fd)) { + res = PTR_ERR(fd); + PRINT_ERROR("filp_open(%s) returned error %d", file_name, res); + goto out; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) + if ((fd->f_op == NULL) || + (fd->f_op->readv == NULL) || + (fd->f_op->writev == NULL)) { +#else + if ((fd->f_op == NULL) || + (fd->f_op->aio_read == NULL) || + (fd->f_op->aio_write == NULL)) { +#endif + PRINT_ERROR("%s", "Wrong f_op or FS doesn't have required " + "capabilities"); + res = -EINVAL; + goto out_close; + } + + inode = fd->f_dentry->d_inode; + + if (blockio && !S_ISBLK(inode->i_mode)) { + PRINT_ERROR("File %s is NOT a block device", file_name); + res = -EINVAL; + goto out_close; + } + + if (S_ISREG(inode->i_mode)) + /* Nothing to do*/; + else if (S_ISBLK(inode->i_mode)) + inode = inode->i_bdev->bd_inode; + else { + res = -EINVAL; + goto out_close; + } + + *file_size = inode->i_size; + +out_close: + filp_close(fd, NULL); + +out: + TRACE_EXIT_RES(res); + return res; +} + /************************************************************** * Function: vdisk_attach * @@ -420,7 +492,6 @@ static int vdisk_attach(struct scst_device *dev) { int res = 0; loff_t err; - struct file *fd; struct scst_vdisk_dev *virt_dev = NULL, *vv; struct list_head *vd; @@ -462,56 +533,12 @@ static int vdisk_attach(struct scst_device *dev) if (!virt_dev->cdrom_empty) { if (virt_dev->nullio) - err = 3LL*1024*1024*1024*1024/2; + err = VDISK_NULLIO_SIZE; else { - struct inode *inode; - - fd = vdisk_open(virt_dev); - if (IS_ERR(fd)) { - res = PTR_ERR(fd); - PRINT_ERROR("filp_open(%s) returned error %d", - virt_dev->file_name, res); + res = vdisk_get_check_file_size(virt_dev->file_name, + virt_dev->blockio, &err); + if (res != 0) goto out; - } - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) - if ((fd->f_op == NULL) || - (fd->f_op->readv == NULL) || - (fd->f_op->writev == NULL)) { -#else - if ((fd->f_op == NULL) || - (fd->f_op->aio_read == NULL) || - (fd->f_op->aio_write == NULL)) { -#endif - PRINT_ERROR("%s", "Wrong f_op or FS doesn't " - "have required capabilities"); - res = -EINVAL; - filp_close(fd, NULL); - goto out; - } - - inode = fd->f_dentry->d_inode; - - if (virt_dev->blockio && !S_ISBLK(inode->i_mode)) { - PRINT_ERROR("File %s is NOT a block device", - virt_dev->file_name); - res = -EINVAL; - filp_close(fd, NULL); - goto out; - } - - if (S_ISREG(inode->i_mode)) - /* Nothing to do*/; - else if (S_ISBLK(inode->i_mode)) - inode = inode->i_bdev->bd_inode; - else { - res = -EINVAL; - filp_close(fd, NULL); - goto out; - } - err = inode->i_size; - - filp_close(fd, NULL); } virt_dev->file_size = err; TRACE_DBG("size of file: %lld", (long long unsigned int)err); @@ -2777,6 +2804,46 @@ static void vdisk_report_registering(const char *type, return; } +/* scst_vdisk_mutex supposed to be held */ +static int vdisk_resync_size(struct scst_vdisk_dev *virt_dev) +{ + loff_t err; + int res = 0; + + if (!virt_dev->nullio) { + res = vdisk_get_check_file_size(virt_dev->file_name, + virt_dev->blockio, &err); + if (res != 0) + goto out; + } else + err = VDISK_NULLIO_SIZE; + + res = scst_suspend_activity(true); + if (res != 0) + goto out; + + virt_dev->file_size = err; + virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift; + + scst_dev_del_all_thr_data(virt_dev->dev); + + PRINT_INFO("New size of SCSI target virtual disk %s " + "(fs=%lldMB, bs=%d, nblocks=%lld, cyln=%lld%s)", + virt_dev->name, virt_dev->file_size >> 20, + virt_dev->block_size, + (long long unsigned int)virt_dev->nblocks, + (long long unsigned int)virt_dev->nblocks/64/32, + virt_dev->nblocks < 64*32 ? " !WARNING! cyln less " + "than 1" : ""); + + scst_capacity_data_changed(virt_dev->dev); + + scst_resume_activity(); + +out: + return res; +} + /* * Called when a file in the /proc/VDISK_NAME/VDISK_NAME is written */ @@ -2810,6 +2877,9 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, action = 0; } else if (!strncmp("open ", p, 5)) { p += 5; + action = 1; + } else if (!strncmp("resync_size ", p, 12)) { + p += 12; action = 2; } else { PRINT_ERROR("Unknown action \"%s\"", p); @@ -2834,12 +2904,11 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, goto out_up; } - if (action) { + if (action == 1) { /* open */ virt_dev = NULL; list_for_each_entry(vv, &vdisk_dev_list, - vdisk_dev_list_entry) - { + vdisk_dev_list_entry) { if (strcmp(vv->name, name) == 0) { virt_dev = vv; break; @@ -2992,11 +3061,10 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, "vdisk_dev_list", virt_dev->name, virt_dev->file_name, virt_dev->virt_id, virt_dev->block_size); - } else { /* close */ + } else if (action == 0) { /* close */ virt_dev = NULL; list_for_each_entry(vv, &vdisk_dev_list, - vdisk_dev_list_entry) - { + vdisk_dev_list_entry) { if (strcmp(vv->name, name) == 0) { virt_dev = vv; break; @@ -3016,6 +3084,24 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, kfree(virt_dev->file_name); kfree(virt_dev); + } else { /* resync_size */ + virt_dev = NULL; + list_for_each_entry(vv, &vdisk_dev_list, + vdisk_dev_list_entry) { + if (strcmp(vv->name, name) == 0) { + virt_dev = vv; + break; + } + } + if (virt_dev == NULL) { + PRINT_ERROR("Device %s not found", name); + res = -EINVAL; + goto out_up; + } + + res = vdisk_resync_size(virt_dev); + if (res != 0) + goto out_up; } res = length; @@ -3169,9 +3255,7 @@ out: /* scst_vdisk_mutex supposed to be held */ static int vcdrom_change(char *p, char *name) { - struct file *fd; loff_t err; - mm_segment_t old_fs; struct scst_vdisk_dev *virt_dev, *vv; char *file_name, *fn, *old_fn; int len; @@ -3179,8 +3263,7 @@ static int vcdrom_change(char *p, char *name) virt_dev = NULL; list_for_each_entry(vv, &vcdrom_dev_list, - vdisk_dev_list_entry) - { + vdisk_dev_list_entry) { if (strcmp(vv->name, name) == 0) { virt_dev = vv; break; @@ -3225,45 +3308,17 @@ static int vcdrom_change(char *p, char *name) strncpy(fn, file_name, len); virt_dev->file_name = fn; - fd = vdisk_open(virt_dev); - if (IS_ERR(fd)) { - res = PTR_ERR(fd); - PRINT_ERROR("filp_open(%s) returned an error %d", - virt_dev->file_name, res); + res = vdisk_get_check_file_size(virt_dev->file_name, + virt_dev->blockio, &err); + if (res != 0) goto out_free; - } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) - if ((fd->f_op == NULL) || (fd->f_op->readv == NULL)) { -#else - if ((fd->f_op == NULL) || (fd->f_op->aio_read == NULL)) { -#endif - PRINT_ERROR("%s", "Wrong f_op or FS doesn't " - "have required capabilities"); - res = -EINVAL; - filp_close(fd, NULL); - goto out_free; - } - /* seek to end */ - old_fs = get_fs(); - set_fs(get_ds()); - if (fd->f_op->llseek) - err = fd->f_op->llseek(fd, 0, 2/*SEEK_END*/); - else - err = default_llseek(fd, 0, 2/*SEEK_END*/); - set_fs(old_fs); - filp_close(fd, NULL); - if (err < 0) { - res = err; - PRINT_ERROR("llseek %s returned an error %d", - virt_dev->file_name, res); - goto out_free; - } } else { - len = 0; err = 0; - fn = NULL; - virt_dev->file_name = fn; + virt_dev->file_name = NULL; } + + if (virt_dev->nullio) + err = VDISK_NULLIO_SIZE; res = scst_suspend_activity(true); if (res != 0) diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 8195a310a..d201a18f5 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -37,8 +37,10 @@ static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev); static void scst_check_internal_sense(struct scst_device *dev, int result, uint8_t *sense, int sense_len); +static void __scst_check_set_UA(struct scst_tgt_dev *tgt_dev, + const uint8_t *sense, int sense_len, int flags); static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, - const uint8_t *sense, int sense_len, int head); + const uint8_t *sense, int sense_len, int flags); static void scst_free_all_UA(struct scst_tgt_dev *tgt_dev); static void scst_release_space(struct scst_cmd *cmd); static void scst_sess_free_tgt_devs(struct scst_session *sess); @@ -61,7 +63,8 @@ int scst_alloc_sense(struct scst_cmd *cmd, int atomic) TRACE_ENTRY(); - sBUG_ON(cmd->sense != NULL); + if (cmd->sense != NULL) + goto memzero; cmd->sense = mempool_alloc(scst_sense_mempool, gfp_mask); if (cmd->sense == NULL) { @@ -71,6 +74,7 @@ int scst_alloc_sense(struct scst_cmd *cmd, int atomic) goto out; } +memzero: memset(cmd->sense, 0, SCST_SENSE_BUFFERSIZE); out: @@ -146,20 +150,55 @@ out: } EXPORT_SYMBOL(scst_set_cmd_error); -void scst_set_sense(uint8_t *buffer, int len, int key, - int asc, int ascq) +void scst_set_sense(uint8_t *buffer, int len, int key, int asc, int ascq) { + sBUG_ON(len < SCST_STANDARD_SENSE_LEN); + memset(buffer, 0, len); + buffer[0] = 0x70; /* Error Code */ buffer[2] = key; /* Sense Key */ buffer[7] = 0x0a; /* Additional Sense Length */ buffer[12] = asc; /* ASC */ buffer[13] = ascq; /* ASCQ */ + TRACE_BUFFER("Sense set", buffer, len); return; } EXPORT_SYMBOL(scst_set_sense); +bool scst_analyze_sense(const uint8_t *sense, int len, unsigned int valid_mask, + int key, int asc, int ascq) +{ + bool res = false; + + if (len < 14) + goto out; + + /* Error Code */ + if (sense[0] != 0x70) + goto out; + + /* Sense Key */ + if ((valid_mask & SCST_SENSE_KEY_VALID) && (sense[2] != key)) + goto out; + + /* ASC */ + if ((valid_mask & SCST_SENSE_ASC_VALID) && (sense[12] != asc)) + goto out; + + /* ASCQ */ + if ((valid_mask & SCST_SENSE_ASCQ_VALID) && (sense[13] != ascq)) + goto out; + + res = true; + +out: + TRACE_EXIT_RES((int)res); + return res; +} +EXPORT_SYMBOL(scst_analyze_sense); + static void scst_set_cmd_error_sense(struct scst_cmd *cmd, uint8_t *sense, unsigned int len) { @@ -207,7 +246,7 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq) asc, ascq); /* Protect sess_tgt_dev_list_hash */ - mutex_lock(&scst_mutex); + mutex_lock(&scst_mutex); for (i = 0; i < TGT_DEV_HASH_SIZE; i++) { struct list_head *sess_tgt_dev_list_head = @@ -219,15 +258,14 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq) spin_lock_bh(&tgt_dev->tgt_dev_lock); if (!list_empty(&tgt_dev->UA_list)) { struct scst_tgt_dev_UA *ua; - uint8_t *sense; ua = list_entry(tgt_dev->UA_list.next, typeof(*ua), UA_list_entry); - sense = ua->UA_sense_buffer; - if ((sense[2] == UNIT_ATTENTION) && - (sense[12] == 0x29) && - (sense[13] == 0)) { - scst_set_sense(sense, + if (scst_analyze_sense(ua->UA_sense_buffer, + sizeof(ua->UA_sense_buffer), + SCST_SENSE_ALL_VALID, + SCST_LOAD_SENSE(scst_sense_reset_UA))) { + scst_set_sense(ua->UA_sense_buffer, sizeof(ua->UA_sense_buffer), key, asc, ascq); } else @@ -240,13 +278,307 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq) } } - mutex_unlock(&scst_mutex); + mutex_unlock(&scst_mutex); TRACE_EXIT(); return; } EXPORT_SYMBOL(scst_set_initial_UA); +static struct scst_aen *scst_alloc_aen(struct scst_tgt_dev *tgt_dev) +{ + struct scst_aen *aen; + + TRACE_ENTRY(); + + aen = mempool_alloc(scst_aen_mempool, GFP_KERNEL); + if (aen == NULL) { + PRINT_ERROR("AEN memory allocation failed. Corresponding " + "event notification will not be performed (initiator " + "%s)", tgt_dev->sess->initiator_name); + goto out; + } + memset(aen, 0, sizeof(*aen)); + + aen->sess = tgt_dev->sess; + scst_sess_get(aen->sess); + + aen->lun = scst_pack_lun(tgt_dev->lun); + +out: + TRACE_EXIT_HRES((unsigned long)aen); + return aen; +}; + +static void scst_free_aen(struct scst_aen *aen) +{ + TRACE_ENTRY(); + + scst_sess_put(aen->sess); + mempool_free(aen, scst_aen_mempool); + + TRACE_EXIT(); + return; +}; + +/* No locks */ +void scst_capacity_data_changed(struct scst_device *dev) +{ + struct scst_tgt_dev *tgt_dev; + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + + TRACE_ENTRY(); + + if (dev->type != TYPE_DISK) { + TRACE_MGMT_DBG("Device type %d isn't for CAPACITY DATA " + "CHANGED UA", dev->type); + goto out; + } + + TRACE_MGMT_DBG("CAPACITY DATA CHANGED (dev %p)", dev); + + scst_set_sense(sense_buffer, sizeof(sense_buffer), + SCST_LOAD_SENSE(scst_sense_capacity_data_changed)); + + mutex_lock(&scst_mutex); + + list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, + dev_tgt_dev_list_entry) { + struct scst_tgt_template *tgtt = tgt_dev->sess->tgt->tgtt; + + if (tgtt->report_aen != NULL) { + struct scst_aen *aen; + int rc; + + aen = scst_alloc_aen(tgt_dev); + if (aen == NULL) + goto queue_ua; + + aen->event_fn = SCST_AEN_SCSI; + aen->aen_sense_len = SCST_STANDARD_SENSE_LEN; + scst_set_sense(aen->aen_sense, aen->aen_sense_len, + SCST_LOAD_SENSE(scst_sense_capacity_data_changed)); + + TRACE_DBG("Calling target's %s report_aen(%p)", + tgtt->name, aen); + rc = tgtt->report_aen(aen); + TRACE_DBG("Target's %s report_aen(%p) returned %d", + tgtt->name, aen, rc); + if (rc == SCST_AEN_RES_SUCCESS) + continue; + + scst_free_aen(aen); + } +queue_ua: + TRACE_MGMT_DBG("Queuing CAPACITY DATA CHANGED UA (tgt_dev %p)", + tgt_dev); + scst_check_set_UA(tgt_dev, sense_buffer, + sizeof(sense_buffer), 0); + } + + mutex_unlock(&scst_mutex); + +out: + TRACE_EXIT(); + return; +} +EXPORT_SYMBOL(scst_capacity_data_changed); + +static inline bool scst_is_report_luns_changed_type(int type) +{ + switch (type) { + case TYPE_DISK: + case TYPE_TAPE: + case TYPE_PRINTER: + case TYPE_PROCESSOR: + case TYPE_WORM: + case TYPE_ROM: + case TYPE_SCANNER: + case TYPE_MOD: + case TYPE_MEDIUM_CHANGER: + case TYPE_RAID: + case TYPE_ENCLOSURE: + return true; + default: + return false; + } +} + +/* scst_mutex supposed to be held */ +void scst_queue_report_luns_changed_UA(struct scst_session *sess, int flags) +{ + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + struct list_head *shead; + struct scst_tgt_dev *tgt_dev; + int i; + + TRACE_ENTRY(); + + scst_set_sense(sense_buffer, sizeof(sense_buffer), + SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed)); + + TRACE_MGMT_DBG("Queuing REPORTED LUNS DATA CHANGED UA " + "(sess %p)", sess); + + for (i = 0; i < TGT_DEV_HASH_SIZE; i++) { + shead = &sess->sess_tgt_dev_list_hash[i]; + + list_for_each_entry(tgt_dev, shead, + sess_tgt_dev_list_entry) { + spin_lock_bh(&tgt_dev->tgt_dev_lock); + } + } + + for (i = 0; i < TGT_DEV_HASH_SIZE; i++) { + shead = &sess->sess_tgt_dev_list_hash[i]; + + list_for_each_entry(tgt_dev, shead, + sess_tgt_dev_list_entry) { + if (!scst_is_report_luns_changed_type( + tgt_dev->dev->type)) + continue; + + __scst_check_set_UA(tgt_dev, sense_buffer, + sizeof(sense_buffer), + flags | SCST_SET_UA_FLAG_GLOBAL); + } + } + + for (i = TGT_DEV_HASH_SIZE-1; i >= 0; i--) { + shead = &sess->sess_tgt_dev_list_hash[i]; + + list_for_each_entry_reverse(tgt_dev, + shead, sess_tgt_dev_list_entry) { + spin_unlock_bh(&tgt_dev->tgt_dev_lock); + } + } + + TRACE_EXIT(); + return; +} + +/* The activity supposed to be suspended and scst_mutex held */ +void scst_report_luns_changed(struct scst_acg *acg) +{ + struct scst_session *sess; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("REPORTED LUNS DATA CHANGED (acg %s)", acg->acg_name); + + list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) { + int i; + struct list_head *shead; + struct scst_tgt_dev *tgt_dev; + struct scst_tgt_template *tgtt = sess->tgt->tgtt; + + for (i = 0; i < TGT_DEV_HASH_SIZE; i++) { + shead = &sess->sess_tgt_dev_list_hash[i]; + + list_for_each_entry(tgt_dev, shead, + sess_tgt_dev_list_entry) { + if (scst_is_report_luns_changed_type( + tgt_dev->dev->type)) + goto found; + } + } + TRACE_MGMT_DBG("Not found a device capable REPORTED " + "LUNS DATA CHANGED UA (sess %p)", sess); + continue; +found: + if (tgtt->report_aen != NULL) { + struct scst_aen *aen; + int rc; + + aen = scst_alloc_aen(tgt_dev); + if (aen == NULL) + goto queue_ua; + + aen->event_fn = SCST_AEN_SCSI; + aen->aen_sense_len = SCST_STANDARD_SENSE_LEN; + scst_set_sense(aen->aen_sense, aen->aen_sense_len, + SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed)); + + TRACE_DBG("Calling target's %s report_aen(%p)", + tgtt->name, aen); + rc = tgtt->report_aen(aen); + TRACE_DBG("Target's %s report_aen(%p) returned %d", + tgtt->name, aen, rc); + if (rc == SCST_AEN_RES_SUCCESS) + continue; + + scst_free_aen(aen); + } + +queue_ua: + scst_queue_report_luns_changed_UA(sess, 0); + } + + TRACE_EXIT(); + return; +} + +void scst_aen_done(struct scst_aen *aen) +{ + TRACE_ENTRY(); + + TRACE_MGMT_DBG("AEN %p (fn %d) done (initiator %s)", aen, + aen->event_fn, aen->sess->initiator_name); + + if (aen->delivery_status == SCST_AEN_RES_SUCCESS) + goto out_free; + + if (aen->event_fn != SCST_AEN_SCSI) + goto out_free; + + TRACE_MGMT_DBG("Delivery of SCSI AEN failed (initiator %s)", + aen->sess->initiator_name); + + if (scst_analyze_sense(aen->aen_sense, aen->aen_sense_len, + SCST_SENSE_ALL_VALID, + SCST_LOAD_SENSE( + scst_sense_reported_luns_data_changed))) { + mutex_lock(&scst_mutex); + scst_queue_report_luns_changed_UA(aen->sess, + SCST_SET_UA_FLAG_AT_HEAD); + mutex_unlock(&scst_mutex); + } else if (scst_analyze_sense(aen->aen_sense, aen->aen_sense_len, + SCST_SENSE_ALL_VALID, + SCST_LOAD_SENSE(scst_sense_capacity_data_changed))) { + /* tgt_dev might get dead, so we need to reseek it */ + struct list_head *shead; + struct scst_tgt_dev *tgt_dev; + uint64_t lun; + + lun = scst_unpack_lun((uint8_t *)&aen->lun, sizeof(aen->lun)); + + mutex_lock(&scst_mutex); + + shead = &aen->sess->sess_tgt_dev_list_hash[HASH_VAL(lun)]; + list_for_each_entry(tgt_dev, shead, + sess_tgt_dev_list_entry) { + if (tgt_dev->lun == lun) { + TRACE_MGMT_DBG("Queuing CAPACITY DATA CHANGED " + "UA (tgt_dev %p)", tgt_dev); + scst_check_set_UA(tgt_dev, aen->aen_sense, + aen->aen_sense_len, + SCST_SET_UA_FLAG_AT_HEAD); + break; + } + } + + mutex_unlock(&scst_mutex); + } else + PRINT_ERROR("%s", "Unknown SCSI AEN"); + +out_free: + scst_free_aen(aen); + + TRACE_EXIT(); + return; +} +EXPORT_SYMBOL(scst_aen_done); + int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) { int res; @@ -274,12 +606,15 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd) res = SCST_CMD_STATE_XMIT_RESP; break; + case SCST_CMD_STATE_PREPROCESS_DONE: case SCST_CMD_STATE_PREPARE_SPACE: case SCST_CMD_STATE_RDY_TO_XFER: + case SCST_CMD_STATE_DATA_WAIT: case SCST_CMD_STATE_TGT_PRE_EXEC: case SCST_CMD_STATE_SEND_FOR_EXEC: case SCST_CMD_STATE_LOCAL_EXEC: case SCST_CMD_STATE_REAL_EXEC: + case SCST_CMD_STATE_REAL_EXECUTING: res = SCST_CMD_STATE_PRE_DEV_DONE; break; @@ -559,6 +894,7 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, struct list_head *sess_tgt_dev_list_head; struct scst_tgt_template *vtt = sess->tgt->tgtt; int rc, i; + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; TRACE_ENTRY(); @@ -652,11 +988,9 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, &tgt_dev->tgt_dev_flags); } - spin_lock_bh(&scst_temp_UA_lock); - scst_set_sense(scst_temp_UA, sizeof(scst_temp_UA), + scst_set_sense(sense_buffer, sizeof(sense_buffer), SCST_LOAD_SENSE(scst_sense_reset_UA)); - scst_alloc_set_UA(tgt_dev, scst_temp_UA, sizeof(scst_temp_UA), 0); - spin_unlock_bh(&scst_temp_UA_lock); + scst_alloc_set_UA(tgt_dev, sense_buffer, sizeof(sense_buffer), 0); tm_dbg_init_tgt_dev(tgt_dev, acg_dev); @@ -726,11 +1060,11 @@ void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA) spin_unlock_bh(&tgt_dev->tgt_dev_lock); if (queue_UA) { - spin_lock_bh(&scst_temp_UA_lock); - scst_set_sense(scst_temp_UA, sizeof(scst_temp_UA), + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + scst_set_sense(sense_buffer, sizeof(sense_buffer), SCST_LOAD_SENSE(scst_sense_nexus_loss_UA)); - scst_check_set_UA(tgt_dev, scst_temp_UA, sizeof(scst_temp_UA), 0); - spin_unlock_bh(&scst_temp_UA_lock); + scst_check_set_UA(tgt_dev, sense_buffer, + sizeof(sense_buffer), 0); } TRACE_EXIT(); @@ -879,24 +1213,24 @@ int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev, &tmp_tgt_dev_list); } -out: - if (res == 0) { - if (dev->virt_name != NULL) { - PRINT_INFO("Added device %s to group %s (LUN %lld, " - "rd_only %d)", dev->virt_name, acg->acg_name, - (long long unsigned int)lun, - read_only); - } else { - PRINT_INFO("Added device %d:%d:%d:%d to group %s (LUN " - "%lld, rd_only %d)", - dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun, acg->acg_name, - (long long unsigned int)lun, - read_only); - } + scst_report_luns_changed(acg); + + if (dev->virt_name != NULL) { + PRINT_INFO("Added device %s to group %s (LUN %lld, " + "rd_only %d)", dev->virt_name, acg->acg_name, + (long long unsigned int)lun, + read_only); + } else { + PRINT_INFO("Added device %d:%d:%d:%d to group %s (LUN " + "%lld, rd_only %d)", + dev->scsi_dev->host->host_no, + dev->scsi_dev->channel, dev->scsi_dev->id, + dev->scsi_dev->lun, acg->acg_name, + (long long unsigned int)lun, + read_only); } +out: TRACE_EXIT_RES(res); return res; @@ -938,19 +1272,19 @@ int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev) } scst_free_acg_dev(acg_dev); -out: - if (res == 0) { - if (dev->virt_name != NULL) { - PRINT_INFO("Removed device %s from group %s", - dev->virt_name, acg->acg_name); - } else { - PRINT_INFO("Removed device %d:%d:%d:%d from group %s", - dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun, acg->acg_name); - } + scst_report_luns_changed(acg); + + if (dev->virt_name != NULL) { + PRINT_INFO("Removed device %s from group %s", + dev->virt_name, acg->acg_name); + } else { + PRINT_INFO("Removed device %d:%d:%d:%d from group %s", + dev->scsi_dev->host->host_no, + dev->scsi_dev->channel, dev->scsi_dev->id, + dev->scsi_dev->lun, acg->acg_name); } +out: TRACE_EXIT_RES(res); return res; } @@ -1078,9 +1412,8 @@ out: int scst_prepare_request_sense(struct scst_cmd *orig_cmd) { int res = 0; -#define sbuf_size 252 static const uint8_t request_sense[6] = - { REQUEST_SENSE, 0, 0, 0, sbuf_size, 0 }; + { REQUEST_SENSE, 0, 0, 0, SCST_SENSE_BUFFERSIZE, 0 }; struct scst_cmd *rs_cmd; TRACE_ENTRY(); @@ -1092,7 +1425,8 @@ int scst_prepare_request_sense(struct scst_cmd *orig_cmd) orig_cmd->sense = NULL; } - rs_cmd = scst_create_prepare_internal_cmd(orig_cmd, sbuf_size); + rs_cmd = scst_create_prepare_internal_cmd(orig_cmd, + SCST_SENSE_BUFFERSIZE); if (rs_cmd == NULL) goto out_error; @@ -1100,7 +1434,7 @@ int scst_prepare_request_sense(struct scst_cmd *orig_cmd) rs_cmd->cdb_len = sizeof(request_sense); rs_cmd->data_direction = SCST_DATA_READ; rs_cmd->expected_data_direction = rs_cmd->data_direction; - rs_cmd->expected_transfer_len = sbuf_size; + rs_cmd->expected_transfer_len = SCST_SENSE_BUFFERSIZE; rs_cmd->expected_values_set = 1; TRACE(TRACE_MGMT_MINOR, "Adding REQUEST SENSE cmd %p to head of active " @@ -1117,7 +1451,6 @@ out: out_error: res = -1; goto out; -#undef sbuf_size } static void scst_complete_request_sense(struct scst_cmd *req_cmd) @@ -1246,7 +1579,7 @@ static void scst_send_release(struct scst_device *dev) { struct scsi_device *scsi_dev; unsigned char cdb[6]; - unsigned char *sense; + uint8_t sense[SCSI_SENSE_BUFFERSIZE]; int rc, i; TRACE_ENTRY(); @@ -1254,9 +1587,6 @@ static void scst_send_release(struct scst_device *dev) if (dev->scsi_dev == NULL) goto out; - /* We can't afford missing RELEASE due to memory shortage */ - sense = kmalloc(SCST_SENSE_BUFFERSIZE, GFP_KERNEL|__GFP_NOFAIL); - scsi_dev = dev->scsi_dev; for (i = 0; i < 5; i++) { @@ -1265,7 +1595,7 @@ static void scst_send_release(struct scst_device *dev) cdb[1] = (scsi_dev->scsi_level <= SCSI_2) ? ((scsi_dev->lun << 5) & 0xe0) : 0; - memset(sense, 0, SCST_SENSE_BUFFERSIZE); + memset(sense, 0, sizeof(sense)); TRACE(TRACE_DEBUG | TRACE_SCSI, "%s", "Sending RELEASE req to " "SCSI mid-level"); @@ -1277,15 +1607,12 @@ static void scst_send_release(struct scst_device *dev) break; } else { PRINT_ERROR("RELEASE failed: %d", rc); - PRINT_BUFFER("RELEASE sense", sense, - SCST_SENSE_BUFFERSIZE); - scst_check_internal_sense(dev, rc, - sense, SCST_SENSE_BUFFERSIZE); + PRINT_BUFFER("RELEASE sense", sense, sizeof(sense)); + scst_check_internal_sense(dev, rc, sense, + sizeof(sense)); } } - kfree(sense); - out: TRACE_EXIT(); return; @@ -2117,6 +2444,19 @@ out: } EXPORT_SYMBOL(scst_get_cdb_info); +/* Packs SCST LUN back to SCSI form using peripheral device addressing method */ +uint64_t scst_pack_lun(const uint64_t lun) +{ + uint64_t res; + uint16_t *p = (uint16_t *)&res; + + res = lun; + *p = cpu_to_be16(*p); + + TRACE_EXIT_HRES((unsigned long)res); + return res; +} + /* * Routine to extract a lun number from an 8-byte LUN structure * in network byte order (BE). @@ -2670,7 +3010,7 @@ int scst_obtain_device_parameters(struct scst_device *dev) int res = 0, i; uint8_t cmd[16]; uint8_t buffer[4+0x0A]; - uint8_t sense_buffer[SCST_SENSE_BUFFERSIZE]; + uint8_t sense_buffer[SCSI_SENSE_BUFFERSIZE]; TRACE_ENTRY(); @@ -2741,7 +3081,10 @@ int scst_obtain_device_parameters(struct scst_device *dev) if ( #endif SCST_SENSE_VALID(sense_buffer)) { - if (sense_buffer[2] == ILLEGAL_REQUEST) { + if (scst_analyze_sense(sense_buffer, + sizeof(sense_buffer), + SCST_SENSE_KEY_VALID, + ILLEGAL_REQUEST, 0, 0)) { TRACE(TRACE_SCSI|TRACE_MGMT_MINOR, "Device %d:%d:%d:%d doesn't" " support control mode page," @@ -2759,7 +3102,10 @@ int scst_obtain_device_parameters(struct scst_device *dev) dev->has_own_order_mgmt); res = 0; goto out; - } else if (sense_buffer[2] == NOT_READY) { + } else if (scst_analyze_sense(sense_buffer, + sizeof(sense_buffer), + SCST_SENSE_KEY_VALID, + NOT_READY, 0, 0)) { TRACE(TRACE_SCSI, "Device %d:%d:%d:%d not ready", dev->scsi_dev->host->host_no, @@ -2863,13 +3209,11 @@ void scst_process_reset(struct scst_device *dev, } if (setUA) { - /* BH already off */ - spin_lock(&scst_temp_UA_lock); - scst_set_sense(scst_temp_UA, sizeof(scst_temp_UA), + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + scst_set_sense(sense_buffer, sizeof(sense_buffer), SCST_LOAD_SENSE(scst_sense_reset_UA)); - scst_dev_check_set_local_UA(dev, exclude_cmd, scst_temp_UA, - sizeof(scst_temp_UA)); - spin_unlock(&scst_temp_UA_lock); + scst_dev_check_set_local_UA(dev, exclude_cmd, sense_buffer, + sizeof(sense_buffer)); } TRACE_EXIT(); @@ -2878,8 +3222,10 @@ void scst_process_reset(struct scst_device *dev, int scst_set_pending_UA(struct scst_cmd *cmd) { - int res = 0; + int res = 0, i; struct scst_tgt_dev_UA *UA_entry; + bool first = true, global_unlock = false; + struct scst_session *sess = cmd->sess; TRACE_ENTRY(); @@ -2887,6 +3233,7 @@ int scst_set_pending_UA(struct scst_cmd *cmd) spin_lock_bh(&cmd->tgt_dev->tgt_dev_lock); +again: /* UA list could be cleared behind us, so retest */ if (list_empty(&cmd->tgt_dev->UA_list)) { TRACE_DBG("%s", @@ -2901,6 +3248,28 @@ int scst_set_pending_UA(struct scst_cmd *cmd) TRACE_DBG("next %p UA_entry %p", cmd->tgt_dev->UA_list.next, UA_entry); + if (UA_entry->global_UA && first) { + TRACE_MGMT_DBG("Global UA %p detected", UA_entry); + + spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock); + + mutex_lock(&scst_mutex); + + for (i = 0; i < TGT_DEV_HASH_SIZE; i++) { + struct list_head *sess_tgt_dev_list_head = + &sess->sess_tgt_dev_list_hash[i]; + struct scst_tgt_dev *tgt_dev; + list_for_each_entry(tgt_dev, sess_tgt_dev_list_head, + sess_tgt_dev_list_entry) { + spin_lock_bh(&tgt_dev->tgt_dev_lock); + } + } + + first = false; + global_unlock = true; + goto again; + } + scst_set_cmd_error_sense(cmd, UA_entry->UA_sense_buffer, sizeof(UA_entry->UA_sense_buffer)); @@ -2908,6 +3277,33 @@ int scst_set_pending_UA(struct scst_cmd *cmd) list_del(&UA_entry->UA_list_entry); + if (UA_entry->global_UA) { + for (i = 0; i < TGT_DEV_HASH_SIZE; i++) { + struct list_head *sess_tgt_dev_list_head = + &sess->sess_tgt_dev_list_hash[i]; + struct scst_tgt_dev *tgt_dev; + + list_for_each_entry(tgt_dev, sess_tgt_dev_list_head, + sess_tgt_dev_list_entry) { + struct scst_tgt_dev_UA *ua; + list_for_each_entry(ua, &tgt_dev->UA_list, + UA_list_entry) { + if (ua->global_UA && + memcmp(ua->UA_sense_buffer, + UA_entry->UA_sense_buffer, + sizeof(ua->UA_sense_buffer)) == 0) { + TRACE_MGMT_DBG("Freeing not " + "needed global UA %p", + ua); + list_del(&ua->UA_list_entry); + mempool_free(ua, scst_ua_mempool); + break; + } + } + } + } + } + mempool_free(UA_entry, scst_ua_mempool); if (list_empty(&cmd->tgt_dev->UA_list)) { @@ -2915,20 +3311,32 @@ int scst_set_pending_UA(struct scst_cmd *cmd) &cmd->tgt_dev->tgt_dev_flags); } +out_unlock: + if (global_unlock) { + for (i = TGT_DEV_HASH_SIZE-1; i >= 0; i--) { + struct list_head *sess_tgt_dev_list_head = + &sess->sess_tgt_dev_list_hash[i]; + struct scst_tgt_dev *tgt_dev; + list_for_each_entry_reverse(tgt_dev, sess_tgt_dev_list_head, + sess_tgt_dev_list_entry) { + spin_unlock_bh(&tgt_dev->tgt_dev_lock); + } + } + + mutex_unlock(&scst_mutex); + + spin_lock_bh(&cmd->tgt_dev->tgt_dev_lock); + } + spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock); -out: TRACE_EXIT_RES(res); return res; - -out_unlock: - spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock); - goto out; } /* Called under tgt_dev_lock and BH off */ static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, - const uint8_t *sense, int sense_len, int head) + const uint8_t *sense, int sense_len, int flags) { struct scst_tgt_dev_UA *UA_entry = NULL; @@ -2944,6 +3352,10 @@ static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, } memset(UA_entry, 0, sizeof(*UA_entry)); + UA_entry->global_UA = (flags & SCST_SET_UA_FLAG_GLOBAL) != 0; + if (UA_entry->global_UA) + TRACE_MGMT_DBG("Queuing global UA %p", UA_entry); + if (sense_len > (int)sizeof(UA_entry->UA_sense_buffer)) sense_len = sizeof(UA_entry->UA_sense_buffer); memcpy(UA_entry->UA_sense_buffer, sense, sense_len); @@ -2952,7 +3364,7 @@ static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, TRACE_MGMT_DBG("Adding new UA to tgt_dev %p", tgt_dev); - if (head) + if (flags & SCST_SET_UA_FLAG_AT_HEAD) list_add(&UA_entry->UA_list_entry, &tgt_dev->UA_list); else list_add_tail(&UA_entry->UA_list_entry, &tgt_dev->UA_list); @@ -2962,20 +3374,19 @@ out: return; } -void scst_check_set_UA(struct scst_tgt_dev *tgt_dev, - const uint8_t *sense, int sense_len, int head) +/* tgt_dev_lock supposed to be held and BH off */ +static void __scst_check_set_UA(struct scst_tgt_dev *tgt_dev, + const uint8_t *sense, int sense_len, int flags) { int skip_UA = 0; struct scst_tgt_dev_UA *UA_entry_tmp; + int len = min((int)sizeof(UA_entry_tmp->UA_sense_buffer), sense_len); TRACE_ENTRY(); - spin_lock_bh(&tgt_dev->tgt_dev_lock); - list_for_each_entry(UA_entry_tmp, &tgt_dev->UA_list, UA_list_entry) { - if (memcmp(sense, UA_entry_tmp->UA_sense_buffer, - sense_len) == 0) { + if (memcmp(sense, UA_entry_tmp->UA_sense_buffer, len) == 0) { TRACE_MGMT_DBG("%s", "UA already exists"); skip_UA = 1; break; @@ -2983,8 +3394,19 @@ void scst_check_set_UA(struct scst_tgt_dev *tgt_dev, } if (skip_UA == 0) - scst_alloc_set_UA(tgt_dev, sense, sense_len, head); + scst_alloc_set_UA(tgt_dev, sense, len, flags); + TRACE_EXIT(); + return; +} + +void scst_check_set_UA(struct scst_tgt_dev *tgt_dev, + const uint8_t *sense, int sense_len, int flags) +{ + TRACE_ENTRY(); + + spin_lock_bh(&tgt_dev->tgt_dev_lock); + __scst_check_set_UA(tgt_dev, sense, sense_len, flags); spin_unlock_bh(&tgt_dev->tgt_dev_lock); TRACE_EXIT(); @@ -3021,7 +3443,8 @@ void __scst_dev_check_set_UA(struct scst_device *dev, TRACE(TRACE_MGMT_MINOR, "Processing UA dev %p", dev); /* Check for reset UA */ - if (sense[12] == SCST_SENSE_ASC_UA_RESET) + if (scst_analyze_sense(sense, sense_len, SCST_SENSE_ASC_VALID, + 0, SCST_SENSE_ASC_UA_RESET, 0)) scst_process_reset(dev, (exclude != NULL) ? exclude->sess : NULL, exclude, NULL, false); @@ -3044,7 +3467,7 @@ static void scst_free_all_UA(struct scst_tgt_dev *tgt_dev) TRACE_MGMT_DBG("Clearing UA for tgt_dev lun %lld", (long long unsigned int)tgt_dev->lun); list_del(&UA_entry->UA_list_entry); - kfree(UA_entry); + mempool_free(UA_entry, scst_ua_mempool); } INIT_LIST_HEAD(&tgt_dev->UA_list); clear_bit(SCST_TGT_DEV_UA_PENDING, &tgt_dev->tgt_dev_flags); diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index c6062a644..f30178f7c 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -85,6 +85,8 @@ static struct kmem_cache *scst_ua_cachep; mempool_t *scst_ua_mempool; static struct kmem_cache *scst_sense_cachep; mempool_t *scst_sense_mempool; +static struct kmem_cache *scst_aen_cachep; +mempool_t *scst_aen_mempool; struct kmem_cache *scst_tgtd_cachep; struct kmem_cache *scst_sess_cachep; struct kmem_cache *scst_acgd_cachep; @@ -133,13 +135,6 @@ static int suspend_count; static int scst_virt_dev_last_id; /* protected by scst_mutex */ -/* - * This buffer and lock are intended to avoid memory allocation, which - * could fail in improper places. - */ -spinlock_t scst_temp_UA_lock; -uint8_t scst_temp_UA[SCST_SENSE_BUFFERSIZE]; - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) #if defined(CONFIG_BLOCK) && defined(SCST_ALLOC_IO_CONTEXT_EXPORTED) static struct io_context *scst_ioc; @@ -1765,8 +1760,6 @@ static int __init init_scst(void) mutex_init(&scst_suspend_mutex); INIT_LIST_HEAD(&scst_cmd_lists_list); scst_virt_dev_last_id = 1; - spin_lock_init(&scst_temp_UA_lock); - spin_lock_init(&scst_main_cmd_lists.cmd_list_lock); INIT_LIST_HEAD(&scst_main_cmd_lists.active_cmd_list); init_waitqueue_head(&scst_main_cmd_lists.cmd_list_waitQ); @@ -1807,7 +1800,8 @@ static int __init init_scst(void) INIT_CACHEP(scst_sense_cachep, scst_sense, out_destroy_ua_cache); } - INIT_CACHEP(scst_cmd_cachep, scst_cmd, out_destroy_sense_cache); + INIT_CACHEP(scst_aen_cachep, scst_aen, out_destroy_sense_cache); + INIT_CACHEP(scst_cmd_cachep, scst_cmd, out_destroy_aen_cache); INIT_CACHEP(scst_sess_cachep, scst_session, out_destroy_cmd_cache); INIT_CACHEP(scst_tgtd_cachep, scst_tgt_dev, out_destroy_sess_cache); INIT_CACHEP(scst_acgd_cachep, scst_acg_dev, out_destroy_tgt_cache); @@ -1843,6 +1837,13 @@ static int __init init_scst(void) goto out_destroy_ua_mempool; } + scst_aen_mempool = mempool_create(100, mempool_alloc_slab, + mempool_free_slab, scst_aen_cachep); + if (scst_aen_mempool == NULL) { + res = -ENOMEM; + goto out_destroy_sense_mempool; + } + if (scst_max_cmd_mem == 0) { struct sysinfo si; si_meminfo(&si); @@ -1870,7 +1871,7 @@ static int __init init_scst(void) res = scst_sgv_pools_init( ((uint64_t)scst_max_cmd_mem << 10) >> (PAGE_SHIFT - 10), 0); if (res != 0) - goto out_destroy_sense_mempool; + goto out_destroy_aen_mempool; scst_default_acg = scst_alloc_add_acg(SCST_DEFAULT_ACG_NAME); if (scst_default_acg == NULL) { @@ -1925,6 +1926,9 @@ out_free_acg: out_destroy_sgv_pool: scst_sgv_pools_deinit(); +out_destroy_aen_mempool: + mempool_destroy(scst_aen_mempool); + out_destroy_sense_mempool: mempool_destroy(scst_sense_mempool); @@ -1949,6 +1953,9 @@ out_destroy_sess_cache: out_destroy_cmd_cache: kmem_cache_destroy(scst_cmd_cachep); +out_destroy_aen_cache: + kmem_cache_destroy(scst_aen_cachep); + out_destroy_sense_cache: kmem_cache_destroy(scst_sense_cachep); @@ -1987,11 +1994,13 @@ static void __exit exit_scst(void) mempool_destroy(scst_mgmt_stub_mempool); mempool_destroy(scst_ua_mempool); mempool_destroy(scst_sense_mempool); + mempool_destroy(scst_aen_mempool); DEINIT_CACHEP(scst_mgmt_cachep); DEINIT_CACHEP(scst_mgmt_stub_cachep); DEINIT_CACHEP(scst_ua_cachep); DEINIT_CACHEP(scst_sense_cachep); + DEINIT_CACHEP(scst_aen_cachep); DEINIT_CACHEP(scst_cmd_cachep); DEINIT_CACHEP(scst_sess_cachep); DEINIT_CACHEP(scst_tgtd_cachep); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index f9f365257..b5da43d6a 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -126,6 +126,7 @@ extern mempool_t *scst_mgmt_mempool; extern mempool_t *scst_mgmt_stub_mempool; extern mempool_t *scst_ua_mempool; extern mempool_t *scst_sense_mempool; +extern mempool_t *scst_aen_mempool; extern struct kmem_cache *scst_cmd_cachep; extern struct kmem_cache *scst_sess_cachep; @@ -190,9 +191,6 @@ extern int scst_cmd_threads_count(void); extern int __scst_add_cmd_threads(int num); extern void __scst_del_cmd_threads(int num); -extern spinlock_t scst_temp_UA_lock; -extern uint8_t scst_temp_UA[SCST_SENSE_BUFFERSIZE]; - extern struct scst_dev_type scst_null_devtype; extern struct scst_cmd *__scst_check_deferred_commands( @@ -326,6 +324,7 @@ enum scst_sg_copy_dir { }; void scst_copy_sg(struct scst_cmd *cmd, enum scst_sg_copy_dir); +uint64_t scst_pack_lun(const uint64_t lun); uint64_t scst_unpack_lun(const uint8_t *lun, int len); struct scst_mgmt_cmd *scst_alloc_mgmt_cmd(gfp_t gfp_mask); @@ -357,10 +356,15 @@ static inline void scst_dev_check_set_UA(struct scst_device *dev, void scst_dev_check_set_local_UA(struct scst_device *dev, struct scst_cmd *exclude, const uint8_t *sense, int sense_len); +#define SCST_SET_UA_FLAG_AT_HEAD 1 +#define SCST_SET_UA_FLAG_GLOBAL 2 + void scst_check_set_UA(struct scst_tgt_dev *tgt_dev, - const uint8_t *sense, int sense_len, int head); + const uint8_t *sense, int sense_len, int flags); int scst_set_pending_UA(struct scst_cmd *cmd); +void scst_report_luns_changed(struct scst_acg *acg); + void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, int other_ini, int call_dev_task_mgmt_fn); void scst_process_reset(struct scst_device *dev, diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index fd5c19346..d95c9529a 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -1321,13 +1321,12 @@ static void scst_cmd_done(void *data, char *sense, int result, int resid) if (cmd == NULL) goto out; - scst_do_cmd_done(cmd, result, sense, SCST_SENSE_BUFFERSIZE, resid); + scst_do_cmd_done(cmd, result, sense, SCSI_SENSE_BUFFERSIZE, resid); cmd->state = SCST_CMD_STATE_PRE_DEV_DONE; scst_proccess_redirect_cmd(cmd, - scst_optimize_post_exec_context(cmd, scst_estimate_context()), - 0); + scst_optimize_post_exec_context(cmd, scst_estimate_context()), 0); out: TRACE_EXIT(); @@ -1490,6 +1489,37 @@ inc_dev_cnt: out_compl: cmd->completed = 1; + /* Clear left sense_reported_luns_data_changed UA, if any. */ + + mutex_lock(&scst_mutex); /* protect sess_tgt_dev_list_hash */ + for (i = 0; i < TGT_DEV_HASH_SIZE; i++) { + struct list_head *sess_tgt_dev_list_head = + &cmd->sess->sess_tgt_dev_list_hash[i]; + + list_for_each_entry(tgt_dev, sess_tgt_dev_list_head, + sess_tgt_dev_list_entry) { + struct scst_tgt_dev_UA *ua; + + spin_lock_bh(&tgt_dev->tgt_dev_lock); + list_for_each_entry(ua, &tgt_dev->UA_list, + UA_list_entry) { + if (scst_analyze_sense(ua->UA_sense_buffer, + sizeof(ua->UA_sense_buffer), + SCST_SENSE_ALL_VALID, + SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed))) { + TRACE_MGMT_DBG("Freeing not needed " + "REPORTED LUNS DATA CHANGED UA " + "%p", ua); + list_del(&ua->UA_list_entry); + mempool_free(ua, scst_ua_mempool); + break; + } + } + spin_unlock_bh(&tgt_dev->tgt_dev_lock); + } + } + mutex_unlock(&scst_mutex); + out_done: /* Report the result */ cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME); @@ -2275,7 +2305,10 @@ static int scst_check_sense(struct scst_cmd *cmd) /* Check Unit Attention Sense Key */ if (scst_is_ua_sense(cmd->sense)) { - if (cmd->sense[12] == SCST_SENSE_ASC_UA_RESET) { + if (scst_analyze_sense(cmd->sense, + SCST_SENSE_BUFFERSIZE, + SCST_SENSE_ASC_VALID, + 0, SCST_SENSE_ASC_UA_RESET, 0)) { if (cmd->double_ua_possible) { TRACE(TRACE_MGMT_MINOR, "Double UA " "detected for device %p", dev); @@ -2501,10 +2534,11 @@ static int scst_pre_dev_done(struct scst_cmd *cmd) (cmd->status == SAM_STAT_CHECK_CONDITION) && SCST_SENSE_VALID(cmd->sense) && scst_is_ua_sense(cmd->sense) && - (cmd->sense[12] == 0x2a) && (cmd->sense[13] == 0x01)) { - TRACE(TRACE_SCSI, - "MODE PARAMETERS CHANGED UA (lun %lld)", - (long long unsigned int)cmd->lun); + scst_analyze_sense(cmd->sense, SCST_SENSE_BUFFERSIZE, + SCST_SENSE_ASCx_VALID, + 0, 0x2a, 0x01)) { + TRACE(TRACE_SCSI, "MODE PARAMETERS CHANGED UA (lun " + "%lld)", (long long unsigned int)cmd->lun); cmd->state = SCST_CMD_STATE_MODE_SELECT_CHECKS; goto out; } @@ -2529,6 +2563,8 @@ static int scst_mode_select_checks(struct scst_cmd *cmd) (cmd->cdb[0] == MODE_SELECT_10) || (cmd->cdb[0] == LOG_SELECT))) { struct scst_device *dev = cmd->dev; + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + if (atomic && (dev->scsi_dev != NULL)) { TRACE_DBG("%s", "MODE/LOG SELECT: thread " "context required"); @@ -2541,19 +2577,17 @@ static int scst_mode_select_checks(struct scst_cmd *cmd) (long long unsigned int)cmd->lun); spin_lock_bh(&dev->dev_lock); - spin_lock(&scst_temp_UA_lock); if (cmd->cdb[0] == LOG_SELECT) { - scst_set_sense(scst_temp_UA, - sizeof(scst_temp_UA), + scst_set_sense(sense_buffer, + sizeof(sense_buffer), UNIT_ATTENTION, 0x2a, 0x02); } else { - scst_set_sense(scst_temp_UA, - sizeof(scst_temp_UA), + scst_set_sense(sense_buffer, + sizeof(sense_buffer), UNIT_ATTENTION, 0x2a, 0x01); } - scst_dev_check_set_local_UA(dev, cmd, scst_temp_UA, - sizeof(scst_temp_UA)); - spin_unlock(&scst_temp_UA_lock); + scst_dev_check_set_local_UA(dev, cmd, sense_buffer, + sizeof(sense_buffer)); spin_unlock_bh(&dev->dev_lock); if (dev->scsi_dev != NULL) @@ -2562,11 +2596,20 @@ static int scst_mode_select_checks(struct scst_cmd *cmd) } else if ((cmd->status == SAM_STAT_CHECK_CONDITION) && SCST_SENSE_VALID(cmd->sense) && scst_is_ua_sense(cmd->sense) && - (((cmd->sense[12] == 0x2a) && (cmd->sense[13] == 0x01)) || - (cmd->sense[12] == 0x29) /* reset */ || - (cmd->sense[12] == 0x28) /* medium changed */ || + /* mode parameters changed */ + (scst_analyze_sense(cmd->sense, SCST_SENSE_BUFFERSIZE, + SCST_SENSE_ASCx_VALID, + 0, 0x2a, 0x01) || + scst_analyze_sense(cmd->sense, SCST_SENSE_BUFFERSIZE, + SCST_SENSE_ASC_VALID, + 0, 0x29, 0) /* reset */ || + scst_analyze_sense(cmd->sense, SCST_SENSE_BUFFERSIZE, + SCST_SENSE_ASC_VALID, + 0, 0x28, 0) /* medium changed */ || /* cleared by another ini (just in case) */ - (cmd->sense[12] == 0x2F))) { + scst_analyze_sense(cmd->sense, SCST_SENSE_BUFFERSIZE, + SCST_SENSE_ASC_VALID, + 0, 0x2F, 0))) { if (atomic) { TRACE_DBG("Possible parameters changed UA %x: " "thread context required", cmd->sense[12]); @@ -2934,7 +2977,8 @@ static int scst_finish_cmd(struct scst_cmd *cmd) TRACE_MGMT_DBG("Requeuing UA for delivery failed cmd " "%p", cmd); scst_check_set_UA(cmd->tgt_dev, cmd->sense, - SCST_SENSE_BUFFERSIZE, 1); + SCST_SENSE_BUFFERSIZE, + SCST_SET_UA_FLAG_AT_HEAD); } } @@ -4192,14 +4236,15 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd) mutex_unlock(&scst_mutex); if (!dev->tas) { + uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; + + scst_set_sense(sense_buffer, sizeof(sense_buffer), + SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA)); + list_for_each_entry(tgt_dev, &UA_tgt_devs, - extra_tgt_dev_list_entry) { - spin_lock_bh(&scst_temp_UA_lock); - scst_set_sense(scst_temp_UA, sizeof(scst_temp_UA), - SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA)); - scst_check_set_UA(tgt_dev, scst_temp_UA, - sizeof(scst_temp_UA), 0); - spin_unlock_bh(&scst_temp_UA_lock); + extra_tgt_dev_list_entry) { + scst_check_set_UA(tgt_dev, sense_buffer, + sizeof(sense_buffer), 0); } }