Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scsi: target: Add virtual remote target

Create virtual remote target module.

This can be used to see a whole ACL/LUN/TPG configuration from all nodes in
storage cluster. For example, it permits setting up remote ports in ALUA
port groups. To report all ports in a cluster in REPORT TARGET PORT GROUP
command.

Suggested-by: Konstantin Shelekhin <k.shelekhin@yadro.com>
Signed-off-by: Dmitry Bogdanov <d.bogdanov@yadro.com>
Link: https://lore.kernel.org/r/20230313181110.20566-13-d.bogdanov@yadro.com
Reviewed-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Dmitry Bogdanov and committed by
Martin K. Petersen
075a5d35 df02beb9

+300
+1
drivers/target/Kconfig
··· 47 47 source "drivers/target/tcm_fc/Kconfig" 48 48 source "drivers/target/iscsi/Kconfig" 49 49 source "drivers/target/sbp/Kconfig" 50 + source "drivers/target/tcm_remote/Kconfig" 50 51 51 52 endif
+1
drivers/target/Makefile
··· 30 30 obj-$(CONFIG_TCM_FC) += tcm_fc/ 31 31 obj-$(CONFIG_ISCSI_TARGET) += iscsi/ 32 32 obj-$(CONFIG_SBP_TARGET) += sbp/ 33 + obj-$(CONFIG_REMOTE_TARGET) += tcm_remote/
+8
drivers/target/tcm_remote/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + config REMOTE_TARGET 3 + tristate "TCM Virtual Remote target" 4 + depends on SCSI 5 + help 6 + Say Y here to enable the TCM Virtual Remote fabric 7 + That fabric is a dummy fabric to tell TCM about configuration 8 + of TPG/ACL/LUN on peer nodes in a cluster.
+2
drivers/target/tcm_remote/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + obj-$(CONFIG_REMOTE_TARGET) += tcm_remote.o
+268
drivers/target/tcm_remote/tcm_remote.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + 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 + 20 + static 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 + 25 + static char *tcm_remote_get_endpoint_wwn(struct se_portal_group *se_tpg) 26 + { 27 + /* 28 + * Return the passed NAA identifier for the Target Port 29 + */ 30 + return &remote_tpg(se_tpg)->remote_hba->remote_wwn_address[0]; 31 + } 32 + 33 + static 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 0x83 37 + * to represent the SCSI Target Port. 38 + */ 39 + return remote_tpg(se_tpg)->remote_tpgt; 40 + } 41 + 42 + static int tcm_remote_dummy_cmd_fn(struct se_cmd *se_cmd) 43 + { 44 + return 0; 45 + } 46 + 47 + static void tcm_remote_dummy_cmd_void_fn(struct se_cmd *se_cmd) 48 + { 49 + 50 + } 51 + 52 + static 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 + 70 + static 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 + 79 + static 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 + 87 + static 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 Endpoint 114 + */ 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 + 126 + static 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 Endpoint 139 + */ 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 + 150 + static 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 Name 165 + * 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 + 193 + check_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; 205 + out: 206 + kfree(remote_hba); 207 + return ERR_PTR(ret); 208 + } 209 + 210 + static 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 + 221 + static 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 + 226 + CONFIGFS_ATTR_RO(tcm_remote_wwn_, version); 227 + 228 + static struct configfs_attribute *tcm_remote_wwn_attrs[] = { 229 + &tcm_remote_wwn_attr_version, 230 + NULL, 231 + }; 232 + 233 + static 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 + 254 + static int __init tcm_remote_fabric_init(void) 255 + { 256 + return target_register_template(&remote_ops); 257 + } 258 + 259 + static void __exit tcm_remote_fabric_exit(void) 260 + { 261 + target_unregister_template(&remote_ops); 262 + } 263 + 264 + MODULE_DESCRIPTION("TCM virtual remote target"); 265 + MODULE_AUTHOR("Dmitry Bogdanov <d.bogdanov@yadro.com>"); 266 + MODULE_LICENSE("GPL"); 267 + module_init(tcm_remote_fabric_init); 268 + module_exit(tcm_remote_fabric_exit);
+20
drivers/target/tcm_remote/tcm_remote.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #include <linux/types.h> 3 + #include <linux/device.h> 4 + 5 + #define TCM_REMOTE_VERSION "v0.1" 6 + #define TL_WWN_ADDR_LEN 256 7 + #define TL_TPGS_PER_HBA 32 8 + 9 + struct tcm_remote_tpg { 10 + unsigned short remote_tpgt; 11 + struct se_portal_group remote_se_tpg; 12 + struct tcm_remote_hba *remote_hba; 13 + }; 14 + 15 + struct tcm_remote_hba { 16 + u8 remote_proto_id; 17 + unsigned char remote_wwn_address[TL_WWN_ADDR_LEN]; 18 + struct tcm_remote_tpg remote_hba_tpgs[TL_TPGS_PER_HBA]; 19 + struct se_wwn remote_hba_wwn; 20 + };