aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/nvmfd/ctl.c
blob: 5f01ec8e5bc87a087d6326c7e3e4bdda472cf899 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2023 Chelsio Communications, Inc.
 * Written by: John Baldwin <jhb@FreeBSD.org>
 */

#include <sys/param.h>
#include <sys/linker.h>
#include <sys/nv.h>
#include <sys/time.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <libnvmf.h>
#include <string.h>

#include <cam/ctl/ctl.h>
#include <cam/ctl/ctl_io.h>
#include <cam/ctl/ctl_ioctl.h>

#include "internal.h"

static int ctl_fd = -1;
static int ctl_port;

static void
open_ctl(void)
{
	if (ctl_fd > 0)
		return;

	ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
	if (ctl_fd == -1 && errno == ENOENT) {
		if (kldload("ctl") == -1)
			err(1, "Failed to load ctl.ko");
		ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
	}
	if (ctl_fd == -1)
		err(1, "Failed to open %s", CTL_DEFAULT_DEV);
}

void
init_ctl_port(const char *subnqn, const struct nvmf_association_params *params)
{
	char result_buf[256];
	struct ctl_port_entry entry;
	struct ctl_req req;
	nvlist_t *nvl;

	open_ctl();

	nvl = nvlist_create(0);

	nvlist_add_string(nvl, "subnqn", subnqn);

	/* XXX: Hardcoded in discovery.c */
	nvlist_add_stringf(nvl, "portid", "%u", 1);

	nvlist_add_stringf(nvl, "max_io_qsize", "%u", params->max_io_qsize);

	memset(&req, 0, sizeof(req));
	strlcpy(req.driver, "nvmf", sizeof(req.driver));
	req.reqtype = CTL_REQ_CREATE;
	req.args = nvlist_pack(nvl, &req.args_len);
	if (req.args == NULL)
		errx(1, "Failed to pack nvlist for CTL_PORT/CTL_REQ_CREATE");
	req.result = result_buf;
	req.result_len = sizeof(result_buf);
	if (ioctl(ctl_fd, CTL_PORT_REQ, &req) != 0)
		err(1, "ioctl(CTL_PORT/CTL_REQ_CREATE)");
	if (req.status == CTL_LUN_ERROR)
		errx(1, "Failed to create CTL port: %s", req.error_str);
	if (req.status != CTL_LUN_OK)
		errx(1, "Failed to create CTL port: %d", req.status);

	nvlist_destroy(nvl);
	nvl = nvlist_unpack(result_buf, req.result_len, 0);
	if (nvl == NULL)
		errx(1, "Failed to unpack nvlist from CTL_PORT/CTL_REQ_CREATE");

	ctl_port = nvlist_get_number(nvl, "port_id");
	nvlist_destroy(nvl);

	memset(&entry, 0, sizeof(entry));
	entry.targ_port = ctl_port;
	if (ioctl(ctl_fd, CTL_ENABLE_PORT, &entry) != 0)
		errx(1, "ioctl(CTL_ENABLE_PORT)");
}

void
shutdown_ctl_port(const char *subnqn)
{
	struct ctl_req req;
	nvlist_t *nvl;

	open_ctl();

	nvl = nvlist_create(0);

	nvlist_add_string(nvl, "subnqn", subnqn);

	memset(&req, 0, sizeof(req));
	strlcpy(req.driver, "nvmf", sizeof(req.driver));
	req.reqtype = CTL_REQ_REMOVE;
	req.args = nvlist_pack(nvl, &req.args_len);
	if (req.args == NULL)
		errx(1, "Failed to pack nvlist for CTL_PORT/CTL_REQ_REMOVE");
	if (ioctl(ctl_fd, CTL_PORT_REQ, &req) != 0)
		err(1, "ioctl(CTL_PORT/CTL_REQ_REMOVE)");
	if (req.status == CTL_LUN_ERROR)
		errx(1, "Failed to remove CTL port: %s", req.error_str);
	if (req.status != CTL_LUN_OK)
		errx(1, "Failed to remove CTL port: %d", req.status);

	nvlist_destroy(nvl);
}

void
ctl_handoff_qpair(struct nvmf_qpair *qp,
    const struct nvmf_fabric_connect_cmd *cmd,
    const struct nvmf_fabric_connect_data *data)
{
	struct ctl_nvmf req;
	int error;

	memset(&req, 0, sizeof(req));
	req.type = CTL_NVMF_HANDOFF;
	error = nvmf_handoff_controller_qpair(qp, &req.data.handoff);
	if (error != 0) {
		warnc(error, "Failed to prepare qpair for handoff");
		return;
	}

	req.data.handoff.cmd = cmd;
	req.data.handoff.data = data;
	if (ioctl(ctl_fd, CTL_NVMF, &req) != 0)
		warn("ioctl(CTL_NVMF/CTL_NVMF_HANDOFF)");
}