管理组件传输协议 (MCTP)¶
net/mctp/ 包含对 MCTP 的协议支持,如 DMTF 标准 DSP0236 中定义。物理接口驱动程序(规范中的“绑定”)在 drivers/net/mctp/ 中提供。
核心代码提供了一个基于套接字的接口,通过 AF_MCTP, SOCK_DGRAM 套接字发送和接收 MCTP 消息。
结构:接口 & 网络¶
内核通过两个项目对本地 MCTP 拓扑进行建模:接口和网络。
接口(或“链接”)是 MCTP 物理传输绑定的一个实例(如 DSP0236 第 3.2.47 节定义),可能连接到特定的硬件设备。它表示为一个 struct netdevice
。
网络通过端点 ID(由 DSP0236 第 3.2.31 节描述)为 MCTP 端点定义了一个唯一的地址空间。网络有一个用户可见的标识符,允许从用户空间引用。路由定义特定于一个网络。
接口与一个网络关联。一个网络可能与一个或多个接口关联。
如果存在多个网络,则每个网络可能包含其他网络也存在的端点 ID (EID)。
Sockets API¶
协议定义¶
MCTP 使用 AF_MCTP
/ PF_MCTP
作为地址和协议族。由于 MCTP 是基于消息的,因此只支持 SOCK_DGRAM
套接字。
int sd = socket(AF_MCTP, SOCK_DGRAM, 0);
protocol
参数的唯一(当前)值为 0。
与所有套接字地址族一样,源地址和目标地址使用 sockaddr
类型指定,该类型具有单字节端点地址。
typedef __u8 mctp_eid_t;
struct mctp_addr {
mctp_eid_t s_addr;
};
struct sockaddr_mctp {
__kernel_sa_family_t smctp_family;
unsigned int smctp_network;
struct mctp_addr smctp_addr;
__u8 smctp_type;
__u8 smctp_tag;
};
#define MCTP_NET_ANY 0x0
#define MCTP_ADDR_ANY 0xff
系统调用行为¶
以下章节描述了标准套接字系统调用的 MCTP 特定行为。选择这些行为是为了与现有的套接字 API 紧密映射。
bind()
:设置本地套接字地址¶
接收传入请求数据包的套接字将使用 bind()
系统调用绑定到本地地址。
struct sockaddr_mctp addr;
addr.smctp_family = AF_MCTP;
addr.smctp_network = MCTP_NET_ANY;
addr.smctp_addr.s_addr = MCTP_ADDR_ANY;
addr.smctp_type = MCTP_TYPE_PLDM;
addr.smctp_tag = MCTP_TAG_OWNER;
int rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
这建立了套接字的本地地址。与网络、地址和消息类型匹配的传入 MCTP 消息将由此套接字接收。“传入”的引用在这里很重要;绑定的套接字将只接收设置了 TO 位的消息,以指示传入的请求消息,而不是响应。
smctp_tag
值将配置从该套接字的远程端接受的标签。鉴于上述情况,唯一有效的值是 MCTP_TAG_OWNER
,这将导致远程“拥有”的标签路由到该套接字。由于设置了 MCTP_TAG_OWNER
,因此 smctp_tag
的最低有效 3 位未使用;调用者必须将它们设置为零。
MCTP_NET_ANY
的 smctp_network
值将配置套接字以接收来自任何本地连接网络的传入数据包。特定的网络值将导致套接字仅接收来自该网络的传入消息。
smctp_addr
字段指定要绑定的本地地址。 MCTP_ADDR_ANY
的值将配置套接字以接收寻址到任何本地目标 EID 的消息。
smctp_type
字段指定要接收的消息类型。只有类型的低 7 位与传入消息匹配(即,最高有效 IC 位不属于匹配)。这会导致套接字接收带有和不带有消息完整性检查尾部的包。
sendto()
, sendmsg()
, send()
:传输 MCTP 消息¶
使用 sendto()
、sendmsg()
或 send()
系统调用之一传输 MCTP 消息。以 sendto()
作为主要示例。
struct sockaddr_mctp addr;
char buf[14];
ssize_t len;
/* set message destination */
addr.smctp_family = AF_MCTP;
addr.smctp_network = 0;
addr.smctp_addr.s_addr = 8;
addr.smctp_tag = MCTP_TAG_OWNER;
addr.smctp_type = MCTP_TYPE_ECHO;
/* arbitrary message to send, with message-type header */
buf[0] = MCTP_TYPE_ECHO;
memcpy(buf + 1, "hello, world!", sizeof(buf) - 1);
len = sendto(sd, buf, sizeof(buf), 0,
(struct sockaddr_mctp *)&addr, sizeof(addr));
addr
的网络和地址字段定义了要发送到的远程地址。如果 smctp_tag
具有 MCTP_TAG_OWNER
,则内核将忽略 MCTP_TAG_VALUE
中设置的任何位,并生成适合目标 EID 的标记值。如果未设置 MCTP_TAG_OWNER
,则将使用指定的标记值发送消息。如果无法分配标签值,则系统调用将报告 EAGAIN
的 errno。
应用程序必须提供消息类型字节作为传递给 sendto()
的消息缓冲区的第一个字节。如果要将消息完整性检查包含在已传输的消息中,还必须在消息缓冲区中提供它,并且消息类型字节的最高有效位必须为 1。
sendmsg()
系统调用允许更紧凑的参数接口,并将消息缓冲区指定为分散/聚集列表。目前,未定义任何辅助消息类型(用于传递给 sendmsg()
的 msg_control
数据)。
在使用 MCTP_TAG_OWNER
指定的情况下,在未连接的套接字上传输消息将导致分配标签,如果尚未为该目标分配任何有效标签。 (destination-eid,tag)元组充当隐式本地套接字地址,以允许套接字接收对此传出消息的响应。如果已执行任何先前的分配(针对不同的远程 EID),则该分配将丢失。
套接字将仅接收他们已发送的请求的响应(TO=1),并且只能响应他们已接收的请求(TO=0)。
recvfrom()
, recvmsg()
, recv()
:接收 MCTP 消息¶
应用程序可以使用 recvfrom()
、recvmsg()
或 recv()
系统调用之一接收 MCTP 消息。以 recvfrom()
作为主要示例。
struct sockaddr_mctp addr;
socklen_t addrlen;
char buf[14];
ssize_t len;
addrlen = sizeof(addr);
len = recvfrom(sd, buf, sizeof(buf), 0,
(struct sockaddr_mctp *)&addr, &addrlen);
/* We can expect addr to describe an MCTP address */
assert(addrlen >= sizeof(buf));
assert(addr.smctp_family == AF_MCTP);
printf("received %zd bytes from remote EID %d\n", rc, addr.smctp_addr);
recvfrom
和 recvmsg
的 address 参数填充了传入消息的远程地址,包括标签值(为了回复该消息,这是必需的)。
消息缓冲区的第一个字节将包含消息类型字节。如果完整性检查在消息之后,它将包含在接收到的缓冲区中。
recv()
系统调用的行为类似,但不向应用程序提供远程地址。因此,只有在远程地址已知或消息不需要回复时,这些才有用。
与发送调用一样,套接字将仅接收他们已发送的请求的响应(TO=1),并且只能响应他们已接收的请求(TO=0)。
ioctl(SIOCMCTPALLOCTAG)
和 ioctl(SIOCMCTPDROPTAG)
¶
这些标签通过显式分配(和删除)标签值,而不是内核在 sendmsg()
时自动分配每个消息的标签,从而使应用程序可以更好地控制 MCTP 消息标签。
通常,如果您的 MCTP 协议不适合通常的请求/响应模型,您只需要使用这些 ioctl。例如,如果您需要在多个请求中持久化标签,或者一个请求可能生成多个响应。在这些情况下,ioctl 允许您将标签分配(和释放)与单独的消息发送和接收操作分离。
两个 ioctl 都传递一个指向 struct mctp_ioc_tag_ctl
的指针。
struct mctp_ioc_tag_ctl {
mctp_eid_t peer_addr;
__u8 tag;
__u16 flags;
};
SIOCMCTPALLOCTAG
为特定的对等方分配一个标签,应用程序可以在将来的 sendmsg()
调用中使用该标签。应用程序使用远程 EID 填充 peer_addr
成员。其他字段必须为零。
返回时,tag
成员将填充已分配的标签值。分配的标签将具有以下标签位集。
MCTP_TAG_OWNER
:如果您是标签所有者,则分配标签才有意义。
MCTP_TAG_PREALLOC
:向sendmsg()
指示这是一个预分配的标签。...以及最低有效三位(
MCTP_TAG_MASK
)中的实际标签值。请注意,零是有效的标签值。
标签值应按原样用于 struct sockaddr_mctp
的 smctp_tag
成员。
SIOCMCTPDROPTAG
释放先前由 SIOCMCTPALLOCTAG
ioctl 分配的标签。peer_addr
必须与分配时使用的相同,并且 tag
值必须与从分配返回的标签完全匹配(包括 MCTP_TAG_OWNER
和 MCTP_TAG_PREALLOC
位)。 flags
字段必须为零。
内核内部¶
MCTP 堆栈中存在一些可能的包流。
本地 TX 到远程端点,消息 <= MTU
sendmsg() -> mctp_local_output() : route lookup -> rt->output() (== mctp_route_output) -> dev_queue_xmit()
本地 TX 到远程端点,消息 > MTU
sendmsg() -> mctp_local_output() -> mctp_do_fragment_route() : creates packet-sized skbs. For each new skb: -> rt->output() (== mctp_route_output) -> dev_queue_xmit()
远程 TX 到本地端点,单包消息
mctp_pkttype_receive() : route lookup -> rt->output() (== mctp_route_input) : sk_key lookup -> sock_queue_rcv_skb()
远程 TX 到本地端点,多包消息
mctp_pkttype_receive() : route lookup -> rt->output() (== mctp_route_input) : sk_key lookup : stores skb in struct sk_key->reasm_head mctp_pkttype_receive() : route lookup -> rt->output() (== mctp_route_input) : sk_key lookup : finds existing reassembly in sk_key->reasm_head : appends new fragment -> sock_queue_rcv_skb()
关键引用计数¶
键被以下内容引用:
一个 skb:在路由输出期间,存储在
skb->cb
中。netns 和 sock 列表。
键可以与设备关联,在这种情况下,它们保存对设备 (通过
key->dev
设置,通过dev->key_count
计数)的引用。多个键可以引用该设备。