162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic SCSI-3 ALUA SCSI Device Handler 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007-2010 Hannes Reinecke, SUSE Linux Products GmbH. 662306a36Sopenharmony_ci * All rights reserved. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <asm/unaligned.h> 1262306a36Sopenharmony_ci#include <scsi/scsi.h> 1362306a36Sopenharmony_ci#include <scsi/scsi_proto.h> 1462306a36Sopenharmony_ci#include <scsi/scsi_dbg.h> 1562306a36Sopenharmony_ci#include <scsi/scsi_eh.h> 1662306a36Sopenharmony_ci#include <scsi/scsi_dh.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define ALUA_DH_NAME "alua" 1962306a36Sopenharmony_ci#define ALUA_DH_VER "2.0" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define TPGS_SUPPORT_NONE 0x00 2262306a36Sopenharmony_ci#define TPGS_SUPPORT_OPTIMIZED 0x01 2362306a36Sopenharmony_ci#define TPGS_SUPPORT_NONOPTIMIZED 0x02 2462306a36Sopenharmony_ci#define TPGS_SUPPORT_STANDBY 0x04 2562306a36Sopenharmony_ci#define TPGS_SUPPORT_UNAVAILABLE 0x08 2662306a36Sopenharmony_ci#define TPGS_SUPPORT_LBA_DEPENDENT 0x10 2762306a36Sopenharmony_ci#define TPGS_SUPPORT_OFFLINE 0x40 2862306a36Sopenharmony_ci#define TPGS_SUPPORT_TRANSITION 0x80 2962306a36Sopenharmony_ci#define TPGS_SUPPORT_ALL 0xdf 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define RTPG_FMT_MASK 0x70 3262306a36Sopenharmony_ci#define RTPG_FMT_EXT_HDR 0x10 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define TPGS_MODE_UNINITIALIZED -1 3562306a36Sopenharmony_ci#define TPGS_MODE_NONE 0x0 3662306a36Sopenharmony_ci#define TPGS_MODE_IMPLICIT 0x1 3762306a36Sopenharmony_ci#define TPGS_MODE_EXPLICIT 0x2 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define ALUA_RTPG_SIZE 128 4062306a36Sopenharmony_ci#define ALUA_FAILOVER_TIMEOUT 60 4162306a36Sopenharmony_ci#define ALUA_FAILOVER_RETRIES 5 4262306a36Sopenharmony_ci#define ALUA_RTPG_DELAY_MSECS 5 4362306a36Sopenharmony_ci#define ALUA_RTPG_RETRY_DELAY 2 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* device handler flags */ 4662306a36Sopenharmony_ci#define ALUA_OPTIMIZE_STPG 0x01 4762306a36Sopenharmony_ci#define ALUA_RTPG_EXT_HDR_UNSUPP 0x02 4862306a36Sopenharmony_ci/* State machine flags */ 4962306a36Sopenharmony_ci#define ALUA_PG_RUN_RTPG 0x10 5062306a36Sopenharmony_ci#define ALUA_PG_RUN_STPG 0x20 5162306a36Sopenharmony_ci#define ALUA_PG_RUNNING 0x40 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic uint optimize_stpg; 5462306a36Sopenharmony_cimodule_param(optimize_stpg, uint, S_IRUGO|S_IWUSR); 5562306a36Sopenharmony_ciMODULE_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."); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic LIST_HEAD(port_group_list); 5862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(port_group_lock); 5962306a36Sopenharmony_cistatic struct workqueue_struct *kaluad_wq; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct alua_port_group { 6262306a36Sopenharmony_ci struct kref kref; 6362306a36Sopenharmony_ci struct rcu_head rcu; 6462306a36Sopenharmony_ci struct list_head node; 6562306a36Sopenharmony_ci struct list_head dh_list; 6662306a36Sopenharmony_ci unsigned char device_id_str[256]; 6762306a36Sopenharmony_ci int device_id_len; 6862306a36Sopenharmony_ci int group_id; 6962306a36Sopenharmony_ci int tpgs; 7062306a36Sopenharmony_ci int state; 7162306a36Sopenharmony_ci int pref; 7262306a36Sopenharmony_ci int valid_states; 7362306a36Sopenharmony_ci unsigned flags; /* used for optimizing STPG */ 7462306a36Sopenharmony_ci unsigned char transition_tmo; 7562306a36Sopenharmony_ci unsigned long expiry; 7662306a36Sopenharmony_ci unsigned long interval; 7762306a36Sopenharmony_ci struct delayed_work rtpg_work; 7862306a36Sopenharmony_ci spinlock_t lock; 7962306a36Sopenharmony_ci struct list_head rtpg_list; 8062306a36Sopenharmony_ci struct scsi_device *rtpg_sdev; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct alua_dh_data { 8462306a36Sopenharmony_ci struct list_head node; 8562306a36Sopenharmony_ci struct alua_port_group __rcu *pg; 8662306a36Sopenharmony_ci int group_id; 8762306a36Sopenharmony_ci spinlock_t pg_lock; 8862306a36Sopenharmony_ci struct scsi_device *sdev; 8962306a36Sopenharmony_ci int init_error; 9062306a36Sopenharmony_ci struct mutex init_mutex; 9162306a36Sopenharmony_ci bool disabled; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistruct alua_queue_data { 9562306a36Sopenharmony_ci struct list_head entry; 9662306a36Sopenharmony_ci activate_complete callback_fn; 9762306a36Sopenharmony_ci void *callback_data; 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define ALUA_POLICY_SWITCH_CURRENT 0 10162306a36Sopenharmony_ci#define ALUA_POLICY_SWITCH_ALL 1 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void alua_rtpg_work(struct work_struct *work); 10462306a36Sopenharmony_cistatic bool alua_rtpg_queue(struct alua_port_group *pg, 10562306a36Sopenharmony_ci struct scsi_device *sdev, 10662306a36Sopenharmony_ci struct alua_queue_data *qdata, bool force); 10762306a36Sopenharmony_cistatic void alua_check(struct scsi_device *sdev, bool force); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void release_port_group(struct kref *kref) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct alua_port_group *pg; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci pg = container_of(kref, struct alua_port_group, kref); 11462306a36Sopenharmony_ci if (pg->rtpg_sdev) 11562306a36Sopenharmony_ci flush_delayed_work(&pg->rtpg_work); 11662306a36Sopenharmony_ci spin_lock(&port_group_lock); 11762306a36Sopenharmony_ci list_del(&pg->node); 11862306a36Sopenharmony_ci spin_unlock(&port_group_lock); 11962306a36Sopenharmony_ci kfree_rcu(pg, rcu); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* 12362306a36Sopenharmony_ci * submit_rtpg - Issue a REPORT TARGET GROUP STATES command 12462306a36Sopenharmony_ci * @sdev: sdev the command should be sent to 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_cistatic int submit_rtpg(struct scsi_device *sdev, unsigned char *buff, 12762306a36Sopenharmony_ci int bufflen, struct scsi_sense_hdr *sshdr, int flags) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci u8 cdb[MAX_COMMAND_SIZE]; 13062306a36Sopenharmony_ci blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV | 13162306a36Sopenharmony_ci REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; 13262306a36Sopenharmony_ci const struct scsi_exec_args exec_args = { 13362306a36Sopenharmony_ci .sshdr = sshdr, 13462306a36Sopenharmony_ci }; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Prepare the command. */ 13762306a36Sopenharmony_ci memset(cdb, 0x0, MAX_COMMAND_SIZE); 13862306a36Sopenharmony_ci cdb[0] = MAINTENANCE_IN; 13962306a36Sopenharmony_ci if (!(flags & ALUA_RTPG_EXT_HDR_UNSUPP)) 14062306a36Sopenharmony_ci cdb[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT; 14162306a36Sopenharmony_ci else 14262306a36Sopenharmony_ci cdb[1] = MI_REPORT_TARGET_PGS; 14362306a36Sopenharmony_ci put_unaligned_be32(bufflen, &cdb[6]); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return scsi_execute_cmd(sdev, cdb, opf, buff, bufflen, 14662306a36Sopenharmony_ci ALUA_FAILOVER_TIMEOUT * HZ, 14762306a36Sopenharmony_ci ALUA_FAILOVER_RETRIES, &exec_args); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* 15162306a36Sopenharmony_ci * submit_stpg - Issue a SET TARGET PORT GROUP command 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * Currently we're only setting the current target port group state 15462306a36Sopenharmony_ci * to 'active/optimized' and let the array firmware figure out 15562306a36Sopenharmony_ci * the states of the remaining groups. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistatic int submit_stpg(struct scsi_device *sdev, int group_id, 15862306a36Sopenharmony_ci struct scsi_sense_hdr *sshdr) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci u8 cdb[MAX_COMMAND_SIZE]; 16162306a36Sopenharmony_ci unsigned char stpg_data[8]; 16262306a36Sopenharmony_ci int stpg_len = 8; 16362306a36Sopenharmony_ci blk_opf_t opf = REQ_OP_DRV_OUT | REQ_FAILFAST_DEV | 16462306a36Sopenharmony_ci REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; 16562306a36Sopenharmony_ci const struct scsi_exec_args exec_args = { 16662306a36Sopenharmony_ci .sshdr = sshdr, 16762306a36Sopenharmony_ci }; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Prepare the data buffer */ 17062306a36Sopenharmony_ci memset(stpg_data, 0, stpg_len); 17162306a36Sopenharmony_ci stpg_data[4] = SCSI_ACCESS_STATE_OPTIMAL; 17262306a36Sopenharmony_ci put_unaligned_be16(group_id, &stpg_data[6]); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Prepare the command. */ 17562306a36Sopenharmony_ci memset(cdb, 0x0, MAX_COMMAND_SIZE); 17662306a36Sopenharmony_ci cdb[0] = MAINTENANCE_OUT; 17762306a36Sopenharmony_ci cdb[1] = MO_SET_TARGET_PGS; 17862306a36Sopenharmony_ci put_unaligned_be32(stpg_len, &cdb[6]); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return scsi_execute_cmd(sdev, cdb, opf, stpg_data, 18162306a36Sopenharmony_ci stpg_len, ALUA_FAILOVER_TIMEOUT * HZ, 18262306a36Sopenharmony_ci ALUA_FAILOVER_RETRIES, &exec_args); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic struct alua_port_group *alua_find_get_pg(char *id_str, size_t id_size, 18662306a36Sopenharmony_ci int group_id) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct alua_port_group *pg; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (!id_str || !id_size || !strlen(id_str)) 19162306a36Sopenharmony_ci return NULL; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci list_for_each_entry(pg, &port_group_list, node) { 19462306a36Sopenharmony_ci if (pg->group_id != group_id) 19562306a36Sopenharmony_ci continue; 19662306a36Sopenharmony_ci if (!pg->device_id_len || pg->device_id_len != id_size) 19762306a36Sopenharmony_ci continue; 19862306a36Sopenharmony_ci if (strncmp(pg->device_id_str, id_str, id_size)) 19962306a36Sopenharmony_ci continue; 20062306a36Sopenharmony_ci if (!kref_get_unless_zero(&pg->kref)) 20162306a36Sopenharmony_ci continue; 20262306a36Sopenharmony_ci return pg; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return NULL; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* 20962306a36Sopenharmony_ci * alua_alloc_pg - Allocate a new port_group structure 21062306a36Sopenharmony_ci * @sdev: scsi device 21162306a36Sopenharmony_ci * @group_id: port group id 21262306a36Sopenharmony_ci * @tpgs: target port group settings 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * Allocate a new port_group structure for a given 21562306a36Sopenharmony_ci * device. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistatic struct alua_port_group *alua_alloc_pg(struct scsi_device *sdev, 21862306a36Sopenharmony_ci int group_id, int tpgs) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct alua_port_group *pg, *tmp_pg; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci pg = kzalloc(sizeof(struct alua_port_group), GFP_KERNEL); 22362306a36Sopenharmony_ci if (!pg) 22462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci pg->device_id_len = scsi_vpd_lun_id(sdev, pg->device_id_str, 22762306a36Sopenharmony_ci sizeof(pg->device_id_str)); 22862306a36Sopenharmony_ci if (pg->device_id_len <= 0) { 22962306a36Sopenharmony_ci /* 23062306a36Sopenharmony_ci * TPGS supported but no device identification found. 23162306a36Sopenharmony_ci * Generate private device identification. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 23462306a36Sopenharmony_ci "%s: No device descriptors found\n", 23562306a36Sopenharmony_ci ALUA_DH_NAME); 23662306a36Sopenharmony_ci pg->device_id_str[0] = '\0'; 23762306a36Sopenharmony_ci pg->device_id_len = 0; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci pg->group_id = group_id; 24062306a36Sopenharmony_ci pg->tpgs = tpgs; 24162306a36Sopenharmony_ci pg->state = SCSI_ACCESS_STATE_OPTIMAL; 24262306a36Sopenharmony_ci pg->valid_states = TPGS_SUPPORT_ALL; 24362306a36Sopenharmony_ci if (optimize_stpg) 24462306a36Sopenharmony_ci pg->flags |= ALUA_OPTIMIZE_STPG; 24562306a36Sopenharmony_ci kref_init(&pg->kref); 24662306a36Sopenharmony_ci INIT_DELAYED_WORK(&pg->rtpg_work, alua_rtpg_work); 24762306a36Sopenharmony_ci INIT_LIST_HEAD(&pg->rtpg_list); 24862306a36Sopenharmony_ci INIT_LIST_HEAD(&pg->node); 24962306a36Sopenharmony_ci INIT_LIST_HEAD(&pg->dh_list); 25062306a36Sopenharmony_ci spin_lock_init(&pg->lock); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci spin_lock(&port_group_lock); 25362306a36Sopenharmony_ci tmp_pg = alua_find_get_pg(pg->device_id_str, pg->device_id_len, 25462306a36Sopenharmony_ci group_id); 25562306a36Sopenharmony_ci if (tmp_pg) { 25662306a36Sopenharmony_ci spin_unlock(&port_group_lock); 25762306a36Sopenharmony_ci kfree(pg); 25862306a36Sopenharmony_ci return tmp_pg; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci list_add(&pg->node, &port_group_list); 26262306a36Sopenharmony_ci spin_unlock(&port_group_lock); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return pg; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* 26862306a36Sopenharmony_ci * alua_check_tpgs - Evaluate TPGS setting 26962306a36Sopenharmony_ci * @sdev: device to be checked 27062306a36Sopenharmony_ci * 27162306a36Sopenharmony_ci * Examine the TPGS setting of the sdev to find out if ALUA 27262306a36Sopenharmony_ci * is supported. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_cistatic int alua_check_tpgs(struct scsi_device *sdev) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int tpgs = TPGS_MODE_NONE; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* 27962306a36Sopenharmony_ci * ALUA support for non-disk devices is fraught with 28062306a36Sopenharmony_ci * difficulties, so disable it for now. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci if (sdev->type != TYPE_DISK) { 28362306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 28462306a36Sopenharmony_ci "%s: disable for non-disk devices\n", 28562306a36Sopenharmony_ci ALUA_DH_NAME); 28662306a36Sopenharmony_ci return tpgs; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci tpgs = scsi_device_tpgs(sdev); 29062306a36Sopenharmony_ci switch (tpgs) { 29162306a36Sopenharmony_ci case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT: 29262306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 29362306a36Sopenharmony_ci "%s: supports implicit and explicit TPGS\n", 29462306a36Sopenharmony_ci ALUA_DH_NAME); 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci case TPGS_MODE_EXPLICIT: 29762306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "%s: supports explicit TPGS\n", 29862306a36Sopenharmony_ci ALUA_DH_NAME); 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci case TPGS_MODE_IMPLICIT: 30162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "%s: supports implicit TPGS\n", 30262306a36Sopenharmony_ci ALUA_DH_NAME); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci case TPGS_MODE_NONE: 30562306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "%s: not supported\n", 30662306a36Sopenharmony_ci ALUA_DH_NAME); 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci default: 30962306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 31062306a36Sopenharmony_ci "%s: unsupported TPGS setting %d\n", 31162306a36Sopenharmony_ci ALUA_DH_NAME, tpgs); 31262306a36Sopenharmony_ci tpgs = TPGS_MODE_NONE; 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return tpgs; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* 32062306a36Sopenharmony_ci * alua_check_vpd - Evaluate INQUIRY vpd page 0x83 32162306a36Sopenharmony_ci * @sdev: device to be checked 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * Extract the relative target port and the target port group 32462306a36Sopenharmony_ci * descriptor from the list of identificators. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_cistatic int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h, 32762306a36Sopenharmony_ci int tpgs) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int rel_port = -1, group_id; 33062306a36Sopenharmony_ci struct alua_port_group *pg, *old_pg = NULL; 33162306a36Sopenharmony_ci bool pg_updated = false; 33262306a36Sopenharmony_ci unsigned long flags; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci group_id = scsi_vpd_tpg_id(sdev, &rel_port); 33562306a36Sopenharmony_ci if (group_id < 0) { 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * Internal error; TPGS supported but required 33862306a36Sopenharmony_ci * VPD identification descriptors not present. 33962306a36Sopenharmony_ci * Disable ALUA support 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 34262306a36Sopenharmony_ci "%s: No target port descriptors found\n", 34362306a36Sopenharmony_ci ALUA_DH_NAME); 34462306a36Sopenharmony_ci return SCSI_DH_DEV_UNSUPP; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci pg = alua_alloc_pg(sdev, group_id, tpgs); 34862306a36Sopenharmony_ci if (IS_ERR(pg)) { 34962306a36Sopenharmony_ci if (PTR_ERR(pg) == -ENOMEM) 35062306a36Sopenharmony_ci return SCSI_DH_NOMEM; 35162306a36Sopenharmony_ci return SCSI_DH_DEV_UNSUPP; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci if (pg->device_id_len) 35462306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 35562306a36Sopenharmony_ci "%s: device %s port group %x rel port %x\n", 35662306a36Sopenharmony_ci ALUA_DH_NAME, pg->device_id_str, 35762306a36Sopenharmony_ci group_id, rel_port); 35862306a36Sopenharmony_ci else 35962306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 36062306a36Sopenharmony_ci "%s: port group %x rel port %x\n", 36162306a36Sopenharmony_ci ALUA_DH_NAME, group_id, rel_port); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci kref_get(&pg->kref); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* Check for existing port group references */ 36662306a36Sopenharmony_ci spin_lock(&h->pg_lock); 36762306a36Sopenharmony_ci old_pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock)); 36862306a36Sopenharmony_ci if (old_pg != pg) { 36962306a36Sopenharmony_ci /* port group has changed. Update to new port group */ 37062306a36Sopenharmony_ci if (h->pg) { 37162306a36Sopenharmony_ci spin_lock_irqsave(&old_pg->lock, flags); 37262306a36Sopenharmony_ci list_del_rcu(&h->node); 37362306a36Sopenharmony_ci spin_unlock_irqrestore(&old_pg->lock, flags); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci rcu_assign_pointer(h->pg, pg); 37662306a36Sopenharmony_ci pg_updated = true; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci spin_lock_irqsave(&pg->lock, flags); 38062306a36Sopenharmony_ci if (pg_updated) 38162306a36Sopenharmony_ci list_add_rcu(&h->node, &pg->dh_list); 38262306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci spin_unlock(&h->pg_lock); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci alua_rtpg_queue(pg, sdev, NULL, true); 38762306a36Sopenharmony_ci kref_put(&pg->kref, release_port_group); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (old_pg) 39062306a36Sopenharmony_ci kref_put(&old_pg->kref, release_port_group); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return SCSI_DH_OK; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic char print_alua_state(unsigned char state) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci switch (state) { 39862306a36Sopenharmony_ci case SCSI_ACCESS_STATE_OPTIMAL: 39962306a36Sopenharmony_ci return 'A'; 40062306a36Sopenharmony_ci case SCSI_ACCESS_STATE_ACTIVE: 40162306a36Sopenharmony_ci return 'N'; 40262306a36Sopenharmony_ci case SCSI_ACCESS_STATE_STANDBY: 40362306a36Sopenharmony_ci return 'S'; 40462306a36Sopenharmony_ci case SCSI_ACCESS_STATE_UNAVAILABLE: 40562306a36Sopenharmony_ci return 'U'; 40662306a36Sopenharmony_ci case SCSI_ACCESS_STATE_LBA: 40762306a36Sopenharmony_ci return 'L'; 40862306a36Sopenharmony_ci case SCSI_ACCESS_STATE_OFFLINE: 40962306a36Sopenharmony_ci return 'O'; 41062306a36Sopenharmony_ci case SCSI_ACCESS_STATE_TRANSITIONING: 41162306a36Sopenharmony_ci return 'T'; 41262306a36Sopenharmony_ci default: 41362306a36Sopenharmony_ci return 'X'; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic enum scsi_disposition alua_check_sense(struct scsi_device *sdev, 41862306a36Sopenharmony_ci struct scsi_sense_hdr *sense_hdr) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct alua_dh_data *h = sdev->handler_data; 42162306a36Sopenharmony_ci struct alua_port_group *pg; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci switch (sense_hdr->sense_key) { 42462306a36Sopenharmony_ci case NOT_READY: 42562306a36Sopenharmony_ci if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) { 42662306a36Sopenharmony_ci /* 42762306a36Sopenharmony_ci * LUN Not Accessible - ALUA state transition 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci rcu_read_lock(); 43062306a36Sopenharmony_ci pg = rcu_dereference(h->pg); 43162306a36Sopenharmony_ci if (pg) 43262306a36Sopenharmony_ci pg->state = SCSI_ACCESS_STATE_TRANSITIONING; 43362306a36Sopenharmony_ci rcu_read_unlock(); 43462306a36Sopenharmony_ci alua_check(sdev, false); 43562306a36Sopenharmony_ci return NEEDS_RETRY; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci case UNIT_ATTENTION: 43962306a36Sopenharmony_ci if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) { 44062306a36Sopenharmony_ci /* 44162306a36Sopenharmony_ci * Power On, Reset, or Bus Device Reset. 44262306a36Sopenharmony_ci * Might have obscured a state transition, 44362306a36Sopenharmony_ci * so schedule a recheck. 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci alua_check(sdev, true); 44662306a36Sopenharmony_ci return ADD_TO_MLQUEUE; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x04) 44962306a36Sopenharmony_ci /* 45062306a36Sopenharmony_ci * Device internal reset 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci return ADD_TO_MLQUEUE; 45362306a36Sopenharmony_ci if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x01) 45462306a36Sopenharmony_ci /* 45562306a36Sopenharmony_ci * Mode Parameters Changed 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_ci return ADD_TO_MLQUEUE; 45862306a36Sopenharmony_ci if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) { 45962306a36Sopenharmony_ci /* 46062306a36Sopenharmony_ci * ALUA state changed 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ci alua_check(sdev, true); 46362306a36Sopenharmony_ci return ADD_TO_MLQUEUE; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) { 46662306a36Sopenharmony_ci /* 46762306a36Sopenharmony_ci * Implicit ALUA state transition failed 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci alua_check(sdev, true); 47062306a36Sopenharmony_ci return ADD_TO_MLQUEUE; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x03) 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * Inquiry data has changed 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ci return ADD_TO_MLQUEUE; 47762306a36Sopenharmony_ci if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x0e) 47862306a36Sopenharmony_ci /* 47962306a36Sopenharmony_ci * REPORTED_LUNS_DATA_HAS_CHANGED is reported 48062306a36Sopenharmony_ci * when switching controllers on targets like 48162306a36Sopenharmony_ci * Intel Multi-Flex. We can just retry. 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci return ADD_TO_MLQUEUE; 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return SCSI_RETURN_NOT_HANDLED; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci/* 49162306a36Sopenharmony_ci * alua_tur - Send a TEST UNIT READY 49262306a36Sopenharmony_ci * @sdev: device to which the TEST UNIT READY command should be send 49362306a36Sopenharmony_ci * 49462306a36Sopenharmony_ci * Send a TEST UNIT READY to @sdev to figure out the device state 49562306a36Sopenharmony_ci * Returns SCSI_DH_RETRY if the sense code is NOT READY/ALUA TRANSITIONING, 49662306a36Sopenharmony_ci * SCSI_DH_OK if no error occurred, and SCSI_DH_IO otherwise. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_cistatic int alua_tur(struct scsi_device *sdev) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct scsi_sense_hdr sense_hdr; 50162306a36Sopenharmony_ci int retval; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci retval = scsi_test_unit_ready(sdev, ALUA_FAILOVER_TIMEOUT * HZ, 50462306a36Sopenharmony_ci ALUA_FAILOVER_RETRIES, &sense_hdr); 50562306a36Sopenharmony_ci if (sense_hdr.sense_key == NOT_READY && 50662306a36Sopenharmony_ci sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) 50762306a36Sopenharmony_ci return SCSI_DH_RETRY; 50862306a36Sopenharmony_ci else if (retval) 50962306a36Sopenharmony_ci return SCSI_DH_IO; 51062306a36Sopenharmony_ci else 51162306a36Sopenharmony_ci return SCSI_DH_OK; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci/* 51562306a36Sopenharmony_ci * alua_rtpg - Evaluate REPORT TARGET GROUP STATES 51662306a36Sopenharmony_ci * @sdev: the device to be evaluated. 51762306a36Sopenharmony_ci * 51862306a36Sopenharmony_ci * Evaluate the Target Port Group State. 51962306a36Sopenharmony_ci * Returns SCSI_DH_DEV_OFFLINED if the path is 52062306a36Sopenharmony_ci * found to be unusable. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_cistatic int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct scsi_sense_hdr sense_hdr; 52562306a36Sopenharmony_ci struct alua_port_group *tmp_pg; 52662306a36Sopenharmony_ci int len, k, off, bufflen = ALUA_RTPG_SIZE; 52762306a36Sopenharmony_ci int group_id_old, state_old, pref_old, valid_states_old; 52862306a36Sopenharmony_ci unsigned char *desc, *buff; 52962306a36Sopenharmony_ci unsigned err; 53062306a36Sopenharmony_ci int retval; 53162306a36Sopenharmony_ci unsigned int tpg_desc_tbl_off; 53262306a36Sopenharmony_ci unsigned char orig_transition_tmo; 53362306a36Sopenharmony_ci unsigned long flags; 53462306a36Sopenharmony_ci bool transitioning_sense = false; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci group_id_old = pg->group_id; 53762306a36Sopenharmony_ci state_old = pg->state; 53862306a36Sopenharmony_ci pref_old = pg->pref; 53962306a36Sopenharmony_ci valid_states_old = pg->valid_states; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (!pg->expiry) { 54262306a36Sopenharmony_ci unsigned long transition_tmo = ALUA_FAILOVER_TIMEOUT * HZ; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (pg->transition_tmo) 54562306a36Sopenharmony_ci transition_tmo = pg->transition_tmo * HZ; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci pg->expiry = round_jiffies_up(jiffies + transition_tmo); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci buff = kzalloc(bufflen, GFP_KERNEL); 55162306a36Sopenharmony_ci if (!buff) 55262306a36Sopenharmony_ci return SCSI_DH_DEV_TEMP_BUSY; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci retry: 55562306a36Sopenharmony_ci err = 0; 55662306a36Sopenharmony_ci retval = submit_rtpg(sdev, buff, bufflen, &sense_hdr, pg->flags); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (retval) { 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * Some (broken) implementations have a habit of returning 56162306a36Sopenharmony_ci * an error during things like firmware update etc. 56262306a36Sopenharmony_ci * But if the target only supports active/optimized there's 56362306a36Sopenharmony_ci * not much we can do; it's not that we can switch paths 56462306a36Sopenharmony_ci * or anything. 56562306a36Sopenharmony_ci * So ignore any errors to avoid spurious failures during 56662306a36Sopenharmony_ci * path failover. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci if ((pg->valid_states & ~TPGS_SUPPORT_OPTIMIZED) == 0) { 56962306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 57062306a36Sopenharmony_ci "%s: ignoring rtpg result %d\n", 57162306a36Sopenharmony_ci ALUA_DH_NAME, retval); 57262306a36Sopenharmony_ci kfree(buff); 57362306a36Sopenharmony_ci return SCSI_DH_OK; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci if (retval < 0 || !scsi_sense_valid(&sense_hdr)) { 57662306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 57762306a36Sopenharmony_ci "%s: rtpg failed, result %d\n", 57862306a36Sopenharmony_ci ALUA_DH_NAME, retval); 57962306a36Sopenharmony_ci kfree(buff); 58062306a36Sopenharmony_ci if (retval < 0) 58162306a36Sopenharmony_ci return SCSI_DH_DEV_TEMP_BUSY; 58262306a36Sopenharmony_ci if (host_byte(retval) == DID_NO_CONNECT) 58362306a36Sopenharmony_ci return SCSI_DH_RES_TEMP_UNAVAIL; 58462306a36Sopenharmony_ci return SCSI_DH_IO; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* 58862306a36Sopenharmony_ci * submit_rtpg() has failed on existing arrays 58962306a36Sopenharmony_ci * when requesting extended header info, and 59062306a36Sopenharmony_ci * the array doesn't support extended headers, 59162306a36Sopenharmony_ci * even though it shouldn't according to T10. 59262306a36Sopenharmony_ci * The retry without rtpg_ext_hdr_req set 59362306a36Sopenharmony_ci * handles this. 59462306a36Sopenharmony_ci * Note: some arrays return a sense key of ILLEGAL_REQUEST 59562306a36Sopenharmony_ci * with ASC 00h if they don't support the extended header. 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_ci if (!(pg->flags & ALUA_RTPG_EXT_HDR_UNSUPP) && 59862306a36Sopenharmony_ci sense_hdr.sense_key == ILLEGAL_REQUEST) { 59962306a36Sopenharmony_ci pg->flags |= ALUA_RTPG_EXT_HDR_UNSUPP; 60062306a36Sopenharmony_ci goto retry; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci /* 60362306a36Sopenharmony_ci * If the array returns with 'ALUA state transition' 60462306a36Sopenharmony_ci * sense code here it cannot return RTPG data during 60562306a36Sopenharmony_ci * transition. So set the state to 'transitioning' directly. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci if (sense_hdr.sense_key == NOT_READY && 60862306a36Sopenharmony_ci sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) { 60962306a36Sopenharmony_ci transitioning_sense = true; 61062306a36Sopenharmony_ci goto skip_rtpg; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci /* 61362306a36Sopenharmony_ci * Retry on any other UNIT ATTENTION occurred. 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_ci if (sense_hdr.sense_key == UNIT_ATTENTION) 61662306a36Sopenharmony_ci err = SCSI_DH_RETRY; 61762306a36Sopenharmony_ci if (err == SCSI_DH_RETRY && 61862306a36Sopenharmony_ci pg->expiry != 0 && time_before(jiffies, pg->expiry)) { 61962306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "%s: rtpg retry\n", 62062306a36Sopenharmony_ci ALUA_DH_NAME); 62162306a36Sopenharmony_ci scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr); 62262306a36Sopenharmony_ci kfree(buff); 62362306a36Sopenharmony_ci return err; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci sdev_printk(KERN_ERR, sdev, "%s: rtpg failed\n", 62662306a36Sopenharmony_ci ALUA_DH_NAME); 62762306a36Sopenharmony_ci scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr); 62862306a36Sopenharmony_ci kfree(buff); 62962306a36Sopenharmony_ci pg->expiry = 0; 63062306a36Sopenharmony_ci return SCSI_DH_IO; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci len = get_unaligned_be32(&buff[0]) + 4; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (len > bufflen) { 63662306a36Sopenharmony_ci /* Resubmit with the correct length */ 63762306a36Sopenharmony_ci kfree(buff); 63862306a36Sopenharmony_ci bufflen = len; 63962306a36Sopenharmony_ci buff = kmalloc(bufflen, GFP_KERNEL); 64062306a36Sopenharmony_ci if (!buff) { 64162306a36Sopenharmony_ci sdev_printk(KERN_WARNING, sdev, 64262306a36Sopenharmony_ci "%s: kmalloc buffer failed\n",__func__); 64362306a36Sopenharmony_ci /* Temporary failure, bypass */ 64462306a36Sopenharmony_ci pg->expiry = 0; 64562306a36Sopenharmony_ci return SCSI_DH_DEV_TEMP_BUSY; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci goto retry; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci orig_transition_tmo = pg->transition_tmo; 65162306a36Sopenharmony_ci if ((buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR && buff[5] != 0) 65262306a36Sopenharmony_ci pg->transition_tmo = buff[5]; 65362306a36Sopenharmony_ci else 65462306a36Sopenharmony_ci pg->transition_tmo = ALUA_FAILOVER_TIMEOUT; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (orig_transition_tmo != pg->transition_tmo) { 65762306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 65862306a36Sopenharmony_ci "%s: transition timeout set to %d seconds\n", 65962306a36Sopenharmony_ci ALUA_DH_NAME, pg->transition_tmo); 66062306a36Sopenharmony_ci pg->expiry = jiffies + pg->transition_tmo * HZ; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if ((buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR) 66462306a36Sopenharmony_ci tpg_desc_tbl_off = 8; 66562306a36Sopenharmony_ci else 66662306a36Sopenharmony_ci tpg_desc_tbl_off = 4; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci for (k = tpg_desc_tbl_off, desc = buff + tpg_desc_tbl_off; 66962306a36Sopenharmony_ci k < len; 67062306a36Sopenharmony_ci k += off, desc += off) { 67162306a36Sopenharmony_ci u16 group_id = get_unaligned_be16(&desc[2]); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci spin_lock_irqsave(&port_group_lock, flags); 67462306a36Sopenharmony_ci tmp_pg = alua_find_get_pg(pg->device_id_str, pg->device_id_len, 67562306a36Sopenharmony_ci group_id); 67662306a36Sopenharmony_ci spin_unlock_irqrestore(&port_group_lock, flags); 67762306a36Sopenharmony_ci if (tmp_pg) { 67862306a36Sopenharmony_ci if (spin_trylock_irqsave(&tmp_pg->lock, flags)) { 67962306a36Sopenharmony_ci if ((tmp_pg == pg) || 68062306a36Sopenharmony_ci !(tmp_pg->flags & ALUA_PG_RUNNING)) { 68162306a36Sopenharmony_ci struct alua_dh_data *h; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci tmp_pg->state = desc[0] & 0x0f; 68462306a36Sopenharmony_ci tmp_pg->pref = desc[0] >> 7; 68562306a36Sopenharmony_ci rcu_read_lock(); 68662306a36Sopenharmony_ci list_for_each_entry_rcu(h, 68762306a36Sopenharmony_ci &tmp_pg->dh_list, node) { 68862306a36Sopenharmony_ci if (!h->sdev) 68962306a36Sopenharmony_ci continue; 69062306a36Sopenharmony_ci h->sdev->access_state = desc[0]; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci rcu_read_unlock(); 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci if (tmp_pg == pg) 69562306a36Sopenharmony_ci tmp_pg->valid_states = desc[1]; 69662306a36Sopenharmony_ci spin_unlock_irqrestore(&tmp_pg->lock, flags); 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci kref_put(&tmp_pg->kref, release_port_group); 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci off = 8 + (desc[7] * 4); 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci skip_rtpg: 70462306a36Sopenharmony_ci spin_lock_irqsave(&pg->lock, flags); 70562306a36Sopenharmony_ci if (transitioning_sense) 70662306a36Sopenharmony_ci pg->state = SCSI_ACCESS_STATE_TRANSITIONING; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (group_id_old != pg->group_id || state_old != pg->state || 70962306a36Sopenharmony_ci pref_old != pg->pref || valid_states_old != pg->valid_states) 71062306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 71162306a36Sopenharmony_ci "%s: port group %02x state %c %s supports %c%c%c%c%c%c%c\n", 71262306a36Sopenharmony_ci ALUA_DH_NAME, pg->group_id, print_alua_state(pg->state), 71362306a36Sopenharmony_ci pg->pref ? "preferred" : "non-preferred", 71462306a36Sopenharmony_ci pg->valid_states&TPGS_SUPPORT_TRANSITION?'T':'t', 71562306a36Sopenharmony_ci pg->valid_states&TPGS_SUPPORT_OFFLINE?'O':'o', 71662306a36Sopenharmony_ci pg->valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l', 71762306a36Sopenharmony_ci pg->valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u', 71862306a36Sopenharmony_ci pg->valid_states&TPGS_SUPPORT_STANDBY?'S':'s', 71962306a36Sopenharmony_ci pg->valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n', 72062306a36Sopenharmony_ci pg->valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a'); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci switch (pg->state) { 72362306a36Sopenharmony_ci case SCSI_ACCESS_STATE_TRANSITIONING: 72462306a36Sopenharmony_ci if (time_before(jiffies, pg->expiry)) { 72562306a36Sopenharmony_ci /* State transition, retry */ 72662306a36Sopenharmony_ci pg->interval = ALUA_RTPG_RETRY_DELAY; 72762306a36Sopenharmony_ci err = SCSI_DH_RETRY; 72862306a36Sopenharmony_ci } else { 72962306a36Sopenharmony_ci struct alua_dh_data *h; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* Transitioning time exceeded, set port to standby */ 73262306a36Sopenharmony_ci err = SCSI_DH_IO; 73362306a36Sopenharmony_ci pg->state = SCSI_ACCESS_STATE_STANDBY; 73462306a36Sopenharmony_ci pg->expiry = 0; 73562306a36Sopenharmony_ci rcu_read_lock(); 73662306a36Sopenharmony_ci list_for_each_entry_rcu(h, &pg->dh_list, node) { 73762306a36Sopenharmony_ci if (!h->sdev) 73862306a36Sopenharmony_ci continue; 73962306a36Sopenharmony_ci h->sdev->access_state = 74062306a36Sopenharmony_ci (pg->state & SCSI_ACCESS_STATE_MASK); 74162306a36Sopenharmony_ci if (pg->pref) 74262306a36Sopenharmony_ci h->sdev->access_state |= 74362306a36Sopenharmony_ci SCSI_ACCESS_STATE_PREFERRED; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci rcu_read_unlock(); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci case SCSI_ACCESS_STATE_OFFLINE: 74962306a36Sopenharmony_ci /* Path unusable */ 75062306a36Sopenharmony_ci err = SCSI_DH_DEV_OFFLINED; 75162306a36Sopenharmony_ci pg->expiry = 0; 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci default: 75462306a36Sopenharmony_ci /* Useable path if active */ 75562306a36Sopenharmony_ci err = SCSI_DH_OK; 75662306a36Sopenharmony_ci pg->expiry = 0; 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 76062306a36Sopenharmony_ci kfree(buff); 76162306a36Sopenharmony_ci return err; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci/* 76562306a36Sopenharmony_ci * alua_stpg - Issue a SET TARGET PORT GROUP command 76662306a36Sopenharmony_ci * 76762306a36Sopenharmony_ci * Issue a SET TARGET PORT GROUP command and evaluate the 76862306a36Sopenharmony_ci * response. Returns SCSI_DH_RETRY per default to trigger 76962306a36Sopenharmony_ci * a re-evaluation of the target group state or SCSI_DH_OK 77062306a36Sopenharmony_ci * if no further action needs to be taken. 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_cistatic unsigned alua_stpg(struct scsi_device *sdev, struct alua_port_group *pg) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci int retval; 77562306a36Sopenharmony_ci struct scsi_sense_hdr sense_hdr; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (!(pg->tpgs & TPGS_MODE_EXPLICIT)) { 77862306a36Sopenharmony_ci /* Only implicit ALUA supported, retry */ 77962306a36Sopenharmony_ci return SCSI_DH_RETRY; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci switch (pg->state) { 78262306a36Sopenharmony_ci case SCSI_ACCESS_STATE_OPTIMAL: 78362306a36Sopenharmony_ci return SCSI_DH_OK; 78462306a36Sopenharmony_ci case SCSI_ACCESS_STATE_ACTIVE: 78562306a36Sopenharmony_ci if ((pg->flags & ALUA_OPTIMIZE_STPG) && 78662306a36Sopenharmony_ci !pg->pref && 78762306a36Sopenharmony_ci (pg->tpgs & TPGS_MODE_IMPLICIT)) 78862306a36Sopenharmony_ci return SCSI_DH_OK; 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci case SCSI_ACCESS_STATE_STANDBY: 79162306a36Sopenharmony_ci case SCSI_ACCESS_STATE_UNAVAILABLE: 79262306a36Sopenharmony_ci break; 79362306a36Sopenharmony_ci case SCSI_ACCESS_STATE_OFFLINE: 79462306a36Sopenharmony_ci return SCSI_DH_IO; 79562306a36Sopenharmony_ci case SCSI_ACCESS_STATE_TRANSITIONING: 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci default: 79862306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 79962306a36Sopenharmony_ci "%s: stpg failed, unhandled TPGS state %d", 80062306a36Sopenharmony_ci ALUA_DH_NAME, pg->state); 80162306a36Sopenharmony_ci return SCSI_DH_NOSYS; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci retval = submit_stpg(sdev, pg->group_id, &sense_hdr); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (retval) { 80662306a36Sopenharmony_ci if (retval < 0 || !scsi_sense_valid(&sense_hdr)) { 80762306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 80862306a36Sopenharmony_ci "%s: stpg failed, result %d", 80962306a36Sopenharmony_ci ALUA_DH_NAME, retval); 81062306a36Sopenharmony_ci if (retval < 0) 81162306a36Sopenharmony_ci return SCSI_DH_DEV_TEMP_BUSY; 81262306a36Sopenharmony_ci } else { 81362306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "%s: stpg failed\n", 81462306a36Sopenharmony_ci ALUA_DH_NAME); 81562306a36Sopenharmony_ci scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr); 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci /* Retry RTPG */ 81962306a36Sopenharmony_ci return SCSI_DH_RETRY; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/* 82362306a36Sopenharmony_ci * The caller must call scsi_device_put() on the returned pointer if it is not 82462306a36Sopenharmony_ci * NULL. 82562306a36Sopenharmony_ci */ 82662306a36Sopenharmony_cistatic struct scsi_device * __must_check 82762306a36Sopenharmony_cialua_rtpg_select_sdev(struct alua_port_group *pg) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct alua_dh_data *h; 83062306a36Sopenharmony_ci struct scsi_device *sdev = NULL, *prev_sdev; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci lockdep_assert_held(&pg->lock); 83362306a36Sopenharmony_ci if (WARN_ON(!pg->rtpg_sdev)) 83462306a36Sopenharmony_ci return NULL; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* 83762306a36Sopenharmony_ci * RCU protection isn't necessary for dh_list here 83862306a36Sopenharmony_ci * as we hold pg->lock, but for access to h->pg. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci rcu_read_lock(); 84162306a36Sopenharmony_ci list_for_each_entry_rcu(h, &pg->dh_list, node) { 84262306a36Sopenharmony_ci if (!h->sdev) 84362306a36Sopenharmony_ci continue; 84462306a36Sopenharmony_ci if (h->sdev == pg->rtpg_sdev) { 84562306a36Sopenharmony_ci h->disabled = true; 84662306a36Sopenharmony_ci continue; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci if (rcu_dereference(h->pg) == pg && 84962306a36Sopenharmony_ci !h->disabled && 85062306a36Sopenharmony_ci !scsi_device_get(h->sdev)) { 85162306a36Sopenharmony_ci sdev = h->sdev; 85262306a36Sopenharmony_ci break; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci rcu_read_unlock(); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (!sdev) { 85862306a36Sopenharmony_ci pr_warn("%s: no device found for rtpg\n", 85962306a36Sopenharmony_ci (pg->device_id_len ? 86062306a36Sopenharmony_ci (char *)pg->device_id_str : "(nameless PG)")); 86162306a36Sopenharmony_ci return NULL; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "rtpg retry on different device\n"); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci prev_sdev = pg->rtpg_sdev; 86762306a36Sopenharmony_ci pg->rtpg_sdev = sdev; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return prev_sdev; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic void alua_rtpg_work(struct work_struct *work) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct alua_port_group *pg = 87562306a36Sopenharmony_ci container_of(work, struct alua_port_group, rtpg_work.work); 87662306a36Sopenharmony_ci struct scsi_device *sdev, *prev_sdev = NULL; 87762306a36Sopenharmony_ci LIST_HEAD(qdata_list); 87862306a36Sopenharmony_ci int err = SCSI_DH_OK; 87962306a36Sopenharmony_ci struct alua_queue_data *qdata, *tmp; 88062306a36Sopenharmony_ci struct alua_dh_data *h; 88162306a36Sopenharmony_ci unsigned long flags; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci spin_lock_irqsave(&pg->lock, flags); 88462306a36Sopenharmony_ci sdev = pg->rtpg_sdev; 88562306a36Sopenharmony_ci if (!sdev) { 88662306a36Sopenharmony_ci WARN_ON(pg->flags & ALUA_PG_RUN_RTPG); 88762306a36Sopenharmony_ci WARN_ON(pg->flags & ALUA_PG_RUN_STPG); 88862306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 88962306a36Sopenharmony_ci kref_put(&pg->kref, release_port_group); 89062306a36Sopenharmony_ci return; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci pg->flags |= ALUA_PG_RUNNING; 89362306a36Sopenharmony_ci if (pg->flags & ALUA_PG_RUN_RTPG) { 89462306a36Sopenharmony_ci int state = pg->state; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci pg->flags &= ~ALUA_PG_RUN_RTPG; 89762306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 89862306a36Sopenharmony_ci if (state == SCSI_ACCESS_STATE_TRANSITIONING) { 89962306a36Sopenharmony_ci if (alua_tur(sdev) == SCSI_DH_RETRY) { 90062306a36Sopenharmony_ci spin_lock_irqsave(&pg->lock, flags); 90162306a36Sopenharmony_ci pg->flags &= ~ALUA_PG_RUNNING; 90262306a36Sopenharmony_ci pg->flags |= ALUA_PG_RUN_RTPG; 90362306a36Sopenharmony_ci if (!pg->interval) 90462306a36Sopenharmony_ci pg->interval = ALUA_RTPG_RETRY_DELAY; 90562306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 90662306a36Sopenharmony_ci queue_delayed_work(kaluad_wq, &pg->rtpg_work, 90762306a36Sopenharmony_ci pg->interval * HZ); 90862306a36Sopenharmony_ci return; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci /* Send RTPG on failure or if TUR indicates SUCCESS */ 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci err = alua_rtpg(sdev, pg); 91362306a36Sopenharmony_ci spin_lock_irqsave(&pg->lock, flags); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* If RTPG failed on the current device, try using another */ 91662306a36Sopenharmony_ci if (err == SCSI_DH_RES_TEMP_UNAVAIL && 91762306a36Sopenharmony_ci (prev_sdev = alua_rtpg_select_sdev(pg))) 91862306a36Sopenharmony_ci err = SCSI_DH_IMM_RETRY; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (err == SCSI_DH_RETRY || err == SCSI_DH_IMM_RETRY || 92162306a36Sopenharmony_ci pg->flags & ALUA_PG_RUN_RTPG) { 92262306a36Sopenharmony_ci pg->flags &= ~ALUA_PG_RUNNING; 92362306a36Sopenharmony_ci if (err == SCSI_DH_IMM_RETRY) 92462306a36Sopenharmony_ci pg->interval = 0; 92562306a36Sopenharmony_ci else if (!pg->interval && !(pg->flags & ALUA_PG_RUN_RTPG)) 92662306a36Sopenharmony_ci pg->interval = ALUA_RTPG_RETRY_DELAY; 92762306a36Sopenharmony_ci pg->flags |= ALUA_PG_RUN_RTPG; 92862306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 92962306a36Sopenharmony_ci goto queue_rtpg; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci if (err != SCSI_DH_OK) 93262306a36Sopenharmony_ci pg->flags &= ~ALUA_PG_RUN_STPG; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci if (pg->flags & ALUA_PG_RUN_STPG) { 93562306a36Sopenharmony_ci pg->flags &= ~ALUA_PG_RUN_STPG; 93662306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 93762306a36Sopenharmony_ci err = alua_stpg(sdev, pg); 93862306a36Sopenharmony_ci spin_lock_irqsave(&pg->lock, flags); 93962306a36Sopenharmony_ci if (err == SCSI_DH_RETRY || pg->flags & ALUA_PG_RUN_RTPG) { 94062306a36Sopenharmony_ci pg->flags |= ALUA_PG_RUN_RTPG; 94162306a36Sopenharmony_ci pg->interval = 0; 94262306a36Sopenharmony_ci pg->flags &= ~ALUA_PG_RUNNING; 94362306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 94462306a36Sopenharmony_ci goto queue_rtpg; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci list_splice_init(&pg->rtpg_list, &qdata_list); 94962306a36Sopenharmony_ci /* 95062306a36Sopenharmony_ci * We went through an RTPG, for good or bad. 95162306a36Sopenharmony_ci * Re-enable all devices for the next attempt. 95262306a36Sopenharmony_ci */ 95362306a36Sopenharmony_ci list_for_each_entry(h, &pg->dh_list, node) 95462306a36Sopenharmony_ci h->disabled = false; 95562306a36Sopenharmony_ci pg->rtpg_sdev = NULL; 95662306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (prev_sdev) 95962306a36Sopenharmony_ci scsi_device_put(prev_sdev); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci list_for_each_entry_safe(qdata, tmp, &qdata_list, entry) { 96262306a36Sopenharmony_ci list_del(&qdata->entry); 96362306a36Sopenharmony_ci if (qdata->callback_fn) 96462306a36Sopenharmony_ci qdata->callback_fn(qdata->callback_data, err); 96562306a36Sopenharmony_ci kfree(qdata); 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci spin_lock_irqsave(&pg->lock, flags); 96862306a36Sopenharmony_ci pg->flags &= ~ALUA_PG_RUNNING; 96962306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 97062306a36Sopenharmony_ci scsi_device_put(sdev); 97162306a36Sopenharmony_ci kref_put(&pg->kref, release_port_group); 97262306a36Sopenharmony_ci return; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ciqueue_rtpg: 97562306a36Sopenharmony_ci if (prev_sdev) 97662306a36Sopenharmony_ci scsi_device_put(prev_sdev); 97762306a36Sopenharmony_ci queue_delayed_work(kaluad_wq, &pg->rtpg_work, pg->interval * HZ); 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci/** 98162306a36Sopenharmony_ci * alua_rtpg_queue() - cause RTPG to be submitted asynchronously 98262306a36Sopenharmony_ci * @pg: ALUA port group associated with @sdev. 98362306a36Sopenharmony_ci * @sdev: SCSI device for which to submit an RTPG. 98462306a36Sopenharmony_ci * @qdata: Information about the callback to invoke after the RTPG. 98562306a36Sopenharmony_ci * @force: Whether or not to submit an RTPG if a work item that will submit an 98662306a36Sopenharmony_ci * RTPG already has been scheduled. 98762306a36Sopenharmony_ci * 98862306a36Sopenharmony_ci * Returns true if and only if alua_rtpg_work() will be called asynchronously. 98962306a36Sopenharmony_ci * That function is responsible for calling @qdata->fn(). 99062306a36Sopenharmony_ci * 99162306a36Sopenharmony_ci * Context: may be called from atomic context (alua_check()) only if the caller 99262306a36Sopenharmony_ci * holds an sdev reference. 99362306a36Sopenharmony_ci */ 99462306a36Sopenharmony_cistatic bool alua_rtpg_queue(struct alua_port_group *pg, 99562306a36Sopenharmony_ci struct scsi_device *sdev, 99662306a36Sopenharmony_ci struct alua_queue_data *qdata, bool force) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci int start_queue = 0; 99962306a36Sopenharmony_ci unsigned long flags; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (WARN_ON_ONCE(!pg) || scsi_device_get(sdev)) 100262306a36Sopenharmony_ci return false; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci spin_lock_irqsave(&pg->lock, flags); 100562306a36Sopenharmony_ci if (qdata) { 100662306a36Sopenharmony_ci list_add_tail(&qdata->entry, &pg->rtpg_list); 100762306a36Sopenharmony_ci pg->flags |= ALUA_PG_RUN_STPG; 100862306a36Sopenharmony_ci force = true; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci if (pg->rtpg_sdev == NULL) { 101162306a36Sopenharmony_ci struct alua_dh_data *h = sdev->handler_data; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci rcu_read_lock(); 101462306a36Sopenharmony_ci if (h && rcu_dereference(h->pg) == pg) { 101562306a36Sopenharmony_ci pg->interval = 0; 101662306a36Sopenharmony_ci pg->flags |= ALUA_PG_RUN_RTPG; 101762306a36Sopenharmony_ci kref_get(&pg->kref); 101862306a36Sopenharmony_ci pg->rtpg_sdev = sdev; 101962306a36Sopenharmony_ci start_queue = 1; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci rcu_read_unlock(); 102262306a36Sopenharmony_ci } else if (!(pg->flags & ALUA_PG_RUN_RTPG) && force) { 102362306a36Sopenharmony_ci pg->flags |= ALUA_PG_RUN_RTPG; 102462306a36Sopenharmony_ci /* Do not queue if the worker is already running */ 102562306a36Sopenharmony_ci if (!(pg->flags & ALUA_PG_RUNNING)) { 102662306a36Sopenharmony_ci kref_get(&pg->kref); 102762306a36Sopenharmony_ci start_queue = 1; 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (start_queue) { 103462306a36Sopenharmony_ci if (queue_delayed_work(kaluad_wq, &pg->rtpg_work, 103562306a36Sopenharmony_ci msecs_to_jiffies(ALUA_RTPG_DELAY_MSECS))) 103662306a36Sopenharmony_ci sdev = NULL; 103762306a36Sopenharmony_ci else 103862306a36Sopenharmony_ci kref_put(&pg->kref, release_port_group); 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci if (sdev) 104162306a36Sopenharmony_ci scsi_device_put(sdev); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci return true; 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci/* 104762306a36Sopenharmony_ci * alua_initialize - Initialize ALUA state 104862306a36Sopenharmony_ci * @sdev: the device to be initialized 104962306a36Sopenharmony_ci * 105062306a36Sopenharmony_ci * For the prep_fn to work correctly we have 105162306a36Sopenharmony_ci * to initialize the ALUA state for the device. 105262306a36Sopenharmony_ci */ 105362306a36Sopenharmony_cistatic int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci int err = SCSI_DH_DEV_UNSUPP, tpgs; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci mutex_lock(&h->init_mutex); 105862306a36Sopenharmony_ci h->disabled = false; 105962306a36Sopenharmony_ci tpgs = alua_check_tpgs(sdev); 106062306a36Sopenharmony_ci if (tpgs != TPGS_MODE_NONE) 106162306a36Sopenharmony_ci err = alua_check_vpd(sdev, h, tpgs); 106262306a36Sopenharmony_ci h->init_error = err; 106362306a36Sopenharmony_ci mutex_unlock(&h->init_mutex); 106462306a36Sopenharmony_ci return err; 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci/* 106762306a36Sopenharmony_ci * alua_set_params - set/unset the optimize flag 106862306a36Sopenharmony_ci * @sdev: device on the path to be activated 106962306a36Sopenharmony_ci * params - parameters in the following format 107062306a36Sopenharmony_ci * "no_of_params\0param1\0param2\0param3\0...\0" 107162306a36Sopenharmony_ci * For example, to set the flag pass the following parameters 107262306a36Sopenharmony_ci * from multipath.conf 107362306a36Sopenharmony_ci * hardware_handler "2 alua 1" 107462306a36Sopenharmony_ci */ 107562306a36Sopenharmony_cistatic int alua_set_params(struct scsi_device *sdev, const char *params) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct alua_dh_data *h = sdev->handler_data; 107862306a36Sopenharmony_ci struct alua_port_group *pg = NULL; 107962306a36Sopenharmony_ci unsigned int optimize = 0, argc; 108062306a36Sopenharmony_ci const char *p = params; 108162306a36Sopenharmony_ci int result = SCSI_DH_OK; 108262306a36Sopenharmony_ci unsigned long flags; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if ((sscanf(params, "%u", &argc) != 1) || (argc != 1)) 108562306a36Sopenharmony_ci return -EINVAL; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci while (*p++) 108862306a36Sopenharmony_ci ; 108962306a36Sopenharmony_ci if ((sscanf(p, "%u", &optimize) != 1) || (optimize > 1)) 109062306a36Sopenharmony_ci return -EINVAL; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci rcu_read_lock(); 109362306a36Sopenharmony_ci pg = rcu_dereference(h->pg); 109462306a36Sopenharmony_ci if (!pg) { 109562306a36Sopenharmony_ci rcu_read_unlock(); 109662306a36Sopenharmony_ci return -ENXIO; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci spin_lock_irqsave(&pg->lock, flags); 109962306a36Sopenharmony_ci if (optimize) 110062306a36Sopenharmony_ci pg->flags |= ALUA_OPTIMIZE_STPG; 110162306a36Sopenharmony_ci else 110262306a36Sopenharmony_ci pg->flags &= ~ALUA_OPTIMIZE_STPG; 110362306a36Sopenharmony_ci spin_unlock_irqrestore(&pg->lock, flags); 110462306a36Sopenharmony_ci rcu_read_unlock(); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci return result; 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci/* 111062306a36Sopenharmony_ci * alua_activate - activate a path 111162306a36Sopenharmony_ci * @sdev: device on the path to be activated 111262306a36Sopenharmony_ci * 111362306a36Sopenharmony_ci * We're currently switching the port group to be activated only and 111462306a36Sopenharmony_ci * let the array figure out the rest. 111562306a36Sopenharmony_ci * There may be other arrays which require us to switch all port groups 111662306a36Sopenharmony_ci * based on a certain policy. But until we actually encounter them it 111762306a36Sopenharmony_ci * should be okay. 111862306a36Sopenharmony_ci */ 111962306a36Sopenharmony_cistatic int alua_activate(struct scsi_device *sdev, 112062306a36Sopenharmony_ci activate_complete fn, void *data) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci struct alua_dh_data *h = sdev->handler_data; 112362306a36Sopenharmony_ci int err = SCSI_DH_OK; 112462306a36Sopenharmony_ci struct alua_queue_data *qdata; 112562306a36Sopenharmony_ci struct alua_port_group *pg; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci qdata = kzalloc(sizeof(*qdata), GFP_KERNEL); 112862306a36Sopenharmony_ci if (!qdata) { 112962306a36Sopenharmony_ci err = SCSI_DH_RES_TEMP_UNAVAIL; 113062306a36Sopenharmony_ci goto out; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci qdata->callback_fn = fn; 113362306a36Sopenharmony_ci qdata->callback_data = data; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci mutex_lock(&h->init_mutex); 113662306a36Sopenharmony_ci rcu_read_lock(); 113762306a36Sopenharmony_ci pg = rcu_dereference(h->pg); 113862306a36Sopenharmony_ci if (!pg || !kref_get_unless_zero(&pg->kref)) { 113962306a36Sopenharmony_ci rcu_read_unlock(); 114062306a36Sopenharmony_ci kfree(qdata); 114162306a36Sopenharmony_ci err = h->init_error; 114262306a36Sopenharmony_ci mutex_unlock(&h->init_mutex); 114362306a36Sopenharmony_ci goto out; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci rcu_read_unlock(); 114662306a36Sopenharmony_ci mutex_unlock(&h->init_mutex); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci if (alua_rtpg_queue(pg, sdev, qdata, true)) { 114962306a36Sopenharmony_ci fn = NULL; 115062306a36Sopenharmony_ci } else { 115162306a36Sopenharmony_ci kfree(qdata); 115262306a36Sopenharmony_ci err = SCSI_DH_DEV_OFFLINED; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci kref_put(&pg->kref, release_port_group); 115562306a36Sopenharmony_ciout: 115662306a36Sopenharmony_ci if (fn) 115762306a36Sopenharmony_ci fn(data, err); 115862306a36Sopenharmony_ci return 0; 115962306a36Sopenharmony_ci} 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci/* 116262306a36Sopenharmony_ci * alua_check - check path status 116362306a36Sopenharmony_ci * @sdev: device on the path to be checked 116462306a36Sopenharmony_ci * 116562306a36Sopenharmony_ci * Check the device status 116662306a36Sopenharmony_ci */ 116762306a36Sopenharmony_cistatic void alua_check(struct scsi_device *sdev, bool force) 116862306a36Sopenharmony_ci{ 116962306a36Sopenharmony_ci struct alua_dh_data *h = sdev->handler_data; 117062306a36Sopenharmony_ci struct alua_port_group *pg; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci rcu_read_lock(); 117362306a36Sopenharmony_ci pg = rcu_dereference(h->pg); 117462306a36Sopenharmony_ci if (!pg || !kref_get_unless_zero(&pg->kref)) { 117562306a36Sopenharmony_ci rcu_read_unlock(); 117662306a36Sopenharmony_ci return; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci rcu_read_unlock(); 117962306a36Sopenharmony_ci alua_rtpg_queue(pg, sdev, NULL, force); 118062306a36Sopenharmony_ci kref_put(&pg->kref, release_port_group); 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci/* 118462306a36Sopenharmony_ci * alua_prep_fn - request callback 118562306a36Sopenharmony_ci * 118662306a36Sopenharmony_ci * Fail I/O to all paths not in state 118762306a36Sopenharmony_ci * active/optimized or active/non-optimized. 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_cistatic blk_status_t alua_prep_fn(struct scsi_device *sdev, struct request *req) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci struct alua_dh_data *h = sdev->handler_data; 119262306a36Sopenharmony_ci struct alua_port_group *pg; 119362306a36Sopenharmony_ci unsigned char state = SCSI_ACCESS_STATE_OPTIMAL; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci rcu_read_lock(); 119662306a36Sopenharmony_ci pg = rcu_dereference(h->pg); 119762306a36Sopenharmony_ci if (pg) 119862306a36Sopenharmony_ci state = pg->state; 119962306a36Sopenharmony_ci rcu_read_unlock(); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci switch (state) { 120262306a36Sopenharmony_ci case SCSI_ACCESS_STATE_OPTIMAL: 120362306a36Sopenharmony_ci case SCSI_ACCESS_STATE_ACTIVE: 120462306a36Sopenharmony_ci case SCSI_ACCESS_STATE_LBA: 120562306a36Sopenharmony_ci case SCSI_ACCESS_STATE_TRANSITIONING: 120662306a36Sopenharmony_ci return BLK_STS_OK; 120762306a36Sopenharmony_ci default: 120862306a36Sopenharmony_ci req->rq_flags |= RQF_QUIET; 120962306a36Sopenharmony_ci return BLK_STS_IOERR; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic void alua_rescan(struct scsi_device *sdev) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct alua_dh_data *h = sdev->handler_data; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci alua_initialize(sdev, h); 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci/* 122162306a36Sopenharmony_ci * alua_bus_attach - Attach device handler 122262306a36Sopenharmony_ci * @sdev: device to be attached to 122362306a36Sopenharmony_ci */ 122462306a36Sopenharmony_cistatic int alua_bus_attach(struct scsi_device *sdev) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci struct alua_dh_data *h; 122762306a36Sopenharmony_ci int err; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci h = kzalloc(sizeof(*h) , GFP_KERNEL); 123062306a36Sopenharmony_ci if (!h) 123162306a36Sopenharmony_ci return SCSI_DH_NOMEM; 123262306a36Sopenharmony_ci spin_lock_init(&h->pg_lock); 123362306a36Sopenharmony_ci rcu_assign_pointer(h->pg, NULL); 123462306a36Sopenharmony_ci h->init_error = SCSI_DH_OK; 123562306a36Sopenharmony_ci h->sdev = sdev; 123662306a36Sopenharmony_ci INIT_LIST_HEAD(&h->node); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci mutex_init(&h->init_mutex); 123962306a36Sopenharmony_ci err = alua_initialize(sdev, h); 124062306a36Sopenharmony_ci if (err != SCSI_DH_OK && err != SCSI_DH_DEV_OFFLINED) 124162306a36Sopenharmony_ci goto failed; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci sdev->handler_data = h; 124462306a36Sopenharmony_ci return SCSI_DH_OK; 124562306a36Sopenharmony_cifailed: 124662306a36Sopenharmony_ci kfree(h); 124762306a36Sopenharmony_ci return err; 124862306a36Sopenharmony_ci} 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci/* 125162306a36Sopenharmony_ci * alua_bus_detach - Detach device handler 125262306a36Sopenharmony_ci * @sdev: device to be detached from 125362306a36Sopenharmony_ci */ 125462306a36Sopenharmony_cistatic void alua_bus_detach(struct scsi_device *sdev) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci struct alua_dh_data *h = sdev->handler_data; 125762306a36Sopenharmony_ci struct alua_port_group *pg; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci spin_lock(&h->pg_lock); 126062306a36Sopenharmony_ci pg = rcu_dereference_protected(h->pg, lockdep_is_held(&h->pg_lock)); 126162306a36Sopenharmony_ci rcu_assign_pointer(h->pg, NULL); 126262306a36Sopenharmony_ci spin_unlock(&h->pg_lock); 126362306a36Sopenharmony_ci if (pg) { 126462306a36Sopenharmony_ci spin_lock_irq(&pg->lock); 126562306a36Sopenharmony_ci list_del_rcu(&h->node); 126662306a36Sopenharmony_ci spin_unlock_irq(&pg->lock); 126762306a36Sopenharmony_ci kref_put(&pg->kref, release_port_group); 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci sdev->handler_data = NULL; 127062306a36Sopenharmony_ci synchronize_rcu(); 127162306a36Sopenharmony_ci kfree(h); 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic struct scsi_device_handler alua_dh = { 127562306a36Sopenharmony_ci .name = ALUA_DH_NAME, 127662306a36Sopenharmony_ci .module = THIS_MODULE, 127762306a36Sopenharmony_ci .attach = alua_bus_attach, 127862306a36Sopenharmony_ci .detach = alua_bus_detach, 127962306a36Sopenharmony_ci .prep_fn = alua_prep_fn, 128062306a36Sopenharmony_ci .check_sense = alua_check_sense, 128162306a36Sopenharmony_ci .activate = alua_activate, 128262306a36Sopenharmony_ci .rescan = alua_rescan, 128362306a36Sopenharmony_ci .set_params = alua_set_params, 128462306a36Sopenharmony_ci}; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_cistatic int __init alua_init(void) 128762306a36Sopenharmony_ci{ 128862306a36Sopenharmony_ci int r; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci kaluad_wq = alloc_workqueue("kaluad", WQ_MEM_RECLAIM, 0); 129162306a36Sopenharmony_ci if (!kaluad_wq) 129262306a36Sopenharmony_ci return -ENOMEM; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci r = scsi_register_device_handler(&alua_dh); 129562306a36Sopenharmony_ci if (r != 0) { 129662306a36Sopenharmony_ci printk(KERN_ERR "%s: Failed to register scsi device handler", 129762306a36Sopenharmony_ci ALUA_DH_NAME); 129862306a36Sopenharmony_ci destroy_workqueue(kaluad_wq); 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci return r; 130162306a36Sopenharmony_ci} 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_cistatic void __exit alua_exit(void) 130462306a36Sopenharmony_ci{ 130562306a36Sopenharmony_ci scsi_unregister_device_handler(&alua_dh); 130662306a36Sopenharmony_ci destroy_workqueue(kaluad_wq); 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cimodule_init(alua_init); 131062306a36Sopenharmony_cimodule_exit(alua_exit); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ciMODULE_DESCRIPTION("DM Multipath ALUA support"); 131362306a36Sopenharmony_ciMODULE_AUTHOR("Hannes Reinecke <hare@suse.de>"); 131462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 131562306a36Sopenharmony_ciMODULE_VERSION(ALUA_DH_VER); 1316