Add ALUA command filtering

From Bart Van Assche <bvanassche@acm.org>



git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@4848 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2013-04-30 04:27:53 +00:00
parent d9591083cb
commit 12abbb8e40
7 changed files with 515 additions and 42 deletions

View File

@@ -2595,6 +2595,9 @@ struct scst_tgt_dev {
unsigned short tgt_dev_valid_sense_len;
uint8_t tgt_dev_sense[SCST_SENSE_BUFFERSIZE];
/* ALUA command filter */
bool (*alua_filter)(struct scst_cmd *cmd);
#ifndef CONFIG_SCST_PROC
/* sysfs release completion */
struct completion *tgt_dev_kobj_release_cmpl;

View File

@@ -278,6 +278,8 @@ static inline int scst_sense_response_code(const uint8_t *sense)
#define scst_sense_no_sense NO_SENSE, 0x00, 0
/* NOT_READY is 2 */
#define scst_sense_tp_transitioning NOT_READY, 0x04, 0x0A
#define scst_sense_tp_unav NOT_READY, 0x04, 0x0C
#define scst_sense_not_ready NOT_READY, 0x04, 0x10
#define scst_sense_no_medium NOT_READY, 0x3a, 0
@@ -354,6 +356,10 @@ static inline int scst_sense_response_code(const uint8_t *sense)
* The definitions below are only used when this header file is included during
* compilation of SCST's user space components.
*/
#ifndef GET_EVENT_STATUS_NOTIFICATION
/* Upstream commit 93aae17a (v2.6.38) */
#define GET_EVENT_STATUS_NOTIFICATION 0x4a
#endif
#ifndef READ_16
#define READ_16 0x88
#endif
@@ -496,6 +502,7 @@ enum {
* See also the documentation of the REPORT TARGET PORT GROUPS command in SPC-4.
*/
enum scst_tg_state {
SCST_TG_STATE_UNDEFINED = -1,
SCST_TG_STATE_OPTIMIZED = 0x0,
SCST_TG_STATE_NONOPTIMIZED = 0x1,
SCST_TG_STATE_STANDBY = 0x2,

View File

@@ -4180,6 +4180,8 @@ static int scst_alloc_add_tgt_dev(struct scst_session *sess,
head = &sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(tgt_dev->lun)];
list_add_tail(&tgt_dev->sess_tgt_dev_list_entry, head);
scst_tg_init_tgt_dev(tgt_dev);
*out_tgt_dev = tgt_dev;
out:

View File

@@ -410,9 +410,13 @@ struct scst_dev_group *scst_lookup_dg_by_kobj(struct kobject *kobj);
int scst_dg_dev_add(struct scst_dev_group *dg, const char *name);
int scst_dg_dev_remove_by_name(struct scst_dev_group *dg, const char *name);
int scst_dg_dev_remove_by_dev(struct scst_device *dev);
const char *scst_alua_state_name(enum scst_tg_state s);
enum scst_tg_state scst_alua_name_to_state(const char *n);
int scst_tg_add(struct scst_dev_group *dg, const char *name);
int scst_tg_remove_by_name(struct scst_dev_group *dg, const char *name);
void scst_tg_init_tgt_dev(struct scst_tgt_dev *tgt_dev);
int scst_tg_set_state(struct scst_target_group *tg, enum scst_tg_state state);
int scst_tg_set_preferred(struct scst_target_group *tg, bool preferred);
int scst_tg_tgt_add(struct scst_target_group *tg, const char *name);
int scst_tg_tgt_remove_by_name(struct scst_target_group *tg, const char *name);
void scst_tg_tgt_remove_by_tgt(struct scst_tgt *tgt);

View File

@@ -5082,69 +5082,85 @@ static ssize_t scst_tg_preferred_show(struct kobject *kobj,
tg->preferred, SCST_SYSFS_KEY_MARK "\n");
}
static ssize_t scst_tg_preferred_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
static int scst_tg_preferred_store_work_fn(struct scst_sysfs_work_item *w)
{
struct scst_target_group *tg;
unsigned long preferred;
char ch[8];
char *cmd;
int res;
TRACE_ENTRY();
tg = container_of(kobj, struct scst_target_group, kobj);
snprintf(ch, sizeof(ch), "%.*s", min_t(int, count, sizeof(ch)-1), buf);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(ch, 0, &preferred);
#else
res = strict_strtoul(ch, 0, &preferred);
#endif
cmd = w->buf;
tg = container_of(w->kobj, struct scst_target_group, kobj);
res = strict_strtoul(cmd, 0, &preferred);
if (res)
goto out;
res = -EINVAL;
if (preferred != 0 && preferred != 1)
goto out;
tg->preferred = preferred;
res = count;
res = scst_tg_set_preferred(tg, preferred);
out:
kobject_put(w->kobj);
TRACE_EXIT_RES(res);
return res;
}
static ssize_t scst_tg_preferred_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *cmd;
struct scst_sysfs_work_item *work;
int res;
res = -ENOMEM;
cmd = kasprintf(GFP_KERNEL, "%.*s", (int)count, buf);
if (!cmd)
goto out;
res = scst_alloc_sysfs_work(scst_tg_preferred_store_work_fn, false,
&work);
if (res)
goto out;
swap(work->buf, cmd);
work->kobj = kobj;
kobject_get(kobj);
res = scst_sysfs_queue_wait_work(work);
if (res)
goto out;
res = count;
out:
kfree(cmd);
return res;
}
static struct kobj_attribute scst_tg_preferred =
__ATTR(preferred, S_IRUGO | S_IWUSR, scst_tg_preferred_show,
scst_tg_preferred_store);
static struct { enum scst_tg_state s; const char *n; } scst_tg_state_names[] = {
{ SCST_TG_STATE_OPTIMIZED, "active" },
{ SCST_TG_STATE_NONOPTIMIZED, "nonoptimized" },
{ SCST_TG_STATE_STANDBY, "standby" },
{ SCST_TG_STATE_UNAVAILABLE, "unavailable" },
{ SCST_TG_STATE_OFFLINE, "offline" },
{ SCST_TG_STATE_TRANSITIONING, "transitioning" },
};
static ssize_t scst_tg_state_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct scst_target_group *tg;
int i;
const char *n;
tg = container_of(kobj, struct scst_target_group, kobj);
for (i = ARRAY_SIZE(scst_tg_state_names) - 1; i >= 0; i--)
if (scst_tg_state_names[i].s == tg->state)
break;
n = scst_alua_state_name(tg->state);
return scnprintf(buf, PAGE_SIZE, "%s\n" SCST_SYSFS_KEY_MARK "\n",
i >= 0 ? scst_tg_state_names[i].n : "???");
n ? n : "???");
}
static int scst_tg_state_store_work_fn(struct scst_sysfs_work_item *w)
{
struct scst_target_group *tg;
char *cmd, *p;
int i, res;
int res;
enum scst_tg_state s;
TRACE_ENTRY();
@@ -5155,14 +5171,13 @@ static int scst_tg_state_store_work_fn(struct scst_sysfs_work_item *w)
if (p)
*p = '\0';
for (i = ARRAY_SIZE(scst_tg_state_names) - 1; i >= 0; i--)
if (strcmp(scst_tg_state_names[i].n, cmd) == 0)
break;
s = scst_alua_name_to_state(cmd);
res = -EINVAL;
if (i < 0)
if (s == SCST_TG_STATE_UNDEFINED)
goto out;
res = scst_tg_set_state(tg, scst_tg_state_names[i].s);
res = scst_tg_set_state(tg, s);
out:
kobject_put(w->kobj);
TRACE_EXIT_RES(res);

View File

@@ -3992,6 +3992,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
*/
static int __scst_init_cmd(struct scst_cmd *cmd)
{
bool (*alua_filter)(struct scst_cmd *cmd);
int res = 0;
TRACE_ENTRY();
@@ -4032,6 +4033,12 @@ static int __scst_init_cmd(struct scst_cmd *cmd)
if (unlikely(failure))
goto out_busy;
alua_filter = ACCESS_ONCE(cmd->tgt_dev->alua_filter);
if (unlikely(alua_filter && !alua_filter(cmd))) {
scst_set_cmd_abnormal_done_state(cmd);
goto out;
}
/*
* SCST_IMPLICIT_HQ for unknown commands not implemented for
* case when set_sn_on_restart_cmd not set, because custom parse

View File

@@ -15,6 +15,7 @@
* GNU General Public License for more details.
*/
#include <linux/moduleparam.h>
#include <asm/unaligned.h>
#ifdef INSIDE_KERNEL_TREE
#include <scst/scst.h>
@@ -23,8 +24,54 @@
#endif
#include "scst_priv.h"
struct alua_state_and_name {
enum scst_tg_state s;
char *n;
};
static const struct alua_state_and_name scst_tg_state_names[] = {
{ SCST_TG_STATE_OPTIMIZED, "active" },
{ SCST_TG_STATE_NONOPTIMIZED, "nonoptimized" },
{ SCST_TG_STATE_STANDBY, "standby" },
{ SCST_TG_STATE_UNAVAILABLE, "unavailable" },
{ SCST_TG_STATE_OFFLINE, "offline" },
{ SCST_TG_STATE_TRANSITIONING, "transitioning" },
};
static struct list_head scst_dev_group_list;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) || \
defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
static int alua_invariant_check;
#else
static bool alua_invariant_check;
#endif
module_param(alua_invariant_check, bool, 0644);
MODULE_PARM_DESC(alua_invariant_check,
"Enables a run-time ALUA state invariant check.");
const char *scst_alua_state_name(enum scst_tg_state s)
{
int i;
for (i = 0; i < ARRAY_SIZE(scst_tg_state_names); i++)
if (scst_tg_state_names[i].s == s)
return scst_tg_state_names[i].n;
return NULL;
}
enum scst_tg_state scst_alua_name_to_state(const char *n)
{
int i;
for (i = 0; i < ARRAY_SIZE(scst_tg_state_names); i++)
if (strcmp(scst_tg_state_names[i].n, n) == 0)
return scst_tg_state_names[i].s;
return SCST_TG_STATE_UNDEFINED;
}
/* Look up a device by name. */
static struct scst_device *__lookup_dev(const char *name)
{
@@ -81,6 +128,21 @@ __lookup_tg_by_name(struct scst_dev_group *dg, const char *name)
return NULL;
}
/* Look up a target group by target port. */
static struct scst_target_group *
__lookup_tg_by_tgt(struct scst_dev_group *dg, const struct scst_tgt *tgt)
{
struct scst_target_group *tg;
struct scst_tg_tgt *tg_tgt;
list_for_each_entry(tg, &dg->tg_list, entry)
list_for_each_entry(tg_tgt, &tg->tgt_list, entry)
if (tg_tgt->tgt == tgt)
return tg;
return NULL;
}
/* Look up a device node by device pointer in the given device group. */
static struct scst_dg_dev *__lookup_dg_dev_by_dev(struct scst_dev_group *dg,
struct scst_device *dev)
@@ -165,6 +227,223 @@ static struct kobj_type scst_tg_tgt_ktype = {
.release = scst_release_tg_tgt,
};
/*
* Whether or not to accept a command in the ALUA unavailable and transitioning
* states.
*/
static bool scst_tg_accept(struct scst_cmd *cmd)
{
switch (cmd->cdb[0]) {
case TEST_UNIT_READY:
case GET_EVENT_STATUS_NOTIFICATION:
case INQUIRY:
case MODE_SENSE:
case MODE_SENSE_10:
case READ_CAPACITY:
case REPORT_LUNS:
case REQUEST_SENSE:
return true;
case SERVICE_ACTION_IN:
switch (cmd->cdb[1] & 0x1f) {
case SAI_READ_CAPACITY_16:
return true;
}
break;
case MAINTENANCE_IN:
switch (cmd->cdb[1] & 0x1f) {
case MI_REPORT_TARGET_PGS:
return true;
}
break;
case MAINTENANCE_OUT:
switch (cmd->cdb[1] & 0x1f) {
case MO_SET_TARGET_PGS:
return true;
}
break;
}
return false;
}
/*
* Whether or not to accept a command in the ALUA unavailable state.
*/
static bool scst_tg_accept_unav(struct scst_cmd *cmd)
{
bool process_cmd = scst_tg_accept(cmd);
if (!process_cmd)
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_tp_unav));
return process_cmd;
}
/*
* Whether or not to accept a command in the ALUA transitioning state.
*/
static bool scst_tg_accept_transitioning(struct scst_cmd *cmd)
{
bool process_cmd = scst_tg_accept(cmd);
if (!process_cmd)
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_tp_transitioning));
return process_cmd;
}
static bool (*scst_alua_filter[])(struct scst_cmd *cmd) = {
[SCST_TG_STATE_OPTIMIZED] = NULL,
[SCST_TG_STATE_NONOPTIMIZED] = NULL,
[SCST_TG_STATE_STANDBY] = NULL,
[SCST_TG_STATE_UNAVAILABLE] = scst_tg_accept_unav,
[SCST_TG_STATE_LBA_DEPENDENT] = NULL,
[SCST_TG_STATE_OFFLINE] = scst_tg_accept_unav,
[SCST_TG_STATE_TRANSITIONING] = scst_tg_accept_transitioning,
};
/*
* Check whether the tgt_dev ALUA filter is consistent with the target group
* ALUA state.
*/
static void scst_check_alua_invariant(void)
{
struct scst_device *dev;
struct scst_tgt_dev *tgt_dev;
struct scst_dev_group *dg;
struct scst_target_group *tg;
enum scst_tg_state expected_state;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
if (!alua_invariant_check)
return;
list_for_each_entry(dev, &scst_dev_list, dev_list_entry) {
dg = __lookup_dg_by_dev(dev);
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
tg = dg ? __lookup_tg_by_tgt(dg, tgt_dev->acg_dev->acg->tgt) :
NULL;
expected_state = tg ? tg->state : SCST_TG_STATE_OPTIMIZED;
if (tgt_dev->alua_filter != scst_alua_filter[expected_state]) {
PRINT_ERROR("LUN %s/%s/%s/%lld/%s: ALUA filter"
" %p <> %p",
tgt_dev->acg_dev->acg->tgt->tgt_name,
tgt_dev->acg_dev->acg->acg_name ? :
"(default)",
tgt_dev->sess->initiator_name,
tgt_dev->lun,
tgt_dev->dev->virt_name ? : "(null)",
tgt_dev->alua_filter,
scst_alua_filter[expected_state]);
}
}
}
}
/* Update the ALUA filter of a tgt_dev */
static void scst_update_tgt_dev_alua_filter(struct scst_tgt_dev *tgt_dev,
enum scst_tg_state state)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
tgt_dev->alua_filter = scst_alua_filter[state];
}
/* Update the ALUA filter after an ALUA state change and generate UA */
static void scst_tg_change_tgt_dev_state(struct scst_tgt_dev *tgt_dev,
enum scst_tg_state state,
bool gen_ua)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
TRACE_MGMT_DBG("ALUA state of tgt_dev %p has changed", tgt_dev);
scst_update_tgt_dev_alua_filter(tgt_dev, state);
if (gen_ua)
scst_gen_aen_or_ua(tgt_dev,
SCST_LOAD_SENSE(scst_sense_asym_access_state_changed));
}
/* Initialize ALUA state of LUN tgt_dev */
void scst_tg_init_tgt_dev(struct scst_tgt_dev *tgt_dev)
{
struct scst_dev_group *dg;
struct scst_target_group *tg;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
dg = __lookup_dg_by_dev(tgt_dev->dev);
if (dg) {
tg = __lookup_tg_by_tgt(dg, tgt_dev->acg_dev->acg->tgt);
if (tg) {
scst_tg_change_tgt_dev_state(tgt_dev, tg->state, true);
scst_check_alua_invariant();
}
}
}
/*
* Update the ALUA filter of all tgt_devs associated with target group @tg
* and target @tgt.
*/
static void scst_update_tgt_alua_filter(struct scst_target_group *tg,
struct scst_tgt *tgt)
{
struct scst_dg_dev *dgd;
struct scst_tgt_dev *tgt_dev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
list_for_each_entry(dgd, &tg->dg->dev_list, entry) {
list_for_each_entry(tgt_dev, &dgd->dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
if (tgt_dev->acg_dev->acg->tgt == tgt)
scst_update_tgt_dev_alua_filter(tgt_dev,
tg->state);
}
}
scst_check_alua_invariant();
}
/*
* Reset the ALUA filter of all tgt_devs associated with target group @tg
* and target @tgt.
*/
static void scst_reset_tgt_alua_filter(struct scst_target_group *tg,
struct scst_tgt *tgt)
{
struct scst_dg_dev *dgd;
struct scst_tgt_dev *tgt_dev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
list_for_each_entry(dgd, &tg->dg->dev_list, entry) {
list_for_each_entry(tgt_dev, &dgd->dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
if (tgt_dev->acg_dev->acg->tgt == tgt)
scst_update_tgt_dev_alua_filter(tgt_dev,
SCST_TG_STATE_OPTIMIZED);
}
}
scst_check_alua_invariant();
}
/**
* scst_tg_tgt_add() - Add a target to a target group.
*/
@@ -204,6 +483,7 @@ int scst_tg_tgt_add(struct scst_target_group *tg, const char *name)
if (res)
goto out_unlock;
list_add_tail(&tg_tgt->entry, &tg->tgt_list);
scst_update_tgt_alua_filter(tg, tgt);
res = 0;
mutex_unlock(&scst_mutex);
out:
@@ -222,6 +502,7 @@ static void __scst_tg_tgt_remove(struct scst_target_group *tg,
TRACE_ENTRY();
list_del(&tg_tgt->entry);
scst_tg_tgt_sysfs_del(tg, tg_tgt);
scst_reset_tgt_alua_filter(tg, tg_tgt->tgt);
kobject_put(&tg_tgt->kobj);
TRACE_EXIT();
}
@@ -379,16 +660,28 @@ out:
return res;
}
int scst_tg_set_state(struct scst_target_group *tg, enum scst_tg_state state)
/*
* Update the ALUA filter of those LUNs (tgt_dev) whose target port is a member
* of target group @tg and that export a device that is a member of the device
* group @tg->dg.
*/
static void __scst_tg_set_state(struct scst_target_group *tg,
enum scst_tg_state state,
struct scst_tgt *no_ua_tgt)
{
struct scst_dg_dev *dg_dev;
struct scst_device *dev;
struct scst_tgt_dev *tgt_dev;
int res;
struct scst_tg_tgt *tg_tgt;
struct scst_tgt *tgt;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out;
sBUG_ON(state >= ARRAY_SIZE(scst_alua_filter));
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
if (tg->state == state)
return;
tg->state = state;
@@ -396,12 +689,106 @@ int scst_tg_set_state(struct scst_target_group *tg, enum scst_tg_state state)
dev = dg_dev->dev;
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
TRACE_MGMT_DBG("ALUA state of tgt_dev %p has changed",
tgt_dev);
scst_gen_aen_or_ua(tgt_dev,
SCST_LOAD_SENSE(scst_sense_asym_access_state_changed));
tgt = tgt_dev->sess->tgt;
list_for_each_entry(tg_tgt, &tg->tgt_list, entry) {
if (tg_tgt->tgt == tgt) {
scst_tg_change_tgt_dev_state(tgt_dev,
state, tgt != no_ua_tgt);
break;
}
}
}
}
scst_check_alua_invariant();
PRINT_INFO("Changed ALUA state of %s/%s into %s", tg->dg->name,
tg->name, scst_alua_state_name(state));
}
int scst_tg_set_state(struct scst_target_group *tg, enum scst_tg_state state)
{
int res;
res = -EINVAL;
if (state >= ARRAY_SIZE(scst_alua_filter))
goto out;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out;
__scst_tg_set_state(tg, state, NULL);
mutex_unlock(&scst_mutex);
out:
return res;
}
/*
* Generate an ASYMMETRIC ACCESS STATE CHANGED check condition after the value
* of the "preferred" state of a target port group has been changed. Although
* not required by SPC-4, generating this check condition terminates an
* initiator-side STPG loop. An initiator typically retries an STPG after
* having received a LOGICAL UNIT NOT ACCESSIBLE, ASYMMETRIC ACCESS STATE
* TRANSITION but stops resending an STPG when it receives an ASYMMETRIC
* ACCESS STATE CHANGED check condition.
*/
static void __scst_gen_alua_state_changed_ua(struct scst_target_group *tg)
{
struct scst_dg_dev *dg_dev;
struct scst_device *dev;
struct scst_tgt_dev *tgt_dev;
struct scst_tg_tgt *tg_tgt;
struct scst_tgt *tgt;
list_for_each_entry(dg_dev, &tg->dg->dev_list, entry) {
dev = dg_dev->dev;
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
tgt = tgt_dev->sess->tgt;
list_for_each_entry(tg_tgt, &tg->tgt_list, entry) {
if (tg_tgt->tgt == tgt) {
scst_gen_aen_or_ua(tgt_dev,
SCST_LOAD_SENSE(scst_sense_asym_access_state_changed));
break;
}
}
}
}
}
static void __scst_tg_set_preferred(struct scst_target_group *tg,
bool preferred)
{
bool prev_preferred;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
if (tg->preferred == preferred)
return;
prev_preferred = tg->preferred;
tg->preferred = preferred;
PRINT_INFO("Changed preferred state of %s/%s from %d into %d",
tg->dg->name, tg->name, prev_preferred,
preferred);
__scst_gen_alua_state_changed_ua(tg);
}
int scst_tg_set_preferred(struct scst_target_group *tg,
bool preferred)
{
int res;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out;
__scst_tg_set_preferred(tg, preferred);
mutex_unlock(&scst_mutex);
out:
return res;
@@ -411,6 +798,50 @@ out:
* Device group contents manipulation.
*/
/*
* Update the ALUA filter of all tgt_devs associated with device group @dg
* and device @dev.
*/
static void scst_update_dev_alua_filter(struct scst_dev_group *dg,
struct scst_device *dev)
{
struct scst_tgt_dev *tgt_dev;
struct scst_target_group *tg;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
tg = __lookup_tg_by_tgt(dg, tgt_dev->acg_dev->acg->tgt);
if (tg)
scst_update_tgt_dev_alua_filter(tgt_dev, tg->state);
}
scst_check_alua_invariant();
}
/*
* Reset the ALUA filter of all tgt_devs associated with device @dev. Note:
* each device is member of at most one device group.
*/
static void scst_reset_dev_alua_filter(struct scst_device *dev)
{
struct scst_tgt_dev *tgt_dev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
lockdep_assert_held(&scst_mutex);
#endif
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry)
scst_update_tgt_dev_alua_filter(tgt_dev,
SCST_TG_STATE_OPTIMIZED);
scst_check_alua_invariant();
}
/**
* scst_dg_dev_add() - Add a device to a device group.
*
@@ -443,6 +874,7 @@ int scst_dg_dev_add(struct scst_dev_group *dg, const char *name)
if (res)
goto out_unlock;
list_add_tail(&dgdev->entry, &dg->dev_list);
scst_update_dev_alua_filter(dg, dev);
mutex_unlock(&scst_mutex);
out:
@@ -460,6 +892,7 @@ static void __scst_dg_dev_remove(struct scst_dev_group *dg,
{
list_del(&dgdev->entry);
scst_dg_dev_sysfs_del(dg, dgdev);
scst_reset_dev_alua_filter(dgdev->dev);
kfree(dgdev);
}
@@ -581,6 +1014,8 @@ static void __scst_dg_remove(struct scst_dev_group *dg)
list_del(&dg->entry);
scst_dg_sysfs_del(dg);
list_for_each_entry(tg, &dg->tg_list, entry)
__scst_tg_set_state(tg, SCST_TG_STATE_OPTIMIZED, NULL);
while (!list_empty(&dg->dev_list)) {
dgdev = list_first_entry(&dg->dev_list, struct scst_dg_dev,
entry);