77 */
88
99#include <errno.h>
10+ #include <stdbool.h>
1011#include <stdio.h>
1112#include <stdlib.h>
1213#include <unistd.h>
@@ -84,10 +85,16 @@ struct nvme_mi_transport_mctp {
8485 int sd ;
8586};
8687
88+ static int ioctl_tag (int sd , unsigned long req , struct mctp_ioc_tag_ctl * ctl )
89+ {
90+ return ioctl (sd , req , ctl );
91+ }
92+
8793static struct __mi_mctp_socket_ops ops = {
8894 socket ,
8995 sendmsg ,
9096 recvmsg ,
97+ ioctl_tag ,
9198};
9299
93100void __nvme_mi_mctp_set_ops (const struct __mi_mctp_socket_ops * newops )
@@ -96,6 +103,105 @@ void __nvme_mi_mctp_set_ops(const struct __mi_mctp_socket_ops *newops)
96103}
97104static const struct nvme_mi_transport nvme_mi_transport_mctp ;
98105
106+ #ifdef SIOCMCTPALLOCTAG
107+ static __u8 nvme_mi_mctp_tag_alloc (struct nvme_mi_ep * ep )
108+ {
109+ struct nvme_mi_transport_mctp * mctp ;
110+ struct mctp_ioc_tag_ctl ctl = { 0 };
111+ static bool logged ;
112+ int rc ;
113+
114+ mctp = ep -> transport_data ;
115+
116+ ctl .peer_addr = mctp -> eid ;
117+
118+ errno = 0 ;
119+ rc = ops .ioctl_tag (mctp -> sd , SIOCMCTPALLOCTAG , & ctl );
120+ if (rc ) {
121+ if (!logged ) {
122+ /* not necessarily fatal, just means we can't handle
123+ * "more processing required" messages */
124+ nvme_msg (ep -> root , LOG_INFO ,
125+ "System does not support explicit tag allocation\n" );
126+ logged = true;
127+ }
128+ return MCTP_TAG_OWNER ;
129+ }
130+
131+ return ctl .tag ;
132+ }
133+
134+ static void nvme_mi_mctp_tag_drop (struct nvme_mi_ep * ep , __u8 tag )
135+ {
136+ struct nvme_mi_transport_mctp * mctp ;
137+ struct mctp_ioc_tag_ctl ctl = { 0 };
138+
139+ mctp = ep -> transport_data ;
140+
141+ if (!(tag & MCTP_TAG_PREALLOC ))
142+ return ;
143+
144+ ctl .peer_addr = mctp -> eid ;
145+ ctl .tag = tag ;
146+
147+ ops .ioctl_tag (mctp -> sd , SIOCMCTPDROPTAG , & ctl );
148+ }
149+
150+ #else /* !defined SIOMCTPTAGALLOC */
151+
152+ static __u8 nvme_mi_mctp_tag_alloc (struct nvme_mi_ep * ep )
153+ {
154+ static bool logged ;
155+ if (!logged ) {
156+ nvme_msg (ep -> root , LOG_INFO ,
157+ "Build does not support explicit tag allocation\n" );
158+ logged = true;
159+ }
160+ return MCTP_TAG_OWNER ;
161+ }
162+
163+ static void nvme_mi_mctp_tag_drop (struct nvme_mi_ep * ep , __u8 tag )
164+ {
165+ }
166+
167+ #endif /* !defined SIOMCTPTAGALLOC */
168+
169+ static bool nvme_mi_mctp_resp_is_mpr (struct nvme_mi_resp * resp , size_t len )
170+ {
171+ struct nvme_mi_msg_resp * msg ;
172+ __le32 mic ;
173+ __u32 crc ;
174+
175+ if (len != sizeof (* msg ) + sizeof (mic ))
176+ return false;
177+
178+ msg = (struct nvme_mi_msg_resp * )resp -> hdr ;
179+
180+ if (msg -> status != NVME_MI_RESP_MPR )
181+ return false;
182+
183+ /* We can't use verify_resp_mic here, as the response structure has
184+ * not been laid-out properly in resp yet (this is deferred until
185+ * we have the actual response).
186+ *
187+ * We know the data is a fixed size, and linear in the hdr buf, so
188+ * calculation is fairly simple. We do need to find the MIC data
189+ * though, which could either be in the header buf (if the original
190+ * header was larger than the minimal header message), or the start of
191+ * the data buf (otherwise).
192+ */
193+ if (resp -> hdr_len > sizeof (* msg ))
194+ mic = * (__le32 * )(msg + 1 );
195+ else
196+ mic = * (__le32 * )(resp -> data );
197+
198+ crc = ~nvme_mi_crc32_update (0xffffffff , msg , sizeof (* msg ));
199+ if (le32_to_cpu (mic ) != crc )
200+ return false;
201+
202+ return true;
203+ }
204+
99205static int nvme_mi_mctp_submit (struct nvme_mi_ep * ep ,
100206 struct nvme_mi_req * req ,
101207 struct nvme_mi_resp * resp )
@@ -106,19 +212,25 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
106212 struct sockaddr_mctp addr ;
107213 ssize_t len ;
108214 __le32 mic ;
109- int i ;
215+ int i , rc ;
216+ __u8 tag ;
110217
111218 if (ep -> transport != & nvme_mi_transport_mctp )
112219 return - EINVAL ;
113220
221+ /* we need enough space for at least a generic (/error) response */
222+ if (resp -> hdr_len < sizeof (struct nvme_mi_msg_resp ))
223+ return - EINVAL ;
224+
114225 mctp = ep -> transport_data ;
226+ tag = nvme_mi_mctp_tag_alloc (ep );
115227
116228 memset (& addr , 0 , sizeof (addr ));
117229 addr .smctp_family = AF_MCTP ;
118230 addr .smctp_network = mctp -> net ;
119231 addr .smctp_addr .s_addr = mctp -> eid ;
120232 addr .smctp_type = MCTP_TYPE_NVME | MCTP_TYPE_MIC ;
121- addr .smctp_tag = MCTP_TAG_OWNER ;
233+ addr .smctp_tag = tag ;
122234
123235 i = 0 ;
124236 req_iov [i ].iov_base = ((__u8 * )req -> hdr ) + 1 ;
@@ -146,7 +258,8 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
146258 if (len < 0 ) {
147259 nvme_msg (ep -> root , LOG_ERR ,
148260 "Failure sending MCTP message: %m\n" );
149- return len ;
261+ rc = len ;
262+ goto out ;
150263 }
151264
152265 resp_iov [0 ].iov_base = ((__u8 * )resp -> hdr ) + 1 ;
@@ -164,17 +277,20 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
164277 resp_msg .msg_iov = resp_iov ;
165278 resp_msg .msg_iovlen = 3 ;
166279
280+ retry :
281+ rc = -1 ;
167282 len = ops .recvmsg (mctp -> sd , & resp_msg , 0 );
168283
169284 if (len < 0 ) {
170285 nvme_msg (ep -> root , LOG_ERR ,
171286 "Failure receiving MCTP message: %m\n" );
172- return len ;
287+ goto out ;
173288 }
174289
290+
175291 if (len == 0 ) {
176292 nvme_msg (ep -> root , LOG_WARNING , "No data from MCTP endpoint\n" );
177- return -1 ;
293+ goto out ;
178294 }
179295
180296 /* Re-add the type byte, so we can work on aligned lengths from here */
@@ -188,15 +304,28 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
188304 nvme_msg (ep -> root , LOG_ERR ,
189305 "Invalid MCTP response: too short (%zd bytes, needed %zd)\n" ,
190306 len , 8 + sizeof (mic ));
191- return - EIO ;
307+ goto out ;
192308 }
193309
194310 /* We can't have header/payload data that isn't a multiple of 4 bytes */
195311 if (len & 0x3 ) {
196312 nvme_msg (ep -> root , LOG_WARNING ,
197313 "Response message has unaligned length (%zd)!\n" ,
198314 len );
199- return - EIO ;
315+ goto out ;
316+ }
317+
318+ /* Check for a More Processing Required response. This is a slight
319+ * layering violation, as we're pre-checking the MIC and inspecting
320+ * header fields. However, we need to do this in the transport in order
321+ * to keep the tag allocated and retry the recvmsg
322+ */
323+ if (nvme_mi_mctp_resp_is_mpr (resp , len )) {
324+ nvme_msg (ep -> root , LOG_DEBUG ,
325+ "Received More Processing Required, waiting for response\n" );
326+ /* TODO: when we implement timeouts, inspect the MPR response
327+ * for the estimated completion time. */
328+ goto retry ;
200329 }
201330
202331 /* If we have a shorter than expected response, we need to find the
@@ -226,7 +355,12 @@ static int nvme_mi_mctp_submit(struct nvme_mi_ep *ep,
226355
227356 resp -> mic = le32_to_cpu (mic );
228357
229- return 0 ;
358+ rc = 0 ;
359+
360+ out :
361+ nvme_mi_mctp_tag_drop (ep , tag );
362+
363+ return rc ;
230364}
231365
232366static void nvme_mi_mctp_close (struct nvme_mi_ep * ep )
0 commit comments