Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/target/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 100 kB image not shown  

Quelle  target_core_configfs.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*******************************************************************************
 * Filename:  target_core_configfs.c
 *
 * This file contains ConfigFS logic for the Generic Target Engine project.
 *
 * (c) Copyright 2008-2013 Datera, Inc.
 *
 * Nicholas A. Bellinger <nab@kernel.org>
 *
 * based on configfs Copyright (C) 2005 Oracle.  All rights reserved.
 *
 ****************************************************************************/


#include <linux/kstrtox.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <generated/utsrelease.h>
#include <linux/utsname.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/parser.h>
#include <linux/syscalls.h>
#include <linux/configfs.h>
#include <linux/spinlock.h>

#include <target/target_core_base.h>
#include <target/target_core_backend.h>
#include <target/target_core_fabric.h>

#include "target_core_internal.h"
#include "target_core_alua.h"
#include "target_core_pr.h"
#include "target_core_rd.h"
#include "target_core_xcopy.h"

#define TB_CIT_SETUP(_name, _item_ops, _group_ops, _attrs)  \
static void target_core_setup_##_name##_cit(struct target_backend *tb) \
{         \
 struct config_item_type *cit = &tb->tb_##_name##_cit;  \
         \
 cit->ct_item_ops = _item_ops;     \
 cit->ct_group_ops = _group_ops;     \
 cit->ct_attrs = _attrs;      \
 cit->ct_owner = tb->ops->owner;     \
 pr_debug("Setup generic %s\n", __stringify(_name));  \
}

#define TB_CIT_SETUP_DRV(_name, _item_ops, _group_ops)   \
static void target_core_setup_##_name##_cit(struct target_backend *tb) \
{         \
 struct config_item_type *cit = &tb->tb_##_name##_cit;  \
         \
 cit->ct_item_ops = _item_ops;     \
 cit->ct_group_ops = _group_ops;     \
 cit->ct_attrs = tb->ops->tb_##_name##_attrs;   \
 cit->ct_owner = tb->ops->owner;     \
 pr_debug("Setup generic %s\n", __stringify(_name));  \
}

extern struct t10_alua_lu_gp *default_lu_gp;

static LIST_HEAD(g_tf_list);
static DEFINE_MUTEX(g_tf_lock);

static struct config_group target_core_hbagroup;
static struct config_group alua_group;
static struct config_group alua_lu_gps_group;

static unsigned int target_devices;
static DEFINE_MUTEX(target_devices_lock);

static inline struct se_hba *
item_to_hba(struct config_item *item)
{
 return container_of(to_config_group(item), struct se_hba, hba_group);
}

/*
 * Attributes for /sys/kernel/config/target/
 */

static ssize_t target_core_item_version_show(struct config_item *item,
  char *page)
{
 return sprintf(page, "Target Engine Core ConfigFS Infrastructure %s"
  " on %s/%s on "UTS_RELEASE"\n", TARGET_CORE_VERSION,
  utsname()->sysname, utsname()->machine);
}

CONFIGFS_ATTR_RO(target_core_item_, version);

char db_root[DB_ROOT_LEN] = DB_ROOT_DEFAULT;
static char db_root_stage[DB_ROOT_LEN];

static ssize_t target_core_item_dbroot_show(struct config_item *item,
         char *page)
{
 return sprintf(page, "%s\n", db_root);
}

static ssize_t target_core_item_dbroot_store(struct config_item *item,
     const char *page, size_t count)
{
 ssize_t read_bytes;
 struct file *fp;
 ssize_t r = -EINVAL;

 mutex_lock(&target_devices_lock);
 if (target_devices) {
  pr_err("db_root: cannot be changed because it's in use\n");
  goto unlock;
 }

 if (count > (DB_ROOT_LEN - 1)) {
  pr_err("db_root: count %d exceeds DB_ROOT_LEN-1: %u\n",
         (int)count, DB_ROOT_LEN - 1);
  goto unlock;
 }

 read_bytes = scnprintf(db_root_stage, DB_ROOT_LEN, "%s", page);
 if (!read_bytes)
  goto unlock;

 if (db_root_stage[read_bytes - 1] == '\n')
  db_root_stage[read_bytes - 1] = '\0';

 /* validate new db root before accepting it */
 fp = filp_open(db_root_stage, O_RDONLY, 0);
 if (IS_ERR(fp)) {
  pr_err("db_root: cannot open: %s\n", db_root_stage);
  goto unlock;
 }
 if (!S_ISDIR(file_inode(fp)->i_mode)) {
  filp_close(fp, NULL);
  pr_err("db_root: not a directory: %s\n", db_root_stage);
  goto unlock;
 }
 filp_close(fp, NULL);

 strscpy(db_root, db_root_stage);
 pr_debug("Target_Core_ConfigFS: db_root set to %s\n", db_root);

 r = read_bytes;

unlock:
 mutex_unlock(&target_devices_lock);
 return r;
}

CONFIGFS_ATTR(target_core_item_, dbroot);

static struct target_fabric_configfs *target_core_get_fabric(
 const char *name)
{
 struct target_fabric_configfs *tf;

 if (!name)
  return NULL;

 mutex_lock(&g_tf_lock);
 list_for_each_entry(tf, &g_tf_list, tf_list) {
  const char *cmp_name = tf->tf_ops->fabric_alias;
  if (!cmp_name)
   cmp_name = tf->tf_ops->fabric_name;
  if (!strcmp(cmp_name, name)) {
   atomic_inc(&tf->tf_access_cnt);
   mutex_unlock(&g_tf_lock);
   return tf;
  }
 }
 mutex_unlock(&g_tf_lock);

 return NULL;
}

/*
 * Called from struct target_core_group_ops->make_group()
 */

static struct config_group *target_core_register_fabric(
 struct config_group *group,
 const char *name)
{
 struct target_fabric_configfs *tf;
 int ret;

 pr_debug("Target_Core_ConfigFS: REGISTER -> group: %p name:"
   " %s\n", group, name);

 tf = target_core_get_fabric(name);
 if (!tf) {
  pr_debug("target_core_register_fabric() trying autoload for %s\n",
    name);

  /*
 * Below are some hardcoded request_module() calls to automatically
 * local fabric modules when the following is called:
 *
 * mkdir -p /sys/kernel/config/target/$MODULE_NAME
 *
 * Note that this does not limit which TCM fabric module can be
 * registered, but simply provids auto loading logic for modules with
 * mkdir(2) system calls with known TCM fabric modules.
 */


  if (!strncmp(name, "iscsi", 5)) {
   /*
 * Automatically load the LIO Target fabric module when the
 * following is called:
 *
 * mkdir -p $CONFIGFS/target/iscsi
 */

   ret = request_module("iscsi_target_mod");
   if (ret < 0) {
    pr_debug("request_module() failed for"
             " iscsi_target_mod.ko: %d\n", ret);
    return ERR_PTR(-EINVAL);
   }
  } else if (!strncmp(name, "loopback", 8)) {
   /*
 * Automatically load the tcm_loop fabric module when the
 * following is called:
 *
 * mkdir -p $CONFIGFS/target/loopback
 */

   ret = request_module("tcm_loop");
   if (ret < 0) {
    pr_debug("request_module() failed for"
             " tcm_loop.ko: %d\n", ret);
    return ERR_PTR(-EINVAL);
   }
  }

  tf = target_core_get_fabric(name);
 }

 if (!tf) {
  pr_debug("target_core_get_fabric() failed for %s\n",
           name);
  return ERR_PTR(-EINVAL);
 }
 pr_debug("Target_Core_ConfigFS: REGISTER -> Located fabric:"
   " %s\n", tf->tf_ops->fabric_name);
 /*
 * On a successful target_core_get_fabric() look, the returned
 * struct target_fabric_configfs *tf will contain a usage reference.
 */

 pr_debug("Target_Core_ConfigFS: REGISTER tfc_wwn_cit -> %p\n",
   &tf->tf_wwn_cit);

 config_group_init_type_name(&tf->tf_group, name, &tf->tf_wwn_cit);

 config_group_init_type_name(&tf->tf_disc_group, "discovery_auth",
   &tf->tf_discovery_cit);
 configfs_add_default_group(&tf->tf_disc_group, &tf->tf_group);

 pr_debug("Target_Core_ConfigFS: REGISTER -> Allocated Fabric: %s\n",
   config_item_name(&tf->tf_group.cg_item));
 return &tf->tf_group;
}

/*
 * Called from struct target_core_group_ops->drop_item()
 */

static void target_core_deregister_fabric(
 struct config_group *group,
 struct config_item *item)
{
 struct target_fabric_configfs *tf = container_of(
  to_config_group(item), struct target_fabric_configfs, tf_group);

 pr_debug("Target_Core_ConfigFS: DEREGISTER -> Looking up %s in"
  " tf list\n", config_item_name(item));

 pr_debug("Target_Core_ConfigFS: DEREGISTER -> located fabric:"
   " %s\n", tf->tf_ops->fabric_name);
 atomic_dec(&tf->tf_access_cnt);

 pr_debug("Target_Core_ConfigFS: DEREGISTER -> Releasing ci"
   " %s\n", config_item_name(item));

 configfs_remove_default_groups(&tf->tf_group);
 config_item_put(item);
}

static struct configfs_group_operations target_core_fabric_group_ops = {
 .make_group = &target_core_register_fabric,
 .drop_item = &target_core_deregister_fabric,
};

/*
 * All item attributes appearing in /sys/kernel/target/ appear here.
 */

static struct configfs_attribute *target_core_fabric_item_attrs[] = {
 &target_core_item_attr_version,
 &target_core_item_attr_dbroot,
 NULL,
};

/*
 * Provides Fabrics Groups and Item Attributes for /sys/kernel/config/target/
 */

static const struct config_item_type target_core_fabrics_item = {
 .ct_group_ops = &target_core_fabric_group_ops,
 .ct_attrs = target_core_fabric_item_attrs,
 .ct_owner = THIS_MODULE,
};

static struct configfs_subsystem target_core_fabrics = {
 .su_group = {
  .cg_item = {
   .ci_namebuf = "target",
   .ci_type = &target_core_fabrics_item,
  },
 },
};

int target_depend_item(struct config_item *item)
{
 return configfs_depend_item(&target_core_fabrics, item);
}
EXPORT_SYMBOL(target_depend_item);

void target_undepend_item(struct config_item *item)
{
 return configfs_undepend_item(item);
}
EXPORT_SYMBOL(target_undepend_item);

/*##############################################################################
// Start functions called by external Target Fabrics Modules
//############################################################################*/

static int target_disable_feature(struct se_portal_group *se_tpg)
{
 return 0;
}

static u32 target_default_get_inst_index(struct se_portal_group *se_tpg)
{
 return 1;
}

static u32 target_default_sess_get_index(struct se_session *se_sess)
{
 return 0;
}

static void target_set_default_node_attributes(struct se_node_acl *se_acl)
{
}

static int target_default_get_cmd_state(struct se_cmd *se_cmd)
{
 return 0;
}

static int target_fabric_tf_ops_check(const struct target_core_fabric_ops *tfo)
{
 if (tfo->fabric_alias) {
  if (strlen(tfo->fabric_alias) >= TARGET_FABRIC_NAME_SIZE) {
   pr_err("Passed alias: %s exceeds "
    "TARGET_FABRIC_NAME_SIZE\n", tfo->fabric_alias);
   return -EINVAL;
  }
 }
 if (!tfo->fabric_name) {
  pr_err("Missing tfo->fabric_name\n");
  return -EINVAL;
 }
 if (strlen(tfo->fabric_name) >= TARGET_FABRIC_NAME_SIZE) {
  pr_err("Passed name: %s exceeds "
   "TARGET_FABRIC_NAME_SIZE\n", tfo->fabric_name);
  return -EINVAL;
 }
 if (!tfo->tpg_get_wwn) {
  pr_err("Missing tfo->tpg_get_wwn()\n");
  return -EINVAL;
 }
 if (!tfo->tpg_get_tag) {
  pr_err("Missing tfo->tpg_get_tag()\n");
  return -EINVAL;
 }
 if (!tfo->release_cmd) {
  pr_err("Missing tfo->release_cmd()\n");
  return -EINVAL;
 }
 if (!tfo->write_pending) {
  pr_err("Missing tfo->write_pending()\n");
  return -EINVAL;
 }
 if (!tfo->queue_data_in) {
  pr_err("Missing tfo->queue_data_in()\n");
  return -EINVAL;
 }
 if (!tfo->queue_status) {
  pr_err("Missing tfo->queue_status()\n");
  return -EINVAL;
 }
 if (!tfo->queue_tm_rsp) {
  pr_err("Missing tfo->queue_tm_rsp()\n");
  return -EINVAL;
 }
 if (!tfo->aborted_task) {
  pr_err("Missing tfo->aborted_task()\n");
  return -EINVAL;
 }
 if (!tfo->check_stop_free) {
  pr_err("Missing tfo->check_stop_free()\n");
  return -EINVAL;
 }
 /*
 * We at least require tfo->fabric_make_wwn(), tfo->fabric_drop_wwn()
 * tfo->fabric_make_tpg() and tfo->fabric_drop_tpg() in
 * target_core_fabric_configfs.c WWN+TPG group context code.
 */

 if (!tfo->fabric_make_wwn) {
  pr_err("Missing tfo->fabric_make_wwn()\n");
  return -EINVAL;
 }
 if (!tfo->fabric_drop_wwn) {
  pr_err("Missing tfo->fabric_drop_wwn()\n");
  return -EINVAL;
 }
 if (!tfo->fabric_make_tpg) {
  pr_err("Missing tfo->fabric_make_tpg()\n");
  return -EINVAL;
 }
 if (!tfo->fabric_drop_tpg) {
  pr_err("Missing tfo->fabric_drop_tpg()\n");
  return -EINVAL;
 }

 return 0;
}

static void target_set_default_ops(struct target_core_fabric_ops *tfo)
{
 if (!tfo->tpg_check_demo_mode)
  tfo->tpg_check_demo_mode = target_disable_feature;

 if (!tfo->tpg_check_demo_mode_cache)
  tfo->tpg_check_demo_mode_cache = target_disable_feature;

 if (!tfo->tpg_check_demo_mode_write_protect)
  tfo->tpg_check_demo_mode_write_protect = target_disable_feature;

 if (!tfo->tpg_check_prod_mode_write_protect)
  tfo->tpg_check_prod_mode_write_protect = target_disable_feature;

 if (!tfo->tpg_get_inst_index)
  tfo->tpg_get_inst_index = target_default_get_inst_index;

 if (!tfo->sess_get_index)
  tfo->sess_get_index = target_default_sess_get_index;

 if (!tfo->set_default_node_attributes)
  tfo->set_default_node_attributes = target_set_default_node_attributes;

 if (!tfo->get_cmd_state)
  tfo->get_cmd_state = target_default_get_cmd_state;
}

int target_register_template(const struct target_core_fabric_ops *fo)
{
 struct target_core_fabric_ops *tfo;
 struct target_fabric_configfs *tf;
 int ret;

 ret = target_fabric_tf_ops_check(fo);
 if (ret)
  return ret;

 tf = kzalloc(sizeof(struct target_fabric_configfs), GFP_KERNEL);
 if (!tf) {
  pr_err("%s: could not allocate memory!\n", __func__);
  return -ENOMEM;
 }
 tfo = kzalloc(sizeof(struct target_core_fabric_ops), GFP_KERNEL);
 if (!tfo) {
  kfree(tf);
  pr_err("%s: could not allocate memory!\n", __func__);
  return -ENOMEM;
 }
 memcpy(tfo, fo, sizeof(*tfo));
 target_set_default_ops(tfo);

 INIT_LIST_HEAD(&tf->tf_list);
 atomic_set(&tf->tf_access_cnt, 0);
 tf->tf_ops = tfo;
 target_fabric_setup_cits(tf);

 mutex_lock(&g_tf_lock);
 list_add_tail(&tf->tf_list, &g_tf_list);
 mutex_unlock(&g_tf_lock);

 return 0;
}
EXPORT_SYMBOL(target_register_template);

void target_unregister_template(const struct target_core_fabric_ops *fo)
{
 struct target_fabric_configfs *t;

 mutex_lock(&g_tf_lock);
 list_for_each_entry(t, &g_tf_list, tf_list) {
  if (!strcmp(t->tf_ops->fabric_name, fo->fabric_name)) {
   BUG_ON(atomic_read(&t->tf_access_cnt));
   list_del(&t->tf_list);
   mutex_unlock(&g_tf_lock);
   /*
 * Wait for any outstanding fabric se_deve_entry->rcu_head
 * callbacks to complete post kfree_rcu(), before allowing
 * fabric driver unload of TFO->module to proceed.
 */

   rcu_barrier();
   kfree(t->tf_tpg_base_cit.ct_attrs);
   kfree(t->tf_ops);
   kfree(t);
   return;
  }
 }
 mutex_unlock(&g_tf_lock);
}
EXPORT_SYMBOL(target_unregister_template);

/*##############################################################################
// Stop functions called by external Target Fabrics Modules
//############################################################################*/


static inline struct se_dev_attrib *to_attrib(struct config_item *item)
{
 return container_of(to_config_group(item), struct se_dev_attrib,
   da_group);
}

/* Start functions for struct config_item_type tb_dev_attrib_cit */
#define DEF_CONFIGFS_ATTRIB_SHOW(_name)     \
static ssize_t _name##_show(struct config_item *item, char *page) \
{         \
 return snprintf(page, PAGE_SIZE, "%u\n", to_attrib(item)->_name); \
}

DEF_CONFIGFS_ATTRIB_SHOW(emulate_model_alias);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_dpo);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_fua_write);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_fua_read);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_write_cache);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_ua_intlck_ctrl);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_tas);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_tpu);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_tpws);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_caw);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_3pc);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_pr);
DEF_CONFIGFS_ATTRIB_SHOW(pi_prot_type);
DEF_CONFIGFS_ATTRIB_SHOW(hw_pi_prot_type);
DEF_CONFIGFS_ATTRIB_SHOW(pi_prot_verify);
DEF_CONFIGFS_ATTRIB_SHOW(enforce_pr_isids);
DEF_CONFIGFS_ATTRIB_SHOW(is_nonrot);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_rest_reord);
DEF_CONFIGFS_ATTRIB_SHOW(force_pr_aptpl);
DEF_CONFIGFS_ATTRIB_SHOW(hw_block_size);
DEF_CONFIGFS_ATTRIB_SHOW(block_size);
DEF_CONFIGFS_ATTRIB_SHOW(hw_max_sectors);
DEF_CONFIGFS_ATTRIB_SHOW(optimal_sectors);
DEF_CONFIGFS_ATTRIB_SHOW(hw_queue_depth);
DEF_CONFIGFS_ATTRIB_SHOW(queue_depth);
DEF_CONFIGFS_ATTRIB_SHOW(max_unmap_lba_count);
DEF_CONFIGFS_ATTRIB_SHOW(max_unmap_block_desc_count);
DEF_CONFIGFS_ATTRIB_SHOW(unmap_granularity);
DEF_CONFIGFS_ATTRIB_SHOW(unmap_granularity_alignment);
DEF_CONFIGFS_ATTRIB_SHOW(unmap_zeroes_data);
DEF_CONFIGFS_ATTRIB_SHOW(max_write_same_len);
DEF_CONFIGFS_ATTRIB_SHOW(emulate_rsoc);
DEF_CONFIGFS_ATTRIB_SHOW(submit_type);

#define DEF_CONFIGFS_ATTRIB_STORE_U32(_name)    \
static ssize_t _name##_store(struct config_item *item, const char *page,\
  size_t count)      \
{         \
 struct se_dev_attrib *da = to_attrib(item);   \
 u32 val;       \
 int ret;       \
         \
 ret = kstrtou32(page, 0, &val);     \
 if (ret < 0)       \
  return ret;      \
 da->_name = val;      \
 return count;       \
}

DEF_CONFIGFS_ATTRIB_STORE_U32(max_unmap_lba_count);
DEF_CONFIGFS_ATTRIB_STORE_U32(max_unmap_block_desc_count);
DEF_CONFIGFS_ATTRIB_STORE_U32(unmap_granularity);
DEF_CONFIGFS_ATTRIB_STORE_U32(unmap_granularity_alignment);
DEF_CONFIGFS_ATTRIB_STORE_U32(max_write_same_len);

#define DEF_CONFIGFS_ATTRIB_STORE_BOOL(_name)    \
static ssize_t _name##_store(struct config_item *item, const char *page, \
  size_t count)      \
{         \
 struct se_dev_attrib *da = to_attrib(item);   \
 bool flag;       \
 int ret;       \
         \
 ret = kstrtobool(page, &flag);     \
 if (ret < 0)       \
  return ret;      \
 da->_name = flag;      \
 return count;       \
}

DEF_CONFIGFS_ATTRIB_STORE_BOOL(emulate_fua_write);
DEF_CONFIGFS_ATTRIB_STORE_BOOL(emulate_caw);
DEF_CONFIGFS_ATTRIB_STORE_BOOL(emulate_3pc);
DEF_CONFIGFS_ATTRIB_STORE_BOOL(emulate_pr);
DEF_CONFIGFS_ATTRIB_STORE_BOOL(enforce_pr_isids);
DEF_CONFIGFS_ATTRIB_STORE_BOOL(is_nonrot);

#define DEF_CONFIGFS_ATTRIB_STORE_STUB(_name)    \
static ssize_t _name##_store(struct config_item *item, const char *page,\
  size_t count)      \
{         \
 printk_once(KERN_WARNING     \
  "ignoring deprecated %s attribute\n",   \
  __stringify(_name));     \
 return count;       \
}

DEF_CONFIGFS_ATTRIB_STORE_STUB(emulate_dpo);
DEF_CONFIGFS_ATTRIB_STORE_STUB(emulate_fua_read);

static void dev_set_t10_wwn_model_alias(struct se_device *dev)
{
 const char *configname;

 configname = config_item_name(&dev->dev_group.cg_item);
 if (strlen(configname) >= INQUIRY_MODEL_LEN) {
  pr_warn("dev[%p]: Backstore name '%s' is too long for "
   "INQUIRY_MODEL, truncating to 15 characters\n", dev,
   configname);
 }
 /*
 * XXX We can't use sizeof(dev->t10_wwn.model) (INQUIRY_MODEL_LEN + 1)
 * here without potentially breaking existing setups, so continue to
 * truncate one byte shorter than what can be carried in INQUIRY.
 */

 strscpy(dev->t10_wwn.model, configname, INQUIRY_MODEL_LEN);
}

static ssize_t emulate_model_alias_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 struct se_device *dev = da->da_dev;
 bool flag;
 int ret;

 if (dev->export_count) {
  pr_err("dev[%p]: Unable to change model alias"
   " while export_count is %d\n",
   dev, dev->export_count);
  return -EINVAL;
 }

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 BUILD_BUG_ON(sizeof(dev->t10_wwn.model) != INQUIRY_MODEL_LEN + 1);
 if (flag)
  dev_set_t10_wwn_model_alias(dev);
 else
  strscpy(dev->t10_wwn.model, dev->transport->inquiry_prod);
 da->emulate_model_alias = flag;
 return count;
}

static ssize_t emulate_write_cache_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 bool flag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 if (flag && da->da_dev->transport->get_write_cache) {
  pr_err("emulate_write_cache not supported for this device\n");
  return -EINVAL;
 }

 da->emulate_write_cache = flag;
 pr_debug("dev[%p]: SE Device WRITE_CACHE_EMULATION flag: %d\n",
   da->da_dev, flag);
 return count;
}

static ssize_t emulate_ua_intlck_ctrl_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 u32 val;
 int ret;

 ret = kstrtou32(page, 0, &val);
 if (ret < 0)
  return ret;

 if (val != TARGET_UA_INTLCK_CTRL_CLEAR
  && val != TARGET_UA_INTLCK_CTRL_NO_CLEAR
  && val != TARGET_UA_INTLCK_CTRL_ESTABLISH_UA) {
  pr_err("Illegal value %d\n", val);
  return -EINVAL;
 }

 if (da->da_dev->export_count) {
  pr_err("dev[%p]: Unable to change SE Device"
   " UA_INTRLCK_CTRL while export_count is %d\n",
   da->da_dev, da->da_dev->export_count);
  return -EINVAL;
 }
 da->emulate_ua_intlck_ctrl = val;
 pr_debug("dev[%p]: SE Device UA_INTRLCK_CTRL flag: %d\n",
  da->da_dev, val);
 return count;
}

static ssize_t emulate_tas_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 bool flag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 if (da->da_dev->export_count) {
  pr_err("dev[%p]: Unable to change SE Device TAS while"
   " export_count is %d\n",
   da->da_dev, da->da_dev->export_count);
  return -EINVAL;
 }
 da->emulate_tas = flag;
 pr_debug("dev[%p]: SE Device TASK_ABORTED status bit: %s\n",
  da->da_dev, flag ? "Enabled" : "Disabled");

 return count;
}

static int target_try_configure_unmap(struct se_device *dev,
          const char *config_opt)
{
 if (!dev->transport->configure_unmap) {
  pr_err("Generic Block Discard not supported\n");
  return -ENOSYS;
 }

 if (!target_dev_configured(dev)) {
  pr_err("Generic Block Discard setup for %s requires device to be configured\n",
         config_opt);
  return -ENODEV;
 }

 if (!dev->transport->configure_unmap(dev)) {
  pr_err("Generic Block Discard setup for %s failed\n",
         config_opt);
  return -ENOSYS;
 }

 return 0;
}

static ssize_t emulate_tpu_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 struct se_device *dev = da->da_dev;
 bool flag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 /*
 * We expect this value to be non-zero when generic Block Layer
 * Discard supported is detected iblock_create_virtdevice().
 */

 if (flag && !da->max_unmap_block_desc_count) {
  ret = target_try_configure_unmap(dev, "emulate_tpu");
  if (ret)
   return ret;
 }

 da->emulate_tpu = flag;
 pr_debug("dev[%p]: SE Device Thin Provisioning UNMAP bit: %d\n",
  da->da_dev, flag);
 return count;
}

static ssize_t emulate_tpws_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 struct se_device *dev = da->da_dev;
 bool flag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 /*
 * We expect this value to be non-zero when generic Block Layer
 * Discard supported is detected iblock_create_virtdevice().
 */

 if (flag && !da->max_unmap_block_desc_count) {
  ret = target_try_configure_unmap(dev, "emulate_tpws");
  if (ret)
   return ret;
 }

 da->emulate_tpws = flag;
 pr_debug("dev[%p]: SE Device Thin Provisioning WRITE_SAME: %d\n",
    da->da_dev, flag);
 return count;
}

static ssize_t pi_prot_type_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 int old_prot = da->pi_prot_type, ret;
 struct se_device *dev = da->da_dev;
 u32 flag;

 ret = kstrtou32(page, 0, &flag);
 if (ret < 0)
  return ret;

 if (flag != 0 && flag != 1 && flag != 2 && flag != 3) {
  pr_err("Illegal value %d for pi_prot_type\n", flag);
  return -EINVAL;
 }
 if (flag == 2) {
  pr_err("DIF TYPE2 protection currently not supported\n");
  return -ENOSYS;
 }
 if (da->hw_pi_prot_type) {
  pr_warn("DIF protection enabled on underlying hardware,"
   " ignoring\n");
  return count;
 }
 if (!dev->transport->init_prot || !dev->transport->free_prot) {
  /* 0 is only allowed value for non-supporting backends */
  if (flag == 0)
   return count;

  pr_err("DIF protection not supported by backend: %s\n",
         dev->transport->name);
  return -ENOSYS;
 }
 if (!target_dev_configured(dev)) {
  pr_err("DIF protection requires device to be configured\n");
  return -ENODEV;
 }
 if (dev->export_count) {
  pr_err("dev[%p]: Unable to change SE Device PROT type while"
         " export_count is %d\n", dev, dev->export_count);
  return -EINVAL;
 }

 da->pi_prot_type = flag;

 if (flag && !old_prot) {
  ret = dev->transport->init_prot(dev);
  if (ret) {
   da->pi_prot_type = old_prot;
   da->pi_prot_verify = (bool) da->pi_prot_type;
   return ret;
  }

 } else if (!flag && old_prot) {
  dev->transport->free_prot(dev);
 }

 da->pi_prot_verify = (bool) da->pi_prot_type;
 pr_debug("dev[%p]: SE Device Protection Type: %d\n", dev, flag);
 return count;
}

/* always zero, but attr needs to remain RW to avoid userspace breakage */
static ssize_t pi_prot_format_show(struct config_item *item, char *page)
{
 return snprintf(page, PAGE_SIZE, "0\n");
}

static ssize_t pi_prot_format_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 struct se_device *dev = da->da_dev;
 bool flag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 if (!flag)
  return count;

 if (!dev->transport->format_prot) {
  pr_err("DIF protection format not supported by backend %s\n",
         dev->transport->name);
  return -ENOSYS;
 }
 if (!target_dev_configured(dev)) {
  pr_err("DIF protection format requires device to be configured\n");
  return -ENODEV;
 }
 if (dev->export_count) {
  pr_err("dev[%p]: Unable to format SE Device PROT type while"
         " export_count is %d\n", dev, dev->export_count);
  return -EINVAL;
 }

 ret = dev->transport->format_prot(dev);
 if (ret)
  return ret;

 pr_debug("dev[%p]: SE Device Protection Format complete\n", dev);
 return count;
}

static ssize_t pi_prot_verify_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 bool flag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 if (!flag) {
  da->pi_prot_verify = flag;
  return count;
 }
 if (da->hw_pi_prot_type) {
  pr_warn("DIF protection enabled on underlying hardware,"
   " ignoring\n");
  return count;
 }
 if (!da->pi_prot_type) {
  pr_warn("DIF protection not supported by backend, ignoring\n");
  return count;
 }
 da->pi_prot_verify = flag;

 return count;
}

static ssize_t force_pr_aptpl_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 bool flag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;
 if (da->da_dev->export_count) {
  pr_err("dev[%p]: Unable to set force_pr_aptpl while"
         " export_count is %d\n",
         da->da_dev, da->da_dev->export_count);
  return -EINVAL;
 }

 da->force_pr_aptpl = flag;
 pr_debug("dev[%p]: SE Device force_pr_aptpl: %d\n", da->da_dev, flag);
 return count;
}

static ssize_t emulate_rest_reord_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 bool flag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 if (flag != 0) {
  printk(KERN_ERR "dev[%p]: SE Device emulation of restricted"
   " reordering not implemented\n", da->da_dev);
  return -ENOSYS;
 }
 da->emulate_rest_reord = flag;
 pr_debug("dev[%p]: SE Device emulate_rest_reord: %d\n",
  da->da_dev, flag);
 return count;
}

static ssize_t unmap_zeroes_data_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 struct se_device *dev = da->da_dev;
 bool flag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 if (da->da_dev->export_count) {
  pr_err("dev[%p]: Unable to change SE Device"
         " unmap_zeroes_data while export_count is %d\n",
         da->da_dev, da->da_dev->export_count);
  return -EINVAL;
 }
 /*
 * We expect this value to be non-zero when generic Block Layer
 * Discard supported is detected iblock_configure_device().
 */

 if (flag && !da->max_unmap_block_desc_count) {
  ret = target_try_configure_unmap(dev, "unmap_zeroes_data");
  if (ret)
   return ret;
 }
 da->unmap_zeroes_data = flag;
 pr_debug("dev[%p]: SE Device Thin Provisioning LBPRZ bit: %d\n",
   da->da_dev, flag);
 return count;
}

/*
 * Note, this can only be called on unexported SE Device Object.
 */

static ssize_t queue_depth_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 struct se_device *dev = da->da_dev;
 u32 val;
 int ret;

 ret = kstrtou32(page, 0, &val);
 if (ret < 0)
  return ret;

 if (dev->export_count) {
  pr_err("dev[%p]: Unable to change SE Device TCQ while"
   " export_count is %d\n",
   dev, dev->export_count);
  return -EINVAL;
 }
 if (!val) {
  pr_err("dev[%p]: Illegal ZERO value for queue_depth\n", dev);
  return -EINVAL;
 }

 if (val > dev->dev_attrib.queue_depth) {
  if (val > dev->dev_attrib.hw_queue_depth) {
   pr_err("dev[%p]: Passed queue_depth:"
    " %u exceeds TCM/SE_Device MAX"
    " TCQ: %u\n", dev, val,
    dev->dev_attrib.hw_queue_depth);
   return -EINVAL;
  }
 }
 da->queue_depth = dev->queue_depth = val;
 pr_debug("dev[%p]: SE Device TCQ Depth changed to: %u\n", dev, val);
 return count;
}

static ssize_t optimal_sectors_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 u32 val;
 int ret;

 ret = kstrtou32(page, 0, &val);
 if (ret < 0)
  return ret;

 if (da->da_dev->export_count) {
  pr_err("dev[%p]: Unable to change SE Device"
   " optimal_sectors while export_count is %d\n",
   da->da_dev, da->da_dev->export_count);
  return -EINVAL;
 }
 if (val > da->hw_max_sectors) {
  pr_err("dev[%p]: Passed optimal_sectors %u cannot be"
   " greater than hw_max_sectors: %u\n",
   da->da_dev, val, da->hw_max_sectors);
  return -EINVAL;
 }

 da->optimal_sectors = val;
 pr_debug("dev[%p]: SE Device optimal_sectors changed to %u\n",
   da->da_dev, val);
 return count;
}

static ssize_t block_size_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 u32 val;
 int ret;

 ret = kstrtou32(page, 0, &val);
 if (ret < 0)
  return ret;

 if (da->da_dev->export_count) {
  pr_err("dev[%p]: Unable to change SE Device block_size"
   " while export_count is %d\n",
   da->da_dev, da->da_dev->export_count);
  return -EINVAL;
 }

 if (val != 512 && val != 1024 && val != 2048 && val != 4096) {
  pr_err("dev[%p]: Illegal value for block_device: %u"
   " for SE device, must be 512, 1024, 2048 or 4096\n",
   da->da_dev, val);
  return -EINVAL;
 }

 da->block_size = val;

 pr_debug("dev[%p]: SE Device block_size changed to %u\n",
   da->da_dev, val);
 return count;
}

static ssize_t alua_support_show(struct config_item *item, char *page)
{
 struct se_dev_attrib *da = to_attrib(item);
 u8 flags = da->da_dev->transport_flags;

 return snprintf(page, PAGE_SIZE, "%d\n",
   flags & TRANSPORT_FLAG_PASSTHROUGH_ALUA ? 0 : 1);
}

static ssize_t alua_support_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 struct se_device *dev = da->da_dev;
 bool flag, oldflag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 oldflag = !(dev->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_ALUA);
 if (flag == oldflag)
  return count;

 if (!(dev->transport->transport_flags_changeable &
       TRANSPORT_FLAG_PASSTHROUGH_ALUA)) {
  pr_err("dev[%p]: Unable to change SE Device alua_support:"
   " alua_support has fixed value\n", dev);
  return -ENOSYS;
 }

 if (flag)
  dev->transport_flags &= ~TRANSPORT_FLAG_PASSTHROUGH_ALUA;
 else
  dev->transport_flags |= TRANSPORT_FLAG_PASSTHROUGH_ALUA;
 return count;
}

static ssize_t pgr_support_show(struct config_item *item, char *page)
{
 struct se_dev_attrib *da = to_attrib(item);
 u8 flags = da->da_dev->transport_flags;

 return snprintf(page, PAGE_SIZE, "%d\n",
   flags & TRANSPORT_FLAG_PASSTHROUGH_PGR ? 0 : 1);
}

static ssize_t pgr_support_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 struct se_device *dev = da->da_dev;
 bool flag, oldflag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 oldflag = !(dev->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_PGR);
 if (flag == oldflag)
  return count;

 if (!(dev->transport->transport_flags_changeable &
       TRANSPORT_FLAG_PASSTHROUGH_PGR)) {
  pr_err("dev[%p]: Unable to change SE Device pgr_support:"
   " pgr_support has fixed value\n", dev);
  return -ENOSYS;
 }

 if (flag)
  dev->transport_flags &= ~TRANSPORT_FLAG_PASSTHROUGH_PGR;
 else
  dev->transport_flags |= TRANSPORT_FLAG_PASSTHROUGH_PGR;
 return count;
}

static ssize_t emulate_rsoc_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 bool flag;
 int ret;

 ret = kstrtobool(page, &flag);
 if (ret < 0)
  return ret;

 da->emulate_rsoc = flag;
 pr_debug("dev[%p]: SE Device REPORT_SUPPORTED_OPERATION_CODES_EMULATION flag: %d\n",
   da->da_dev, flag);
 return count;
}

static ssize_t submit_type_store(struct config_item *item, const char *page,
     size_t count)
{
 struct se_dev_attrib *da = to_attrib(item);
 int ret;
 u8 val;

 ret = kstrtou8(page, 0, &val);
 if (ret < 0)
  return ret;

 if (val > TARGET_QUEUE_SUBMIT)
  return -EINVAL;

 da->submit_type = val;
 return count;
}

CONFIGFS_ATTR(, emulate_model_alias);
CONFIGFS_ATTR(, emulate_dpo);
CONFIGFS_ATTR(, emulate_fua_write);
CONFIGFS_ATTR(, emulate_fua_read);
CONFIGFS_ATTR(, emulate_write_cache);
CONFIGFS_ATTR(, emulate_ua_intlck_ctrl);
CONFIGFS_ATTR(, emulate_tas);
CONFIGFS_ATTR(, emulate_tpu);
CONFIGFS_ATTR(, emulate_tpws);
CONFIGFS_ATTR(, emulate_caw);
CONFIGFS_ATTR(, emulate_3pc);
CONFIGFS_ATTR(, emulate_pr);
CONFIGFS_ATTR(, emulate_rsoc);
CONFIGFS_ATTR(, pi_prot_type);
CONFIGFS_ATTR_RO(, hw_pi_prot_type);
CONFIGFS_ATTR(, pi_prot_format);
CONFIGFS_ATTR(, pi_prot_verify);
CONFIGFS_ATTR(, enforce_pr_isids);
CONFIGFS_ATTR(, is_nonrot);
CONFIGFS_ATTR(, emulate_rest_reord);
CONFIGFS_ATTR(, force_pr_aptpl);
CONFIGFS_ATTR_RO(, hw_block_size);
CONFIGFS_ATTR(, block_size);
CONFIGFS_ATTR_RO(, hw_max_sectors);
CONFIGFS_ATTR(, optimal_sectors);
CONFIGFS_ATTR_RO(, hw_queue_depth);
CONFIGFS_ATTR(, queue_depth);
CONFIGFS_ATTR(, max_unmap_lba_count);
CONFIGFS_ATTR(, max_unmap_block_desc_count);
CONFIGFS_ATTR(, unmap_granularity);
CONFIGFS_ATTR(, unmap_granularity_alignment);
CONFIGFS_ATTR(, unmap_zeroes_data);
CONFIGFS_ATTR(, max_write_same_len);
CONFIGFS_ATTR(, alua_support);
CONFIGFS_ATTR(, pgr_support);
CONFIGFS_ATTR(, submit_type);

/*
 * dev_attrib attributes for devices using the target core SBC/SPC
 * interpreter.  Any backend using spc_parse_cdb should be using
 * these.
 */

struct configfs_attribute *sbc_attrib_attrs[] = {
 &attr_emulate_model_alias,
 &attr_emulate_dpo,
 &attr_emulate_fua_write,
 &attr_emulate_fua_read,
 &attr_emulate_write_cache,
 &attr_emulate_ua_intlck_ctrl,
 &attr_emulate_tas,
 &attr_emulate_tpu,
 &attr_emulate_tpws,
 &attr_emulate_caw,
 &attr_emulate_3pc,
 &attr_emulate_pr,
 &attr_pi_prot_type,
 &attr_hw_pi_prot_type,
 &attr_pi_prot_format,
 &attr_pi_prot_verify,
 &attr_enforce_pr_isids,
 &attr_is_nonrot,
 &attr_emulate_rest_reord,
 &attr_force_pr_aptpl,
 &attr_hw_block_size,
 &attr_block_size,
 &attr_hw_max_sectors,
 &attr_optimal_sectors,
 &attr_hw_queue_depth,
 &attr_queue_depth,
 &attr_max_unmap_lba_count,
 &attr_max_unmap_block_desc_count,
 &attr_unmap_granularity,
 &attr_unmap_granularity_alignment,
 &attr_unmap_zeroes_data,
 &attr_max_write_same_len,
 &attr_alua_support,
 &attr_pgr_support,
 &attr_emulate_rsoc,
 &attr_submit_type,
 NULL,
};
EXPORT_SYMBOL(sbc_attrib_attrs);

/*
 * Minimal dev_attrib attributes for devices passing through CDBs.
 * In this case we only provide a few read-only attributes for
 * backwards compatibility.
 */

struct configfs_attribute *passthrough_attrib_attrs[] = {
 &attr_hw_pi_prot_type,
 &attr_hw_block_size,
 &attr_hw_max_sectors,
 &attr_hw_queue_depth,
 &attr_emulate_pr,
 &attr_alua_support,
 &attr_pgr_support,
 &attr_submit_type,
 NULL,
};
EXPORT_SYMBOL(passthrough_attrib_attrs);

/*
 * pr related dev_attrib attributes for devices passing through CDBs,
 * but allowing in core pr emulation.
 */

struct configfs_attribute *passthrough_pr_attrib_attrs[] = {
 &attr_enforce_pr_isids,
 &attr_force_pr_aptpl,
 NULL,
};
EXPORT_SYMBOL(passthrough_pr_attrib_attrs);

TB_CIT_SETUP_DRV(dev_attrib, NULL, NULL);
TB_CIT_SETUP_DRV(dev_action, NULL, NULL);

/* End functions for struct config_item_type tb_dev_attrib_cit */

/*  Start functions for struct config_item_type tb_dev_wwn_cit */

static struct t10_wwn *to_t10_wwn(struct config_item *item)
{
 return container_of(to_config_group(item), struct t10_wwn, t10_wwn_group);
}

static ssize_t target_check_inquiry_data(char *buf)
{
 size_t len;
 int i;

 len = strlen(buf);

 /*
 * SPC 4.3.1:
 * ASCII data fields shall contain only ASCII printable characters
 * (i.e., code values 20h to 7Eh) and may be terminated with one or
 * more ASCII null (00h) characters.
 */

 for (i = 0; i < len; i++) {
  if (buf[i] < 0x20 || buf[i] > 0x7E) {
   pr_err("Emulated T10 Inquiry Data contains non-ASCII-printable characters\n");
   return -EINVAL;
  }
 }

 return len;
}

/*
 * STANDARD and VPD page 0x83 T10 Vendor Identification
 */

static ssize_t target_wwn_vendor_id_show(struct config_item *item,
  char *page)
{
 return sprintf(page, "%s\n", &to_t10_wwn(item)->vendor[0]);
}

static ssize_t target_wwn_vendor_id_store(struct config_item *item,
  const char *page, size_t count)
{
 struct t10_wwn *t10_wwn = to_t10_wwn(item);
 struct se_device *dev = t10_wwn->t10_dev;
 /* +2 to allow for a trailing (stripped) '\n' and null-terminator */
 unsigned char buf[INQUIRY_VENDOR_LEN + 2];
 char *stripped = NULL;
 ssize_t len;
 ssize_t ret;

 len = strscpy(buf, page);
 if (len > 0) {
  /* Strip any newline added from userspace. */
  stripped = strstrip(buf);
  len = strlen(stripped);
 }
 if (len < 0 || len > INQUIRY_VENDOR_LEN) {
  pr_err("Emulated T10 Vendor Identification exceeds"
   " INQUIRY_VENDOR_LEN: " __stringify(INQUIRY_VENDOR_LEN)
   "\n");
  return -EOVERFLOW;
 }

 ret = target_check_inquiry_data(stripped);

 if (ret < 0)
  return ret;

 /*
 * Check to see if any active exports exist.  If they do exist, fail
 * here as changing this information on the fly (underneath the
 * initiator side OS dependent multipath code) could cause negative
 * effects.
 */

 if (dev->export_count) {
  pr_err("Unable to set T10 Vendor Identification while"
   " active %d exports exist\n", dev->export_count);
  return -EINVAL;
 }

 BUILD_BUG_ON(sizeof(dev->t10_wwn.vendor) != INQUIRY_VENDOR_LEN + 1);
 strscpy(dev->t10_wwn.vendor, stripped);

 pr_debug("Target_Core_ConfigFS: Set emulated T10 Vendor Identification:"
   " %s\n", dev->t10_wwn.vendor);

 return count;
}

static ssize_t target_wwn_product_id_show(struct config_item *item,
  char *page)
{
 return sprintf(page, "%s\n", &to_t10_wwn(item)->model[0]);
}

static ssize_t target_wwn_product_id_store(struct config_item *item,
  const char *page, size_t count)
{
 struct t10_wwn *t10_wwn = to_t10_wwn(item);
 struct se_device *dev = t10_wwn->t10_dev;
 /* +2 to allow for a trailing (stripped) '\n' and null-terminator */
 unsigned char buf[INQUIRY_MODEL_LEN + 2];
 char *stripped = NULL;
 ssize_t len;
 ssize_t ret;

 len = strscpy(buf, page);
 if (len > 0) {
  /* Strip any newline added from userspace. */
  stripped = strstrip(buf);
  len = strlen(stripped);
 }
 if (len < 0 || len > INQUIRY_MODEL_LEN) {
  pr_err("Emulated T10 Vendor exceeds INQUIRY_MODEL_LEN: "
    __stringify(INQUIRY_MODEL_LEN)
   "\n");
  return -EOVERFLOW;
 }

 ret = target_check_inquiry_data(stripped);

 if (ret < 0)
  return ret;

 /*
 * Check to see if any active exports exist.  If they do exist, fail
 * here as changing this information on the fly (underneath the
 * initiator side OS dependent multipath code) could cause negative
 * effects.
 */

 if (dev->export_count) {
  pr_err("Unable to set T10 Model while active %d exports exist\n",
   dev->export_count);
  return -EINVAL;
 }

 BUILD_BUG_ON(sizeof(dev->t10_wwn.model) != INQUIRY_MODEL_LEN + 1);
 strscpy(dev->t10_wwn.model, stripped);

 pr_debug("Target_Core_ConfigFS: Set emulated T10 Model Identification: %s\n",
   dev->t10_wwn.model);

 return count;
}

static ssize_t target_wwn_revision_show(struct config_item *item,
  char *page)
{
 return sprintf(page, "%s\n", &to_t10_wwn(item)->revision[0]);
}

static ssize_t target_wwn_revision_store(struct config_item *item,
  const char *page, size_t count)
{
 struct t10_wwn *t10_wwn = to_t10_wwn(item);
 struct se_device *dev = t10_wwn->t10_dev;
 /* +2 to allow for a trailing (stripped) '\n' and null-terminator */
 unsigned char buf[INQUIRY_REVISION_LEN + 2];
 char *stripped = NULL;
 ssize_t len;
 ssize_t ret;

 len = strscpy(buf, page);
 if (len > 0) {
  /* Strip any newline added from userspace. */
  stripped = strstrip(buf);
  len = strlen(stripped);
 }
 if (len < 0 || len > INQUIRY_REVISION_LEN) {
  pr_err("Emulated T10 Revision exceeds INQUIRY_REVISION_LEN: "
    __stringify(INQUIRY_REVISION_LEN)
   "\n");
  return -EOVERFLOW;
 }

 ret = target_check_inquiry_data(stripped);

 if (ret < 0)
  return ret;

 /*
 * Check to see if any active exports exist.  If they do exist, fail
 * here as changing this information on the fly (underneath the
 * initiator side OS dependent multipath code) could cause negative
 * effects.
 */

 if (dev->export_count) {
  pr_err("Unable to set T10 Revision while active %d exports exist\n",
   dev->export_count);
  return -EINVAL;
 }

 BUILD_BUG_ON(sizeof(dev->t10_wwn.revision) != INQUIRY_REVISION_LEN + 1);
 strscpy(dev->t10_wwn.revision, stripped);

 pr_debug("Target_Core_ConfigFS: Set emulated T10 Revision: %s\n",
   dev->t10_wwn.revision);

 return count;
}

static ssize_t
target_wwn_company_id_show(struct config_item *item,
    char *page)
{
 return snprintf(page, PAGE_SIZE, "%#08x\n",
   to_t10_wwn(item)->company_id);
}

static ssize_t
target_wwn_company_id_store(struct config_item *item,
     const char *page, size_t count)
{
 struct t10_wwn *t10_wwn = to_t10_wwn(item);
 struct se_device *dev = t10_wwn->t10_dev;
 u32 val;
 int ret;

 /*
 * The IEEE COMPANY_ID field should contain a 24-bit canonical
 * form OUI assigned by the IEEE.
 */

 ret = kstrtou32(page, 0, &val);
 if (ret < 0)
  return ret;

 if (val >= 0x1000000)
  return -EOVERFLOW;

 /*
 * Check to see if any active exports exist. If they do exist, fail
 * here as changing this information on the fly (underneath the
 * initiator side OS dependent multipath code) could cause negative
 * effects.
 */

 if (dev->export_count) {
  pr_err("Unable to set Company ID while %u exports exist\n",
         dev->export_count);
  return -EINVAL;
 }

 t10_wwn->company_id = val;

 pr_debug("Target_Core_ConfigFS: Set IEEE Company ID: %#08x\n",
   t10_wwn->company_id);

 return count;
}

/*
 * VPD page 0x80 Unit serial
 */

static ssize_t target_wwn_vpd_unit_serial_show(struct config_item *item,
  char *page)
{
 return sprintf(page, "T10 VPD Unit Serial Number: %s\n",
  &to_t10_wwn(item)->unit_serial[0]);
}

static ssize_t target_wwn_vpd_unit_serial_store(struct config_item *item,
  const char *page, size_t count)
{
 struct t10_wwn *t10_wwn = to_t10_wwn(item);
 struct se_device *dev = t10_wwn->t10_dev;
 unsigned char buf[INQUIRY_VPD_SERIAL_LEN] = { };

 /*
 * If Linux/SCSI subsystem_api_t plugin got a VPD Unit Serial
 * from the struct scsi_device level firmware, do not allow
 * VPD Unit Serial to be emulated.
 *
 * Note this struct scsi_device could also be emulating VPD
 * information from its drivers/scsi LLD.  But for now we assume
 * it is doing 'the right thing' wrt a world wide unique
 * VPD Unit Serial Number that OS dependent multipath can depend on.
 */

 if (dev->dev_flags & DF_FIRMWARE_VPD_UNIT_SERIAL) {
  pr_err("Underlying SCSI device firmware provided VPD"
   " Unit Serial, ignoring request\n");
  return -EOPNOTSUPP;
 }

 if (strlen(page) >= INQUIRY_VPD_SERIAL_LEN) {
  pr_err("Emulated VPD Unit Serial exceeds"
  " INQUIRY_VPD_SERIAL_LEN: %d\n", INQUIRY_VPD_SERIAL_LEN);
  return -EOVERFLOW;
 }
 /*
 * Check to see if any active $FABRIC_MOD exports exist.  If they
 * do exist, fail here as changing this information on the fly
 * (underneath the initiator side OS dependent multipath code)
 * could cause negative effects.
 */

 if (dev->export_count) {
  pr_err("Unable to set VPD Unit Serial while"
   " active %d $FABRIC_MOD exports exist\n",
   dev->export_count);
  return -EINVAL;
 }

 /*
 * This currently assumes ASCII encoding for emulated VPD Unit Serial.
 *
 * Also, strip any newline added from the userspace
 * echo $UUID > $TARGET/$HBA/$STORAGE_OBJECT/wwn/vpd_unit_serial
 */

 snprintf(buf, INQUIRY_VPD_SERIAL_LEN, "%s", page);
 snprintf(dev->t10_wwn.unit_serial, INQUIRY_VPD_SERIAL_LEN,
   "%s", strstrip(buf));
 dev->dev_flags |= DF_EMULATED_VPD_UNIT_SERIAL;

 pr_debug("Target_Core_ConfigFS: Set emulated VPD Unit Serial:"
   " %s\n", dev->t10_wwn.unit_serial);

 return count;
}

/*
 * VPD page 0x83 Protocol Identifier
 */

static ssize_t target_wwn_vpd_protocol_identifier_show(struct config_item *item,
  char *page)
{
 struct t10_wwn *t10_wwn = to_t10_wwn(item);
 struct t10_vpd *vpd;
 unsigned char buf[VPD_TMP_BUF_SIZE] = { };
 ssize_t len = 0;

 spin_lock(&t10_wwn->t10_vpd_lock);
 list_for_each_entry(vpd, &t10_wwn->t10_vpd_list, vpd_list) {
  if (!vpd->protocol_identifier_set)
   continue;

  transport_dump_vpd_proto_id(vpd, buf, VPD_TMP_BUF_SIZE);

  if (len + strlen(buf) >= PAGE_SIZE)
   break;

  len += sprintf(page+len, "%s", buf);
 }
 spin_unlock(&t10_wwn->t10_vpd_lock);

 return len;
}

/*
 * Generic wrapper for dumping VPD identifiers by association.
 */

#define DEF_DEV_WWN_ASSOC_SHOW(_name, _assoc)    \
static ssize_t target_wwn_##_name##_show(struct config_item *item, \
  char *page)      \
{         \
 struct t10_wwn *t10_wwn = to_t10_wwn(item);   \
 struct t10_vpd *vpd;      \
 unsigned char buf[VPD_TMP_BUF_SIZE];    \
 ssize_t len = 0;      \
         \
 spin_lock(&t10_wwn->t10_vpd_lock);    \
 list_for_each_entry(vpd, &t10_wwn->t10_vpd_list, vpd_list) { \
  if (vpd->association != _assoc)    \
   continue;     \
         \
  memset(buf, 0, VPD_TMP_BUF_SIZE);   \
  transport_dump_vpd_assoc(vpd, buf, VPD_TMP_BUF_SIZE); \
  if (len + strlen(buf) >= PAGE_SIZE)   \
   break;      \
  len += sprintf(page+len, "%s", buf);   \
         \
  memset(buf, 0, VPD_TMP_BUF_SIZE);   \
  transport_dump_vpd_ident_type(vpd, buf, VPD_TMP_BUF_SIZE); \
  if (len + strlen(buf) >= PAGE_SIZE)   \
   break;      \
  len += sprintf(page+len, "%s", buf);   \
         \
  memset(buf, 0, VPD_TMP_BUF_SIZE);   \
  transport_dump_vpd_ident(vpd, buf, VPD_TMP_BUF_SIZE); \
  if (len + strlen(buf) >= PAGE_SIZE)   \
   break;      \
  len += sprintf(page+len, "%s", buf);   \
 }        \
 spin_unlock(&t10_wwn->t10_vpd_lock);    \
         \
 return len;       \
}

/* VPD page 0x83 Association: Logical Unit */
DEF_DEV_WWN_ASSOC_SHOW(vpd_assoc_logical_unit, 0x00);
/* VPD page 0x83 Association: Target Port */
DEF_DEV_WWN_ASSOC_SHOW(vpd_assoc_target_port, 0x10);
/* VPD page 0x83 Association: SCSI Target Device */
DEF_DEV_WWN_ASSOC_SHOW(vpd_assoc_scsi_target_device, 0x20);

CONFIGFS_ATTR(target_wwn_, vendor_id);
CONFIGFS_ATTR(target_wwn_, product_id);
CONFIGFS_ATTR(target_wwn_, revision);
CONFIGFS_ATTR(target_wwn_, company_id);
CONFIGFS_ATTR(target_wwn_, vpd_unit_serial);
CONFIGFS_ATTR_RO(target_wwn_, vpd_protocol_identifier);
CONFIGFS_ATTR_RO(target_wwn_, vpd_assoc_logical_unit);
CONFIGFS_ATTR_RO(target_wwn_, vpd_assoc_target_port);
CONFIGFS_ATTR_RO(target_wwn_, vpd_assoc_scsi_target_device);

static struct configfs_attribute *target_core_dev_wwn_attrs[] = {
 &target_wwn_attr_vendor_id,
 &target_wwn_attr_product_id,
 &target_wwn_attr_revision,
 &target_wwn_attr_company_id,
 &target_wwn_attr_vpd_unit_serial,
 &target_wwn_attr_vpd_protocol_identifier,
 &target_wwn_attr_vpd_assoc_logical_unit,
 &target_wwn_attr_vpd_assoc_target_port,
 &target_wwn_attr_vpd_assoc_scsi_target_device,
 NULL,
};

TB_CIT_SETUP(dev_wwn, NULL, NULL, target_core_dev_wwn_attrs);

/*  End functions for struct config_item_type tb_dev_wwn_cit */

/*  Start functions for struct config_item_type tb_dev_pr_cit */

static struct se_device *pr_to_dev(struct config_item *item)
{
 return container_of(to_config_group(item), struct se_device,
   dev_pr_group);
}

static ssize_t target_core_dev_pr_show_spc3_res(struct se_device *dev,
  char *page)
{
 struct se_node_acl *se_nacl;
 struct t10_pr_registration *pr_reg;
 char i_buf[PR_REG_ISID_ID_LEN] = { };

 pr_reg = dev->dev_pr_res_holder;
 if (!pr_reg)
  return sprintf(page, "No SPC-3 Reservation holder\n");

 se_nacl = pr_reg->pr_reg_nacl;
 core_pr_dump_initiator_port(pr_reg, i_buf, PR_REG_ISID_ID_LEN);

 return sprintf(page, "SPC-3 Reservation: %s Initiator: %s%s\n",
  se_nacl->se_tpg->se_tpg_tfo->fabric_name,
  se_nacl->initiatorname, i_buf);
}

static ssize_t target_core_dev_pr_show_spc2_res(struct se_device *dev,
  char *page)
{
 struct se_session *sess = dev->reservation_holder;
 struct se_node_acl *se_nacl;
 ssize_t len;

 if (sess) {
  se_nacl = sess->se_node_acl;
  len = sprintf(page,
         "SPC-2 Reservation: %s Initiator: %s\n",
         se_nacl->se_tpg->se_tpg_tfo->fabric_name,
         se_nacl->initiatorname);
 } else {
  len = sprintf(page, "No SPC-2 Reservation holder\n");
 }
 return len;
}

static ssize_t target_pr_res_holder_show(struct config_item *item, char *page)
{
 struct se_device *dev = pr_to_dev(item);
 int ret;

 if (!dev->dev_attrib.emulate_pr)
  return sprintf(page, "SPC_RESERVATIONS_DISABLED\n");

 if (dev->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_PGR)
  return sprintf(page, "Passthrough\n");

 spin_lock(&dev->dev_reservation_lock);
 if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
  ret = target_core_dev_pr_show_spc2_res(dev, page);
 else
  ret = target_core_dev_pr_show_spc3_res(dev, page);
 spin_unlock(&dev->dev_reservation_lock);
 return ret;
}

static ssize_t target_pr_res_pr_all_tgt_pts_show(struct config_item *item,
  char *page)
{
 struct se_device *dev = pr_to_dev(item);
 ssize_t len = 0;

 spin_lock(&dev->dev_reservation_lock);
 if (!dev->dev_pr_res_holder) {
  len = sprintf(page, "No SPC-3 Reservation holder\n");
 } else if (dev->dev_pr_res_holder->pr_reg_all_tg_pt) {
  len = sprintf(page, "SPC-3 Reservation: All Target"
   " Ports registration\n");
 } else {
  len = sprintf(page, "SPC-3 Reservation: Single"
   " Target Port registration\n");
 }

 spin_unlock(&dev->dev_reservation_lock);
 return len;
}

static ssize_t target_pr_res_pr_generation_show(struct config_item *item,
  char *page)
{
 return sprintf(page, "0x%08x\n", pr_to_dev(item)->t10_pr.pr_generation);
}


static ssize_t target_pr_res_pr_holder_tg_port_show(struct config_item *item,
  char *page)
{
 struct se_device *dev = pr_to_dev(item);
 struct se_node_acl *se_nacl;
 struct se_portal_group *se_tpg;
 struct t10_pr_registration *pr_reg;
 const struct target_core_fabric_ops *tfo;
 ssize_t len = 0;

 spin_lock(&dev->dev_reservation_lock);
 pr_reg = dev->dev_pr_res_holder;
 if (!pr_reg) {
  len = sprintf(page, "No SPC-3 Reservation holder\n");
  goto out_unlock;
 }

 se_nacl = pr_reg->pr_reg_nacl;
 se_tpg = se_nacl->se_tpg;
 tfo = se_tpg->se_tpg_tfo;

 len += sprintf(page+len, "SPC-3 Reservation: %s"
  " Target Node Endpoint: %s\n", tfo->fabric_name,
  tfo->tpg_get_wwn(se_tpg));
 len += sprintf(page+len, "SPC-3 Reservation: Relative Port"
  " Identifier Tag: %hu %s Portal Group Tag: %hu"
  " %s Logical Unit: %llu\n", pr_reg->tg_pt_sep_rtpi,
  tfo->fabric_name, tfo->tpg_get_tag(se_tpg),
  tfo->fabric_name, pr_reg->pr_aptpl_target_lun);

out_unlock:
 spin_unlock(&dev->dev_reservation_lock);
 return len;
}


static ssize_t target_pr_res_pr_registered_i_pts_show(struct config_item *item,
  char *page)
{
 struct se_device *dev = pr_to_dev(item);
 const struct target_core_fabric_ops *tfo;
 struct t10_pr_registration *pr_reg;
 unsigned char buf[384];
 char i_buf[PR_REG_ISID_ID_LEN];
 ssize_t len = 0;
 int reg_count = 0;

 len += sprintf(page+len, "SPC-3 PR Registrations:\n");

 spin_lock(&dev->t10_pr.registration_lock);
 list_for_each_entry(pr_reg, &dev->t10_pr.registration_list,
   pr_reg_list) {

  memset(buf, 0, 384);
  memset(i_buf, 0, PR_REG_ISID_ID_LEN);
  tfo = pr_reg->pr_reg_nacl->se_tpg->se_tpg_tfo;
  core_pr_dump_initiator_port(pr_reg, i_buf,
     PR_REG_ISID_ID_LEN);
  sprintf(buf, "%s Node: %s%s Key: 0x%016Lx PRgen: 0x%08x\n",
   tfo->fabric_name,
   pr_reg->pr_reg_nacl->initiatorname, i_buf, pr_reg->pr_res_key,
   pr_reg->pr_res_generation);

  if (len + strlen(buf) >= PAGE_SIZE)
   break;

  len += sprintf(page+len, "%s", buf);
  reg_count++;
 }
 spin_unlock(&dev->t10_pr.registration_lock);

 if (!reg_count)
  len += sprintf(page+len, "None\n");

 return len;
}

static ssize_t target_pr_res_pr_type_show(struct config_item *item, char *page)
{
 struct se_device *dev = pr_to_dev(item);
 struct t10_pr_registration *pr_reg;
 ssize_t len = 0;

 spin_lock(&dev->dev_reservation_lock);
 pr_reg = dev->dev_pr_res_holder;
 if (pr_reg) {
  len = sprintf(page, "SPC-3 Reservation Type: %s\n",
   core_scsi3_pr_dump_type(pr_reg->pr_res_type));
 } else {
  len = sprintf(page, "No SPC-3 Reservation holder\n");
 }

 spin_unlock(&dev->dev_reservation_lock);
 return len;
}

static ssize_t target_pr_res_type_show(struct config_item *item, char *page)
{
 struct se_device *dev = pr_to_dev(item);

 if (!dev->dev_attrib.emulate_pr)
  return sprintf(page, "SPC_RESERVATIONS_DISABLED\n");
 if (dev->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_PGR)
  return sprintf(page, "SPC_PASSTHROUGH\n");
 if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
  return sprintf(page, "SPC2_RESERVATIONS\n");

 return sprintf(page, "SPC3_PERSISTENT_RESERVATIONS\n");
}

static ssize_t target_pr_res_aptpl_active_show(struct config_item *item,
  char *page)
{
 struct se_device *dev = pr_to_dev(item);

 if (!dev->dev_attrib.emulate_pr ||
     (dev->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_PGR))
  return 0;

 return sprintf(page, "APTPL Bit Status: %s\n",
  (dev->t10_pr.pr_aptpl_active) ? "Activated" : "Disabled");
}

static ssize_t target_pr_res_aptpl_metadata_show(struct config_item *item,
  char *page)
{
 struct se_device *dev = pr_to_dev(item);

 if (!dev->dev_attrib.emulate_pr ||
     (dev->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_PGR))
  return 0;

 return sprintf(page, "Ready to process PR APTPL metadata..\n");
}

enum {
 Opt_initiator_fabric, Opt_initiator_node, Opt_initiator_sid,
 Opt_sa_res_key, Opt_res_holder, Opt_res_type, Opt_res_scope,
 Opt_res_all_tg_pt, Opt_mapped_lun, Opt_target_fabric,
 Opt_target_node, Opt_tpgt, Opt_port_rtpi, Opt_target_lun, Opt_err
};

static match_table_t tokens = {
 {Opt_initiator_fabric, "initiator_fabric=%s"},
 {Opt_initiator_node, "initiator_node=%s"},
 {Opt_initiator_sid, "initiator_sid=%s"},
 {Opt_sa_res_key, "sa_res_key=%s"},
 {Opt_res_holder, "res_holder=%d"},
 {Opt_res_type, "res_type=%d"},
 {Opt_res_scope, "res_scope=%d"},
 {Opt_res_all_tg_pt, "res_all_tg_pt=%d"},
 {Opt_mapped_lun, "mapped_lun=%u"},
 {Opt_target_fabric, "target_fabric=%s"},
 {Opt_target_node, "target_node=%s"},
 {Opt_tpgt, "tpgt=%d"},
 {Opt_port_rtpi, "port_rtpi=%d"},
 {Opt_target_lun, "target_lun=%u"},
 {Opt_err, NULL}
};

static ssize_t target_pr_res_aptpl_metadata_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_device *dev = pr_to_dev(item);
 unsigned char *i_fabric = NULL, *i_port = NULL, *isid = NULL;
 unsigned char *t_fabric = NULL, *t_port = NULL;
 char *orig, *ptr, *opts;
 substring_t args[MAX_OPT_ARGS];
 unsigned long long tmp_ll;
 u64 sa_res_key = 0;
 u64 mapped_lun = 0, target_lun = 0;
 int ret = -1, res_holder = 0, all_tg_pt = 0, arg, token;
 u16 tpgt = 0;
 u8 type = 0;

 if (!dev->dev_attrib.emulate_pr ||
     (dev->transport_flags & TRANSPORT_FLAG_PASSTHROUGH_PGR))
  return count;
 if (dev->dev_reservation_flags & DRF_SPC2_RESERVATIONS)
  return count;

 if (dev->export_count) {
  pr_debug("Unable to process APTPL metadata while"
   " active fabric exports exist\n");
  return -EINVAL;
 }

 opts = kstrdup(page, GFP_KERNEL);
 if (!opts)
  return -ENOMEM;

 orig = opts;
 while ((ptr = strsep(&opts, ",\n")) != NULL) {
  if (!*ptr)
   continue;

  token = match_token(ptr, tokens, args);
  switch (token) {
  case Opt_initiator_fabric:
   i_fabric = match_strdup(args);
   if (!i_fabric) {
    ret = -ENOMEM;
    goto out;
   }
   break;
  case Opt_initiator_node:
   i_port = match_strdup(args);
   if (!i_port) {
    ret = -ENOMEM;
    goto out;
   }
   if (strlen(i_port) >= PR_APTPL_MAX_IPORT_LEN) {
    pr_err("APTPL metadata initiator_node="
     " exceeds PR_APTPL_MAX_IPORT_LEN: %d\n",
     PR_APTPL_MAX_IPORT_LEN);
    ret = -EINVAL;
    break;
   }
   break;
  case Opt_initiator_sid:
   isid = match_strdup(args);
   if (!isid) {
    ret = -ENOMEM;
    goto out;
   }
   if (strlen(isid) >= PR_REG_ISID_LEN) {
    pr_err("APTPL metadata initiator_isid"
     "= exceeds PR_REG_ISID_LEN: %d\n",
     PR_REG_ISID_LEN);
    ret = -EINVAL;
    break;
   }
   break;
  case Opt_sa_res_key:
   ret = match_u64(args,  &tmp_ll);
   if (ret < 0) {
    pr_err("kstrtoull() failed for sa_res_key=\n");
    goto out;
   }
   sa_res_key = (u64)tmp_ll;
   break;
  /*
 * PR APTPL Metadata for Reservation
 */

  case Opt_res_holder:
   ret = match_int(args, &arg);
   if (ret)
    goto out;
   res_holder = arg;
   break;
  case Opt_res_type:
   ret = match_int(args, &arg);
   if (ret)
    goto out;
   type = (u8)arg;
   break;
  case Opt_res_scope:
   ret = match_int(args, &arg);
   if (ret)
    goto out;
   break;
  case Opt_res_all_tg_pt:
   ret = match_int(args, &arg);
   if (ret)
    goto out;
   all_tg_pt = (int)arg;
   break;
  case Opt_mapped_lun:
   ret = match_u64(args, &tmp_ll);
   if (ret)
    goto out;
   mapped_lun = (u64)tmp_ll;
   break;
  /*
 * PR APTPL Metadata for Target Port
 */

  case Opt_target_fabric:
   t_fabric = match_strdup(args);
   if (!t_fabric) {
    ret = -ENOMEM;
    goto out;
   }
   break;
  case Opt_target_node:
   t_port = match_strdup(args);
   if (!t_port) {
    ret = -ENOMEM;
    goto out;
   }
   if (strlen(t_port) >= PR_APTPL_MAX_TPORT_LEN) {
    pr_err("APTPL metadata target_node="
     " exceeds PR_APTPL_MAX_TPORT_LEN: %d\n",
     PR_APTPL_MAX_TPORT_LEN);
    ret = -EINVAL;
    break;
   }
   break;
  case Opt_tpgt:
   ret = match_int(args, &arg);
   if (ret)
    goto out;
   tpgt = (u16)arg;
   break;
  case Opt_port_rtpi:
   ret = match_int(args, &arg);
   if (ret)
    goto out;
   break;
  case Opt_target_lun:
   ret = match_u64(args, &tmp_ll);
   if (ret)
    goto out;
   target_lun = (u64)tmp_ll;
   break;
  default:
   break;
  }
 }

 if (!i_port || !t_port || !sa_res_key) {
  pr_err("Illegal parameters for APTPL registration\n");
  ret = -EINVAL;
  goto out;
 }

 if (res_holder && !(type)) {
  pr_err("Illegal PR type: 0x%02x for reservation"
    " holder\n", type);
  ret = -EINVAL;
  goto out;
 }

 ret = core_scsi3_alloc_aptpl_registration(&dev->t10_pr, sa_res_key,
   i_port, isid, mapped_lun, t_port, tpgt, target_lun,
   res_holder, all_tg_pt, type);
out:
 kfree(i_fabric);
 kfree(i_port);
 kfree(isid);
 kfree(t_fabric);
 kfree(t_port);
 kfree(orig);
 return (ret == 0) ? count : ret;
}


CONFIGFS_ATTR_RO(target_pr_, res_holder);
CONFIGFS_ATTR_RO(target_pr_, res_pr_all_tgt_pts);
CONFIGFS_ATTR_RO(target_pr_, res_pr_generation);
CONFIGFS_ATTR_RO(target_pr_, res_pr_holder_tg_port);
CONFIGFS_ATTR_RO(target_pr_, res_pr_registered_i_pts);
CONFIGFS_ATTR_RO(target_pr_, res_pr_type);
CONFIGFS_ATTR_RO(target_pr_, res_type);
CONFIGFS_ATTR_RO(target_pr_, res_aptpl_active);
CONFIGFS_ATTR(target_pr_, res_aptpl_metadata);

static struct configfs_attribute *target_core_dev_pr_attrs[] = {
 &target_pr_attr_res_holder,
 &target_pr_attr_res_pr_all_tgt_pts,
 &target_pr_attr_res_pr_generation,
 &target_pr_attr_res_pr_holder_tg_port,
 &target_pr_attr_res_pr_registered_i_pts,
 &target_pr_attr_res_pr_type,
 &target_pr_attr_res_type,
 &target_pr_attr_res_aptpl_active,
 &target_pr_attr_res_aptpl_metadata,
 NULL,
};

TB_CIT_SETUP(dev_pr, NULL, NULL, target_core_dev_pr_attrs);

/*  End functions for struct config_item_type tb_dev_pr_cit */

/*  Start functions for struct config_item_type tb_dev_cit */

static inline struct se_device *to_device(struct config_item *item)
{
 return container_of(to_config_group(item), struct se_device, dev_group);
}

static ssize_t target_dev_info_show(struct config_item *item, char *page)
{
 struct se_device *dev = to_device(item);
 int bl = 0;
 ssize_t read_bytes = 0;

 transport_dump_dev_state(dev, page, &bl);
 read_bytes += bl;
 read_bytes += dev->transport->show_configfs_dev_params(dev,
   page+read_bytes);
 return read_bytes;
}

static ssize_t target_dev_control_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_device *dev = to_device(item);

 return dev->transport->set_configfs_dev_params(dev, page, count);
}

static ssize_t target_dev_alias_show(struct config_item *item, char *page)
{
 struct se_device *dev = to_device(item);

 if (!(dev->dev_flags & DF_USING_ALIAS))
  return 0;

 return snprintf(page, PAGE_SIZE, "%s\n", dev->dev_alias);
}

static ssize_t target_dev_alias_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_device *dev = to_device(item);
 struct se_hba *hba = dev->se_hba;
 ssize_t read_bytes;

 if (count > (SE_DEV_ALIAS_LEN-1)) {
  pr_err("alias count: %d exceeds"
   " SE_DEV_ALIAS_LEN-1: %u\n", (int)count,
   SE_DEV_ALIAS_LEN-1);
  return -EINVAL;
 }

 read_bytes = snprintf(&dev->dev_alias[0], SE_DEV_ALIAS_LEN, "%s", page);
 if (!read_bytes)
  return -EINVAL;
 if (dev->dev_alias[read_bytes - 1] == '\n')
  dev->dev_alias[read_bytes - 1] = '\0';

 dev->dev_flags |= DF_USING_ALIAS;

 pr_debug("Target_Core_ConfigFS: %s/%s set alias: %s\n",
  config_item_name(&hba->hba_group.cg_item),
  config_item_name(&dev->dev_group.cg_item),
  dev->dev_alias);

 return read_bytes;
}

static ssize_t target_dev_udev_path_show(struct config_item *item, char *page)
{
 struct se_device *dev = to_device(item);

 if (!(dev->dev_flags & DF_USING_UDEV_PATH))
  return 0;

 return snprintf(page, PAGE_SIZE, "%s\n", dev->udev_path);
}

static ssize_t target_dev_udev_path_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_device *dev = to_device(item);
 struct se_hba *hba = dev->se_hba;
 ssize_t read_bytes;

 if (count > (SE_UDEV_PATH_LEN-1)) {
  pr_err("udev_path count: %d exceeds"
   " SE_UDEV_PATH_LEN-1: %u\n", (int)count,
   SE_UDEV_PATH_LEN-1);
  return -EINVAL;
 }

 read_bytes = snprintf(&dev->udev_path[0], SE_UDEV_PATH_LEN,
   "%s", page);
 if (!read_bytes)
  return -EINVAL;
 if (dev->udev_path[read_bytes - 1] == '\n')
  dev->udev_path[read_bytes - 1] = '\0';

 dev->dev_flags |= DF_USING_UDEV_PATH;

 pr_debug("Target_Core_ConfigFS: %s/%s set udev_path: %s\n",
  config_item_name(&hba->hba_group.cg_item),
  config_item_name(&dev->dev_group.cg_item),
  dev->udev_path);

 return read_bytes;
}

static ssize_t target_dev_enable_show(struct config_item *item, char *page)
{
 struct se_device *dev = to_device(item);

 return snprintf(page, PAGE_SIZE, "%d\n", target_dev_configured(dev));
}

static ssize_t target_dev_enable_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_device *dev = to_device(item);
 char *ptr;
 int ret;

 ptr = strstr(page, "1");
 if (!ptr) {
  pr_err("For dev_enable ops, only valid value"
    " is \"1\"\n");
  return -EINVAL;
 }

 ret = target_configure_device(dev);
 if (ret)
  return ret;
 return count;
}

static ssize_t target_dev_alua_lu_gp_show(struct config_item *item, char *page)
{
 struct se_device *dev = to_device(item);
 struct config_item *lu_ci;
 struct t10_alua_lu_gp *lu_gp;
 struct t10_alua_lu_gp_member *lu_gp_mem;
 ssize_t len = 0;

 lu_gp_mem = dev->dev_alua_lu_gp_mem;
 if (!lu_gp_mem)
  return 0;

 spin_lock(&lu_gp_mem->lu_gp_mem_lock);
 lu_gp = lu_gp_mem->lu_gp;
 if (lu_gp) {
  lu_ci = &lu_gp->lu_gp_group.cg_item;
  len += sprintf(page, "LU Group Alias: %s\nLU Group ID: %hu\n",
   config_item_name(lu_ci), lu_gp->lu_gp_id);
 }
 spin_unlock(&lu_gp_mem->lu_gp_mem_lock);

 return len;
}

static ssize_t target_dev_alua_lu_gp_store(struct config_item *item,
  const char *page, size_t count)
{
 struct se_device *dev = to_device(item);
 struct se_hba *hba = dev->se_hba;
 struct t10_alua_lu_gp *lu_gp = NULL, *lu_gp_new = NULL;
 struct t10_alua_lu_gp_member *lu_gp_mem;
 unsigned char buf[LU_GROUP_NAME_BUF] = { };
 int move = 0;

 lu_gp_mem = dev->dev_alua_lu_gp_mem;
 if (!lu_gp_mem)
  return count;

 if (count > LU_GROUP_NAME_BUF) {
  pr_err("ALUA LU Group Alias too large!\n");
  return -EINVAL;
 }
 memcpy(buf, page, count);
 /*
 * Any ALUA logical unit alias besides "NULL" means we will be
 * making a new group association.
 */

 if (strcmp(strstrip(buf), "NULL")) {
  /*
 * core_alua_get_lu_gp_by_name() will increment reference to
 * struct t10_alua_lu_gp.  This reference is released with
 * core_alua_get_lu_gp_by_name below().
 */

  lu_gp_new = core_alua_get_lu_gp_by_name(strstrip(buf));
  if (!lu_gp_new)
   return -ENODEV;
 }

 spin_lock(&lu_gp_mem->lu_gp_mem_lock);
 lu_gp = lu_gp_mem->lu_gp;
 if (lu_gp) {
  /*
 * Clearing an existing lu_gp association, and replacing
 * with NULL
 */

  if (!lu_gp_new) {
   pr_debug("Target_Core_ConfigFS: Releasing %s/%s"
    " from ALUA LU Group: core/alua/lu_gps/%s, ID:"
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=93 G=94

¤ Dauer der Verarbeitung: 0.36 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.