269 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2 3#include <linux/module.h>4#include <linux/moduleparam.h>5#include <linux/init.h>6#include <linux/slab.h>7#include <linux/types.h>8#include <linux/configfs.h>9#include <scsi/scsi.h>10#include <scsi/scsi_tcq.h>11#include <scsi/scsi_host.h>12#include <scsi/scsi_device.h>13#include <scsi/scsi_cmnd.h>14 15#include <target/target_core_base.h>16#include <target/target_core_fabric.h>17 18#include "tcm_remote.h"19 20static inline struct tcm_remote_tpg *remote_tpg(struct se_portal_group *se_tpg)21{22 return container_of(se_tpg, struct tcm_remote_tpg, remote_se_tpg);23}24 25static char *tcm_remote_get_endpoint_wwn(struct se_portal_group *se_tpg)26{27 /*28 * Return the passed NAA identifier for the Target Port29 */30 return &remote_tpg(se_tpg)->remote_hba->remote_wwn_address[0];31}32 33static u16 tcm_remote_get_tag(struct se_portal_group *se_tpg)34{35 /*36 * This Tag is used when forming SCSI Name identifier in EVPD=1 0x8337 * to represent the SCSI Target Port.38 */39 return remote_tpg(se_tpg)->remote_tpgt;40}41 42static int tcm_remote_dummy_cmd_fn(struct se_cmd *se_cmd)43{44 return 0;45}46 47static void tcm_remote_dummy_cmd_void_fn(struct se_cmd *se_cmd)48{49 50}51 52static char *tcm_remote_dump_proto_id(struct tcm_remote_hba *remote_hba)53{54 switch (remote_hba->remote_proto_id) {55 case SCSI_PROTOCOL_SAS:56 return "SAS";57 case SCSI_PROTOCOL_SRP:58 return "SRP";59 case SCSI_PROTOCOL_FCP:60 return "FCP";61 case SCSI_PROTOCOL_ISCSI:62 return "iSCSI";63 default:64 break;65 }66 67 return "Unknown";68}69 70static int tcm_remote_port_link(71 struct se_portal_group *se_tpg,72 struct se_lun *lun)73{74 pr_debug("TCM_Remote_ConfigFS: Port Link LUN %lld Successful\n",75 lun->unpacked_lun);76 return 0;77}78 79static void tcm_remote_port_unlink(80 struct se_portal_group *se_tpg,81 struct se_lun *lun)82{83 pr_debug("TCM_Remote_ConfigFS: Port Unlink LUN %lld Successful\n",84 lun->unpacked_lun);85}86 87static struct se_portal_group *tcm_remote_make_tpg(88 struct se_wwn *wwn,89 const char *name)90{91 struct tcm_remote_hba *remote_hba = container_of(wwn,92 struct tcm_remote_hba, remote_hba_wwn);93 struct tcm_remote_tpg *remote_tpg;94 unsigned long tpgt;95 int ret;96 97 if (strstr(name, "tpgt_") != name) {98 pr_err("Unable to locate \"tpgt_#\" directory group\n");99 return ERR_PTR(-EINVAL);100 }101 if (kstrtoul(name + 5, 10, &tpgt))102 return ERR_PTR(-EINVAL);103 104 if (tpgt >= TL_TPGS_PER_HBA) {105 pr_err("Passed tpgt: %lu exceeds TL_TPGS_PER_HBA: %u\n",106 tpgt, TL_TPGS_PER_HBA);107 return ERR_PTR(-EINVAL);108 }109 remote_tpg = &remote_hba->remote_hba_tpgs[tpgt];110 remote_tpg->remote_hba = remote_hba;111 remote_tpg->remote_tpgt = tpgt;112 /*113 * Register the remote_tpg as a emulated TCM Target Endpoint114 */115 ret = core_tpg_register(wwn, &remote_tpg->remote_se_tpg,116 remote_hba->remote_proto_id);117 if (ret < 0)118 return ERR_PTR(-ENOMEM);119 120 pr_debug("TCM_Remote_ConfigFS: Allocated Emulated %s Target Port %s,t,0x%04lx\n",121 tcm_remote_dump_proto_id(remote_hba),122 config_item_name(&wwn->wwn_group.cg_item), tpgt);123 return &remote_tpg->remote_se_tpg;124}125 126static void tcm_remote_drop_tpg(struct se_portal_group *se_tpg)127{128 struct se_wwn *wwn = se_tpg->se_tpg_wwn;129 struct tcm_remote_tpg *remote_tpg = container_of(se_tpg,130 struct tcm_remote_tpg, remote_se_tpg);131 struct tcm_remote_hba *remote_hba;132 unsigned short tpgt;133 134 remote_hba = remote_tpg->remote_hba;135 tpgt = remote_tpg->remote_tpgt;136 137 /*138 * Deregister the remote_tpg as a emulated TCM Target Endpoint139 */140 core_tpg_deregister(se_tpg);141 142 remote_tpg->remote_hba = NULL;143 remote_tpg->remote_tpgt = 0;144 145 pr_debug("TCM_Remote_ConfigFS: Deallocated Emulated %s Target Port %s,t,0x%04x\n",146 tcm_remote_dump_proto_id(remote_hba),147 config_item_name(&wwn->wwn_group.cg_item), tpgt);148}149 150static struct se_wwn *tcm_remote_make_wwn(151 struct target_fabric_configfs *tf,152 struct config_group *group,153 const char *name)154{155 struct tcm_remote_hba *remote_hba;156 char *ptr;157 int ret, off = 0;158 159 remote_hba = kzalloc(sizeof(*remote_hba), GFP_KERNEL);160 if (!remote_hba)161 return ERR_PTR(-ENOMEM);162 163 /*164 * Determine the emulated Protocol Identifier and Target Port Name165 * based on the incoming configfs directory name.166 */167 ptr = strstr(name, "naa.");168 if (ptr) {169 remote_hba->remote_proto_id = SCSI_PROTOCOL_SAS;170 goto check_len;171 }172 ptr = strstr(name, "fc.");173 if (ptr) {174 remote_hba->remote_proto_id = SCSI_PROTOCOL_FCP;175 off = 3; /* Skip over "fc." */176 goto check_len;177 }178 ptr = strstr(name, "0x");179 if (ptr) {180 remote_hba->remote_proto_id = SCSI_PROTOCOL_SRP;181 off = 2; /* Skip over "0x" */182 goto check_len;183 }184 ptr = strstr(name, "iqn.");185 if (!ptr) {186 pr_err("Unable to locate prefix for emulated Target Port: %s\n",187 name);188 ret = -EINVAL;189 goto out;190 }191 remote_hba->remote_proto_id = SCSI_PROTOCOL_ISCSI;192 193check_len:194 if (strlen(name) >= TL_WWN_ADDR_LEN) {195 pr_err("Emulated NAA %s Address: %s, exceeds max: %d\n",196 name, tcm_remote_dump_proto_id(remote_hba), TL_WWN_ADDR_LEN);197 ret = -EINVAL;198 goto out;199 }200 snprintf(&remote_hba->remote_wwn_address[0], TL_WWN_ADDR_LEN, "%s", &name[off]);201 202 pr_debug("TCM_Remote_ConfigFS: Allocated emulated Target %s Address: %s\n",203 tcm_remote_dump_proto_id(remote_hba), name);204 return &remote_hba->remote_hba_wwn;205out:206 kfree(remote_hba);207 return ERR_PTR(ret);208}209 210static void tcm_remote_drop_wwn(struct se_wwn *wwn)211{212 struct tcm_remote_hba *remote_hba = container_of(wwn,213 struct tcm_remote_hba, remote_hba_wwn);214 215 pr_debug("TCM_Remote_ConfigFS: Deallocating emulated Target %s Address: %s\n",216 tcm_remote_dump_proto_id(remote_hba),217 remote_hba->remote_wwn_address);218 kfree(remote_hba);219}220 221static ssize_t tcm_remote_wwn_version_show(struct config_item *item, char *page)222{223 return sprintf(page, "TCM Remote Fabric module %s\n", TCM_REMOTE_VERSION);224}225 226CONFIGFS_ATTR_RO(tcm_remote_wwn_, version);227 228static struct configfs_attribute *tcm_remote_wwn_attrs[] = {229 &tcm_remote_wwn_attr_version,230 NULL,231};232 233static const struct target_core_fabric_ops remote_ops = {234 .module = THIS_MODULE,235 .fabric_name = "remote",236 .tpg_get_wwn = tcm_remote_get_endpoint_wwn,237 .tpg_get_tag = tcm_remote_get_tag,238 .check_stop_free = tcm_remote_dummy_cmd_fn,239 .release_cmd = tcm_remote_dummy_cmd_void_fn,240 .write_pending = tcm_remote_dummy_cmd_fn,241 .queue_data_in = tcm_remote_dummy_cmd_fn,242 .queue_status = tcm_remote_dummy_cmd_fn,243 .queue_tm_rsp = tcm_remote_dummy_cmd_void_fn,244 .aborted_task = tcm_remote_dummy_cmd_void_fn,245 .fabric_make_wwn = tcm_remote_make_wwn,246 .fabric_drop_wwn = tcm_remote_drop_wwn,247 .fabric_make_tpg = tcm_remote_make_tpg,248 .fabric_drop_tpg = tcm_remote_drop_tpg,249 .fabric_post_link = tcm_remote_port_link,250 .fabric_pre_unlink = tcm_remote_port_unlink,251 .tfc_wwn_attrs = tcm_remote_wwn_attrs,252};253 254static int __init tcm_remote_fabric_init(void)255{256 return target_register_template(&remote_ops);257}258 259static void __exit tcm_remote_fabric_exit(void)260{261 target_unregister_template(&remote_ops);262}263 264MODULE_DESCRIPTION("TCM virtual remote target");265MODULE_AUTHOR("Dmitry Bogdanov <d.bogdanov@yadro.com>");266MODULE_LICENSE("GPL");267module_init(tcm_remote_fabric_init);268module_exit(tcm_remote_fabric_exit);269