]> www.infradead.org Git - users/sagi/libnvme.git/commit
tree: Improve TCP controller matching algorithm
authorMartin Belanger <martin.belanger@dell.com>
Fri, 18 Aug 2023 01:35:34 +0000 (21:35 -0400)
committerDaniel Wagner <wagi@monom.org>
Wed, 30 Aug 2023 15:21:59 +0000 (17:21 +0200)
commit52a5429136119e98d0e369add8bec523532d8a0f
treef6058cf67e25bbf2947ebd79a83a67e2a3866a20
parentead417d45b3e7409e217224227f5c2015a272a63
tree: Improve TCP controller matching algorithm

The configuration parameters used to connect to a TCP controller are:

1. transport - "tcp"
2. traddr - Destination address (controller's IP address)
3. trsvcid - Service ID (controller's port - typically 8009 or 4420)
4. host-traddr - Source address (host's IP address)
5. host-iface - Physical/Logical interface where the connection will be made

For TCP, transport, traddr, and trsvcid are mandatory, while the
host-traddr and host-iface are optional. The host-traddr and host-iface
can be used as "overrides" to select a different source address and
interface than those that the kernel would choose by default.

When an application using libnvme to connect to a controller does
not specify the host-traddr or host-iface, the kernel will have to
determine the best interface and source address by itself. It does that
by looking up the destination address (traddr) in the routing table to
determine the best interface for the connection. The kernel then
retrieves the primary IP address assigned to that interface and uses that
as the connection's source address. By default, the kernel always uses
the interface's primary address as the connection's source address
unless host-traddr is used to override it.

Prior to version 6.1, the kernel did not reveal the source address or
interface it selected. Therefore, it was impossible for user-space apps
to tell exactly where connections were made. With kernel 6.1 (and later),
the sysfs now exposes the source address as "src_addr=" in the nvme
"address" attribute. The src_addr not only provides us with the
connection's source address, but by scanning the interface map one can
find out which interface owns that source address and precisely determine
on which interface each connection is made.

With TP8010 and the introduction of the Centralized Discovery Controller
(CDC), it is very important for hosts to connect to CDCs with a consistent
source address. That's because of the way the CDC keeps track of all the
hosts that connect to it. In addition to the host NQN, the CDC also checks
the host IP address (the connection's source address) to uniquely identify
a host. This unique identifier is then used for fabric zoning.

With fabric zoning, administrators configure the list of I/O controllers
that a host is allowed to connect to. The CDC sends the list of I/O
controllers to the host in response to a Get Discovery Log Page (DLP)
command from the host. If a host does not connect to the CDC with the
right source address, it will receive invalid DLP entries (wrong zone).
This will cause the host to connect to the wrong I/O controllers.

libnvme tries to avoid making duplicate connections to the same
controller. This avoids consuming precious kernel resources. When an
application requests libnvme to connect to a controller (the candidate
controller), libnvme scans the sysfs to see if an existing connection
matches the candidate. If a matching connection is found, libnvme just
reuses it instead of creating a new one.

Matching the 3 mandatory parameters (transport, traddr, trsvcid) between
existing connections and a candidate connection is easy because they can
never be NULL and can therefore be compared. It is not the case for the
host-traddr and host-iface. These optional parameters can be NULL. A NULL
host-traddr or host-iface means that we have left it to the kernel to
determine the interface and source address to use for the connection.
Therefore, if we want to compare a non-NULL candidate host-traddr to
an existing connection with a NULL host-traddr, we cannot just compare the
two. They will obviously be different. Instead, we need to check the
src_addr of the existing connection to see if it matches the candidate's
host-traddr.

Prior to this patch, libnvme performed a simple string comparison
between the candidate's host-traddr (or host-iface) and the existing
connection's host-traddr (or host-iface). A match would be declared if
both were the same (including both NULL). Also, a match would even be
declared if the existing connection's host-traddr (or host-iface) was
NULL while the candidate's host-traddr (or host-iface) was non-NULL.
This is wrong and can lead to the wrong connections being reused and
the wrong DLP entries returned by the CDC.

With this patch, when a candidate wants a specific source address
(host-traddr != NULL) or interface (host-iface != NULL), libnvme will
now check the src_addr of each existing connection to ensure a 100%
match. If the src_addr is not available (kernel older than 6.1), then we
can still infer the real interface and source address of an existing
connection, if the existing connection has either its host-traddr or
host-iface defined (check code to see how it's done).

It's only when an exsiting connection's src_addr, host-iface, and
host-traddr are all NULL that we cannot clearly match to a candidate.
When that happens, libnvme will take an optimistic approach and
declare a match even though it doesn't have enough info to do so.
This "optimistic match" follows what libnvme was doing prior to this
patch.

Signed-off-by: Martin Belanger <martin.belanger@dell.com>
src/nvme/tree.c