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 static const char smc_lo_dev_name[] = "loopback-ism"; 22 static struct smc_lo_dev *lo_dev; 23 24 static const struct smcd_ops lo_ops = { 25 .query_remote_gid = NULL, 26 .register_dmb = NULL, 27 .unregister_dmb = NULL, 28 .add_vlan_id = NULL, 29 .del_vlan_id = NULL, 30 .set_vlan_required = NULL, 31 .reset_vlan_required = NULL, 32 .signal_event = NULL, 33 .move_data = NULL, 34 .supports_v2 = NULL, 35 .get_local_gid = NULL, 36 .get_chid = NULL, 37 .get_dev = NULL, 38 }; 39 40 static struct smcd_dev *smcd_lo_alloc_dev(const struct smcd_ops *ops, 41 int max_dmbs) 42 { 43 struct smcd_dev *smcd; 44 45 smcd = kzalloc(sizeof(*smcd), GFP_KERNEL); 46 if (!smcd) 47 return NULL; 48 49 smcd->conn = kcalloc(max_dmbs, sizeof(struct smc_connection *), 50 GFP_KERNEL); 51 if (!smcd->conn) 52 goto out_smcd; 53 54 smcd->ops = ops; 55 56 spin_lock_init(&smcd->lock); 57 spin_lock_init(&smcd->lgr_lock); 58 INIT_LIST_HEAD(&smcd->vlan); 59 INIT_LIST_HEAD(&smcd->lgr_list); 60 init_waitqueue_head(&smcd->lgrs_deleted); 61 return smcd; 62 63 out_smcd: 64 kfree(smcd); 65 return NULL; 66 } 67 68 static int smcd_lo_register_dev(struct smc_lo_dev *ldev) 69 { 70 struct smcd_dev *smcd; 71 72 smcd = smcd_lo_alloc_dev(&lo_ops, SMC_LO_MAX_DMBS); 73 if (!smcd) 74 return -ENOMEM; 75 ldev->smcd = smcd; 76 smcd->priv = ldev; 77 78 /* TODO: 79 * register loopback-ism to smcd_dev list. 80 */ 81 return 0; 82 } 83 84 static void smcd_lo_unregister_dev(struct smc_lo_dev *ldev) 85 { 86 struct smcd_dev *smcd = ldev->smcd; 87 88 /* TODO: 89 * unregister loopback-ism from smcd_dev list. 90 */ 91 kfree(smcd->conn); 92 kfree(smcd); 93 } 94 95 static int smc_lo_dev_init(struct smc_lo_dev *ldev) 96 { 97 return smcd_lo_register_dev(ldev); 98 } 99 100 static void smc_lo_dev_exit(struct smc_lo_dev *ldev) 101 { 102 smcd_lo_unregister_dev(ldev); 103 } 104 105 static void smc_lo_dev_release(struct device *dev) 106 { 107 struct smc_lo_dev *ldev = 108 container_of(dev, struct smc_lo_dev, dev); 109 110 kfree(ldev); 111 } 112 113 static int smc_lo_dev_probe(void) 114 { 115 struct smc_lo_dev *ldev; 116 int ret; 117 118 ldev = kzalloc(sizeof(*ldev), GFP_KERNEL); 119 if (!ldev) 120 return -ENOMEM; 121 122 ldev->dev.parent = NULL; 123 ldev->dev.release = smc_lo_dev_release; 124 device_initialize(&ldev->dev); 125 dev_set_name(&ldev->dev, smc_lo_dev_name); 126 127 ret = smc_lo_dev_init(ldev); 128 if (ret) 129 goto free_dev; 130 131 lo_dev = ldev; /* global loopback device */ 132 return 0; 133 134 free_dev: 135 put_device(&ldev->dev); 136 return ret; 137 } 138 139 static void smc_lo_dev_remove(void) 140 { 141 if (!lo_dev) 142 return; 143 144 smc_lo_dev_exit(lo_dev); 145 put_device(&lo_dev->dev); /* device_initialize in smc_lo_dev_probe */ 146 } 147 148 int smc_loopback_init(void) 149 { 150 return smc_lo_dev_probe(); 151 } 152 153 void smc_loopback_exit(void) 154 { 155 smc_lo_dev_remove(); 156 } 157