From dda7f2f71bd6a3a41b25697bef14c3b8ae03b3e0 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Mon, 10 Aug 2009 16:59:16 +0000 Subject: [PATCH] - Added "replace" command to replace one LUN by another and generate INQUIRY DATA HAS CHANGED Unit Attention - Sending INQUIRY DATA HAS CHANGED Unit Attention through AENs added - Minor fixes and cleanups git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1036 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/README | 15 +++++ scst/README_in-tree | 19 +++++- scst/src/scst_lib.c | 155 +++++++++++++++++++++---------------------- scst/src/scst_main.c | 7 +- scst/src/scst_priv.h | 8 ++- scst/src/scst_proc.c | 84 +++++++++++++++++------ 6 files changed, 182 insertions(+), 106 deletions(-) diff --git a/scst/README b/scst/README index 836e195ae..c6c43521c 100644 --- a/scst/README +++ b/scst/README @@ -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" diff --git a/scst/README_in-tree b/scst/README_in-tree index f0abba00b..15f08e821 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -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" diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index d1d5c4d44..bb0274ac9 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -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", diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 33708c306..71781c81b 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -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); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 00e710826..f8b2a4a26 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -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; diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index 734d30404..f6fe400b1 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -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; + 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, + action == SCST_PROC_ACTION_ADD); + if (rc) { + res = rc; goto out_free_up; } - rc = scst_acg_add_dev(acg, dev, virt_lun, read_only); - if (rc) { - PRINT_ERROR("scst_acg_add_dev() returned %d", rc); - res = rc; + + 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;