Skip to content

Commit ad69eca

Browse files
Martin Belangerdwsuse
authored andcommitted
fabrics: Add support to register with a DC
This adds support for the TP8010 by introducing nvmf_registration_ctlr function which performs the registration task with a Discovery Controller. Signed-off-by: Martin Belanger <[email protected]> [dwagner: refactored, reformated, udpated documentation, moved nvme_fetch_cntrltype_dctype_from_id here] Signed-off-by: Daniel Wagner <[email protected]>
1 parent b9d6b33 commit ad69eca

4 files changed

Lines changed: 354 additions & 1 deletion

File tree

libnvme/nvme.i

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
* Authors: Hannes Reinecke <[email protected]>
77
*/
88

9-
%module nvme
9+
%module(docstring="Python bindings for libnvme") nvme
10+
%feature("autodoc", "1");
1011

1112
%include "exception.i"
1213

@@ -574,6 +575,29 @@ struct nvme_ns {
574575
nvme_disconnect_ctrl($self);
575576
}
576577

578+
%feature("autodoc", "@return: True if controller supports explicit registration. False otherwise.") is_registration_supported;
579+
bool is_registration_supported() {
580+
return nvmf_is_registration_supported($self);
581+
}
582+
583+
%feature("autodoc", "@return None on success or Error string on error.") registration_ctlr;
584+
PyObject *registration_ctlr(enum nvmf_dim_tas tas) {
585+
__u32 result;
586+
int status;
587+
588+
status = nvmf_register_ctrl($self, NVMF_DIM_TAS_REGISTER, &result);
589+
if (status != NVME_SC_SUCCESS) {
590+
/* On error, return an error message */
591+
if (status < 0)
592+
return PyUnicode_FromFormat("Status:0x%04x - %s", status, nvme_status_to_string(status, false));
593+
else
594+
return PyUnicode_FromFormat("Result:0x%04x, Status:0x%04x - %s", result, status, nvme_status_to_string(status, false));
595+
}
596+
597+
/* On success, return None */
598+
Py_RETURN_NONE;
599+
}
600+
577601
%newobject discover;
578602
struct nvmf_discovery_log *discover(int max_retries = 6) {
579603
struct nvmf_discovery_log *logp = NULL;

src/libnvme.map

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,10 @@ LIBNVME_1_0 {
325325
nvmf_hostid_from_file;
326326
nvmf_hostnqn_from_file;
327327
nvmf_hostnqn_generate;
328+
nvmf_is_registration_supported;
328329
nvmf_prtype_str;
329330
nvmf_qptype_str;
331+
nvmf_register_ctrl;
330332
nvmf_sectype_str;
331333
nvmf_subtype_str;
332334
nvmf_treq_str;

src/nvme/fabrics.c

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <dirent.h>
2121
#include <inttypes.h>
2222

23+
#include <sys/param.h>
2324
#include <sys/stat.h>
2425
#include <sys/types.h>
2526
#include <arpa/inet.h>
@@ -980,3 +981,299 @@ char *nvmf_hostid_from_file()
980981
{
981982
return nvmf_read_file(nvmf_hostid_file, NVMF_HOSTID_SIZE);
982983
}
984+
985+
/**
986+
* nvmf_get_tel() - Calculate the amount of memory needed for a DIE.
987+
* @hostsymname: Symbolic name (may be NULL)
988+
*
989+
* Each Discovery Information Entry (DIE) must contain at a minimum an
990+
* Extended Attribute for the HostID. The Entry may optionally contain an
991+
* Extended Attribute for the Symbolic Name.
992+
*
993+
* Return: Total Entry Length
994+
*/
995+
static __u32 nvmf_get_tel(const char *hostsymname)
996+
{
997+
__u32 tel = sizeof(struct nvmf_ext_die);
998+
__u16 len;
999+
1000+
/* Host ID is mandatory */
1001+
tel += nvmf_exat_size(sizeof(uuid_t));
1002+
1003+
/* Symbolic name is optional */
1004+
len = hostsymname ? strlen(hostsymname) : 0;
1005+
if (len)
1006+
tel += nvmf_exat_size(len);
1007+
1008+
return tel;
1009+
}
1010+
1011+
/**
1012+
* nvmf_fill_die() - Fill a Discovery Information Entry.
1013+
* @die: Pointer to Discovery Information Entry to be filled
1014+
* @h: Pointer to the host data structure
1015+
* @tel: Length of the DIE
1016+
* @trtype: Transport type
1017+
* @adrfam: Address family
1018+
* @reg_addr: Address to register. Setting this to an empty string tells
1019+
* the DC to infer address from the source address of the socket.
1020+
* @tsas: Transport Specific Address Subtype for the address being
1021+
* registered.
1022+
*/
1023+
static void nvmf_fill_die(struct nvmf_ext_die *die, struct nvme_host *h,
1024+
__u32 tel, __u8 trtype, __u8 adrfam,
1025+
const char *reg_addr, union nvmf_tsas *tsas)
1026+
{
1027+
__u16 numexat = 0;
1028+
size_t symname_len;
1029+
struct nvmf_ext_attr *exat;
1030+
1031+
die->tel = cpu_to_le32(tel);
1032+
die->trtype = trtype;
1033+
die->adrfam = adrfam;
1034+
1035+
memcpy(die->nqn, h->hostnqn, MIN(sizeof(die->nqn), strlen(h->hostnqn)));
1036+
memcpy(die->traddr, reg_addr, MIN(sizeof(die->traddr), strlen(reg_addr)));
1037+
1038+
if (tsas)
1039+
memcpy(&die->tsas, tsas, sizeof(die->tsas));
1040+
1041+
/* Extended Attribute for the HostID (mandatory) */
1042+
numexat++;
1043+
exat = die->exat;
1044+
exat->exattype = cpu_to_le16(NVMF_EXATTYPE_HOSTID);
1045+
exat->exatlen = cpu_to_le16(nvmf_exat_len(sizeof(uuid_t)));
1046+
uuid_parse(h->hostid, exat->exatval);
1047+
1048+
/* Extended Attribute for the Symbolic Name (optional) */
1049+
symname_len = h->hostsymname ? strlen(h->hostsymname) : 0;
1050+
if (symname_len) {
1051+
__u16 exatlen = nvmf_exat_len(symname_len);
1052+
1053+
numexat++;
1054+
exat = nvmf_exat_ptr_next(exat);
1055+
exat->exattype = cpu_to_le16(NVMF_EXATTYPE_SYMNAME);
1056+
exat->exatlen = cpu_to_le16(exatlen);
1057+
memcpy(exat->exatval, h->hostsymname, symname_len);
1058+
/* Per Base specs, ASCII strings must be padded with spaces */
1059+
memset(&exat->exatval[symname_len], ' ', exatlen - symname_len);
1060+
}
1061+
1062+
die->numexat = cpu_to_le16(numexat);
1063+
}
1064+
1065+
/**
1066+
* nvmf_dim() - Explicit reg, dereg, reg-update issuing DIM
1067+
* @c: Host NVMe controller instance maintaining the admin queue used to
1068+
* submit the DIM command to the DC.
1069+
* @tas: Task field of the Command Dword 10 (cdw10). Indicates whether to
1070+
* perform a Registration, Deregistration, or Registration-update.
1071+
* @trtype: Transport type (&enum nvmf_trtype - must be NVMF_TRTYPE_TCP)
1072+
* @adrfam: Address family (&enum nvmf_addr_family)
1073+
* @reg_addr: Address to register. Setting this to an empty string tells
1074+
* the DC to infer address from the source address of the socket.
1075+
* @tsas: Transport Specific Address Subtype for the address being
1076+
* registered.
1077+
* @result: Location where to save the command-specific result returned by
1078+
* the discovery controller.
1079+
*
1080+
* Perform explicit registration, deregistration, or
1081+
* registration-update (specified by @tas) by sending a Discovery
1082+
* Information Management (DIM) command to the Discovery Controller
1083+
* (DC).
1084+
*
1085+
* Return: 0 on success; on failure -1 is returned and errno is set
1086+
*/
1087+
static int nvmf_dim(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u8 trtype,
1088+
__u8 adrfam, const char *reg_addr, union nvmf_tsas *tsas,
1089+
__u32 *result)
1090+
{
1091+
nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL;
1092+
struct nvmf_dim_data *dim;
1093+
struct nvmf_ext_die *die;
1094+
__u32 tdl;
1095+
__u32 tel;
1096+
int ret;
1097+
1098+
struct nvme_dim_args args = {
1099+
.args_size = sizeof(args),
1100+
.fd = nvme_ctrl_get_fd(c),
1101+
.result = result,
1102+
.timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
1103+
.tas = tas
1104+
};
1105+
1106+
if (!c->s) {
1107+
nvme_msg(r, LOG_ERR,
1108+
"%s: failed to perform DIM. subsystem undefined.\n",
1109+
c->name);
1110+
errno = EINVAL;
1111+
return -1;
1112+
}
1113+
1114+
if (!c->s->h) {
1115+
nvme_msg(r, LOG_ERR,
1116+
"%s: failed to perform DIM. host undefined.\n",
1117+
c->name);
1118+
errno = EINVAL;
1119+
return -1;
1120+
}
1121+
1122+
if (!c->s->h->hostid) {
1123+
nvme_msg(r, LOG_ERR,
1124+
"%s: failed to perform DIM. hostid undefined.\n",
1125+
c->name);
1126+
errno = EINVAL;
1127+
return -1;
1128+
}
1129+
1130+
if (!c->s->h->hostnqn) {
1131+
nvme_msg(r, LOG_ERR,
1132+
"%s: failed to perform DIM. hostnqn undefined.\n",
1133+
c->name);
1134+
errno = EINVAL;
1135+
return -1;
1136+
}
1137+
1138+
if (strcmp(c->transport, "tcp")) {
1139+
nvme_msg(r, LOG_ERR,
1140+
"%s: DIM only supported for TCP connections.\n",
1141+
c->name);
1142+
errno = EINVAL;
1143+
return -1;
1144+
}
1145+
1146+
/* Register one Discovery Information Entry (DIE) of size TEL */
1147+
tel = nvmf_get_tel(c->s->h->hostsymname);
1148+
tdl = sizeof(struct nvmf_dim_data) + tel;
1149+
1150+
dim = (struct nvmf_dim_data *)calloc(1, tdl);
1151+
if (!dim) {
1152+
errno = ENOMEM;
1153+
return -1;
1154+
}
1155+
1156+
dim->tdl = cpu_to_le32(tdl);
1157+
dim->nument = cpu_to_le64(1); /* only one DIE to register */
1158+
dim->entfmt = cpu_to_le16(NVMF_DIM_ENTFMT_EXTENDED);
1159+
dim->etype = cpu_to_le16(NVMF_DIM_ETYPE_HOST);
1160+
dim->ektype = cpu_to_le16(0x5F); /* must be 0x5F per specs */
1161+
1162+
memcpy(dim->eid, c->s->h->hostnqn,
1163+
MIN(sizeof(dim->eid), strlen(c->s->h->hostnqn)));
1164+
1165+
ret = get_entity_name(dim->ename, sizeof(dim->ename));
1166+
if (ret <= 0)
1167+
nvme_msg(r, LOG_INFO, "%s: Failed to retrieve ENAME. %s.\n",
1168+
c->name, strerror(errno));
1169+
1170+
ret = get_entity_version(dim->ever, sizeof(dim->ever));
1171+
if (ret <= 0)
1172+
nvme_msg(r, LOG_INFO, "%s: Failed to retrieve EVER.\n", c->name);
1173+
1174+
die = &dim->die->extended;
1175+
nvmf_fill_die(die, c->s->h, tel, trtype, adrfam, reg_addr, tsas);
1176+
1177+
args.data_len = tdl;
1178+
args.data = dim;
1179+
ret = nvme_dim_send(&args);
1180+
1181+
free(dim);
1182+
1183+
return ret;
1184+
}
1185+
1186+
/**
1187+
* nvme_get_adrfam() - Get address family for the address we're registering
1188+
* with the DC.
1189+
*
1190+
* We retrieve this info from the socket itself. If we can't get the source
1191+
* address from the socket, then we'll infer the address family from the
1192+
* address of the DC since the DC address has the same address family.
1193+
*
1194+
* @ctrl: Host NVMe controller instance maintaining the admin queue used to
1195+
* submit the DIM command to the DC.
1196+
*
1197+
* Return: The address family of the source address associated with the
1198+
* socket connected to the DC.
1199+
*/
1200+
static __u8 nvme_get_adrfam(nvme_ctrl_t c)
1201+
{
1202+
struct sockaddr_storage addr;
1203+
__u8 adrfam = NVMF_ADDR_FAMILY_IP4;
1204+
nvme_root_t r = c->s && c->s->h ? c->s->h->r : NULL;
1205+
1206+
if (!inet_pton_with_scope(r, AF_UNSPEC, c->traddr, c->trsvcid, &addr)) {
1207+
if (addr.ss_family == AF_INET6)
1208+
adrfam = NVMF_ADDR_FAMILY_IP6;
1209+
}
1210+
1211+
return adrfam;
1212+
}
1213+
1214+
/* These string definitions must match with the kernel */
1215+
static const char *cntrltype_str[] = {
1216+
[NVME_CTRL_CNTRLTYPE_IO] = "io",
1217+
[NVME_CTRL_CNTRLTYPE_DISCOVERY] = "discovery",
1218+
[NVME_CTRL_CNTRLTYPE_ADMIN] = "admin",
1219+
};
1220+
1221+
static const char *dctype_str[] = {
1222+
[NVME_CTRL_DCTYPE_NOT_REPORTED] = "none",
1223+
[NVME_CTRL_DCTYPE_DDC] = "ddc",
1224+
[NVME_CTRL_DCTYPE_CDC] = "cdc",
1225+
};
1226+
1227+
/**
1228+
* nvme_fetch_cntrltype_dctype_from_id - Get cntrltype and dctype from identify command
1229+
* @c: Controller instance
1230+
*
1231+
* On legacy kernels the cntrltype and dctype are not exposed through the
1232+
* sysfs. We must get them directly from the controller by performing an
1233+
* identify command.
1234+
*/
1235+
static void nvme_fetch_cntrltype_dctype_from_id(nvme_ctrl_t c)
1236+
{
1237+
struct nvme_id_ctrl id = { 0 };
1238+
1239+
if (nvme_ctrl_identify(c, &id))
1240+
return;
1241+
1242+
if (!c->cntrltype) {
1243+
if (id.cntrltype > NVME_CTRL_CNTRLTYPE_ADMIN || !cntrltype_str[id.cntrltype])
1244+
c->cntrltype = strdup("reserved");
1245+
else
1246+
c->cntrltype = strdup(cntrltype_str[id.cntrltype]);
1247+
}
1248+
1249+
if (!c->dctype) {
1250+
if (id.dctype > NVME_CTRL_DCTYPE_CDC || !dctype_str[id.dctype])
1251+
c->dctype = strdup("reserved");
1252+
else
1253+
c->dctype = strdup(dctype_str[id.dctype]);
1254+
}
1255+
}
1256+
1257+
bool nvmf_is_registration_supported(nvme_ctrl_t c)
1258+
{
1259+
if (!c->cntrltype || !c->dctype)
1260+
nvme_fetch_cntrltype_dctype_from_id(c);
1261+
1262+
return !strcmp(c->dctype, "ddc") || !strcmp(c->dctype, "cdc");
1263+
}
1264+
1265+
int nvmf_register_ctrl(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result)
1266+
{
1267+
if (!nvmf_is_registration_supported(c)) {
1268+
errno = ENOTSUP;
1269+
return -1;
1270+
}
1271+
1272+
/* We're registering our source address with the DC. To do
1273+
* that, we can simply send an empty string. This tells the DC
1274+
* to retrieve the source address from the socket and use that
1275+
* as the registration address.
1276+
*/
1277+
return nvmf_dim(c, NVMF_DIM_TAS_REGISTER, NVMF_TRTYPE_TCP,
1278+
nvme_get_adrfam(c), "", NULL, result);
1279+
}

src/nvme/fabrics.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,34 @@ static inline void nvme_chomp(char *s, int l)
241241
s[l--] = '\0';
242242
}
243243

244+
/**
245+
* nvmf_is_registration_supported - check whether registration can be performed.
246+
* @c: Controller instance
247+
*
248+
* Only discovery controllers (DC) that comply with TP8010 support
249+
* explicit registration with the DIM PDU. These can be identified by
250+
* looking at the value of a dctype in the Identify command
251+
* response. A value of 1 (DDC) or 2 (CDC) indicates that the DC
252+
* supports explicit registration.
253+
*
254+
* Return: true if controller supports explicit registration. false
255+
* otherwise.
256+
*/
257+
bool nvmf_is_registration_supported(nvme_ctrl_t c);
258+
259+
/**
260+
* nvmf_register_ctrl() - Perform registration task with a DC
261+
* @c: Controller instance
262+
* @tas: Task field of the Command Dword 10 (cdw10). Indicates whether to
263+
* perform a Registration, Deregistration, or Registration-update.
264+
* @result: The command-specific result returned by the DC upon command
265+
* completion.
266+
*
267+
* Perform registration task with a Discovery Controller (DC). Three
268+
* tasks are supported: register, deregister, and registration update.
269+
*
270+
* Return: 0 on success; on failure -1 is returned and errno is set
271+
*/
272+
int nvmf_register_ctrl(nvme_ctrl_t c, enum nvmf_dim_tas tas, __u32 *result);
273+
244274
#endif /* _LIBNVME_FABRICS_H */

0 commit comments

Comments
 (0)