1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Shared Memory Communications Direct over loopback-ism device. 4 * 5 * Functions for loopback-ism device. 6 * 7 * Copyright (c) 2024, Alibaba Inc. 8 * 9 * Author: Wen Gu <[email protected]> 10 * Tony Lu <[email protected]> 11 * 12 */ 13 14 #include <linux/device.h> 15 #include <linux/types.h> 16 #include <net/smc.h> 17 18 #include "smc_ism.h" 19 #include "smc_loopback.h" 20 21 #define SMC_LO_V2_CAPABLE 0x1 /* loopback-ism acts as ISMv2 */ 22 23 static const char smc_lo_dev_name[] = "loopback-ism"; 24 static struct smc_lo_dev *lo_dev; 25 26 static void smc_lo_generate_ids(struct smc_lo_dev *ldev) 27 { 28 struct smcd_gid *lgid = &ldev->local_gid; 29 uuid_t uuid; 30 31 uuid_gen(&uuid); 32 memcpy(&lgid->gid, &uuid, sizeof(lgid->gid)); 33 memcpy(&lgid->gid_ext, (u8 *)&uuid + sizeof(lgid->gid), 34 sizeof(lgid->gid_ext)); 35 36 ldev->chid = SMC_LO_RESERVED_CHID; 37 } 38 39 static int smc_lo_query_rgid(struct smcd_dev *smcd, struct smcd_gid *rgid, 40 u32 vid_valid, u32 vid) 41 { 42 struct smc_lo_dev *ldev = smcd->priv; 43 44 /* rgid should be the same as lgid */ 45 if (!ldev || rgid->gid != ldev->local_gid.gid || 46 rgid->gid_ext != ldev->local_gid.gid_ext) 47 return -ENETUNREACH; 48 return 0; 49 } 50 51 static int smc_lo_supports_v2(void) 52 { 53 return SMC_LO_V2_CAPABLE; 54 } 55 56 static void smc_lo_get_local_gid(struct smcd_dev *smcd, 57 struct smcd_gid *smcd_gid) 58 { 59 struct smc_lo_dev *ldev = smcd->priv; 60 61 smcd_gid->gid = ldev->local_gid.gid; 62 smcd_gid->gid_ext = ldev->local_gid.gid_ext; 63 } 64 65 static u16 smc_lo_get_chid(struct smcd_dev *smcd) 66 { 67 return ((struct smc_lo_dev *)smcd->priv)->chid; 68 } 69 70 static struct device *smc_lo_get_dev(struct smcd_dev *smcd) 71 { 72 return &((struct smc_lo_dev *)smcd->priv)->dev; 73 } 74 75 static const struct smcd_ops lo_ops = { 76 .query_remote_gid = smc_lo_query_rgid, 77 .register_dmb = NULL, 78 .unregister_dmb = NULL, 79 .add_vlan_id = NULL, 80 .del_vlan_id = NULL, 81 .set_vlan_required = NULL, 82 .reset_vlan_required = NULL, 83 .signal_event = NULL, 84 .move_data = NULL, 85 .supports_v2 = smc_lo_supports_v2, 86 .get_local_gid = smc_lo_get_local_gid, 87 .get_chid = smc_lo_get_chid, 88 .get_dev = smc_lo_get_dev, 89 }; 90 91 static struct smcd_dev *smcd_lo_alloc_dev(const struct smcd_ops *ops, 92 int max_dmbs) 93 { 94 struct smcd_dev *smcd; 95 96 smcd = kzalloc(sizeof(*smcd), GFP_KERNEL); 97 if (!smcd) 98 return NULL; 99 100 smcd->conn = kcalloc(max_dmbs, sizeof(struct smc_connection *), 101 GFP_KERNEL); 102 if (!smcd->conn) 103 goto out_smcd; 104 105 smcd->ops = ops; 106 107 spin_lock_init(&smcd->lock); 108 spin_lock_init(&smcd->lgr_lock); 109 INIT_LIST_HEAD(&smcd->vlan); 110 INIT_LIST_HEAD(&smcd->lgr_list); 111 init_waitqueue_head(&smcd->lgrs_deleted); 112 return smcd; 113 114 out_smcd: 115 kfree(smcd); 116 return NULL; 117 } 118 119 static int smcd_lo_register_dev(struct smc_lo_dev *ldev) 120 { 121 struct smcd_dev *smcd; 122 123 smcd = smcd_lo_alloc_dev(&lo_ops, SMC_LO_MAX_DMBS); 124 if (!smcd) 125 return -ENOMEM; 126 ldev->smcd = smcd; 127 smcd->priv = ldev; 128 129 /* TODO: 130 * register loopback-ism to smcd_dev list. 131 */ 132 return 0; 133 } 134 135 static void smcd_lo_unregister_dev(struct smc_lo_dev *ldev) 136 { 137 struct smcd_dev *smcd = ldev->smcd; 138 139 /* TODO: 140 * unregister loopback-ism from smcd_dev list. 141 */ 142 kfree(smcd->conn); 143 kfree(smcd); 144 } 145 146 static int smc_lo_dev_init(struct smc_lo_dev *ldev) 147 { 148 smc_lo_generate_ids(ldev); 149 return smcd_lo_register_dev(ldev); 150 } 151 152 static void smc_lo_dev_exit(struct smc_lo_dev *ldev) 153 { 154 smcd_lo_unregister_dev(ldev); 155 } 156 157 static void smc_lo_dev_release(struct device *dev) 158 { 159 struct smc_lo_dev *ldev = 160 container_of(dev, struct smc_lo_dev, dev); 161 162 kfree(ldev); 163 } 164 165 static int smc_lo_dev_probe(void) 166 { 167 struct smc_lo_dev *ldev; 168 int ret; 169 170 ldev = kzalloc(sizeof(*ldev), GFP_KERNEL); 171 if (!ldev) 172 return -ENOMEM; 173 174 ldev->dev.parent = NULL; 175 ldev->dev.release = smc_lo_dev_release; 176 device_initialize(&ldev->dev); 177 dev_set_name(&ldev->dev, smc_lo_dev_name); 178 179 ret = smc_lo_dev_init(ldev); 180 if (ret) 181 goto free_dev; 182 183 lo_dev = ldev; /* global loopback device */ 184 return 0; 185 186 free_dev: 187 put_device(&ldev->dev); 188 return ret; 189 } 190 191 static void smc_lo_dev_remove(void) 192 { 193 if (!lo_dev) 194 return; 195 196 smc_lo_dev_exit(lo_dev); 197 put_device(&lo_dev->dev); /* device_initialize in smc_lo_dev_probe */ 198 } 199 200 int smc_loopback_init(void) 201 { 202 return smc_lo_dev_probe(); 203 } 204 205 void smc_loopback_exit(void) 206 { 207 smc_lo_dev_remove(); 208 } 209