From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from us-smtp-delivery-74.mimecast.com (us-smtp-delivery-74.mimecast.com [216.205.24.74]) by mx.groups.io with SMTP id smtpd.web10.22946.1585343130232745970 for ; Fri, 27 Mar 2020 14:05:30 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=YKLKPM6K; spf=pass (domain: redhat.com, ip: 216.205.24.74, mailfrom: lersek@redhat.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1585343129; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=YyAEmfnvOtvJo8L09J4VFolSBF1g/+WR6klDPyPRx9U=; b=YKLKPM6KwPe0uwcRdGCawr5yKFu2phqCdtYzoJjG0cLYYsHxR+dj/9D2v1ygjlIgRhgniP F0ytcdZgxK8sZKrGdh7Kt9EdHP7tcC7EUjpwlBXOkE2bW/bVEJayf3f8wwfFPnadsO+ziM RWCmAzpPaW67+lCNQCTYtyj8M1cVDSM= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-291-KLmwqERpO82BgRNnM9r-BA-1; Fri, 27 Mar 2020 17:05:21 -0400 X-MC-Unique: KLmwqERpO82BgRNnM9r-BA-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 6F844100551A; Fri, 27 Mar 2020 21:05:20 +0000 (UTC) Received: from lacos-laptop-7.usersys.redhat.com (ovpn-114-36.ams2.redhat.com [10.36.114.36]) by smtp.corp.redhat.com (Postfix) with ESMTP id 3F7471001B2D; Fri, 27 Mar 2020 21:05:18 +0000 (UTC) Subject: Re: [edk2-devel] [PATCH v2 15/17] OvmfPkg/PvScsiDxe: Support sending SCSI request and receive response To: Liran Alon , devel@edk2.groups.io Cc: nikita.leshchenko@oracle.com, aaron.young@oracle.com, jordan.l.justen@intel.com, ard.biesheuvel@linaro.org References: <20200325161005.16743-1-liran.alon@oracle.com> <20200325161005.16743-16-liran.alon@oracle.com> <6f32838a-d136-050e-7a05-f817da7954a8@oracle.com> From: "Laszlo Ersek" Message-ID: Date: Fri, 27 Mar 2020 22:05:17 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.9.1 MIME-Version: 1.0 In-Reply-To: <6f32838a-d136-050e-7a05-f817da7954a8@oracle.com> X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=WINDOWS-1252 Content-Transfer-Encoding: quoted-printable On 03/27/20 14:04, Liran Alon wrote: >=20 > On 27/03/2020 14:26, Laszlo Ersek wrote: >> On 03/25/20 17:10, Liran Alon wrote: >>> +/** >>> +=A0 Returns if PVSCSI request ring is full >>> +**/ >>> +STATIC >>> +BOOLEAN >>> +PvScsiIsReqRingFull ( >>> +=A0 IN CONST PVSCSI_DEV=A0=A0 *Dev >>> +=A0 ) >>> +{ >>> +=A0 PVSCSI_RINGS_STATE *RingsState; >>> +=A0 UINT32=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 ReqNumEntries; >>> + >>> +=A0 RingsState =3D Dev->RingDesc.RingState; >>> +=A0 ReqNumEntries =3D 1U << RingsState->ReqNumEntriesLog2; >>> +=A0 return (RingsState->ReqProdIdx - RingsState->CmpConsIdx) >=3D >>> ReqNumEntries; >>> +} >> (Just some thoughts, not a request for changing the code.) >> >> Normally I prefer accessing buffers shared with the device though >> volatile-qualified=A0 pointers. >> >> Meaning, in this case, that every "PCI host" pointer (i.e., each pointer >> that is associated with a PVSCSI_DMA_DESC) would have to be >> volatile-qualified. In particular: >> >> - in patch#13, PVSCSI_RING_DESC would have to be updated like this: >> >>> typedef struct { >>> =A0=A0 volatile PVSCSI_RINGS_STATE=A0=A0 *RingState; >>> =A0=A0 PVSCSI_DMA_DESC=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 RingSt= ateDmaDesc; >>> >>> =A0=A0 volatile PVSCSI_RING_REQ_DESC *RingReqs; >>> =A0=A0 PVSCSI_DMA_DESC=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 RingRe= qsDmaDesc; >>> >>> =A0=A0 volatile PVSCSI_RING_CMP_DESC *RingCmps; >>> =A0=A0 PVSCSI_DMA_DESC=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 RingCm= psDmaDesc; >>> } PVSCSI_RING_DESC; >> - in patch#14, PVSCSI_DEV would change as follows: >> >>> typedef struct { >>> =A0=A0 UINT32=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0 Signature; >>> =A0=A0 EFI_PCI_IO_PROTOCOL=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 *PciIo; >>> =A0=A0 EFI_EVENT=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0 ExitBoot; >>> =A0=A0 UINT64=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0 OriginalPciAttributes; >>> =A0=A0 PVSCSI_RING_DESC=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 Ri= ngDesc; >>> =A0=A0 volatile PVSCSI_DMA_BUFFER=A0=A0=A0=A0=A0 *DmaBuf; >>> =A0=A0 PVSCSI_DMA_DESC=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = DmaBufDmaDesc; >>> =A0=A0 UINT8=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 MaxTarget; >>> =A0=A0 UINT8=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 MaxLun; >>> =A0=A0 UINTN=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 WaitForCmpStallInUsecs; >>> =A0=A0 EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru; >>> =A0=A0 EFI_EXT_SCSI_PASS_THRU_MODE=A0=A0=A0=A0 PassThruMode; >>> } PVSCSI_DEV; >> After these changes, the compiler would (justifiedly) flag a bunch of >> code locations casting away the volatile qualification -- for example, >> in the above function, in the assignment to the "RingsState" local >> variable. >> >> Clearly, most of these compilation errors would have to be fixed (not >> suppressed), because they would be valid. Meaning: >> >> - you'd have to volatile-qualify the "RingsState" local variable in all >> =A0=A0 of PvScsiIsReqRingFull(), PvScsiGetCurrentRequest(), >> =A0=A0 PvScsiWaitForRequestCompletion(); >> >> - you'd also have to volatile-qualify the return types of >> =A0=A0 PvScsiGetCurrentRequest() and PvScsiWaitForRequestCompletion(); >> >> - you'd have to update PopulateRequest() and HandleResponse() too; and >> =A0=A0 the most annoying part of that would be that you could no longer = use >> =A0=A0 CopyMem() and ZeroMem() -- because those functions take >> =A0=A0 pointer-to-void parameters, rather than pointer-to-volatile-void = ones. >> >> (FWIW, we wouldn't have to change the PvScsiFreeSharedPages() prototype >> -- it would be OK to cast away volatile in those calls, as we wouldn't >> dereference the pointers in that case.) >> >> So... the reason I'm not actually requesting these >> volatile-qualifications is that (a) your use of MemoryFence() seems >> mostly OK, and (b) the UEFI Driver Writer's guide recommends *either* >> volatile *or* MemoryFence(). Of course using both techniques at the same >> time is not a problem -- and in code I write I actually like to use both >> at the same time --, but just one suffices too. (See section 4.2.6 >> "Memory ordering" in the DWG.) >> >> The reason I'm writing this up here is because I want the "record" (the >> mailing list archive) to show that we have considered this topic >> explicitly. > I prefer to remain with only memory fences if that's OK by you. Yes, that's fine. > As the code is written now. > As it's allows for potential compiler optimization and leads to more > readable code in my opinion. The UEFI Driver Writer's Guide makes the same argument -- it favors explicit MemoryFence()s over volatile. So your suggestion is entirely valid and I agree with it. >> Back to your patch: >> >> On 03/25/20 17:10, Liran Alon wrote: >>> +=A0 // >>> +=A0 // This cast is safe as MaxLun is defined as UINT8 >>> +=A0 // >>> +=A0 Request->Lun[1] =3D (UINT8)Lun; >>> +=A0 Request->SenseLen =3D Packet->SenseDataLength; >> Ah, *now* I understand why you chose MAX_UINT8 as the size of >> "PVSCSI_DMA_BUFFER.SenseData". Because, "Packet->SenseDataLength" has >> type UINT8, and this way you guarantee that the SCSI client's >> "Packet->SenseDataLength" will always fit in the DMA buffer. >> >> Good solution, but it *absolutely* needs to be documented in patch#14 >> ("OvmfPkg/PvScsiDxe: Introduce DMA communication buffer") -- in fact, >> see my question (4) under patch#14. > Please read the response I have written you to your patch#14 review. > Where I suggest we define a constant in IndustryStandard/Scsi.h for the > limit of the total length of SenseData that is defined to be 252 > according to SCSI specification. MdePkg macro is good, but it should be decoupled from this series. >> >> (2) Also, please add a comment here that a "Dev->DmaBuf->SenseData" >> overflow is not possible due to "Packet->SenseDataLength" having type >> UINT8. >> >> This would be a comment in the same vein as the "MaxLun" reference just >> above -- I find *that* comment very helpful, too. > OK. >>> + >>> +=A0 return EFI_SUCCESS; >>> +} >>> + >>> +/** >>> +=A0 Handle the PVSCSI device response: >>> +=A0 - Copy returned data from DMA communication buffer. >>> +=A0 - Update fields in Extended SCSI Pass Thru Protocol packet as >>> required. >>> +=A0 - Translate response code to EFI status code and host adapter stat= us. >>> +**/ >>> +STATIC >>> +EFI_STATUS >>> +HandleResponse ( >>> +=A0 IN PVSCSI_DEV=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 *Dev, >>> +=A0 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, >>> +=A0 IN CONST PVSCSI_RING_CMP_DESC=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0 *Response >>> +=A0 ) >>> +{ >>> +=A0 // >>> +=A0 // Check if device returned sense data >>> +=A0 // >>> +=A0 if (Response->ScsiStatus =3D=3D >>> EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) { >>> +=A0=A0=A0 // >>> +=A0=A0=A0 // Fix SenseDataLength to amount of data returned >>> +=A0=A0=A0 // >>> +=A0=A0=A0 if (Packet->SenseDataLength > Response->SenseLen) { >>> +=A0=A0=A0=A0=A0 Packet->SenseDataLength =3D (UINT8)Response->SenseLen; >>> +=A0=A0=A0 } >>> +=A0=A0=A0 // >>> +=A0=A0=A0 // Copy sense data from DMA communication buffer >>> +=A0=A0=A0 // >>> +=A0=A0=A0 CopyMem ( >>> +=A0=A0=A0=A0=A0 Packet->SenseData, >>> +=A0=A0=A0=A0=A0 Dev->DmaBuf->SenseData, >>> +=A0=A0=A0=A0=A0 Packet->SenseDataLength >>> +=A0=A0=A0=A0=A0 ); >>> +=A0 } else { >>> +=A0=A0=A0 // >>> +=A0=A0=A0 // Signal no sense data returned >>> +=A0=A0=A0 // >>> +=A0=A0=A0 Packet->SenseDataLength =3D 0; >>> +=A0 } >>> + >>> +=A0 // >>> +=A0 // Copy device output from DMA communication buffer >>> +=A0 // >>> +=A0 if (Packet->DataDirection =3D=3D EFI_EXT_SCSI_DATA_DIRECTION_READ)= { >>> +=A0=A0=A0 CopyMem (Packet->InDataBuffer, Dev->DmaBuf->Data, >>> Packet->InTransferLength); >>> +=A0 } >> I'm unfamilar with the PVSCSI device model, but I think this is not >> general enough. The "PVSCSI_RING_CMP_DESC.DataLen" field suggests that >> short reads are possible at least in theory. >> >> (5) If a short read occurs (Response->DataLen < >> Packet->InTransferLength), then we should adjust >> "Packet->InTransferLength", and also copy that many bytes only. >> >> (6) I think it would be prudent to update "Packet->OutTransferLength" >> too, for short writes. > As you can see below, this is done in case device return > Response->HostStatus as either PvScsiBtStatDatarun or > PvScsiBtStatDataUnderrun. >> >>> + >>> +=A0 // >>> +=A0 // Report target status >>> +=A0 // >>> +=A0 Packet->TargetStatus =3D Response->ScsiStatus; >>> + >>> +=A0 // >>> +=A0 // Host adapter status and function return value depend on >>> +=A0 // device response's host status >>> +=A0 // >>> +=A0 switch (Response->HostStatus) { >>> +=A0=A0=A0 case PvScsiBtStatSuccess: >>> +=A0=A0=A0 case PvScsiBtStatLinkedCommandCompleted: >>> +=A0=A0=A0 case PvScsiBtStatLinkedCommandCompletedWithFlag: >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D EFI_EXT_SCSI_STATUS_HOST= _ADAPTER_OK; >>> +=A0=A0=A0=A0=A0 return EFI_SUCCESS; >>> + >>> +=A0=A0=A0 case PvScsiBtStatSelTimeout: >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D >>> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 EFI_EXT_SCSI_STATUS_HOST= _ADAPTER_SELECTION_TIMEOUT; >>> +=A0=A0=A0=A0=A0 return EFI_TIMEOUT; >>> + >>> +=A0=A0=A0 case PvScsiBtStatDatarun: >>> +=A0=A0=A0 case : >>> +=A0=A0=A0=A0=A0 // >>> +=A0=A0=A0=A0=A0 // Report residual data in overrun/underrun >>> +=A0=A0=A0=A0=A0 // >>> +=A0=A0=A0=A0=A0 if (Packet->DataDirection =3D=3D EFI_EXT_SCSI_DATA_DIR= ECTION_READ) { >>> +=A0=A0=A0=A0=A0=A0=A0 Packet->InTransferLength =3D Response->DataLen; >>> +=A0=A0=A0=A0=A0 } else { >>> +=A0=A0=A0=A0=A0=A0=A0 Packet->OutTransferLength =3D Response->DataLen; >>> +=A0=A0=A0=A0=A0 } >> OK, if we are sure that (a) the device will always report short >> reads/writes like this, and that (b) the above assignments will never >> cause InTransferLength / OutTransferLength to *grow*, then the >> InTransferLength / OutTransferLength adjustments are sufficiently >> covered. > I believe both of these are indeed true. > Even though that current QEMU VMware PVSCSI device emulation code have a > bug that it never sets this in pvscsi_command_complete() when it does > set BTSTAT_DATARUN... >> Still: >> >> (8) The CopyMem() call above should not copy garbage (at the tail). > I don't think it matters. We don't guarantee anything on the content in > Packet->InDataBuffer beyond Packet->InTransferLength. > I think the code is simpler how it is currently written. I'm not convinced, but this is not a question I feel very strongly about. I OK to go with your preference. >> >> Honestly, *if* the PVSCSI device model always sets "Response->DataLen", > I don't think this is the case. >> then I would prefer if: >> >> - we always updated InTransferLength / OutTransferLength (regardless of >> "Response->HostStatus"), >> >> - and we only used these case labels (PvScsiBtStatDatarun / >> PvScsiBtStatDataUnderrun) for setting "Packet->HostAdapterStatus". >> >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D >>> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 EFI_EXT_SCSI_STATUS_HOST= _ADAPTER_DATA_OVERRUN_UNDERRUN; >>> +=A0=A0=A0=A0=A0 return EFI_BAD_BUFFER_SIZE; >> I think EFI_BAD_BUFFER_SIZE is invalid here. According to the UEFI spec, >> EFI_BAD_BUFFER_SIZE means "The SCSI Request Packet was not executed". >> But that's not the case here -- we do have a partially completed >> transfer. >=20 > Hmm... According to the documentation above EFI_SCSI_PASS_THRU_PASSTHRU > in MdePkg/Include/Protocol/ScsiPassThru.h: >=20 > =A0 @retval EFI_BAD_BUFFER_SIZE=A0=A0=A0=A0=A0=A0 The SCSI Request Packet= was > executed, but the > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 entire DataBuffer could not be > transferred. > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 The actual number of bytes > transferred is returned > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 in TransferLength. See > HostAdapterStatus, > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 TargetStatus, SenseDataLength, and > SenseData in > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 that order for additional status > information. >=20 > So I don't know who to believe... It does seem to me that this > documentation in the code makes more sense > and then my current code is correct. What do you think? You are looking at the wrong protocol header file. The top of this header file bears the comment SCSI Pass Through protocol as defined in EFI 1.1. and the UEFI-2.8 spec does not define EFI_SCSI_PASS_THRU_PROTOCOL; it only refers to Mantis ticket 845 with subject "EFI_SCSI_PASS_THRU_PROTOCOL replacement". Instead, please consult EFI_EXT_SCSI_PASS_THRU_PASSTHRU in "MdePkg/Include/Protocol/ScsiPassThruExt.h". There, the EFI_BAD_BUFFER_SIZE return value conforms to the UEFI 2.8 spec ("The SCSI Request Packet was not executed"). >=20 >> >> (9) Thus I feel we should use a "break" here. >> >>> + >>> +=A0=A0=A0 case PvScsiBtStatBusFree: >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D >>> EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE; >>> +=A0=A0=A0=A0=A0 break; >>> + >>> +=A0=A0=A0 case PvScsiBtStatInvPhase: >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D >>> EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR; >>> +=A0=A0=A0=A0=A0 break; >>> + >>> +=A0=A0=A0 case PvScsiBtStatSensFailed: >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D >>> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 EFI_EXT_SCSI_STATUS_HOST= _ADAPTER_REQUEST_SENSE_FAILED; >>> +=A0=A0=A0=A0=A0 break; >>> + >>> +=A0=A0=A0 case PvScsiBtStatTagReject: >>> +=A0=A0=A0 case PvScsiBtStatBadMsg: >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D >>> +=A0=A0=A0=A0=A0=A0=A0=A0=A0 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_R= EJECT; >>> +=A0=A0=A0=A0=A0 break; >>> + >>> +=A0=A0=A0 case PvScsiBtStatBusReset: >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D >>> EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET; >>> +=A0=A0=A0=A0=A0 break; >>> + >>> +=A0=A0=A0 case PvScsiBtStatHaTimeout: >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D >>> EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT; >>> +=A0=A0=A0=A0=A0 return EFI_TIMEOUT; >>> + >>> +=A0=A0=A0 case PvScsiBtStatScsiParity: >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D >>> EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR; >>> +=A0=A0=A0=A0=A0 break; >>> + >>> +=A0=A0=A0 default: >>> +=A0=A0=A0=A0=A0 Packet->HostAdapterStatus =3D >>> EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER; >>> +=A0=A0=A0=A0=A0 break; >>> +=A0 } >>> + >>> +=A0 return EFI_DEVICE_ERROR; >>> +} >>> + >>> >>> =A0 // >>> =A0 // Ext SCSI Pass Thru >>> =A0 // >>> @@ -144,7 +528,62 @@ PvScsiPassThru ( >>> =A0=A0=A0 IN EFI_EVENT=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 Event=A0=A0= =A0 OPTIONAL >>> =A0=A0=A0 ) >>> =A0 { >>> -=A0 return EFI_UNSUPPORTED; >>> +=A0 PVSCSI_DEV=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 *Dev; >>> +=A0 EFI_STATUS=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 Status; >>> +=A0 PVSCSI_RING_REQ_DESC *Request; >>> +=A0 PVSCSI_RING_CMP_DESC *Response; >>> + >>> +=A0 Dev =3D PVSCSI_FROM_PASS_THRU (This); >>> + >>> +=A0 if (PvScsiIsReqRingFull (Dev)) { >>> +=A0=A0=A0 return EFI_NOT_READY; >>> +=A0 } >>> + >>> +=A0 Request =3D PvScsiGetCurrentRequest (Dev); >>> + >>> +=A0 Status =3D PopulateRequest (Dev, Target, Lun, Packet, Request); >>> +=A0 if (EFI_ERROR (Status)) { >>> +=A0=A0=A0 return Status; >>> +=A0 } >>> + >>> +=A0 // >>> +=A0 // Writes to Request must be globally visible before making reques= t >>> +=A0 // available to device >>> +=A0 // >>> +=A0 MemoryFence (); >>> +=A0 Dev->RingDesc.RingState->ReqProdIdx++; >>> + >> (10) Please insert another MemoryFence () here. >=20 > That would be unnecessary and wrong. >=20 > The MemoryFence() here is used to make sure the request is globally > visible before the update to the producer-index. I agree. > As in any > circular-buffer implementation. > There is no need for an additional MemoryFence() here. >=20 > Note that the MMIO access below is guaranteed to be globally visible > only after the write to the producer-index. Yes, that was the goal of my suggestion. What guarantees it? > If EDK2 MMIO accessors wouldn't have guaranteed this, you would have a > very broken code base... > Similar to why Linux MMIO accessors (e.g. writel()) macros guarantee thes= e. >=20 > For example, see how MdePkg/Library/BaseIoLibIntrinsic/IoLib.c > MmioWrite32() internally calls MemoryFence() before and after MMIO > access itself. So basically you are saying that I proposed the right thing, except there is no need to spell it out here, because the MMIO accessor primitives already cover that internally :) I admit that I have not been aware of the internal fences! (And given that there is a specific commit in the git history to push the fences into the source file you mention, namely 9de780dcd6208, I do think my suggestion was not "wrong", only unnecessary.) I do agree that the MemoryFence() need not be added in this spot. Thanks for making me aware of the internal fences! >=20 >> >>> +=A0 Status =3D PvScsiMmioWrite32 (Dev, PvScsiRegOffsetKickRwIo, 0); >>> +=A0 if (EFI_ERROR (Status)) { >>> +=A0=A0=A0 // >>> +=A0=A0=A0 // If kicking the host fails, we must fake a host adapter er= ror. >>> +=A0=A0=A0 // EFI_NOT_READY would save us the effort, but it would also >>> suggest that >>> +=A0=A0=A0 // the caller retry. >>> +=A0=A0=A0 // >>> +=A0=A0=A0 return ReportHostAdapterError (Packet); >>> +=A0 } >>> + >>> +=A0 Status =3D PvScsiWaitForRequestCompletion (Dev); >>> +=A0 if (EFI_ERROR (Status)) { >>> +=A0=A0=A0 // >>> +=A0=A0=A0 // If waiting for request completion fails, we must fake a h= ost >>> adapter >>> +=A0=A0=A0 // error. EFI_NOT_READY would save us the effort, but it wou= ld >>> also suggest >>> +=A0=A0=A0 // that the caller retry. >>> +=A0=A0=A0 // >>> +=A0=A0=A0 return ReportHostAdapterError (Packet); >>> +=A0 } >>> + >> (11) Please insert a MemoryFence() here. >=20 > Why is a MemoryFence() needed here? I don't think that's true. >=20 > PvScsiWaitForRequestCompletion() ends with an MMIO write which is > guaranteed to be a memory fence. Yes, I see that now. My point was that a fence needed to *occur* here. I didn't realize it was already covered, internally. > Thus, there is no need for a MemoryFence() here (to serve as a rmb()) to > make sure the completion-descriptor is globally visible. Agreed. Thanks, Laszlo