管理元件傳輸協議 (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_ANYsmctp_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);

recvfromrecvmsg 的 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_mctpsmctp_tag 成員。

SIOCMCTPDROPTAG 釋放先前由 SIOCMCTPALLOCTAG ioctl 分配的標籤。peer_addr 必須與分配時使用的相同,並且 tag 值必須與從分配返回的標籤完全匹配(包括 MCTP_TAG_OWNERMCTP_TAG_PREALLOC 位)。 flags 欄位必須為零。

核心內部

MCTP 堆疊中存在一些可能的包流。

  1. 本地 TX 到遠端端點,訊息 <= MTU

    sendmsg()
     -> mctp_local_output()
        : route lookup
        -> rt->output() (== mctp_route_output)
           -> dev_queue_xmit()
    
  2. 本地 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()
    
  3. 遠端 TX 到本地端點,單包訊息

    mctp_pkttype_receive()
    : route lookup
    -> rt->output() (== mctp_route_input)
       : sk_key lookup
       -> sock_queue_rcv_skb()
    
  4. 遠端 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 計數)的引用。多個鍵可以引用該裝置。