| 
 | 1 | +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB  | 
 | 2 | +/*  | 
 | 3 | + * Copyright (c) 2018-2025 Advanced Micro Devices, Inc.  All rights reserved.  | 
 | 4 | + */  | 
 | 5 | + | 
 | 6 | +#include <stdio.h>  | 
 | 7 | +#include <stdlib.h>  | 
 | 8 | + | 
 | 9 | +#include "ionic.h"  | 
 | 10 | + | 
 | 11 | +extern const struct verbs_context_ops ionic_ctx_ops;  | 
 | 12 | +struct ionic_global ionic_g;  | 
 | 13 | +FILE *IONIC_DEBUG_FILE;  | 
 | 14 | + | 
 | 15 | +static int ionic_env_val_def(const char *name, int def)  | 
 | 16 | +{  | 
 | 17 | +	const char *env = getenv(name);  | 
 | 18 | + | 
 | 19 | +	if (!env)  | 
 | 20 | +		return def;  | 
 | 21 | + | 
 | 22 | +	return atoi(env);  | 
 | 23 | +}  | 
 | 24 | + | 
 | 25 | +static int ionic_env_val(const char *name)  | 
 | 26 | +{  | 
 | 27 | +	return ionic_env_val_def(name, 0);  | 
 | 28 | +}  | 
 | 29 | + | 
 | 30 | +static int ionic_env_ovrd_cmb(const char *name)  | 
 | 31 | +{  | 
 | 32 | +	const char *env = getenv(name);  | 
 | 33 | +	int val;  | 
 | 34 | + | 
 | 35 | +	if (!env)  | 
 | 36 | +		return -1;  | 
 | 37 | + | 
 | 38 | +	/* flags can be represented as one number or as characters */  | 
 | 39 | +	val = atoi(env);  | 
 | 40 | +	if (strchr(env, 'e'))  | 
 | 41 | +		val |= IONIC_CMB_ENABLE;  | 
 | 42 | +	if (strchr(env, 'x'))  | 
 | 43 | +		val |= IONIC_CMB_ENABLE | IONIC_CMB_EXPDB;  | 
 | 44 | +	if (strchr(env, 'r'))  | 
 | 45 | +		val |= IONIC_CMB_REQUIRE;  | 
 | 46 | +	if (strchr(env, 'w'))  | 
 | 47 | +		val |= IONIC_CMB_WC;  | 
 | 48 | +	if (strchr(env, 'u'))  | 
 | 49 | +		val |= IONIC_CMB_UC;  | 
 | 50 | + | 
 | 51 | +	return val;  | 
 | 52 | +}  | 
 | 53 | + | 
 | 54 | +static int ionic_env_ovrd_sq_cmb(void)  | 
 | 55 | +{  | 
 | 56 | +	return ionic_env_ovrd_cmb("IONIC_SQ_CMB");  | 
 | 57 | +}  | 
 | 58 | + | 
 | 59 | +static int ionic_env_ovrd_rq_cmb(void)  | 
 | 60 | +{  | 
 | 61 | +	return ionic_env_ovrd_cmb("IONIC_RQ_CMB");  | 
 | 62 | +}  | 
 | 63 | + | 
 | 64 | +static int ionic_env_debug(void)  | 
 | 65 | +{  | 
 | 66 | +	if (!(IONIC_DEBUG))  | 
 | 67 | +		return 0;  | 
 | 68 | + | 
 | 69 | +	return ionic_env_val("IONIC_DEBUG");  | 
 | 70 | +}  | 
 | 71 | + | 
 | 72 | +static void ionic_debug_file_close(void)  | 
 | 73 | +{  | 
 | 74 | +	fclose(IONIC_DEBUG_FILE);  | 
 | 75 | +}  | 
 | 76 | + | 
 | 77 | +static void ionic_debug_file_open(void)  | 
 | 78 | +{  | 
 | 79 | +	const char *name = getenv("IONIC_DEBUG_FILE");  | 
 | 80 | + | 
 | 81 | +	pthread_mutex_init(&ionic_g.mut, NULL);  | 
 | 82 | +	list_head_init(&ionic_g.cq_list);  | 
 | 83 | +	list_head_init(&ionic_g.qp_list);  | 
 | 84 | +	ionic_g.init = true;  | 
 | 85 | + | 
 | 86 | +	if (!name)  | 
 | 87 | +		IONIC_DEBUG_FILE = IONIC_DEFAULT_DEBUG_FILE;  | 
 | 88 | +	else  | 
 | 89 | +		IONIC_DEBUG_FILE = fopen(name, "w");  | 
 | 90 | + | 
 | 91 | +	if (!IONIC_DEBUG_FILE) {  | 
 | 92 | +		perror("ionic debug file: ");  | 
 | 93 | +		return;  | 
 | 94 | +	}  | 
 | 95 | + | 
 | 96 | +	if (ionic_env_debug())  | 
 | 97 | +		_ionic_dbg(IONIC_DEBUG_FILE, "Initialized");  | 
 | 98 | + | 
 | 99 | +	atexit(ionic_debug_file_close);  | 
 | 100 | +}  | 
 | 101 | + | 
 | 102 | +static void ionic_debug_file_init(void)  | 
 | 103 | +{  | 
 | 104 | +	static pthread_once_t once = PTHREAD_ONCE_INIT;  | 
 | 105 | + | 
 | 106 | +	pthread_once(&once, ionic_debug_file_open);  | 
 | 107 | +}  | 
 | 108 | + | 
 | 109 | +static struct verbs_context *ionic_alloc_context(struct ibv_device *ibdev,  | 
 | 110 | +						 int cmd_fd,  | 
 | 111 | +						 void *private_data)  | 
 | 112 | +{  | 
 | 113 | +	struct ionic_dev *dev;  | 
 | 114 | +	struct ionic_ctx *ctx;  | 
 | 115 | +	struct uionic_ctx req = {};  | 
 | 116 | +	struct uionic_ctx_resp resp = {};  | 
 | 117 | +	uint64_t mask;  | 
 | 118 | +	int rc;  | 
 | 119 | + | 
 | 120 | +	ionic_debug_file_init();  | 
 | 121 | + | 
 | 122 | +	dev = to_ionic_dev(ibdev);  | 
 | 123 | +	ctx = verbs_init_and_alloc_context(ibdev, cmd_fd, ctx, vctx,  | 
 | 124 | +					   RDMA_DRIVER_IONIC);  | 
 | 125 | +	if (!ctx) {  | 
 | 126 | +		rc = errno;  | 
 | 127 | +		goto err_ctx;  | 
 | 128 | +	}  | 
 | 129 | + | 
 | 130 | +	rc = ibv_cmd_get_context(&ctx->vctx, &req.ibv_cmd, sizeof(req),  | 
 | 131 | +				 NULL, &resp.ibv_resp, sizeof(resp));  | 
 | 132 | +	if (rc)  | 
 | 133 | +		goto err_cmd;  | 
 | 134 | + | 
 | 135 | +	ctx->pg_shift = resp.page_shift;  | 
 | 136 | + | 
 | 137 | +	if (resp.version < IONIC_MIN_RDMA_VERSION) {  | 
 | 138 | +		fprintf(stderr, "ionic: Firmware RDMA Version %u\n",  | 
 | 139 | +			resp.version);  | 
 | 140 | +		fprintf(stderr, "ionic: Driver Min RDMA Version %u\n",  | 
 | 141 | +			IONIC_MIN_RDMA_VERSION);  | 
 | 142 | +		rc = EINVAL;  | 
 | 143 | +		goto err_cmd;  | 
 | 144 | +	}  | 
 | 145 | + | 
 | 146 | +	if (resp.version > IONIC_MAX_RDMA_VERSION) {  | 
 | 147 | +		fprintf(stderr, "ionic: Firmware RDMA Version %u\n",  | 
 | 148 | +			resp.version);  | 
 | 149 | +		fprintf(stderr, "ionic: Driver Max RDMA Version %u\n",  | 
 | 150 | +			IONIC_MAX_RDMA_VERSION);  | 
 | 151 | +		rc = EINVAL;  | 
 | 152 | +		goto err_cmd;  | 
 | 153 | +	}  | 
 | 154 | + | 
 | 155 | +	ctx->version = resp.version;  | 
 | 156 | +	ctx->opcodes = resp.qp_opcodes;  | 
 | 157 | +	if (ctx->version == 1 && ctx->opcodes <= IONIC_V1_OP_BIND_MW) {  | 
 | 158 | +		fprintf(stderr, "ionic: qp opcodes %d want min %d\n",  | 
 | 159 | +			ctx->opcodes, IONIC_V1_OP_BIND_MW + 1);  | 
 | 160 | +		rc = EINVAL;  | 
 | 161 | +		goto err_cmd;  | 
 | 162 | +	}  | 
 | 163 | + | 
 | 164 | +	if (resp.udma_count != 1 && resp.udma_count != 2) {  | 
 | 165 | +		fprintf(stderr, "ionic: udma_count %d invalid\n",  | 
 | 166 | +			resp.udma_count);  | 
 | 167 | +		rc = EINVAL;  | 
 | 168 | +		goto err_cmd;  | 
 | 169 | +	}  | 
 | 170 | +	ctx->udma_count = resp.udma_count;  | 
 | 171 | + | 
 | 172 | +	ctx->sq_qtype = resp.sq_qtype;  | 
 | 173 | +	ctx->rq_qtype = resp.rq_qtype;  | 
 | 174 | +	ctx->cq_qtype = resp.cq_qtype;  | 
 | 175 | + | 
 | 176 | +	ctx->max_stride = resp.max_stride;  | 
 | 177 | + | 
 | 178 | +	ctx->expdb_mask = resp.expdb_mask;  | 
 | 179 | +	ctx->sq_expdb = !!(resp.expdb_qtypes & IONIC_EXPDB_SQ);  | 
 | 180 | +	ctx->rq_expdb = !!(resp.expdb_qtypes & IONIC_EXPDB_RQ);  | 
 | 181 | + | 
 | 182 | +	ctx->ovrd_sq_cmb = ionic_env_ovrd_sq_cmb();  | 
 | 183 | +	ctx->ovrd_rq_cmb = ionic_env_ovrd_rq_cmb();  | 
 | 184 | + | 
 | 185 | +	mask = (1u << ctx->pg_shift) - 1;  | 
 | 186 | +	ctx->dbpage_page = ionic_map_device(1u << ctx->pg_shift, cmd_fd,  | 
 | 187 | +					    resp.dbell_offset & ~mask);  | 
 | 188 | +	if (!ctx->dbpage_page) {  | 
 | 189 | +		rc = errno;  | 
 | 190 | +		goto err_cmd;  | 
 | 191 | +	}  | 
 | 192 | +	ctx->dbpage = ctx->dbpage_page + (resp.dbell_offset & mask);  | 
 | 193 | + | 
 | 194 | +	pthread_mutex_init(&ctx->mut, NULL);  | 
 | 195 | +	ionic_tbl_init(&ctx->qp_tbl);  | 
 | 196 | + | 
 | 197 | +	verbs_set_ops(&ctx->vctx, &ionic_ctx_ops);  | 
 | 198 | + | 
 | 199 | +	if (dev->abi_ver <= 1) {  | 
 | 200 | +		ctx->spec = 0;  | 
 | 201 | +	} else {  | 
 | 202 | +		ctx->spec = resp.max_spec;  | 
 | 203 | +		if (ctx->spec < 0 || ctx->spec > 16)  | 
 | 204 | +			ctx->spec = 0;  | 
 | 205 | +	}  | 
 | 206 | + | 
 | 207 | +	if (ionic_env_debug()) {  | 
 | 208 | +		ctx->dbg_file = IONIC_DEBUG_FILE;  | 
 | 209 | +		ionic_dbg(ctx, "Attached to ctx %p", ctx);  | 
 | 210 | +	}  | 
 | 211 | + | 
 | 212 | +	return &ctx->vctx;  | 
 | 213 | + | 
 | 214 | +err_cmd:  | 
 | 215 | +	verbs_uninit_context(&ctx->vctx);  | 
 | 216 | +err_ctx:  | 
 | 217 | +	errno = rc;  | 
 | 218 | +	return NULL;  | 
 | 219 | +}  | 
 | 220 | + | 
 | 221 | +static const struct verbs_match_ent cna_table[] = {  | 
 | 222 | +	VERBS_DRIVER_ID(RDMA_DRIVER_IONIC),  | 
 | 223 | +	{}  | 
 | 224 | +};  | 
 | 225 | + | 
 | 226 | +static struct verbs_device *ionic_alloc_device(struct verbs_sysfs_dev *sdev)  | 
 | 227 | +{  | 
 | 228 | +	struct ionic_dev *dev;  | 
 | 229 | + | 
 | 230 | +	static_assert(sizeof(struct ionic_v1_cqe) == 32, "bad size");  | 
 | 231 | +	static_assert(sizeof(struct ionic_v1_base_hdr) == 16, "bad size");  | 
 | 232 | +	static_assert(sizeof(struct ionic_v1_recv_bdy) == 48, "bad size");  | 
 | 233 | +	static_assert(sizeof(struct ionic_v1_common_bdy) == 48, "bad size");  | 
 | 234 | +	static_assert(sizeof(struct ionic_v1_atomic_bdy) == 48, "bad size");  | 
 | 235 | +	static_assert(sizeof(struct ionic_v1_bind_mw_bdy) == 48, "bad size");  | 
 | 236 | +	static_assert(sizeof(struct ionic_v1_wqe) == 64, "bad size");  | 
 | 237 | + | 
 | 238 | +	dev = calloc(1, sizeof(*dev));  | 
 | 239 | +	if (!dev)  | 
 | 240 | +		return NULL;  | 
 | 241 | + | 
 | 242 | +	dev->abi_ver = sdev->abi_ver;  | 
 | 243 | + | 
 | 244 | +	return &dev->vdev;  | 
 | 245 | +}  | 
 | 246 | + | 
 | 247 | +static void ionic_uninit_device(struct verbs_device *vdev)  | 
 | 248 | +{  | 
 | 249 | +	struct ionic_dev *dev = to_ionic_dev(&vdev->device);  | 
 | 250 | + | 
 | 251 | +	free(dev);  | 
 | 252 | +}  | 
 | 253 | + | 
 | 254 | +static const struct verbs_device_ops ionic_dev_ops = {  | 
 | 255 | +	.name			= "ionic",  | 
 | 256 | +	.match_min_abi_version	= IONIC_ABI_VERSION,  | 
 | 257 | +	.match_max_abi_version	= IONIC_ABI_VERSION,  | 
 | 258 | +	.match_table		= cna_table,  | 
 | 259 | +	.alloc_device		= ionic_alloc_device,  | 
 | 260 | +	.uninit_device		= ionic_uninit_device,  | 
 | 261 | +	.alloc_context		= ionic_alloc_context,  | 
 | 262 | +};  | 
 | 263 | + | 
 | 264 | +PROVIDER_DRIVER(ionic, ionic_dev_ops);  | 
0 commit comments