管理元件傳輸協議 (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計數)的引用。多個鍵可以引用該裝置。