mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-06 16:40:47 +00:00
270 lines
5.7 KiB
C
270 lines
5.7 KiB
C
|
/*-
|
||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||
|
*
|
||
|
* Copyright (c) 2022-2024 Chelsio Communications, Inc.
|
||
|
* Written by: John Baldwin <jhb@FreeBSD.org>
|
||
|
*/
|
||
|
|
||
|
#include <sys/refcount.h>
|
||
|
#include <assert.h>
|
||
|
#include <errno.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "libnvmf.h"
|
||
|
#include "internal.h"
|
||
|
|
||
|
struct nvmf_association *
|
||
|
nvmf_allocate_association(enum nvmf_trtype trtype, bool controller,
|
||
|
const struct nvmf_association_params *params)
|
||
|
{
|
||
|
struct nvmf_transport_ops *ops;
|
||
|
struct nvmf_association *na;
|
||
|
|
||
|
switch (trtype) {
|
||
|
case NVMF_TRTYPE_TCP:
|
||
|
ops = &tcp_ops;
|
||
|
break;
|
||
|
default:
|
||
|
errno = EINVAL;
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
na = ops->allocate_association(controller, params);
|
||
|
if (na == NULL)
|
||
|
return (NULL);
|
||
|
|
||
|
na->na_ops = ops;
|
||
|
na->na_trtype = trtype;
|
||
|
na->na_controller = controller;
|
||
|
na->na_params = *params;
|
||
|
na->na_last_error = NULL;
|
||
|
refcount_init(&na->na_refs, 1);
|
||
|
return (na);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nvmf_update_assocation(struct nvmf_association *na,
|
||
|
const struct nvme_controller_data *cdata)
|
||
|
{
|
||
|
na->na_ops->update_association(na, cdata);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nvmf_free_association(struct nvmf_association *na)
|
||
|
{
|
||
|
if (refcount_release(&na->na_refs)) {
|
||
|
free(na->na_last_error);
|
||
|
na->na_ops->free_association(na);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char *
|
||
|
nvmf_association_error(const struct nvmf_association *na)
|
||
|
{
|
||
|
return (na->na_last_error);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
na_clear_error(struct nvmf_association *na)
|
||
|
{
|
||
|
free(na->na_last_error);
|
||
|
na->na_last_error = NULL;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
na_error(struct nvmf_association *na, const char *fmt, ...)
|
||
|
{
|
||
|
va_list ap;
|
||
|
char *str;
|
||
|
|
||
|
if (na->na_last_error != NULL)
|
||
|
return;
|
||
|
va_start(ap, fmt);
|
||
|
vasprintf(&str, fmt, ap);
|
||
|
va_end(ap);
|
||
|
na->na_last_error = str;
|
||
|
}
|
||
|
|
||
|
struct nvmf_qpair *
|
||
|
nvmf_allocate_qpair(struct nvmf_association *na,
|
||
|
const struct nvmf_qpair_params *params)
|
||
|
{
|
||
|
struct nvmf_qpair *qp;
|
||
|
|
||
|
na_clear_error(na);
|
||
|
qp = na->na_ops->allocate_qpair(na, params);
|
||
|
if (qp == NULL)
|
||
|
return (NULL);
|
||
|
|
||
|
refcount_acquire(&na->na_refs);
|
||
|
qp->nq_association = na;
|
||
|
qp->nq_admin = params->admin;
|
||
|
TAILQ_INIT(&qp->nq_rx_capsules);
|
||
|
return (qp);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nvmf_free_qpair(struct nvmf_qpair *qp)
|
||
|
{
|
||
|
struct nvmf_association *na;
|
||
|
struct nvmf_capsule *nc, *tc;
|
||
|
|
||
|
TAILQ_FOREACH_SAFE(nc, &qp->nq_rx_capsules, nc_link, tc) {
|
||
|
TAILQ_REMOVE(&qp->nq_rx_capsules, nc, nc_link);
|
||
|
nvmf_free_capsule(nc);
|
||
|
}
|
||
|
na = qp->nq_association;
|
||
|
na->na_ops->free_qpair(qp);
|
||
|
nvmf_free_association(na);
|
||
|
}
|
||
|
|
||
|
struct nvmf_capsule *
|
||
|
nvmf_allocate_command(struct nvmf_qpair *qp, const void *sqe)
|
||
|
{
|
||
|
struct nvmf_capsule *nc;
|
||
|
|
||
|
nc = qp->nq_association->na_ops->allocate_capsule(qp);
|
||
|
if (nc == NULL)
|
||
|
return (NULL);
|
||
|
|
||
|
nc->nc_qpair = qp;
|
||
|
nc->nc_qe_len = sizeof(struct nvme_command);
|
||
|
memcpy(&nc->nc_sqe, sqe, nc->nc_qe_len);
|
||
|
|
||
|
/* 4.2 of NVMe base spec: Fabrics always uses SGL. */
|
||
|
nc->nc_sqe.fuse &= ~NVMEM(NVME_CMD_PSDT);
|
||
|
nc->nc_sqe.fuse |= NVMEF(NVME_CMD_PSDT, NVME_PSDT_SGL);
|
||
|
return (nc);
|
||
|
}
|
||
|
|
||
|
struct nvmf_capsule *
|
||
|
nvmf_allocate_response(struct nvmf_qpair *qp, const void *cqe)
|
||
|
{
|
||
|
struct nvmf_capsule *nc;
|
||
|
|
||
|
nc = qp->nq_association->na_ops->allocate_capsule(qp);
|
||
|
if (nc == NULL)
|
||
|
return (NULL);
|
||
|
|
||
|
nc->nc_qpair = qp;
|
||
|
nc->nc_qe_len = sizeof(struct nvme_completion);
|
||
|
memcpy(&nc->nc_cqe, cqe, nc->nc_qe_len);
|
||
|
return (nc);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
nvmf_capsule_append_data(struct nvmf_capsule *nc, void *buf, size_t len,
|
||
|
bool send)
|
||
|
{
|
||
|
if (nc->nc_qe_len == sizeof(struct nvme_completion))
|
||
|
return (EINVAL);
|
||
|
if (nc->nc_data_len != 0)
|
||
|
return (EBUSY);
|
||
|
|
||
|
nc->nc_data = buf;
|
||
|
nc->nc_data_len = len;
|
||
|
nc->nc_send_data = send;
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
nvmf_free_capsule(struct nvmf_capsule *nc)
|
||
|
{
|
||
|
nc->nc_qpair->nq_association->na_ops->free_capsule(nc);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
nvmf_transmit_capsule(struct nvmf_capsule *nc)
|
||
|
{
|
||
|
return (nc->nc_qpair->nq_association->na_ops->transmit_capsule(nc));
|
||
|
}
|
||
|
|
||
|
int
|
||
|
nvmf_receive_capsule(struct nvmf_qpair *qp, struct nvmf_capsule **ncp)
|
||
|
{
|
||
|
return (qp->nq_association->na_ops->receive_capsule(qp, ncp));
|
||
|
}
|
||
|
|
||
|
const void *
|
||
|
nvmf_capsule_sqe(const struct nvmf_capsule *nc)
|
||
|
{
|
||
|
assert(nc->nc_qe_len == sizeof(struct nvme_command));
|
||
|
return (&nc->nc_sqe);
|
||
|
}
|
||
|
|
||
|
const void *
|
||
|
nvmf_capsule_cqe(const struct nvmf_capsule *nc)
|
||
|
{
|
||
|
assert(nc->nc_qe_len == sizeof(struct nvme_completion));
|
||
|
return (&nc->nc_cqe);
|
||
|
}
|
||
|
|
||
|
uint8_t
|
||
|
nvmf_validate_command_capsule(const struct nvmf_capsule *nc)
|
||
|
{
|
||
|
assert(nc->nc_qe_len == sizeof(struct nvme_command));
|
||
|
|
||
|
if (NVMEV(NVME_CMD_PSDT, nc->nc_sqe.fuse) != NVME_PSDT_SGL)
|
||
|
return (NVME_SC_INVALID_FIELD);
|
||
|
|
||
|
return (nc->nc_qpair->nq_association->na_ops->validate_command_capsule(nc));
|
||
|
}
|
||
|
|
||
|
size_t
|
||
|
nvmf_capsule_data_len(const struct nvmf_capsule *nc)
|
||
|
{
|
||
|
return (nc->nc_qpair->nq_association->na_ops->capsule_data_len(nc));
|
||
|
}
|
||
|
|
||
|
int
|
||
|
nvmf_receive_controller_data(const struct nvmf_capsule *nc,
|
||
|
uint32_t data_offset, void *buf, size_t len)
|
||
|
{
|
||
|
return (nc->nc_qpair->nq_association->na_ops->receive_controller_data(nc,
|
||
|
data_offset, buf, len));
|
||
|
}
|
||
|
|
||
|
int
|
||
|
nvmf_send_controller_data(const struct nvmf_capsule *nc, const void *buf,
|
||
|
size_t len)
|
||
|
{
|
||
|
return (nc->nc_qpair->nq_association->na_ops->send_controller_data(nc,
|
||
|
buf, len));
|
||
|
}
|
||
|
|
||
|
int
|
||
|
nvmf_kernel_handoff_params(struct nvmf_qpair *qp,
|
||
|
struct nvmf_handoff_qpair_params *qparams)
|
||
|
{
|
||
|
memset(qparams, 0, sizeof(*qparams));
|
||
|
qparams->admin = qp->nq_admin;
|
||
|
qparams->sq_flow_control = qp->nq_flow_control;
|
||
|
qparams->qsize = qp->nq_qsize;
|
||
|
qparams->sqhd = qp->nq_sqhd;
|
||
|
qparams->sqtail = qp->nq_sqtail;
|
||
|
return (qp->nq_association->na_ops->kernel_handoff_params(qp, qparams));
|
||
|
}
|
||
|
|
||
|
const char *
|
||
|
nvmf_transport_type(uint8_t trtype)
|
||
|
{
|
||
|
static _Thread_local char buf[8];
|
||
|
|
||
|
switch (trtype) {
|
||
|
case NVMF_TRTYPE_RDMA:
|
||
|
return ("RDMA");
|
||
|
case NVMF_TRTYPE_FC:
|
||
|
return ("Fibre Channel");
|
||
|
case NVMF_TRTYPE_TCP:
|
||
|
return ("TCP");
|
||
|
case NVMF_TRTYPE_INTRA_HOST:
|
||
|
return ("Intra-host");
|
||
|
default:
|
||
|
snprintf(buf, sizeof(buf), "0x%02x\n", trtype);
|
||
|
return (buf);
|
||
|
}
|
||
|
}
|