Delete the alua_port_group usage, as it is more accurate to manage the port group info per-scsi device - see [0]
[0] https://lore.kernel.org/linux-scsi/[email protected]/T/#m4ffc0d07f169b70b8fd2407bae9632aa0f8c1f9a For now, the handler data will be used to hold the ALUA-related info. Signed-off-by: John Garry <[email protected]> --- drivers/scsi/device_handler/scsi_dh_alua.c | 663 ++++++--------------- 1 file changed, 180 insertions(+), 483 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index efb08b9b145a1..067021fffc16f 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -54,41 +54,27 @@ static uint optimize_stpg; module_param(optimize_stpg, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(optimize_stpg, "Allow use of a non-optimized path, rather than sending a STPG, when implicit TPGS is supported (0=No,1=Yes). Default is 0."); -static LIST_HEAD(port_group_list); -static DEFINE_SPINLOCK(port_group_lock); static struct workqueue_struct *kaluad_wq; -struct alua_port_group { - struct kref kref; - struct rcu_head rcu; - struct list_head node; - struct list_head dh_list; - unsigned char device_id_str[256]; - int device_id_len; +struct alua_dh_data { int group_id; - int tpgs; + struct scsi_device *sdev; + int init_error; + struct mutex init_mutex; + bool disabled; + unsigned flags; /* used for optimizing STPG */ + spinlock_t lock; + + /* alua stuff */ int state; int pref; int valid_states; - unsigned flags; /* used for optimizing STPG */ + int tpgs; unsigned char transition_tmo; unsigned long expiry; unsigned long interval; struct delayed_work rtpg_work; - spinlock_t lock; struct list_head rtpg_list; - struct scsi_device *rtpg_sdev; -}; - -struct alua_dh_data { - struct list_head node; - struct alua_port_group __rcu *pg; - int group_id; - spinlock_t pg_lock; - struct scsi_device *sdev; - int init_error; - struct mutex init_mutex; - bool disabled; }; struct alua_queue_data { @@ -101,24 +87,10 @@ struct alua_queue_data { #define ALUA_POLICY_SWITCH_ALL 1 static void alua_rtpg_work(struct work_struct *work); -static bool alua_rtpg_queue(struct alua_port_group *pg, - struct scsi_device *sdev, +static bool alua_rtpg_queue(struct scsi_device *sdev, struct alua_queue_data *qdata, bool force); static void alua_check(struct scsi_device *sdev, bool force); -static void release_port_group(struct kref *kref) -{ - struct alua_port_group *pg; - - pg = container_of(kref, struct alua_port_group, kref); - if (pg->rtpg_sdev) - flush_delayed_work(&pg->rtpg_work); - spin_lock(&port_group_lock); - list_del(&pg->node); - spin_unlock(&port_group_lock); - kfree_rcu(pg, rcu); -} - /* * submit_rtpg - Issue a REPORT TARGET GROUP STATES command * @sdev: sdev the command should be sent to @@ -182,88 +154,6 @@ static int submit_stpg(struct scsi_device *sdev, int group_id, ALUA_FAILOVER_RETRIES, &exec_args); } -static struct alua_port_group *alua_find_get_pg(char *id_str, size_t id_size, - int group_id) -{ - struct alua_port_group *pg; - - if (!id_str || !id_size || !strlen(id_str)) - return NULL; - - list_for_each_entry(pg, &port_group_list, node) { - if (pg->group_id != group_id) - continue; - if (!pg->device_id_len || pg->device_id_len != id_size) - continue; - if (strncmp(pg->device_id_str, id_str, id_size)) - continue; - if (!kref_get_unless_zero(&pg->kref)) - continue; - return pg; - } - - return NULL; -} - -/* - * alua_alloc_pg - Allocate a new port_group structure - * @sdev: scsi device - * @group_id: port group id - * @tpgs: target port group settings - * - * Allocate a new port_group structure for a given - * device. - */ -static struct alua_port_group *alua_alloc_pg(struct scsi_device *sdev, - int group_id, int tpgs) -{ - struct alua_port_group *pg, *tmp_pg; - - pg = kzalloc_obj(struct alua_port_group); - if (!pg) - return ERR_PTR(-ENOMEM); - - pg->device_id_len = scsi_vpd_lun_id(sdev, pg->device_id_str, - sizeof(pg->device_id_str)); - if (pg->device_id_len <= 0) { - /* - * TPGS supported but no device identification found. - * Generate private device identification. - */ - sdev_printk(KERN_INFO, sdev, - "%s: No device descriptors found\n", - ALUA_DH_NAME); - pg->device_id_str[0] = '\0'; - pg->device_id_len = 0; - } - pg->group_id = group_id; - pg->tpgs = tpgs; - pg->state = SCSI_ACCESS_STATE_OPTIMAL; - pg->valid_states = TPGS_SUPPORT_ALL; - if (optimize_stpg) - pg->flags |= ALUA_OPTIMIZE_STPG; - kref_init(&pg->kref); - INIT_DELAYED_WORK(&pg->rtpg_work, alua_rtpg_work); - INIT_LIST_HEAD(&pg->rtpg_list); - INIT_LIST_HEAD(&pg->node); - INIT_LIST_HEAD(&pg->dh_list); - spin_lock_init(&pg->lock); - - spin_lock(&port_group_lock); - tmp_pg = alua_find_get_pg(pg->device_id_str, pg->device_id_len, - group_id); - if (tmp_pg) { - spin_unlock(&port_group_lock); - kfree(pg); - return tmp_pg; - } - - list_add(&pg->node, &port_group_list); - spin_unlock(&port_group_lock); - - return pg; -} - /* * alua_check_tpgs - Evaluate TPGS setting * @sdev: device to be checked @@ -326,13 +216,10 @@ static int alua_check_tpgs(struct scsi_device *sdev) static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h, int tpgs) { - int rel_port = -1, group_id; - struct alua_port_group *pg, *old_pg = NULL; - bool pg_updated = false; - unsigned long flags; + int rel_port = -1; - group_id = scsi_vpd_tpg_id(sdev, &rel_port); - if (group_id < 0) { + h->group_id = scsi_vpd_tpg_id(sdev, &rel_port); + if (h->group_id < 0) { /* * Internal error; TPGS supported but required * VPD identification descriptors not present. @@ -343,51 +230,9 @@ static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h, ALUA_DH_NAME); return SCSI_DH_DEV_UNSUPP; } + h->tpgs = tpgs; - pg = alua_alloc_pg(sdev, group_id, tpgs); - if (IS_ERR(pg)) { - if (PTR_ERR(pg) == -ENOMEM) - return SCSI_DH_NOMEM; - return SCSI_DH_DEV_UNSUPP; - } - if (pg->device_id_len) - sdev_printk(KERN_INFO, sdev, - "%s: device %s port group %x rel port %x\n", - ALUA_DH_NAME, pg->device_id_str, - group_id, rel_port); - else - sdev_printk(KERN_INFO, sdev, - "%s: port group %x rel port %x\n", - ALUA_DH_NAME, group_id, rel_port); - - kref_get(&pg->kref); - - /* Check for existing port group references */ - spin_lock(&h->pg_lock); - old_pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock)); - if (old_pg != pg) { - /* port group has changed. Update to new port group */ - if (h->pg) { - spin_lock_irqsave(&old_pg->lock, flags); - list_del_rcu(&h->node); - spin_unlock_irqrestore(&old_pg->lock, flags); - } - rcu_assign_pointer(h->pg, pg); - pg_updated = true; - } - - spin_lock_irqsave(&pg->lock, flags); - if (pg_updated) - list_add_rcu(&h->node, &pg->dh_list); - spin_unlock_irqrestore(&pg->lock, flags); - - spin_unlock(&h->pg_lock); - - alua_rtpg_queue(pg, sdev, NULL, true); - kref_put(&pg->kref, release_port_group); - - if (old_pg) - kref_put(&old_pg->kref, release_port_group); + alua_rtpg_queue(sdev, NULL, true); return SCSI_DH_OK; } @@ -417,14 +262,8 @@ static char print_alua_state(unsigned char state) static void alua_handle_state_transition(struct scsi_device *sdev) { struct alua_dh_data *h = sdev->handler_data; - struct alua_port_group *pg; - - rcu_read_lock(); - pg = rcu_dereference(h->pg); - if (pg) - pg->state = SCSI_ACCESS_STATE_TRANSITIONING; - rcu_read_unlock(); - alua_check(sdev, false); + + h->state = SCSI_ACCESS_STATE_TRANSITIONING; } static enum scsi_disposition alua_check_sense(struct scsi_device *sdev, @@ -532,10 +371,10 @@ static int alua_tur(struct scsi_device *sdev) * Returns SCSI_DH_DEV_OFFLINED if the path is * found to be unusable. */ -static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) +static int alua_rtpg(struct scsi_device *sdev) { struct scsi_sense_hdr sense_hdr; - struct alua_port_group *tmp_pg; + struct alua_dh_data *h = sdev->handler_data; int len, k, off, bufflen = ALUA_RTPG_SIZE; int group_id_old, state_old, pref_old, valid_states_old; unsigned char *desc, *buff; @@ -545,19 +384,32 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) unsigned char orig_transition_tmo; unsigned long flags; bool transitioning_sense = false; + int rel_port, group_id = scsi_vpd_tpg_id(sdev, &rel_port); + + if (group_id < 0) { + /* + * Internal error; TPGS supported but required + * VPD identification descriptors not present. + * Disable ALUA support + */ + sdev_printk(KERN_INFO, sdev, + "%s: No target port descriptors found\n", + ALUA_DH_NAME); + return SCSI_DH_DEV_UNSUPP; + } - group_id_old = pg->group_id; - state_old = pg->state; - pref_old = pg->pref; - valid_states_old = pg->valid_states; + group_id_old = h->group_id; + state_old = h->state; + pref_old = h->pref; + valid_states_old = h->valid_states; - if (!pg->expiry) { + if (!h->expiry) { unsigned long transition_tmo = ALUA_FAILOVER_TIMEOUT * HZ; - if (pg->transition_tmo) - transition_tmo = pg->transition_tmo * HZ; + if (h->transition_tmo) + transition_tmo = h->transition_tmo * HZ; - pg->expiry = round_jiffies_up(jiffies + transition_tmo); + h->expiry = round_jiffies_up(jiffies + transition_tmo); } buff = kzalloc(bufflen, GFP_KERNEL); @@ -566,7 +418,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) retry: err = 0; - retval = submit_rtpg(sdev, buff, bufflen, &sense_hdr, pg->flags); + retval = submit_rtpg(sdev, buff, bufflen, &sense_hdr, h->flags); if (retval) { /* @@ -578,7 +430,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) * So ignore any errors to avoid spurious failures during * path failover. */ - if ((pg->valid_states & ~TPGS_SUPPORT_OPTIMIZED) == 0) { + if ((h->valid_states & ~TPGS_SUPPORT_OPTIMIZED) == 0) { sdev_printk(KERN_INFO, sdev, "%s: ignoring rtpg result %d\n", ALUA_DH_NAME, retval); @@ -607,9 +459,9 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) * Note: some arrays return a sense key of ILLEGAL_REQUEST * with ASC 00h if they don't support the extended header. */ - if (!(pg->flags & ALUA_RTPG_EXT_HDR_UNSUPP) && + if (!(h->flags & ALUA_RTPG_EXT_HDR_UNSUPP) && sense_hdr.sense_key == ILLEGAL_REQUEST) { - pg->flags |= ALUA_RTPG_EXT_HDR_UNSUPP; + h->flags |= ALUA_RTPG_EXT_HDR_UNSUPP; goto retry; } /* @@ -628,7 +480,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) if (sense_hdr.sense_key == UNIT_ATTENTION) err = SCSI_DH_RETRY; if (err == SCSI_DH_RETRY && - pg->expiry != 0 && time_before(jiffies, pg->expiry)) { + h->expiry != 0 && time_before(jiffies, h->expiry)) { sdev_printk(KERN_ERR, sdev, "%s: rtpg retry\n", ALUA_DH_NAME); scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr); @@ -639,7 +491,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) ALUA_DH_NAME); scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr); kfree(buff); - pg->expiry = 0; + h->expiry = 0; return SCSI_DH_IO; } @@ -654,23 +506,23 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) sdev_printk(KERN_WARNING, sdev, "%s: kmalloc buffer failed\n",__func__); /* Temporary failure, bypass */ - pg->expiry = 0; + h->expiry = 0; return SCSI_DH_DEV_TEMP_BUSY; } goto retry; } - orig_transition_tmo = pg->transition_tmo; + orig_transition_tmo = h->transition_tmo; if ((buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR && buff[5] != 0) - pg->transition_tmo = buff[5]; + h->transition_tmo = buff[5]; else - pg->transition_tmo = ALUA_FAILOVER_TIMEOUT; + h->transition_tmo = ALUA_FAILOVER_TIMEOUT; - if (orig_transition_tmo != pg->transition_tmo) { + if (orig_transition_tmo != h->transition_tmo) { sdev_printk(KERN_INFO, sdev, "%s: transition timeout set to %d seconds\n", - ALUA_DH_NAME, pg->transition_tmo); - pg->expiry = jiffies + pg->transition_tmo * HZ; + ALUA_DH_NAME, h->transition_tmo); + h->expiry = jiffies + h->transition_tmo * HZ; } if ((buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR) @@ -681,95 +533,71 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) for (k = tpg_desc_tbl_off, desc = buff + tpg_desc_tbl_off; k < len; k += off, desc += off) { - u16 group_id = get_unaligned_be16(&desc[2]); - - spin_lock_irqsave(&port_group_lock, flags); - tmp_pg = alua_find_get_pg(pg->device_id_str, pg->device_id_len, - group_id); - spin_unlock_irqrestore(&port_group_lock, flags); - if (tmp_pg) { - if (spin_trylock_irqsave(&tmp_pg->lock, flags)) { - if ((tmp_pg == pg) || - !(tmp_pg->flags & ALUA_PG_RUNNING)) { - struct alua_dh_data *h; - - tmp_pg->state = desc[0] & 0x0f; - tmp_pg->pref = desc[0] >> 7; - rcu_read_lock(); - list_for_each_entry_rcu(h, - &tmp_pg->dh_list, node) { - if (!h->sdev) - continue; - h->sdev->access_state = desc[0]; - } - rcu_read_unlock(); - } - if (tmp_pg == pg) - tmp_pg->valid_states = desc[1]; - spin_unlock_irqrestore(&tmp_pg->lock, flags); - } - kref_put(&tmp_pg->kref, release_port_group); + u16 group_id_desc = get_unaligned_be16(&desc[2]); + + spin_lock_irqsave(&h->lock, flags); + if (group_id_desc == group_id) { + h->group_id = group_id; + WRITE_ONCE(h->state, desc[0] & 0x0f); + h->pref = desc[0] >> 7; + WRITE_ONCE(sdev->access_state, desc[0]); + h->valid_states = desc[1]; } + spin_unlock_irqrestore(&h->lock, flags); off = 8 + (desc[7] * 4); } skip_rtpg: - spin_lock_irqsave(&pg->lock, flags); + spin_lock_irqsave(&h->lock, flags); if (transitioning_sense) - pg->state = SCSI_ACCESS_STATE_TRANSITIONING; + h->state = SCSI_ACCESS_STATE_TRANSITIONING; - if (group_id_old != pg->group_id || state_old != pg->state || - pref_old != pg->pref || valid_states_old != pg->valid_states) + if (group_id_old != h->group_id || state_old != h->state || + pref_old != h->pref || valid_states_old != h->valid_states) sdev_printk(KERN_INFO, sdev, "%s: port group %02x state %c %s supports %c%c%c%c%c%c%c\n", - ALUA_DH_NAME, pg->group_id, print_alua_state(pg->state), - pg->pref ? "preferred" : "non-preferred", - pg->valid_states&TPGS_SUPPORT_TRANSITION?'T':'t', - pg->valid_states&TPGS_SUPPORT_OFFLINE?'O':'o', - pg->valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l', - pg->valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u', - pg->valid_states&TPGS_SUPPORT_STANDBY?'S':'s', - pg->valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n', - pg->valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a'); - - switch (pg->state) { + ALUA_DH_NAME, h->group_id, print_alua_state(h->state), + h->pref ? "preferred" : "non-preferred", + h->valid_states&TPGS_SUPPORT_TRANSITION?'T':'t', + h->valid_states&TPGS_SUPPORT_OFFLINE?'O':'o', + h->valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l', + h->valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u', + h->valid_states&TPGS_SUPPORT_STANDBY?'S':'s', + h->valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n', + h->valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a'); + + switch (h->state) { case SCSI_ACCESS_STATE_TRANSITIONING: - if (time_before(jiffies, pg->expiry)) { + if (time_before(jiffies, h->expiry)) { /* State transition, retry */ - pg->interval = ALUA_RTPG_RETRY_DELAY; + h->interval = ALUA_RTPG_RETRY_DELAY; err = SCSI_DH_RETRY; } else { struct alua_dh_data *h; + unsigned char access_state; /* Transitioning time exceeded, set port to standby */ err = SCSI_DH_IO; - pg->state = SCSI_ACCESS_STATE_STANDBY; - pg->expiry = 0; - rcu_read_lock(); - list_for_each_entry_rcu(h, &pg->dh_list, node) { - if (!h->sdev) - continue; - h->sdev->access_state = - (pg->state & SCSI_ACCESS_STATE_MASK); - if (pg->pref) - h->sdev->access_state |= - SCSI_ACCESS_STATE_PREFERRED; - } - rcu_read_unlock(); + h->state = SCSI_ACCESS_STATE_STANDBY; + h->expiry = 0; + access_state = h->state & SCSI_ACCESS_STATE_MASK; + if (h->pref) + access_state |= SCSI_ACCESS_STATE_PREFERRED; + WRITE_ONCE(sdev->access_state, access_state); } break; case SCSI_ACCESS_STATE_OFFLINE: /* Path unusable */ err = SCSI_DH_DEV_OFFLINED; - pg->expiry = 0; + h->expiry = 0; break; default: /* Useable path if active */ err = SCSI_DH_OK; - pg->expiry = 0; + h->expiry = 0; break; } - spin_unlock_irqrestore(&pg->lock, flags); + spin_unlock_irqrestore(&h->lock, flags); kfree(buff); return err; } @@ -782,22 +610,23 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) * a re-evaluation of the target group state or SCSI_DH_OK * if no further action needs to be taken. */ -static unsigned alua_stpg(struct scsi_device *sdev, struct alua_port_group *pg) +static unsigned alua_stpg(struct scsi_device *sdev) { int retval; struct scsi_sense_hdr sense_hdr; + struct alua_dh_data *h = sdev->handler_data; - if (!(pg->tpgs & TPGS_MODE_EXPLICIT)) { + if (!(h->tpgs & TPGS_MODE_EXPLICIT)) { /* Only implicit ALUA supported, retry */ return SCSI_DH_RETRY; } - switch (pg->state) { + switch (h->state) { case SCSI_ACCESS_STATE_OPTIMAL: return SCSI_DH_OK; case SCSI_ACCESS_STATE_ACTIVE: - if ((pg->flags & ALUA_OPTIMIZE_STPG) && - !pg->pref && - (pg->tpgs & TPGS_MODE_IMPLICIT)) + if ((h->flags & ALUA_OPTIMIZE_STPG) && + !h->pref && + (h->tpgs & TPGS_MODE_IMPLICIT)) return SCSI_DH_OK; break; case SCSI_ACCESS_STATE_STANDBY: @@ -810,10 +639,10 @@ static unsigned alua_stpg(struct scsi_device *sdev, struct alua_port_group *pg) default: sdev_printk(KERN_INFO, sdev, "%s: stpg failed, unhandled TPGS state %d", - ALUA_DH_NAME, pg->state); + ALUA_DH_NAME, h->state); return SCSI_DH_NOSYS; } - retval = submit_stpg(sdev, pg->group_id, &sense_hdr); + retval = submit_stpg(sdev, h->group_id, &sense_hdr); if (retval) { if (retval < 0 || !scsi_sense_valid(&sense_hdr)) { @@ -832,144 +661,75 @@ static unsigned alua_stpg(struct scsi_device *sdev, struct alua_port_group *pg) return SCSI_DH_RETRY; } -/* - * The caller must call scsi_device_put() on the returned pointer if it is not - * NULL. - */ -static struct scsi_device * __must_check -alua_rtpg_select_sdev(struct alua_port_group *pg) -{ - struct alua_dh_data *h; - struct scsi_device *sdev = NULL, *prev_sdev; - - lockdep_assert_held(&pg->lock); - if (WARN_ON(!pg->rtpg_sdev)) - return NULL; - - /* - * RCU protection isn't necessary for dh_list here - * as we hold pg->lock, but for access to h->pg. - */ - rcu_read_lock(); - list_for_each_entry_rcu(h, &pg->dh_list, node) { - if (!h->sdev) - continue; - if (h->sdev == pg->rtpg_sdev) { - h->disabled = true; - continue; - } - if (rcu_dereference(h->pg) == pg && - !h->disabled && - !scsi_device_get(h->sdev)) { - sdev = h->sdev; - break; - } - } - rcu_read_unlock(); - - if (!sdev) { - pr_warn("%s: no device found for rtpg\n", - (pg->device_id_len ? - (char *)pg->device_id_str : "(nameless PG)")); - return NULL; - } - - sdev_printk(KERN_INFO, sdev, "rtpg retry on different device\n"); - - prev_sdev = pg->rtpg_sdev; - pg->rtpg_sdev = sdev; - - return prev_sdev; -} - static void alua_rtpg_work(struct work_struct *work) { - struct alua_port_group *pg = - container_of(work, struct alua_port_group, rtpg_work.work); - struct scsi_device *sdev, *prev_sdev = NULL; + struct alua_dh_data *h = + container_of(work, struct alua_dh_data, rtpg_work.work); + struct scsi_device *sdev = h->sdev; LIST_HEAD(qdata_list); int err = SCSI_DH_OK; struct alua_queue_data *qdata, *tmp; - struct alua_dh_data *h; unsigned long flags; - spin_lock_irqsave(&pg->lock, flags); - sdev = pg->rtpg_sdev; - if (!sdev) { - WARN_ON(pg->flags & ALUA_PG_RUN_RTPG); - WARN_ON(pg->flags & ALUA_PG_RUN_STPG); - spin_unlock_irqrestore(&pg->lock, flags); - kref_put(&pg->kref, release_port_group); - return; - } - pg->flags |= ALUA_PG_RUNNING; - if (pg->flags & ALUA_PG_RUN_RTPG) { - int state = pg->state; + spin_lock_irqsave(&h->lock, flags); + h->flags |= ALUA_PG_RUNNING; + if (h->flags & ALUA_PG_RUN_RTPG) { + int state = h->state; - pg->flags &= ~ALUA_PG_RUN_RTPG; - spin_unlock_irqrestore(&pg->lock, flags); + h->flags &= ~ALUA_PG_RUN_RTPG; + spin_unlock_irqrestore(&h->lock, flags); if (state == SCSI_ACCESS_STATE_TRANSITIONING) { if (alua_tur(sdev) == SCSI_DH_RETRY) { - spin_lock_irqsave(&pg->lock, flags); - pg->flags &= ~ALUA_PG_RUNNING; - pg->flags |= ALUA_PG_RUN_RTPG; - if (!pg->interval) - pg->interval = ALUA_RTPG_RETRY_DELAY; - spin_unlock_irqrestore(&pg->lock, flags); - queue_delayed_work(kaluad_wq, &pg->rtpg_work, - pg->interval * HZ); + spin_lock_irqsave(&h->lock, flags); + h->flags &= ~ALUA_PG_RUNNING; + h->flags |= ALUA_PG_RUN_RTPG; + if (!h->interval) + h->interval = ALUA_RTPG_RETRY_DELAY; + spin_unlock_irqrestore(&h->lock, flags); + queue_delayed_work(kaluad_wq, &h->rtpg_work, + h->interval * HZ); return; } /* Send RTPG on failure or if TUR indicates SUCCESS */ } - err = alua_rtpg(sdev, pg); - spin_lock_irqsave(&pg->lock, flags); + err = alua_rtpg(sdev); + spin_lock_irqsave(&h->lock, flags); - /* If RTPG failed on the current device, try using another */ - if (err == SCSI_DH_RES_TEMP_UNAVAIL && - (prev_sdev = alua_rtpg_select_sdev(pg))) - err = SCSI_DH_IMM_RETRY; - - if (err == SCSI_DH_RETRY || err == SCSI_DH_IMM_RETRY || - pg->flags & ALUA_PG_RUN_RTPG) { - pg->flags &= ~ALUA_PG_RUNNING; + if (err == SCSI_DH_RETRY || h->flags & ALUA_PG_RUN_RTPG) { + h->flags &= ~ALUA_PG_RUNNING; if (err == SCSI_DH_IMM_RETRY) - pg->interval = 0; - else if (!pg->interval && !(pg->flags & ALUA_PG_RUN_RTPG)) - pg->interval = ALUA_RTPG_RETRY_DELAY; - pg->flags |= ALUA_PG_RUN_RTPG; - spin_unlock_irqrestore(&pg->lock, flags); + h->interval = 0; + else if (!h->interval && !(h->flags & ALUA_PG_RUN_RTPG)) + h->interval = ALUA_RTPG_RETRY_DELAY; + h->flags |= ALUA_PG_RUN_RTPG; + spin_unlock_irqrestore(&h->lock, flags); goto queue_rtpg; } if (err != SCSI_DH_OK) - pg->flags &= ~ALUA_PG_RUN_STPG; + h->flags &= ~ALUA_PG_RUN_STPG; } - if (pg->flags & ALUA_PG_RUN_STPG) { - pg->flags &= ~ALUA_PG_RUN_STPG; - spin_unlock_irqrestore(&pg->lock, flags); - err = alua_stpg(sdev, pg); - spin_lock_irqsave(&pg->lock, flags); - if (err == SCSI_DH_RETRY || pg->flags & ALUA_PG_RUN_RTPG) { - pg->flags |= ALUA_PG_RUN_RTPG; - pg->interval = 0; - pg->flags &= ~ALUA_PG_RUNNING; - spin_unlock_irqrestore(&pg->lock, flags); + if (h->flags & ALUA_PG_RUN_STPG) { + h->flags &= ~ALUA_PG_RUN_STPG; + spin_unlock_irqrestore(&h->lock, flags); + err = alua_stpg(sdev); + spin_lock_irqsave(&h->lock, flags); + if (err == SCSI_DH_RETRY || h->flags & ALUA_PG_RUN_RTPG) { + h->flags |= ALUA_PG_RUN_RTPG; + h->interval = 0; + h->flags &= ~ALUA_PG_RUNNING; + spin_unlock_irqrestore(&h->lock, flags); goto queue_rtpg; } } - list_splice_init(&pg->rtpg_list, &qdata_list); + list_splice_init(&h->rtpg_list, &qdata_list); /* * We went through an RTPG, for good or bad. - * Re-enable all devices for the next attempt. + * Re-enable the device for the next attempt. */ - list_for_each_entry(h, &pg->dh_list, node) - h->disabled = false; - pg->rtpg_sdev = NULL; - spin_unlock_irqrestore(&pg->lock, flags); + h->disabled = false; + spin_unlock_irqrestore(&h->lock, flags); - if (prev_sdev) - scsi_device_put(prev_sdev); list_for_each_entry_safe(qdata, tmp, &qdata_list, entry) { list_del(&qdata->entry); @@ -977,22 +737,19 @@ static void alua_rtpg_work(struct work_struct *work) qdata->callback_fn(qdata->callback_data, err); kfree(qdata); } - spin_lock_irqsave(&pg->lock, flags); - pg->flags &= ~ALUA_PG_RUNNING; - spin_unlock_irqrestore(&pg->lock, flags); + spin_lock_irqsave(&h->lock, flags); + h->flags &= ~ALUA_PG_RUNNING; + spin_unlock_irqrestore(&h->lock, flags); scsi_device_put(sdev); - kref_put(&pg->kref, release_port_group); + return; queue_rtpg: - if (prev_sdev) - scsi_device_put(prev_sdev); - queue_delayed_work(kaluad_wq, &pg->rtpg_work, pg->interval * HZ); + queue_delayed_work(kaluad_wq, &h->rtpg_work, h->interval * HZ); } /** * alua_rtpg_queue() - cause RTPG to be submitted asynchronously - * @pg: ALUA port group associated with @sdev. * @sdev: SCSI device for which to submit an RTPG. * @qdata: Information about the callback to invoke after the RTPG. * @force: Whether or not to submit an RTPG if a work item that will submit an @@ -1004,51 +761,34 @@ static void alua_rtpg_work(struct work_struct *work) * Context: may be called from atomic context (alua_check()) only if the caller * holds an sdev reference. */ -static bool alua_rtpg_queue(struct alua_port_group *pg, - struct scsi_device *sdev, +static bool alua_rtpg_queue(struct scsi_device *sdev, struct alua_queue_data *qdata, bool force) { int start_queue = 0; + struct alua_dh_data *h = sdev->handler_data; unsigned long flags; - if (WARN_ON_ONCE(!pg) || scsi_device_get(sdev)) + if (scsi_device_get(sdev)) return false; - spin_lock_irqsave(&pg->lock, flags); + spin_lock_irqsave(&h->lock, flags); if (qdata) { - list_add_tail(&qdata->entry, &pg->rtpg_list); - pg->flags |= ALUA_PG_RUN_STPG; + list_add_tail(&qdata->entry, &h->rtpg_list); + h->flags |= ALUA_PG_RUN_STPG; force = true; } - if (pg->rtpg_sdev == NULL) { - struct alua_dh_data *h = sdev->handler_data; - - rcu_read_lock(); - if (h && rcu_dereference(h->pg) == pg) { - pg->interval = 0; - pg->flags |= ALUA_PG_RUN_RTPG; - kref_get(&pg->kref); - pg->rtpg_sdev = sdev; - start_queue = 1; - } - rcu_read_unlock(); - } else if (!(pg->flags & ALUA_PG_RUN_RTPG) && force) { - pg->flags |= ALUA_PG_RUN_RTPG; + if (!(h->flags & ALUA_PG_RUN_RTPG) && force) { + h->flags |= ALUA_PG_RUN_RTPG; /* Do not queue if the worker is already running */ - if (!(pg->flags & ALUA_PG_RUNNING)) { - kref_get(&pg->kref); + if (!(h->flags & ALUA_PG_RUNNING)) start_queue = 1; - } } - spin_unlock_irqrestore(&pg->lock, flags); - + spin_unlock_irqrestore(&h->lock, flags); if (start_queue) { - if (queue_delayed_work(kaluad_wq, &pg->rtpg_work, + if (queue_delayed_work(kaluad_wq, &h->rtpg_work, msecs_to_jiffies(ALUA_RTPG_DELAY_MSECS))) sdev = NULL; - else - kref_put(&pg->kref, release_port_group); } if (sdev) scsi_device_put(sdev); @@ -1088,7 +828,6 @@ static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h) static int alua_set_params(struct scsi_device *sdev, const char *params) { struct alua_dh_data *h = sdev->handler_data; - struct alua_port_group *pg = NULL; unsigned int optimize = 0, argc; const char *p = params; int result = SCSI_DH_OK; @@ -1102,19 +841,12 @@ static int alua_set_params(struct scsi_device *sdev, const char *params) if ((sscanf(p, "%u", &optimize) != 1) || (optimize > 1)) return -EINVAL; - rcu_read_lock(); - pg = rcu_dereference(h->pg); - if (!pg) { - rcu_read_unlock(); - return -ENXIO; - } - spin_lock_irqsave(&pg->lock, flags); + spin_lock_irqsave(&h->lock, flags); if (optimize) - pg->flags |= ALUA_OPTIMIZE_STPG; + h->flags |= ALUA_OPTIMIZE_STPG; else - pg->flags &= ~ALUA_OPTIMIZE_STPG; - spin_unlock_irqrestore(&pg->lock, flags); - rcu_read_unlock(); + h->flags &= ~ALUA_OPTIMIZE_STPG; + spin_unlock_irqrestore(&h->lock, flags); return result; } @@ -1132,10 +864,8 @@ static int alua_set_params(struct scsi_device *sdev, const char *params) static int alua_activate(struct scsi_device *sdev, activate_complete fn, void *data) { - struct alua_dh_data *h = sdev->handler_data; int err = SCSI_DH_OK; struct alua_queue_data *qdata; - struct alua_port_group *pg; qdata = kzalloc_obj(*qdata); if (!qdata) { @@ -1145,26 +875,12 @@ static int alua_activate(struct scsi_device *sdev, qdata->callback_fn = fn; qdata->callback_data = data; - mutex_lock(&h->init_mutex); - rcu_read_lock(); - pg = rcu_dereference(h->pg); - if (!pg || !kref_get_unless_zero(&pg->kref)) { - rcu_read_unlock(); - kfree(qdata); - err = h->init_error; - mutex_unlock(&h->init_mutex); - goto out; - } - rcu_read_unlock(); - mutex_unlock(&h->init_mutex); - - if (alua_rtpg_queue(pg, sdev, qdata, true)) { + if (alua_rtpg_queue(sdev, qdata, true)) { fn = NULL; } else { kfree(qdata); err = SCSI_DH_DEV_OFFLINED; } - kref_put(&pg->kref, release_port_group); out: if (fn) fn(data, err); @@ -1179,18 +895,7 @@ static int alua_activate(struct scsi_device *sdev, */ static void alua_check(struct scsi_device *sdev, bool force) { - struct alua_dh_data *h = sdev->handler_data; - struct alua_port_group *pg; - - rcu_read_lock(); - pg = rcu_dereference(h->pg); - if (!pg || !kref_get_unless_zero(&pg->kref)) { - rcu_read_unlock(); - return; - } - rcu_read_unlock(); - alua_rtpg_queue(pg, sdev, NULL, force); - kref_put(&pg->kref, release_port_group); + alua_rtpg_queue(sdev, NULL, force); } /* @@ -1202,14 +907,12 @@ static void alua_check(struct scsi_device *sdev, bool force) static blk_status_t alua_prep_fn(struct scsi_device *sdev, struct request *req) { struct alua_dh_data *h = sdev->handler_data; - struct alua_port_group *pg; - unsigned char state = SCSI_ACCESS_STATE_OPTIMAL; + unsigned long flags; + unsigned char state; - rcu_read_lock(); - pg = rcu_dereference(h->pg); - if (pg) - state = pg->state; - rcu_read_unlock(); + spin_lock_irqsave(&h->lock, flags); + state = h->state; + spin_unlock_irqrestore(&h->lock, flags); switch (state) { case SCSI_ACCESS_STATE_OPTIMAL: @@ -1242,20 +945,26 @@ static int alua_bus_attach(struct scsi_device *sdev) h = kzalloc_obj(*h); if (!h) return SCSI_DH_NOMEM; - spin_lock_init(&h->pg_lock); - rcu_assign_pointer(h->pg, NULL); + spin_lock_init(&h->lock); h->init_error = SCSI_DH_OK; h->sdev = sdev; - INIT_LIST_HEAD(&h->node); + INIT_DELAYED_WORK(&h->rtpg_work, alua_rtpg_work); + INIT_LIST_HEAD(&h->rtpg_list); mutex_init(&h->init_mutex); + + h->state = SCSI_ACCESS_STATE_OPTIMAL; + h->valid_states = TPGS_SUPPORT_ALL; + if (optimize_stpg) + h->flags |= ALUA_OPTIMIZE_STPG; + + sdev->handler_data = h; err = alua_initialize(sdev, h); if (err != SCSI_DH_OK && err != SCSI_DH_DEV_OFFLINED) goto failed; - - sdev->handler_data = h; return SCSI_DH_OK; failed: + sdev->handler_data = NULL; kfree(h); return err; } @@ -1267,20 +976,8 @@ static int alua_bus_attach(struct scsi_device *sdev) static void alua_bus_detach(struct scsi_device *sdev) { struct alua_dh_data *h = sdev->handler_data; - struct alua_port_group *pg; - - spin_lock(&h->pg_lock); - pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock)); - rcu_assign_pointer(h->pg, NULL); - spin_unlock(&h->pg_lock); - if (pg) { - spin_lock_irq(&pg->lock); - list_del_rcu(&h->node); - spin_unlock_irq(&pg->lock); - kref_put(&pg->kref, release_port_group); - } + sdev->handler_data = NULL; - synchronize_rcu(); kfree(h); } -- 2.43.5

