- Added "replace" command to replace one LUN by another and generate INQUIRY DATA...
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Mon, 10 Aug 2009 16:59:16 +0000 (16:59 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Mon, 10 Aug 2009 16:59:16 +0000 (16:59 +0000)
 - Sending INQUIRY DATA HAS CHANGED Unit Attention through AENs added
 - Minor fixes and cleanups

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

scst/README
scst/README_in-tree
scst/src/scst_lib.c
scst/src/scst_main.c
scst/src/scst_priv.h
scst/src/scst_proc.c

index 836e195..c6c4352 100644 (file)
@@ -460,6 +460,14 @@ following files and directories under /proc/scsi_tgt:
     the device could be marked as read only. The recommended way to find out
     H:C:I:L numbers is use of lsscsi utility.
 
+  - "replace H:C:I:L lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices
+    replaces by device with host:channel:id:lun existing with LUN "lun"
+    device in group "GROUP_NAME" with generation of INQUIRY DATA HAS
+    CHANGED Unit Attention. If the old device doesn't exist, this
+    command acts as the "add" command. Optionally, the device could be
+    marked as read only. The recommended way to find out H:C:I:L numbers
+    is use of lsscsi utility.
+
   - "del H:C:I:L" to /proc/scsi_tgt/groups/GROUP_NAME/devices deletes device with
     host:channel:id:lun from group "GROUP_NAME". The recommended way to find out
     H:C:I:L numbers is use of lsscsi utility.
@@ -468,6 +476,13 @@ following files and directories under /proc/scsi_tgt:
     device with virtual name "V_NAME" with LUN "lun" in group "GROUP_NAME".
     Optionally, the device could be marked as read only.
 
+  - "replace V_NAME lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices
+    replaces by device with virtual name "V_NAME" existing with LUN
+    "lun" device in group "GROUP_NAME" with generation of INQUIRY DATA
+    HAS CHANGED Unit Attention. If the old device doesn't exist, this
+    command acts as the "add" command. Optionally, the device could
+    be marked as read only.
+
   - "del V_NAME" to /proc/scsi_tgt/groups/GROUP_NAME/devices deletes device with
     virtual name "V_NAME" from group "GROUP_NAME"
 
index f0abba0..15f08e8 100644 (file)
@@ -378,13 +378,28 @@ following files and directories under /proc/scsi_tgt:
     the device could be marked as read only. The recommended way to find out
     H:C:I:L numbers is use of lsscsi utility.
 
+  - "replace H:C:I:L lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices
+    replaces by device with host:channel:id:lun existing with LUN "lun"
+    device in group "GROUP_NAME" with generation of INQUIRY DATA HAS
+    CHANGED Unit Attention. If the old device doesn't exist, this
+    command acts as the "add" command. Optionally, the device could be
+    marked as read only. The recommended way to find out H:C:I:L numbers
+    is use of lsscsi utility.
+
   - "del H:C:I:L" to /proc/scsi_tgt/groups/GROUP_NAME/devices deletes device with
     host:channel:id:lun from group "GROUP_NAME". The recommended way to find out
     H:C:I:L numbers is use of lsscsi utility.
 
   - "add V_NAME lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices adds
-    device with virtual name "V_NAME" with LUN "lun" in group "GROUP_NAME".
-    Optionally, the device could be marked as read only.
+    device with virtual name "V_NAME" with LUN "lun" in group
+    "GROUP_NAME". Optionally, the device could be marked as read only.
+
+  - "replace V_NAME lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices
+    replaces by device with virtual name "V_NAME" existing with LUN
+    "lun" device in group "GROUP_NAME" with generation of INQUIRY DATA
+    HAS CHANGED Unit Attention. If the old device doesn't exist, this
+    command acts as the "add" command. Optionally, the device could be
+    marked as read only.
 
   - "del V_NAME" to /proc/scsi_tgt/groups/GROUP_NAME/devices deletes device with
     virtual name "V_NAME" from group "GROUP_NAME"
index d1d5c4d..bb0274a 100644 (file)
@@ -410,7 +410,8 @@ void scst_set_initial_UA(struct scst_session *sess, int key, int asc, int ascq)
 }
 EXPORT_SYMBOL(scst_set_initial_UA);
 
-static struct scst_aen *scst_alloc_aen(struct scst_tgt_dev *tgt_dev)
+static struct scst_aen *scst_alloc_aen(struct scst_session *sess,
+       uint64_t unpacked_lun)
 {
        struct scst_aen *aen;
 
@@ -420,15 +421,15 @@ static struct scst_aen *scst_alloc_aen(struct scst_tgt_dev *tgt_dev)
        if (aen == NULL) {
                PRINT_ERROR("AEN memory allocation failed. Corresponding "
                        "event notification will not be performed (initiator "
-                       "%s)", tgt_dev->sess->initiator_name);
+                       "%s)", sess->initiator_name);
                goto out;
        }
        memset(aen, 0, sizeof(*aen));
 
-       aen->sess = tgt_dev->sess;
-       scst_sess_get(aen->sess);
+       aen->sess = sess;
+       scst_sess_get(sess);
 
-       aen->lun = scst_pack_lun(tgt_dev->lun);
+       aen->lun = scst_pack_lun(unpacked_lun);
 
 out:
        TRACE_EXIT_HRES((unsigned long)aen);
@@ -446,11 +447,55 @@ static void scst_free_aen(struct scst_aen *aen)
        return;
 };
 
+/* Must be called unded scst_mutex */
+void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
+       int key, int asc, int ascq)
+{
+       struct scst_tgt_template *tgtt = tgt_dev->sess->tgt->tgtt;
+       uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
+
+       TRACE_ENTRY();
+
+       if (tgtt->report_aen != NULL) {
+               struct scst_aen *aen;
+               int rc;
+
+               aen = scst_alloc_aen(tgt_dev->sess, tgt_dev->lun);
+               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,
+                       tgt_dev->dev->d_sense, key, asc, ascq);
+
+               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)
+                       goto out;
+
+               scst_free_aen(aen);
+       }
+
+queue_ua:
+       TRACE_MGMT_DBG("AEN not supported, queuing plain UA (tgt_dev %p)",
+               tgt_dev);
+       scst_set_sense(sense_buffer, sizeof(sense_buffer),
+               tgt_dev->dev->d_sense, key, asc, ascq);
+       scst_check_set_UA(tgt_dev, sense_buffer, sizeof(sense_buffer), 0);
+
+out:
+       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();
 
@@ -466,40 +511,8 @@ void scst_capacity_data_changed(struct scst_device *dev)
 
        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,
-                               tgt_dev->dev->d_sense,
-                               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_set_sense(sense_buffer, sizeof(sense_buffer),
-                       tgt_dev->dev->d_sense,
+               scst_gen_aen_or_ua(tgt_dev,
                        SCST_LOAD_SENSE(scst_sense_capacity_data_changed));
-               scst_check_set_UA(tgt_dev, sense_buffer,
-                       sizeof(sense_buffer), 0);
        }
 
        mutex_unlock(&scst_mutex);
@@ -594,41 +607,43 @@ static void scst_queue_report_luns_changed_UA(struct scst_session *sess,
 static void scst_report_luns_changed_sess(struct scst_session *sess)
 {
        int i;
-       struct list_head *shead;
-       struct scst_tgt_dev *tgt_dev;
        struct scst_tgt_template *tgtt = sess->tgt->tgtt;
+       int d_sense = 0;
+       uint64_t lun = 0;
 
        TRACE_ENTRY();
 
        TRACE_DBG("REPORTED LUNS DATA CHANGED (sess %p)", sess);
 
        for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
+               struct list_head *shead;
+               struct scst_tgt_dev *tgt_dev;
+
                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))
+                                       tgt_dev->dev->type)) {
+                               lun = tgt_dev->lun;
+                               d_sense = tgt_dev->dev->d_sense;
                                goto found;
+                       }
                }
        }
-       TRACE_MGMT_DBG("Not found a device capable REPORTED "
-               "LUNS DATA CHANGED UA (sess %p)", sess);
-       goto out;
 
 found:
        if (tgtt->report_aen != NULL) {
                struct scst_aen *aen;
                int rc;
 
-               aen = scst_alloc_aen(tgt_dev);
+               aen = scst_alloc_aen(sess, lun);
                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,
-                       tgt_dev->dev->d_sense,
+               scst_set_sense(aen->aen_sense, aen->aen_sense_len, d_sense,
                        SCST_LOAD_SENSE(scst_sense_reported_luns_data_changed));
 
                TRACE_DBG("Calling target's %s report_aen(%p)",
@@ -690,10 +705,7 @@ void scst_aen_done(struct scst_aen *aen)
                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 */
+       } else {
                struct list_head *shead;
                struct scst_tgt_dev *tgt_dev;
                uint64_t lun;
@@ -702,12 +714,13 @@ void scst_aen_done(struct scst_aen *aen)
 
                mutex_lock(&scst_mutex);
 
+               /* tgt_dev might get dead, so we need to reseek it */
                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);
+                               TRACE_MGMT_DBG("Requeuing failed AEN UA for "
+                                       "tgt_dev %p", tgt_dev);
                                scst_check_set_UA(tgt_dev, aen->aen_sense,
                                        aen->aen_sense_len,
                                        SCST_SET_UA_FLAG_AT_HEAD);
@@ -716,8 +729,7 @@ void scst_aen_done(struct scst_aen *aen)
                }
 
                mutex_unlock(&scst_mutex);
-       } else
-               PRINT_ERROR("%s", "Unknown SCSI AEN");
+       }
 
 out_free:
        scst_free_aen(aen);
@@ -851,8 +863,6 @@ next:
        list_move_tail(&sess->acg_sess_list_entry, &acg->acg_sess_list);
 
        if (luns_changed) {
-               uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
-
                scst_report_luns_changed_sess(sess);
 
                for (i = 0; i < TGT_DEV_HASH_SIZE; i++) {
@@ -867,13 +877,8 @@ next:
 
                                        tgt_dev->inq_changed_ua_needed = 0;
 
-                                       scst_set_sense(sense_buffer,
-                                               sizeof(sense_buffer),
-                                               tgt_dev->dev->d_sense,
+                                       scst_gen_aen_or_ua(tgt_dev,
                                                SCST_LOAD_SENSE(scst_sense_inquery_data_changed));
-
-                                       scst_check_set_UA(tgt_dev, sense_buffer,
-                                               sizeof(sense_buffer), 0);
                                }
                        }
                }
@@ -1729,7 +1734,7 @@ static void scst_sess_free_tgt_devs(struct scst_session *sess)
 
 /* The activity supposed to be suspended and scst_mutex held */
 int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev,
-                    uint64_t lun, int read_only)
+       uint64_t lun, int read_only, bool gen_scst_report_luns_changed)
 {
        int res = 0;
        struct scst_acg_dev *acg_dev;
@@ -1741,17 +1746,6 @@ int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev,
 
        INIT_LIST_HEAD(&tmp_tgt_dev_list);
 
-#ifdef CONFIG_SCST_EXTRACHECKS
-       list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
-               if (acg_dev->dev == dev) {
-                       PRINT_ERROR("Device is already in group %s",
-                               acg->acg_name);
-                       res = -EINVAL;
-                       goto out;
-               }
-       }
-#endif
-
        acg_dev = scst_alloc_acg_dev(acg, dev, lun);
        if (acg_dev == NULL) {
                res = -ENOMEM;
@@ -1774,7 +1768,8 @@ int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev,
                              &tmp_tgt_dev_list);
        }
 
-       scst_report_luns_changed(acg);
+       if (gen_scst_report_luns_changed)
+               scst_report_luns_changed(acg);
 
        if (dev->virt_name != NULL) {
                PRINT_INFO("Added device %s to group %s (LUN %lld, "
@@ -1805,7 +1800,8 @@ out_free:
 }
 
 /* The activity supposed to be suspended and scst_mutex held */
-int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev)
+int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev,
+       bool gen_scst_report_luns_changed)
 {
        int res = 0;
        struct scst_acg_dev *acg_dev = NULL, *a;
@@ -1833,7 +1829,8 @@ int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev)
        }
        scst_free_acg_dev(acg_dev);
 
-       scst_report_luns_changed(acg);
+       if (gen_scst_report_luns_changed)
+               scst_report_luns_changed(acg);
 
        if (dev->virt_name != NULL) {
                PRINT_INFO("Removed device %s from group %s",
index 33708c3..71781c8 100644 (file)
@@ -724,9 +724,8 @@ static void scst_unregister_device(struct scsi_device *scsidp)
        list_del(&dev->dev_list_entry);
 
        list_for_each_entry_safe(acg_dev, aa, &dev->dev_acg_dev_list,
-                                dev_acg_dev_list_entry)
-       {
-               scst_acg_remove_dev(acg_dev->acg, dev);
+                                dev_acg_dev_list_entry) {
+               scst_acg_remove_dev(acg_dev->acg, dev, true);
        }
 
        scst_assign_dev_handler(dev, &scst_null_devtype);
@@ -876,7 +875,7 @@ void scst_unregister_virtual_device(int id)
        list_for_each_entry_safe(acg_dev, aa, &dev->dev_acg_dev_list,
                                 dev_acg_dev_list_entry)
        {
-               scst_acg_remove_dev(acg_dev->acg, dev);
+               scst_acg_remove_dev(acg_dev->acg, dev, true);
        }
 
        scst_assign_dev_handler(dev, &scst_null_devtype);
index 00e7108..f8b2a4a 100644 (file)
@@ -301,8 +301,9 @@ int scst_sess_alloc_tgt_devs(struct scst_session *sess);
 void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA);
 
 int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev,
-                    uint64_t lun, int read_only);
-int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev);
+       uint64_t lun, int read_only, bool gen_scst_report_luns_changed);
+int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev,
+       bool gen_scst_report_luns_changed);
 
 int scst_acg_add_name(struct scst_acg *acg, const char *name);
 int scst_acg_remove_name(struct scst_acg *acg, const char *name, bool reassign);
@@ -437,6 +438,9 @@ void scst_process_reset(struct scst_device *dev,
 bool scst_is_ua_global(const uint8_t *sense, int len);
 void scst_requeue_ua(struct scst_cmd *cmd);
 
+void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
+       int key, int asc, int ascq);
+
 static inline bool scst_is_ua_command(struct scst_cmd *cmd)
 {
        return (cmd->op_flags & SCST_SKIP_UA) == 0;
index 734d304..f6fe400 100644 (file)
@@ -80,11 +80,12 @@ static struct scst_proc_data scst_dev_handler_proc_data;
 #define SCST_PROC_ACTION_CLEAR          6
 #define SCST_PROC_ACTION_MOVE           7
 #define SCST_PROC_ACTION_DEL            8
-#define SCST_PROC_ACTION_VALUE          9
-#define SCST_PROC_ACTION_ASSIGN                10
-#define SCST_PROC_ACTION_ADD_GROUP     11
-#define SCST_PROC_ACTION_DEL_GROUP     12
-#define SCST_PROC_ACTION_RENAME_GROUP  13
+#define SCST_PROC_ACTION_REPLACE        9
+#define SCST_PROC_ACTION_VALUE         10
+#define SCST_PROC_ACTION_ASSIGN                11
+#define SCST_PROC_ACTION_ADD_GROUP     12
+#define SCST_PROC_ACTION_DEL_GROUP     13
+#define SCST_PROC_ACTION_RENAME_GROUP  14
 
 static struct proc_dir_entry *scst_proc_scsi_tgt;
 static struct proc_dir_entry *scst_proc_groups_root;
@@ -133,8 +134,12 @@ static char *scst_proc_help_string =
 "\n"
 "   echo \"add|del H:C:I:L lun [READ_ONLY]\""
 " >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
+"   echo \"replace H:C:I:L lun [READ_ONLY]\""
+" >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
 "   echo \"add|del V_NAME lun [READ_ONLY]\""
 " >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
+"   echo \"replace V_NAME lun [READ_ONLY]\""
+" >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
 "   echo \"clear\" >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
 "\n"
 "   echo \"add|del NAME\" >/proc/scsi_tgt/groups/GROUP_NAME/names\n"
@@ -1613,8 +1618,12 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
        /*
         * Usage: echo "add|del H:C:I:L lun [READ_ONLY]" \
         *          >/proc/scsi_tgt/groups/GROUP_NAME/devices
+        *   or   echo "replace H:C:I:L lun [READ_ONLY]" \
+        *          >/proc/scsi_tgt/groups/GROUP_NAME/devices
         *   or   echo "add|del V_NAME lun [READ_ONLY]" \
         *          >/proc/scsi_tgt/groups/GROUP_NAME/devices
+        *   or   echo "replace V_NAME lun [READ_ONLY]" \
+        *          >/proc/scsi_tgt/groups/GROUP_NAME/devices
         *   or   echo "clear" >/proc/scsi_tgt/groups/GROUP_NAME/devices
         */
        p = buffer;
@@ -1628,6 +1637,9 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
        } else if (!strncasecmp("del ", p, 4)) {
                p += 4;
                action = SCST_PROC_ACTION_DEL;
+       } else if (!strncasecmp("replace ", p, 8)) {
+               p += 8;
+               action = SCST_PROC_ACTION_REPLACE;
        } else {
                PRINT_ERROR("Unknown action \"%s\"", p);
                res = -EINVAL;
@@ -1648,6 +1660,7 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
        switch (action) {
        case SCST_PROC_ACTION_ADD:
        case SCST_PROC_ACTION_DEL:
+       case SCST_PROC_ACTION_REPLACE:
                while (isspace(*p) && *p != '\0')
                        p++;
                e = p; /* save p */
@@ -1703,6 +1716,10 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
 
        switch (action) {
        case SCST_PROC_ACTION_ADD:
+       case SCST_PROC_ACTION_REPLACE:
+       {
+               bool dev_replaced = false;
+
                e++;
                while (isspace(*e) && *e != '\0')
                        e++;
@@ -1728,35 +1745,64 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
                                break;
                        }
                }
-               if (acg_dev) {
-                       acg_dev = acg_dev_tmp;
-                       PRINT_ERROR("virt lun %d already exists in group %s",
-                                      virt_lun, acg->acg_name);
-                       res = -EINVAL;
-                       goto out_free_up;
+               if (acg_dev != NULL) {
+                       if (action == SCST_PROC_ACTION_ADD) {
+                               PRINT_ERROR("virt lun %d already exists in "
+                                       "group %s", virt_lun, acg->acg_name);
+                               res = -EINVAL;
+                               goto out_free_up;
+                       } else {
+                               /* Replace */
+                               rc = scst_acg_remove_dev(acg, acg_dev->dev,
+                                               false);
+                               if (rc) {
+                                       res = rc;
+                                       goto out_free_up;
+                               }
+                               dev_replaced = true;
+                       }
                }
-               rc = scst_acg_add_dev(acg, dev, virt_lun, read_only);
+
+               rc = scst_acg_add_dev(acg, dev, virt_lun, read_only,
+                       action == SCST_PROC_ACTION_ADD);
                if (rc) {
-                       PRINT_ERROR("scst_acg_add_dev() returned %d", rc);
                        res = rc;
+                       goto out_free_up;
+               }
+
+               if (dev_replaced) {
+                       struct scst_tgt_dev *tgt_dev;
+
+                       list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
+                                       dev_tgt_dev_list_entry) {
+                               if ((tgt_dev->acg_dev->acg == acg) &&
+                                   (tgt_dev->lun == virt_lun)) {
+                                       TRACE_MGMT_DBG("INQUIRY DATA HAS CHANGED"
+                                               " on tgt_dev %p", tgt_dev);
+                                       scst_gen_aen_or_ua(tgt_dev,
+                                               SCST_LOAD_SENSE(scst_sense_inquery_data_changed));
+                               }
+                       }
                }
                break;
+       }
        case SCST_PROC_ACTION_DEL:
-               rc = scst_acg_remove_dev(acg, dev);
+               rc = scst_acg_remove_dev(acg, dev, true);
                if (rc) {
-                       PRINT_ERROR("scst_acg_remove_dev() returned %d", rc);
                        res = rc;
+                       goto out_free_up;
                }
                break;
        case SCST_PROC_ACTION_CLEAR:
                list_for_each_entry_safe(acg_dev, acg_dev_tmp,
                                         &acg->acg_dev_list,
                                         acg_dev_list_entry) {
-                       rc = scst_acg_remove_dev(acg, acg_dev->dev);
+                       rc = scst_acg_remove_dev(acg, acg_dev->dev,
+                               list_is_last(&acg_dev->acg_dev_list_entry,
+                                            &acg->acg_dev_list));
                        if (rc) {
-                               PRINT_ERROR("scst_acg_remove_dev() "
-                                              "return %d", rc);
                                res = rc;
+                               goto out_free_up;
                        }
                }
                break;