Skip to content

Commit 18b3316

Browse files
Martin Belangerigaw
authored andcommitted
tree: Add 2 new public functions to lookup existing controllers
There is duplicate code between libnvme and nvme-cli. When trying to instantiate a controller, both libnvme and nvme-cli scan the sysfs looking for an existing controller that can be reused. This patch adds nvme_ctrl_find() and nvme_ctrl_config_match(). These functions can be used by nvme-cli to lookup existing controllers. This will allow removing the duplicate code in nvme-cli (separate commit to follow on the nvme-cli GitHub repo). Signed-off-by: Martin Belanger <[email protected]>
1 parent 17429f7 commit 18b3316

6 files changed

Lines changed: 571 additions & 274 deletions

File tree

src/libnvme.map

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# SPDX-License-Identifier: LGPL-2.1-or-later
22
LIBNVME_1_6 {
33
global:
4+
nvme_ctrl_config_match;
5+
nvme_ctrl_find;
46
nvme_ctrl_get_src_addr;
57
nvme_ctrl_release_fd;
68
nvme_host_release_fds;

src/nvme/fabrics.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,7 @@ static const char *lookup_context(nvme_root_t r, nvme_ctrl_t c)
834834
NULL,
835835
NULL,
836836
nvme_ctrl_get_trsvcid(c),
837+
NULL,
837838
NULL))
838839
return nvme_subsystem_get_application(s);
839840
}
@@ -863,6 +864,7 @@ int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c,
863864
nvme_ctrl_get_host_traddr(c),
864865
nvme_ctrl_get_host_iface(c),
865866
nvme_ctrl_get_trsvcid(c),
867+
NULL,
866868
NULL);
867869
if (fc) {
868870
const char *key;

src/nvme/private.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ int json_dump_tree(nvme_root_t r);
180180
nvme_ctrl_t __nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
181181
const char *traddr, const char *host_traddr,
182182
const char *host_iface, const char *trsvcid,
183-
nvme_ctrl_t p);
183+
const char *subsysnqn, nvme_ctrl_t p);
184184

185185
#if (LOG_FUNCNAME == 1)
186186
#define __nvme_log_func __func__

src/nvme/tree.c

Lines changed: 186 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
* @host_traddr: Host transport address (source address)
4545
* @host_iface: Host interface for connection (tcp only)
4646
* @iface_list: Interface list (tcp only)
47+
* @addreq: Address comparison function (for traddr, host-traddr)
48+
* @well_known_nqn: Set to "true" when @subsysnqn is the well-known NQN
4749
*/
4850
struct candidate_args {
4951
const char *transport;
@@ -53,7 +55,10 @@ struct candidate_args {
5355
const char *host_traddr;
5456
const char *host_iface;
5557
struct ifaddrs *iface_list;
58+
bool (*addreq)(const char *, const char *);
59+
bool well_known_nqn;
5660
};
61+
typedef bool (*ctrl_match_t)(struct nvme_ctrl *c, struct candidate_args *candidate);
5762

5863
const char *nvme_slots_sysfs_dir = "/sys/bus/pci/slots";
5964

@@ -1257,7 +1262,7 @@ struct nvme_ctrl *nvme_create_ctrl(nvme_root_t r,
12571262
static bool _tcp_ctrl_match_host_traddr_no_src_addr(struct nvme_ctrl *c, struct candidate_args *candidate)
12581263
{
12591264
if (c->cfg.host_traddr)
1260-
return nvme_ipaddrs_eq(candidate->host_traddr, c->cfg.host_traddr);
1265+
return candidate->addreq(candidate->host_traddr, c->cfg.host_traddr);
12611266

12621267
/* If c->cfg.host_traddr is NULL, then the controller (c)
12631268
* uses the interface's primary address as the source
@@ -1405,7 +1410,7 @@ static bool _tcp_opt_params_match(struct nvme_ctrl *c, struct candidate_args *ca
14051410

14061411
/* Check host_traddr only if candidate is interested */
14071412
if (candidate->host_traddr &&
1408-
!nvme_ipaddrs_eq(candidate->host_traddr, src_addr))
1413+
!candidate->addreq(candidate->host_traddr, src_addr))
14091414
return false;
14101415

14111416
/* Check host_iface only if candidate is interested */
@@ -1418,19 +1423,12 @@ static bool _tcp_opt_params_match(struct nvme_ctrl *c, struct candidate_args *ca
14181423
}
14191424

14201425
/**
1421-
* _tcp_lookup_ctrl() - Look for an existing controller that can be reused
1422-
* @s: Subsystem object under which to do the search
1423-
* @transport: Transport type ("tcp")
1424-
* @traddr: Destination IP address
1425-
* @host_iface: Interface for the connection (optional)
1426-
* @host_traddr: Source IP address (optional)
1427-
* @trsvcid: Destination TCP port
1428-
* @p: Starting point is the linked-list (NULL to start at the beginning)
1426+
* _tcp_match_ctrl() - Check if controller matches candidate (TCP only)
1427+
* @c: An existing controller instance
1428+
* @candidate: Candidate ctrl we're trying to match with @c.
14291429
*
14301430
* We want to determine if an existing controller can be re-used
1431-
* for the candidate controller we're trying to instantiate. The
1432-
* candidate is identified by @transport, @traddr, @trsvcid,
1433-
* @host_traddr, and @host_iface.
1431+
* for the candidate controller we're trying to instantiate.
14341432
*
14351433
* For TCP, we do not have a match if the candidate's transport, traddr,
14361434
* trsvcid are not identical to those of the the existing controller.
@@ -1440,92 +1438,204 @@ static bool _tcp_opt_params_match(struct nvme_ctrl *c, struct candidate_args *ca
14401438
* not specify them (both NULL), we can ignore them. Otherwise, we must
14411439
* employ advanced investigation techniques to determine if there's a match.
14421440
*
1443-
* Return: Pointer to the matching controller or NULL.
1441+
* Return: true if a match is found, false otherwise.
14441442
*/
1445-
static nvme_ctrl_t _tcp_lookup_ctrl(nvme_subsystem_t s, const char *transport,
1446-
const char *traddr, const char *host_traddr,
1447-
const char *host_iface, const char *trsvcid,
1448-
nvme_ctrl_t p)
1443+
static bool _tcp_match_ctrl(struct nvme_ctrl *c, struct candidate_args *candidate)
14491444
{
1450-
struct candidate_args candidate = {0};
1451-
struct nvme_ctrl *c, *matching_c = NULL;
1445+
if (!streq0(c->transport, candidate->transport))
1446+
return false;
14521447

1453-
candidate.traddr = traddr;
1454-
candidate.trsvcid = trsvcid;
1455-
candidate.transport = transport;
1456-
candidate.host_iface = host_iface;
1457-
candidate.host_traddr = host_traddr;
1448+
if (!streq0(c->trsvcid, candidate->trsvcid))
1449+
return false;
14581450

1459-
/* For TCP we may need to access the interface map. Let's retrieve
1460-
* and cache the map and use it for the duration of the loop below.
1461-
*/
1462-
if (getifaddrs(&candidate.iface_list) == -1)
1463-
candidate.iface_list = NULL;
1451+
if (!candidate->addreq(c->traddr, candidate->traddr))
1452+
return false;
14641453

1465-
c = p ? nvme_subsystem_next_ctrl(s, p) : nvme_subsystem_first_ctrl(s);
1466-
for (; c != NULL; c = nvme_subsystem_next_ctrl(s, c)) {
1467-
if (!streq0(c->transport, candidate.transport))
1468-
continue;
1454+
if (candidate->well_known_nqn && !nvme_ctrl_is_discovery_ctrl(c))
1455+
return false;
14691456

1470-
if (!streq0(c->trsvcid, candidate.trsvcid))
1471-
continue;
1457+
if (candidate->subsysnqn && !streq0(c->subsysnqn, candidate->subsysnqn))
1458+
return false;
14721459

1473-
if (!nvme_ipaddrs_eq(c->traddr, candidate.traddr))
1474-
continue;
1460+
/* Check host_traddr / host_iface only if candidate is interested */
1461+
if ((candidate->host_iface || candidate->host_traddr) &&
1462+
!_tcp_opt_params_match(c, candidate))
1463+
return false;
14751464

1476-
/* Check host_traddr / host_iface only if candidate is interested */
1477-
if ((candidate.host_iface || candidate.host_traddr) &&
1478-
!_tcp_opt_params_match(c, &candidate))
1479-
continue;
1465+
return true;
1466+
}
14801467

1481-
matching_c = c;
1482-
break;
1468+
/**
1469+
* _match_ctrl() - Check if controller matches candidate (non TCP transport)
1470+
* @c: An existing controller instance
1471+
* @candidate: Candidate ctrl we're trying to match with @c.
1472+
*
1473+
* We want to determine if an existing controller can be re-used
1474+
* for the candidate controller we're trying to instantiate. This function
1475+
* is used for all transports except TCP.
1476+
*
1477+
* Return: true if a match is found, false otherwise.
1478+
*/
1479+
static bool _match_ctrl(struct nvme_ctrl *c, struct candidate_args *candidate)
1480+
{
1481+
if (!streq0(c->transport, candidate->transport))
1482+
return false;
1483+
1484+
if (candidate->traddr && c->traddr &&
1485+
!candidate->addreq(c->traddr, candidate->traddr))
1486+
return false;
1487+
1488+
if (candidate->host_traddr && c->cfg.host_traddr &&
1489+
!candidate->addreq(c->cfg.host_traddr, candidate->host_traddr))
1490+
return false;
1491+
1492+
if (candidate->host_iface && c->cfg.host_iface &&
1493+
!streq0(c->cfg.host_iface, candidate->host_iface))
1494+
return false;
1495+
1496+
if (candidate->trsvcid && c->trsvcid &&
1497+
!streq0(c->trsvcid, candidate->trsvcid))
1498+
return false;
1499+
1500+
if (candidate->well_known_nqn && !nvme_ctrl_is_discovery_ctrl(c))
1501+
return false;
1502+
1503+
if (candidate->subsysnqn && !streq0(c->subsysnqn, candidate->subsysnqn))
1504+
return false;
1505+
1506+
return true;
1507+
}
1508+
/**
1509+
* _candidate_init() - Init candidate and get the matching function
1510+
*
1511+
* @candidate: Candidate struct to initialize
1512+
* @transport: Transport name
1513+
* @traddr: Transport address
1514+
* @trsvcid: Transport service identifier
1515+
* @subsysnqn: Subsystem NQN
1516+
* @host_traddr: Host transport address
1517+
* @host_iface: Host interface name
1518+
* @host_iface: Host interface name
1519+
*
1520+
* The function _candidate_free() must be called to release resources once
1521+
* the candidate object is not longer required.
1522+
*
1523+
* Return: The matching function to use when comparing an existing
1524+
* controller to the candidate controller.
1525+
*/
1526+
static ctrl_match_t _candidate_init(struct candidate_args *candidate,
1527+
const char *transport,
1528+
const char *traddr,
1529+
const char *trsvcid,
1530+
const char *subsysnqn,
1531+
const char *host_traddr,
1532+
const char *host_iface)
1533+
{
1534+
memset(candidate, 0, sizeof(*candidate));
1535+
1536+
candidate->traddr = traddr;
1537+
candidate->trsvcid = trsvcid;
1538+
candidate->transport = transport;
1539+
candidate->subsysnqn = subsysnqn;
1540+
candidate->host_iface = host_iface;
1541+
candidate->host_traddr = host_traddr;
1542+
1543+
if (streq0(subsysnqn, NVME_DISC_SUBSYS_NAME)) {
1544+
/* Since TP8013, the NQN of discovery controllers can be the
1545+
* well-known NQN (i.e. nqn.2014-08.org.nvmexpress.discovery) or
1546+
* a unique NQN. A DC created using the well-known NQN may later
1547+
* display a unique NQN when looked up in the sysfs. Therefore,
1548+
* ignore (i.e. set to NULL) the well-known NQN when looking for
1549+
* a match.
1550+
*/
1551+
candidate->subsysnqn = NULL;
1552+
candidate->well_known_nqn = true;
14831553
}
14841554

1485-
freeifaddrs(candidate.iface_list); /* This is NULL-safe */
1555+
if (streq0(transport, "tcp")) {
1556+
/* For TCP we may need to access the interface map.
1557+
* Let's retrieve and cache the map.
1558+
*/
1559+
if (getifaddrs(&candidate->iface_list) == -1)
1560+
candidate->iface_list = NULL;
14861561

1487-
return matching_c;
1562+
candidate->addreq = nvme_ipaddrs_eq;
1563+
return _tcp_match_ctrl;
1564+
}
1565+
1566+
if (streq0(transport, "rdma")) {
1567+
candidate->addreq = nvme_ipaddrs_eq;
1568+
return _match_ctrl;
1569+
}
1570+
1571+
/* All other transport types */
1572+
candidate->addreq = streqcase0;
1573+
return _match_ctrl;
1574+
}
1575+
1576+
/**
1577+
* _candidate_free() - Release resources allocated by _candidate_init()
1578+
*
1579+
* @candidate: data to free.
1580+
*/
1581+
static void _candidate_free(struct candidate_args *candidate)
1582+
{
1583+
freeifaddrs(candidate->iface_list); /* This is NULL-safe */
14881584
}
14891585

14901586
nvme_ctrl_t __nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
14911587
const char *traddr, const char *host_traddr,
14921588
const char *host_iface, const char *trsvcid,
1493-
nvme_ctrl_t p)
1494-
1589+
const char *subsysnqn, nvme_ctrl_t p)
14951590
{
1496-
struct nvme_ctrl *c;
1497-
bool (*addreq)(const char *, const char *);
1498-
1499-
/* TCP requires special handling */
1500-
if (streq0(transport, "tcp"))
1501-
return _tcp_lookup_ctrl(s, transport, traddr,
1502-
host_traddr, host_iface, trsvcid, p);
1591+
struct nvme_ctrl *c, *matching_c = NULL;
1592+
struct candidate_args candidate;
1593+
ctrl_match_t ctrl_match;
15031594

1504-
if (streq0(transport, "rdma"))
1505-
addreq = nvme_ipaddrs_eq; /* IP address compare for RDMA */
1506-
else
1507-
addreq = streqcase0; /* Case-insensitive for everything else */
1595+
/* Init candidate and get the matching function to use */
1596+
ctrl_match = _candidate_init(&candidate, transport, traddr, trsvcid,
1597+
subsysnqn, host_traddr, host_iface);
15081598

15091599
c = p ? nvme_subsystem_next_ctrl(s, p) : nvme_subsystem_first_ctrl(s);
15101600
for (; c != NULL; c = nvme_subsystem_next_ctrl(s, c)) {
1511-
if (!streq0(c->transport, transport))
1512-
continue;
1513-
if (traddr && c->traddr &&
1514-
!addreq(c->traddr, traddr))
1515-
continue;
1516-
if (host_traddr && c->cfg.host_traddr &&
1517-
!addreq(c->cfg.host_traddr, host_traddr))
1518-
continue;
1519-
if (host_iface && c->cfg.host_iface &&
1520-
!streq0(c->cfg.host_iface, host_iface))
1521-
continue;
1522-
if (trsvcid && c->trsvcid &&
1523-
!streq0(c->trsvcid, trsvcid))
1524-
continue;
1525-
return c;
1601+
if (ctrl_match(c, &candidate)) {
1602+
matching_c = c;
1603+
break;
1604+
}
15261605
}
15271606

1528-
return NULL;
1607+
_candidate_free(&candidate);
1608+
1609+
return matching_c;
1610+
}
1611+
1612+
bool nvme_ctrl_config_match(struct nvme_ctrl *c, const char *transport,
1613+
const char *traddr, const char *trsvcid,
1614+
const char *subsysnqn, const char *host_traddr,
1615+
const char *host_iface)
1616+
{
1617+
bool match;
1618+
ctrl_match_t ctrl_match;
1619+
struct candidate_args candidate;
1620+
1621+
/* Init candidate and get the matching function to use */
1622+
ctrl_match = _candidate_init(&candidate, transport, traddr, trsvcid,
1623+
subsysnqn, host_traddr, host_iface);
1624+
1625+
match = ctrl_match(c, &candidate);
1626+
1627+
_candidate_free(&candidate);
1628+
1629+
return match;
1630+
}
1631+
1632+
nvme_ctrl_t nvme_ctrl_find(nvme_subsystem_t s, const char *transport,
1633+
const char *traddr, const char *trsvcid,
1634+
const char *subsysnqn, const char *host_traddr,
1635+
const char *host_iface)
1636+
{
1637+
return __nvme_lookup_ctrl(s, transport, traddr, host_traddr, host_iface,
1638+
trsvcid, subsysnqn, NULL/*p*/);
15291639
}
15301640

15311641
nvme_ctrl_t nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
@@ -1540,7 +1650,7 @@ nvme_ctrl_t nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport,
15401650
return NULL;
15411651

15421652
c = __nvme_lookup_ctrl(s, transport, traddr, host_traddr,
1543-
host_iface, trsvcid, p);
1653+
host_iface, trsvcid, NULL, p);
15441654
if (c)
15451655
return c;
15461656

0 commit comments

Comments
 (0)