On Tue, 2013-12-17 at 09:18 +0100, Hannes Reinecke wrote:
> Add infrastructure for referrals.
>
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> drivers/target/target_core_alua.c | 153
> ++++++++++++++++++++++++++++++++++
> drivers/target/target_core_alua.h | 4 +-
> drivers/target/target_core_configfs.c | 9 +-
> drivers/target/target_core_device.c | 2 +
> drivers/target/target_core_sbc.c | 5 +-
> drivers/target/target_core_spc.c | 20 +++++
> include/scsi/scsi.h | 1 +
> include/target/target_core_base.h | 18 ++++
> 8 files changed, 209 insertions(+), 3 deletions(-)
>
Applied, with one comment below..
> diff --git a/drivers/target/target_core_alua.c
> b/drivers/target/target_core_alua.c
> index 01f0c71..dbfbf14 100644
> --- a/drivers/target/target_core_alua.c
> +++ b/drivers/target/target_core_alua.c
> @@ -58,6 +58,75 @@ static LIST_HEAD(lu_gps_list);
> struct t10_alua_lu_gp *default_lu_gp;
>
> /*
> + * REPORT REFERRALS
> + *
> + * See sbc3r35 section 5.23
> + */
> +sense_reason_t
> +target_emulate_report_referrals(struct se_cmd *cmd)
> +{
> + struct se_device *dev = cmd->se_dev;
> + struct t10_alua_lba_map *map;
> + struct t10_alua_lba_map_member *map_mem;
> + unsigned char *buf;
> + u32 rd_len = 0, off;
> +
> + if (cmd->data_length < 4) {
> + pr_warn("REPORT REFERRALS allocation length %u too"
> + " small\n", cmd->data_length);
> + return TCM_INVALID_CDB_FIELD;
> + }
> +
> + buf = transport_kmap_data_sg(cmd);
> + if (!buf)
> + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
> +
> + off = 4;
> + spin_lock(&dev->t10_alua.lba_map_lock);
> + if (list_empty(&dev->t10_alua.lba_map_list)) {
> + spin_unlock(&dev->t10_alua.lba_map_lock);
> + transport_kunmap_data_sg(cmd);
> +
> + return TCM_UNSUPPORTED_SCSI_OPCODE;
> + }
> +
> + list_for_each_entry(map, &dev->t10_alua.lba_map_list,
> + lba_map_list) {
> + int desc_num = off + 3;
> + int pg_num;
> +
> + off += 4;
> + put_unaligned_be64(map->lba_map_first_lba, &buf[off]);
> + off += 8;
> + put_unaligned_be64(map->lba_map_last_lba, &buf[off]);
> + off += 8;
> + rd_len += 20;
> + pg_num = 0;
> + list_for_each_entry(map_mem, &map->lba_map_mem_list,
> + lba_map_mem_list) {
> + buf[off++] = map_mem->lba_map_mem_alua_state & 0x0f;
> + off++;
> + buf[off++] = (map_mem->lba_map_mem_alua_pg_id >> 8) &
> 0xff;
> + buf[off++] = (map_mem->lba_map_mem_alua_pg_id & 0xff);
> + rd_len += 4;
> + pg_num++;
> + }
> + buf[desc_num] = pg_num;
> + }
> + spin_unlock(&dev->t10_alua.lba_map_lock);
> +
The above loop needs a check based on cmd->data_length to not overflow
buf here..
Care to send an incremental patch for this..?
--nab
> + /*
> + * Set the RETURN DATA LENGTH set in the header of the DataIN Payload
> + */
> + put_unaligned_be16(rd_len, &buf[2]);
> +
> + transport_kunmap_data_sg(cmd);
> +
> + target_complete_cmd(cmd, GOOD);
> + return 0;
> +}
> +
> +/*
> * REPORT_TARGET_PORT_GROUPS
> *
> * See spc4r17 section 6.27
> @@ -391,6 +460,80 @@ static inline int core_alua_state_nonoptimized(
> return 0;
> }
>
> +static inline int core_alua_state_lba_dependent(
> + struct se_cmd *cmd,
> + struct t10_alua_tg_pt_gp *tg_pt_gp,
> + u8 *alua_ascq)
> +{
> + struct se_device *dev = cmd->se_dev;
> + u32 segment_size, segment_mult, sectors;
> + u64 lba;
> +
> + /* Only need to check for cdb actually containing LBAs */
> + if (!cmd->se_cmd_flags & SCF_SCSI_DATA_CDB)
> + return 0;
> +
> + spin_lock(&dev->t10_alua.lba_map_lock);
> + segment_size = dev->t10_alua.lba_map_segment_size;
> + segment_mult = dev->t10_alua.lba_map_segment_multiplier;
> + sectors = cmd->data_length / dev->dev_attrib.block_size;
> +
> + lba = cmd->t_task_lba;
> + while (lba < cmd->t_task_lba + sectors) {
> + struct t10_alua_lba_map *cur_map = NULL, *map;
> + struct t10_alua_lba_map_member *map_mem;
> +
> + list_for_each_entry(map, &dev->t10_alua.lba_map_list,
> + lba_map_list) {
> + u64 start_lba, last_lba;
> + u64 first_lba = map->lba_map_first_lba;
> +
> + if (segment_mult) {
> + start_lba = lba % (segment_size * segment_mult);
> + last_lba = first_lba + segment_size - 1;
> + if (start_lba >= first_lba &&
> + start_lba <= last_lba) {
> + lba += segment_size;
> + cur_map = map;
> + break;
> + }
> + } else {
> + last_lba = map->lba_map_last_lba;
> + if (lba >= first_lba && lba <= last_lba) {
> + lba = last_lba + 1;
> + cur_map = map;
> + break;
> + }
> + }
> + }
> + if (!cur_map) {
> + spin_unlock(&dev->t10_alua.lba_map_lock);
> + *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
> + return 1;
> + }
> + list_for_each_entry(map_mem, &cur_map->lba_map_mem_list,
> + lba_map_mem_list) {
> + if (map_mem->lba_map_mem_alua_pg_id !=
> + tg_pt_gp->tg_pt_gp_id)
> + continue;
> + switch(map_mem->lba_map_mem_alua_state) {
> + case ALUA_ACCESS_STATE_STANDBY:
> + spin_unlock(&dev->t10_alua.lba_map_lock);
> + *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY;
> + return 1;
> + case ALUA_ACCESS_STATE_UNAVAILABLE:
> + spin_unlock(&dev->t10_alua.lba_map_lock);
> + *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE;
> + return 1;
> + default:
> + break;
> + }
> + }
> + }
> + spin_unlock(&dev->t10_alua.lba_map_lock);
> + return 0;
> +}
> +
> static inline int core_alua_state_standby(
> struct se_cmd *cmd,
> unsigned char *cdb,
> @@ -588,6 +731,9 @@ target_alua_state_check(struct se_cmd *cmd)
> case ALUA_ACCESS_STATE_TRANSITION:
> ret = core_alua_state_transition(cmd, cdb, &alua_ascq);
> break;
> + case ALUA_ACCESS_STATE_LBA_DEPENDENT:
> + ret = core_alua_state_lba_dependent(cmd, tg_pt_gp, &alua_ascq);
> + break;
> /*
> * OFFLINE is a secondary ALUA target port group access state, that is
> * handled above with struct se_port->sep_tg_pt_secondary_offline=1
> @@ -650,6 +796,11 @@ core_alua_check_transition(int state, int valid, int
> *primary)
> goto not_supported;
> *primary = 1;
> break;
> + case ALUA_ACCESS_STATE_LBA_DEPENDENT:
> + if (!(valid & ALUA_LBD_SUP))
> + goto not_supported;
> + *primary = 1;
> + break;
> case ALUA_ACCESS_STATE_OFFLINE:
> /*
> * OFFLINE state is defined as a secondary target port
> @@ -685,6 +836,8 @@ static char *core_alua_dump_state(int state)
> return "Active/Optimized";
> case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED:
> return "Active/NonOptimized";
> + case ALUA_ACCESS_STATE_LBA_DEPENDENT:
> + return "LBA Dependent";
> case ALUA_ACCESS_STATE_STANDBY:
> return "Standby";
> case ALUA_ACCESS_STATE_UNAVAILABLE:
> diff --git a/drivers/target/target_core_alua.h
> b/drivers/target/target_core_alua.h
> index 1a152cd..47950cd 100644
> --- a/drivers/target/target_core_alua.h
> +++ b/drivers/target/target_core_alua.h
> @@ -13,12 +13,13 @@
> /*
> * ASYMMETRIC ACCESS STATE field
> *
> - * from spc4r17 section 6.27 Table 245
> + * from spc4r36j section 6.37 Table 307
> */
> #define ALUA_ACCESS_STATE_ACTIVE_OPTIMIZED 0x0
> #define ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED 0x1
> #define ALUA_ACCESS_STATE_STANDBY 0x2
> #define ALUA_ACCESS_STATE_UNAVAILABLE 0x3
> +#define ALUA_ACCESS_STATE_LBA_DEPENDENT 0x4
> #define ALUA_ACCESS_STATE_OFFLINE 0xe
> #define ALUA_ACCESS_STATE_TRANSITION 0xf
>
> @@ -88,6 +89,7 @@ extern struct kmem_cache *t10_alua_tg_pt_gp_mem_cache;
>
> extern sense_reason_t target_emulate_report_target_port_groups(struct se_cmd
> *);
> extern sense_reason_t target_emulate_set_target_port_groups(struct se_cmd *);
> +extern sense_reason_t target_emulate_report_referrals(struct se_cmd *);
> extern int core_alua_check_nonop_delay(struct se_cmd *);
> extern int core_alua_do_port_transition(struct t10_alua_tg_pt_gp *,
> struct se_device *, struct se_port *,
> diff --git a/drivers/target/target_core_configfs.c
> b/drivers/target/target_core_configfs.c
> index 272755d..e74ef8c 100644
> --- a/drivers/target/target_core_configfs.c
> +++ b/drivers/target/target_core_configfs.c
> @@ -2054,6 +2054,13 @@ static ssize_t
> target_core_alua_tg_pt_gp_store_attr_alua_access_state(
> " transition while TPGS_IMPLICIT_ALUA is disabled\n");
> return -EINVAL;
> }
> + if (tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_EXPLICIT_ALUA &&
> + new_state == ALUA_ACCESS_STATE_LBA_DEPENDENT) {
> + /* LBA DEPENDENT is only allowed with implicit ALUA */
> + pr_err("Unable to process implicit configfs ALUA transition"
> + " while explicit ALUA management is enabled\n");
> + return -EINVAL;
> + }
>
> ret = core_alua_do_port_transition(tg_pt_gp, dev,
> NULL, NULL, new_state, 0);
> @@ -2188,7 +2195,7 @@ SE_DEV_ALUA_SUPPORT_STATE_SHOW(lba_dependent,
> tg_pt_gp_alua_supported_states, ALUA_LBD_SUP);
> SE_DEV_ALUA_SUPPORT_STATE_STORE(lba_dependent,
> tg_pt_gp_alua_supported_states, ALUA_LBD_SUP);
> -SE_DEV_ALUA_TG_PT_ATTR(alua_support_lba_dependent, S_IRUGO | S_IWUSR);
> +SE_DEV_ALUA_TG_PT_ATTR(alua_support_lba_dependent, S_IRUGO);
>
> SE_DEV_ALUA_SUPPORT_STATE_SHOW(unavailable,
> tg_pt_gp_alua_supported_states, ALUA_U_SUP);
> diff --git a/drivers/target/target_core_device.c
> b/drivers/target/target_core_device.c
> index 207b340..3c08f99 100644
> --- a/drivers/target/target_core_device.c
> +++ b/drivers/target/target_core_device.c
> @@ -1439,6 +1439,8 @@ struct se_device *target_alloc_device(struct se_hba
> *hba, const char *name)
> spin_lock_init(&dev->t10_pr.aptpl_reg_lock);
> INIT_LIST_HEAD(&dev->t10_alua.tg_pt_gps_list);
> spin_lock_init(&dev->t10_alua.tg_pt_gps_lock);
> + INIT_LIST_HEAD(&dev->t10_alua.lba_map_list);
> + spin_lock_init(&dev->t10_alua.lba_map_lock);
>
> dev->t10_wwn.t10_dev = dev;
> dev->t10_alua.t10_dev = dev;
> diff --git a/drivers/target/target_core_sbc.c
> b/drivers/target/target_core_sbc.c
> index 52ae54e..6863dbe 100644
> --- a/drivers/target/target_core_sbc.c
> +++ b/drivers/target/target_core_sbc.c
> @@ -33,7 +33,7 @@
>
> #include "target_core_internal.h"
> #include "target_core_ua.h"
> -
> +#include "target_core_alua.h"
>
> static sense_reason_t
> sbc_emulate_readcapacity(struct se_cmd *cmd)
> @@ -731,6 +731,9 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
> case SAI_READ_CAPACITY_16:
> cmd->execute_cmd = sbc_emulate_readcapacity_16;
> break;
> + case SAI_REPORT_REFERRALS:
> + cmd->execute_cmd = target_emulate_report_referrals;
> + break;
> default:
> pr_err("Unsupported SA: 0x%02x\n",
> cmd->t_task_cdb[1] & 0x1f);
> diff --git a/drivers/target/target_core_spc.c
> b/drivers/target/target_core_spc.c
> index 39054d9..f9889fd 100644
> --- a/drivers/target/target_core_spc.c
> +++ b/drivers/target/target_core_spc.c
> @@ -476,6 +476,11 @@ spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char
> *buf)
> /* If WriteCache emulation is enabled, set V_SUP */
> if (spc_check_dev_wce(dev))
> buf[6] = 0x01;
> + /* If an LBA map is present set R_SUP */
> + spin_lock(&cmd->se_dev->t10_alua.lba_map_lock);
> + if (!list_empty(&dev->t10_alua.lba_map_list))
> + buf[8] = 0x10;
> + spin_unlock(&cmd->se_dev->t10_alua.lba_map_lock);
> return 0;
> }
>
> @@ -634,6 +639,20 @@ spc_emulate_evpd_b2(struct se_cmd *cmd, unsigned char
> *buf)
> return 0;
> }
>
> +/* Referrals VPD page */
> +static sense_reason_t
> +spc_emulate_evpd_b3(struct se_cmd *cmd, unsigned char *buf)
> +{
> + struct se_device *dev = cmd->se_dev;
> +
> + buf[0] = dev->transport->get_device_type(dev);
> + buf[3] = 0x0c;
> + put_unaligned_be32(dev->t10_alua.lba_map_segment_size, &buf[8]);
> + put_unaligned_be32(dev->t10_alua.lba_map_segment_size, &buf[12]);
> +
> + return 0;
> +}
> +
> static sense_reason_t
> spc_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf);
>
> @@ -648,6 +667,7 @@ static struct {
> { .page = 0xb0, .emulate = spc_emulate_evpd_b0 },
> { .page = 0xb1, .emulate = spc_emulate_evpd_b1 },
> { .page = 0xb2, .emulate = spc_emulate_evpd_b2 },
> + { .page = 0xb3, .emulate = spc_emulate_evpd_b3 },
> };
>
> /* supported vital product data pages */
> diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
> index 66d42ed..0a4edfe 100644
> --- a/include/scsi/scsi.h
> +++ b/include/scsi/scsi.h
> @@ -155,6 +155,7 @@ enum scsi_timeouts {
> /* values for service action in */
> #define SAI_READ_CAPACITY_16 0x10
> #define SAI_GET_LBA_STATUS 0x12
> +#define SAI_REPORT_REFERRALS 0x13
> /* values for VARIABLE_LENGTH_CMD service action codes
> * see spc4r17 Section D.3.5, table D.7 and D.8 */
> #define VLC_SA_RECEIVE_CREDENTIAL 0x1800
> diff --git a/include/target/target_core_base.h
> b/include/target/target_core_base.h
> index 65390f6..766421b 100644
> --- a/include/target/target_core_base.h
> +++ b/include/target/target_core_base.h
> @@ -247,10 +247,28 @@ typedef enum {
>
> struct se_cmd;
>
> +struct t10_alua_lba_map_member {
> + struct list_head lba_map_mem_list;
> + int lba_map_mem_alua_state;
> + int lba_map_mem_alua_pg_id;
> +};
> +
> +struct t10_alua_lba_map {
> + u64 lba_map_first_lba;
> + u64 lba_map_last_lba;
> + struct list_head lba_map_list;
> + struct list_head lba_map_mem_list;
> +};
> +
> struct t10_alua {
> /* ALUA Target Port Group ID */
> u16 alua_tg_pt_gps_counter;
> u32 alua_tg_pt_gps_count;
> + /* Referrals support */
> + spinlock_t lba_map_lock;
> + u32 lba_map_segment_size;
> + u32 lba_map_segment_multiplier;
> + struct list_head lba_map_list;
> spinlock_t tg_pt_gps_lock;
> struct se_device *t10_dev;
> /* Used for default ALUA Target Port Group */
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html