1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022-2024 Chelsio Communications, Inc.
5 * Written by: John Baldwin <jhb@FreeBSD.org>
6 */
7
8#include <sys/refcount.h>
9#include <assert.h>
10#include <errno.h>
11#include <stdarg.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include "libnvmf.h"
17#include "internal.h"
18
19struct nvmf_association *
20nvmf_allocate_association(enum nvmf_trtype trtype, bool controller,
21    const struct nvmf_association_params *params)
22{
23	struct nvmf_transport_ops *ops;
24	struct nvmf_association *na;
25
26	switch (trtype) {
27	case NVMF_TRTYPE_TCP:
28		ops = &tcp_ops;
29		break;
30	default:
31		errno = EINVAL;
32		return (NULL);
33	}
34
35	na = ops->allocate_association(controller, params);
36	if (na == NULL)
37		return (NULL);
38
39	na->na_ops = ops;
40	na->na_trtype = trtype;
41	na->na_controller = controller;
42	na->na_params = *params;
43	na->na_last_error = NULL;
44	refcount_init(&na->na_refs, 1);
45	return (na);
46}
47
48void
49nvmf_update_assocation(struct nvmf_association *na,
50    const struct nvme_controller_data *cdata)
51{
52	na->na_ops->update_association(na, cdata);
53}
54
55void
56nvmf_free_association(struct nvmf_association *na)
57{
58	if (refcount_release(&na->na_refs)) {
59		free(na->na_last_error);
60		na->na_ops->free_association(na);
61	}
62}
63
64const char *
65nvmf_association_error(const struct nvmf_association *na)
66{
67	return (na->na_last_error);
68}
69
70void
71na_clear_error(struct nvmf_association *na)
72{
73	free(na->na_last_error);
74	na->na_last_error = NULL;
75}
76
77void
78na_error(struct nvmf_association *na, const char *fmt, ...)
79{
80	va_list ap;
81	char *str;
82
83	if (na->na_last_error != NULL)
84		return;
85	va_start(ap, fmt);
86	vasprintf(&str, fmt, ap);
87	va_end(ap);
88	na->na_last_error = str;
89}
90
91struct nvmf_qpair *
92nvmf_allocate_qpair(struct nvmf_association *na,
93    const struct nvmf_qpair_params *params)
94{
95	struct nvmf_qpair *qp;
96
97	na_clear_error(na);
98	qp = na->na_ops->allocate_qpair(na, params);
99	if (qp == NULL)
100		return (NULL);
101
102	refcount_acquire(&na->na_refs);
103	qp->nq_association = na;
104	qp->nq_admin = params->admin;
105	TAILQ_INIT(&qp->nq_rx_capsules);
106	return (qp);
107}
108
109void
110nvmf_free_qpair(struct nvmf_qpair *qp)
111{
112	struct nvmf_association *na;
113	struct nvmf_capsule *nc, *tc;
114
115	TAILQ_FOREACH_SAFE(nc, &qp->nq_rx_capsules, nc_link, tc) {
116		TAILQ_REMOVE(&qp->nq_rx_capsules, nc, nc_link);
117		nvmf_free_capsule(nc);
118	}
119	na = qp->nq_association;
120	na->na_ops->free_qpair(qp);
121	nvmf_free_association(na);
122}
123
124struct nvmf_capsule *
125nvmf_allocate_command(struct nvmf_qpair *qp, const void *sqe)
126{
127	struct nvmf_capsule *nc;
128
129	nc = qp->nq_association->na_ops->allocate_capsule(qp);
130	if (nc == NULL)
131		return (NULL);
132
133	nc->nc_qpair = qp;
134	nc->nc_qe_len = sizeof(struct nvme_command);
135	memcpy(&nc->nc_sqe, sqe, nc->nc_qe_len);
136
137	/* 4.2 of NVMe base spec: Fabrics always uses SGL. */
138	nc->nc_sqe.fuse &= ~NVMEM(NVME_CMD_PSDT);
139	nc->nc_sqe.fuse |= NVMEF(NVME_CMD_PSDT, NVME_PSDT_SGL);
140	return (nc);
141}
142
143struct nvmf_capsule *
144nvmf_allocate_response(struct nvmf_qpair *qp, const void *cqe)
145{
146	struct nvmf_capsule *nc;
147
148	nc = qp->nq_association->na_ops->allocate_capsule(qp);
149	if (nc == NULL)
150		return (NULL);
151
152	nc->nc_qpair = qp;
153	nc->nc_qe_len = sizeof(struct nvme_completion);
154	memcpy(&nc->nc_cqe, cqe, nc->nc_qe_len);
155	return (nc);
156}
157
158int
159nvmf_capsule_append_data(struct nvmf_capsule *nc, void *buf, size_t len,
160    bool send)
161{
162	if (nc->nc_qe_len == sizeof(struct nvme_completion))
163		return (EINVAL);
164	if (nc->nc_data_len != 0)
165		return (EBUSY);
166
167	nc->nc_data = buf;
168	nc->nc_data_len = len;
169	nc->nc_send_data = send;
170	return (0);
171}
172
173void
174nvmf_free_capsule(struct nvmf_capsule *nc)
175{
176	nc->nc_qpair->nq_association->na_ops->free_capsule(nc);
177}
178
179int
180nvmf_transmit_capsule(struct nvmf_capsule *nc)
181{
182	return (nc->nc_qpair->nq_association->na_ops->transmit_capsule(nc));
183}
184
185int
186nvmf_receive_capsule(struct nvmf_qpair *qp, struct nvmf_capsule **ncp)
187{
188	return (qp->nq_association->na_ops->receive_capsule(qp, ncp));
189}
190
191const void *
192nvmf_capsule_sqe(const struct nvmf_capsule *nc)
193{
194	assert(nc->nc_qe_len == sizeof(struct nvme_command));
195	return (&nc->nc_sqe);
196}
197
198const void *
199nvmf_capsule_cqe(const struct nvmf_capsule *nc)
200{
201	assert(nc->nc_qe_len == sizeof(struct nvme_completion));
202	return (&nc->nc_cqe);
203}
204
205uint8_t
206nvmf_validate_command_capsule(const struct nvmf_capsule *nc)
207{
208	assert(nc->nc_qe_len == sizeof(struct nvme_command));
209
210	if (NVMEV(NVME_CMD_PSDT, nc->nc_sqe.fuse) != NVME_PSDT_SGL)
211		return (NVME_SC_INVALID_FIELD);
212
213	return (nc->nc_qpair->nq_association->na_ops->validate_command_capsule(nc));
214}
215
216size_t
217nvmf_capsule_data_len(const struct nvmf_capsule *nc)
218{
219	return (nc->nc_qpair->nq_association->na_ops->capsule_data_len(nc));
220}
221
222int
223nvmf_receive_controller_data(const struct nvmf_capsule *nc,
224    uint32_t data_offset, void *buf, size_t len)
225{
226	return (nc->nc_qpair->nq_association->na_ops->receive_controller_data(nc,
227	    data_offset, buf, len));
228}
229
230int
231nvmf_send_controller_data(const struct nvmf_capsule *nc, const void *buf,
232    size_t len)
233{
234	return (nc->nc_qpair->nq_association->na_ops->send_controller_data(nc,
235	    buf, len));
236}
237
238int
239nvmf_kernel_handoff_params(struct nvmf_qpair *qp,
240    struct nvmf_handoff_qpair_params *qparams)
241{
242	memset(qparams, 0, sizeof(*qparams));
243	qparams->admin = qp->nq_admin;
244	qparams->sq_flow_control = qp->nq_flow_control;
245	qparams->qsize = qp->nq_qsize;
246	qparams->sqhd = qp->nq_sqhd;
247	qparams->sqtail = qp->nq_sqtail;
248	return (qp->nq_association->na_ops->kernel_handoff_params(qp, qparams));
249}
250
251const char *
252nvmf_transport_type(uint8_t trtype)
253{
254	static _Thread_local char buf[8];
255
256	switch (trtype) {
257	case NVMF_TRTYPE_RDMA:
258		return ("RDMA");
259	case NVMF_TRTYPE_FC:
260		return ("Fibre Channel");
261	case NVMF_TRTYPE_TCP:
262		return ("TCP");
263	case NVMF_TRTYPE_INTRA_HOST:
264		return ("Intra-host");
265	default:
266		snprintf(buf, sizeof(buf), "0x%02x\n", trtype);
267		return (buf);
268	}
269}
270