- Support for Async. Event Notifications added
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 17 Mar 2009 17:56:28 +0000 (17:56 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 17 Mar 2009 17:56:28 +0000 (17:56 +0000)
 - Implemented "plug-and-play" notifications about new devices in security groups and changed size of a device both through AENs and Unit Attentions

 - New command SCST_USER_DEVICE_CAPACITY_CHANGED added to scst_user interface to notify SCST core that the corresponding device has changed its capacity

 - New command "resync_size" added to scst_vdisk proc interface to tell scst_vdisk to reread size of the corresponding device.

 - Docs update

 - Minor fixes and cleanups

git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@697 d57e44dd-8a1f-0410-8b47-8ef2f437770f

15 files changed:
scst/README
scst/README_in-tree
scst/include/scst.h
scst/include/scst_const.h
scst/include/scst_user.h
scst/src/dev_handlers/scst_cdrom.c
scst/src/dev_handlers/scst_disk.c
scst/src/dev_handlers/scst_modisk.c
scst/src/dev_handlers/scst_tape.c
scst/src/dev_handlers/scst_user.c
scst/src/dev_handlers/scst_vdisk.c
scst/src/scst_lib.c
scst/src/scst_main.c
scst/src/scst_priv.h
scst/src/scst_targ.c

index 735a6f8..dbff71a 100644 (file)
@@ -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
index a7fceb4..2204823 100644 (file)
@@ -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
index eb207e8..d527a67 100644 (file)
@@ -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 command is expected to be NON-BLOCKING, but can sleep.
+        * This function is used for Asynchronous Event Notifications.
         *
-        * MUST HAVE if low-level protocol supports AEN
+        * 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.
         *
-        * 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
index ad71965..23aeac7 100644 (file)
 /* 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
index edc612b..8d4210a 100644 (file)
@@ -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          \
index 356dbf3..c3043b9 100644 (file)
@@ -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;
        }
 
index e7c5fd6..3228def 100644 (file)
@@ -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;
        }
index 0a24cdc..80d17f0 100644 (file)
@@ -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;
                }
index 0430c90..2ec3367 100644 (file)
@@ -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);
 
index 2dda43e..3b198aa 100644 (file)
@@ -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)
 {
index d7e5813..c815558 100644 (file)
@@ -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);
-                               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);
+                       res = vdisk_get_check_file_size(virt_dev->file_name,
+                               virt_dev->blockio, &err);
+                       if (res != 0)
                                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);
-                       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);
+               res = vdisk_get_check_file_size(virt_dev->file_name,
+                               virt_dev->blockio, &err);
+               if (res != 0)
                        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)
index 8195a31..d201a18 100644 (file)
 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);
index c6062a6..f30178f 100644 (file)
@@ -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);
index f9f3652..b5da43d 100644 (file)
@@ -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,
index fd5c193..d95c952 100644 (file)
@@ -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);
                }
        }