@@ -295,6 +295,53 @@ static void nvme_mi_admin_init_resp(struct nvme_mi_resp *resp,
295295 resp -> hdr_len = sizeof (* hdr );
296296}
297297
298+ static int nvme_mi_admin_parse_status (struct nvme_mi_resp * resp , __u32 * result )
299+ {
300+ struct nvme_mi_admin_resp_hdr * admin_hdr ;
301+ struct nvme_mi_msg_resp * resp_hdr ;
302+ __u32 nvme_status ;
303+ __u32 nvme_result ;
304+
305+ /* we have a few different sources of "result" here: the status header
306+ * in the MI response, the cdw3 status field, and (command specific)
307+ * return values in cdw0. The latter is returned in the result pointer,
308+ * the former two generate return values here
309+ */
310+
311+ if (resp -> hdr_len < sizeof (* resp_hdr )) {
312+ errno = - EPROTO ;
313+ return -1 ;
314+ }
315+ resp_hdr = (struct nvme_mi_msg_resp * )resp -> hdr ;
316+
317+ /* If we have a MI error, we can't be sure there's an admin header
318+ * following; return just the MI status
319+ *
320+ * TODO: this may alias the cdw3 result values, see
321+ * https://github.com/linux-nvme/libnvme/issues/456
322+ */
323+ if (resp_hdr -> status )
324+ return resp_hdr -> status ;
325+
326+ /* We shouldn't hit this, as we'd have an error reported earlier.
327+ * However, for pointer safety, ensure we have a full admin header
328+ */
329+ if (resp -> hdr_len < sizeof (* admin_hdr )) {
330+ errno = EPROTO ;
331+ return -1 ;
332+ }
333+
334+ admin_hdr = (struct nvme_mi_admin_resp_hdr * )resp -> hdr ;
335+ nvme_result = le32_to_cpu (admin_hdr -> cdw0 );
336+ nvme_status = le32_to_cpu (admin_hdr -> cdw3 ) >> 16 ;
337+
338+ /* the result pointer, optionally stored if the caller needs it */
339+ if (result )
340+ * result = nvme_result ;
341+
342+ return nvme_status ;
343+ }
344+
298345int nvme_mi_admin_xfer (nvme_mi_ctrl_t ctrl ,
299346 struct nvme_mi_admin_req_hdr * admin_req ,
300347 size_t req_data_size ,
@@ -414,11 +461,9 @@ int nvme_mi_admin_identify_partial(nvme_mi_ctrl_t ctrl,
414461 if (rc )
415462 return rc ;
416463
417- if (resp_hdr .status )
418- return resp_hdr .status ;
419-
420- if (args -> result )
421- * args -> result = le32_to_cpu (resp_hdr .cdw0 );
464+ rc = nvme_mi_admin_parse_status (& resp , args -> result );
465+ if (rc )
466+ return rc ;
422467
423468 /* callers will expect a full response; if the data buffer isn't
424469 * fully valid, return an error */
@@ -489,12 +534,11 @@ static int __nvme_mi_admin_get_log(nvme_mi_ctrl_t ctrl,
489534 if (rc )
490535 return rc ;
491536
492- if (resp_hdr .status )
493- return resp_hdr .status ;
494-
495- * lenp = resp .data_len ;
537+ rc = nvme_mi_admin_parse_status (& resp , args -> result );
538+ if (!rc )
539+ * lenp = resp .data_len ;
496540
497- return 0 ;
541+ return rc ;
498542}
499543
500544int nvme_mi_admin_get_log (nvme_mi_ctrl_t ctrl , struct nvme_get_log_args * args )
@@ -580,13 +624,7 @@ int nvme_mi_admin_security_send(nvme_mi_ctrl_t ctrl,
580624 if (rc )
581625 return rc ;
582626
583- if (resp_hdr .status )
584- return resp_hdr .status ;
585-
586- if (args -> result )
587- * args -> result = le32_to_cpu (resp_hdr .cdw0 );
588-
589- return 0 ;
627+ return nvme_mi_admin_parse_status (& resp , args -> result );
590628}
591629
592630int nvme_mi_admin_security_recv (nvme_mi_ctrl_t ctrl ,
@@ -632,11 +670,9 @@ int nvme_mi_admin_security_recv(nvme_mi_ctrl_t ctrl,
632670 if (rc )
633671 return rc ;
634672
635- if (resp_hdr .status )
636- return resp_hdr .status ;
637-
638- if (args -> result )
639- * args -> result = resp_hdr .cdw0 ;
673+ rc = nvme_mi_admin_parse_status (& resp , args -> result );
674+ if (rc )
675+ return rc ;
640676
641677 args -> data_len = resp .data_len ;
642678
@@ -673,11 +709,9 @@ int nvme_mi_admin_get_features(nvme_mi_ctrl_t ctrl,
673709 if (rc )
674710 return rc ;
675711
676- if (resp_hdr .status )
677- return resp_hdr .status ;
678-
679- if (args -> result )
680- * args -> result = le32_to_cpu (resp_hdr .cdw0 );
712+ rc = nvme_mi_admin_parse_status (& resp , args -> result );
713+ if (rc )
714+ return rc ;
681715
682716 args -> data_len = resp .data_len ;
683717
@@ -719,11 +753,10 @@ int nvme_mi_admin_set_features(nvme_mi_ctrl_t ctrl,
719753 if (rc )
720754 return rc ;
721755
722- if (resp_hdr .status )
723- return resp_hdr .status ;
756+ rc = nvme_mi_admin_parse_status (& resp , args -> result );
757+ if (rc )
758+ return rc ;
724759
725- if (args -> result )
726- * args -> result = le32_to_cpu (resp_hdr .cdw0 );
727760 args -> data_len = resp .data_len ;
728761
729762 return 0 ;
@@ -762,13 +795,7 @@ int nvme_mi_admin_ns_mgmt(nvme_mi_ctrl_t ctrl,
762795 if (rc )
763796 return rc ;
764797
765- if (resp_hdr .status )
766- return resp_hdr .status ;
767-
768- if (args -> result )
769- * args -> result = le32_to_cpu (resp_hdr .cdw0 );
770-
771- return 0 ;
798+ return nvme_mi_admin_parse_status (& resp , args -> result );
772799}
773800
774801int nvme_mi_admin_ns_attach (nvme_mi_ctrl_t ctrl ,
@@ -801,13 +828,7 @@ int nvme_mi_admin_ns_attach(nvme_mi_ctrl_t ctrl,
801828 if (rc )
802829 return rc ;
803830
804- if (resp_hdr .status )
805- return resp_hdr .status ;
806-
807- if (args -> result )
808- * args -> result = le32_to_cpu (resp_hdr .cdw0 );
809-
810- return 0 ;
831+ return nvme_mi_admin_parse_status (& resp , args -> result );
811832}
812833
813834int nvme_mi_admin_format_nvm (nvme_mi_ctrl_t ctrl ,
@@ -841,13 +862,7 @@ int nvme_mi_admin_format_nvm(nvme_mi_ctrl_t ctrl,
841862 if (rc )
842863 return rc ;
843864
844- if (resp_hdr .status )
845- return resp_hdr .status ;
846-
847- if (args -> result )
848- * args -> result = le32_to_cpu (resp_hdr .cdw0 );
849-
850- return 0 ;
865+ return nvme_mi_admin_parse_status (& resp , args -> result );
851866}
852867
853868int nvme_mi_admin_sanitize_nvm (nvme_mi_ctrl_t ctrl ,
@@ -880,13 +895,7 @@ int nvme_mi_admin_sanitize_nvm(nvme_mi_ctrl_t ctrl,
880895 if (rc )
881896 return rc ;
882897
883- if (resp_hdr .status )
884- return resp_hdr .status ;
885-
886- if (args -> result )
887- * args -> result = le32_to_cpu (resp_hdr .cdw0 );
888-
889- return 0 ;
898+ return nvme_mi_admin_parse_status (& resp , args -> result );
890899}
891900
892901static int nvme_mi_read_data (nvme_mi_ep_t ep , __u32 cdw0 ,
0 commit comments