hyperv: Refactor read packet function

Currently, a packet being read is split into header and data buffers.
Change to a single buffer to simplify logic in drivers, especially
for those that receive packets with variable sized headers.

This change also implements the pending send size functionality for
the receive buffer.

Change-Id: Ic4a3698fcfe813fbcf4fd9c1941102b5adaeb3b2
Reviewed-on: https://review.haiku-os.org/c/haiku/+/10418
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Jérôme Duval <[email protected]>
This commit is contained in:
John Davis
2026-03-07 09:06:41 +00:00
committed by Jérôme Duval
parent 76c0c7d4bb
commit 480c96550c
6 changed files with 75 additions and 85 deletions
+2 -2
View File
@@ -53,8 +53,8 @@ typedef struct hyperv_device_interface {
status_t (*open)(hyperv_device cookie, uint32 txLength, uint32 rxLength,
hyperv_device_callback callback, void* callbackData);
void (*close)(hyperv_device cookie);
status_t (*read_packet)(hyperv_device cookie, vmbus_pkt_header* _header,
uint32* _headerLength, void* _buffer, uint32* _length);
status_t (*read_packet)(hyperv_device cookie, void* buffer, uint32* bufferLength,
uint32* _headerLength, uint32* _dataLength);
status_t (*write_packet)(hyperv_device cookie, uint16 type, const void* buffer,
uint32 length, bool responseRequired, uint64 transactionID);
status_t (*write_gpa_packet)(hyperv_device cookie, uint32 rangeCount,
@@ -211,90 +211,81 @@ VMBusDevice::WriteGPAPacket(uint32 rangeCount, const vmbus_gpa_range* rangesList
status_t
VMBusDevice::PeekPacket(void* _buffer, uint32 length)
VMBusDevice::ReadPacket(void* buffer, uint32* bufferLength, uint32* _headerLength,
uint32* _dataLength)
{
InterruptsSpinLocker locker(fRXLock);
// Ensure at least the requested amount of data is present, plus the shifted packet index
if (_AvailableRX() < length + sizeof(uint64))
return B_DEV_NOT_READY;
uint32 readIndex = atomic_get((int32*)&fRXRing->read_index);
TRACE_TX("Channel %u RX peek read idx 0x%X write idx 0x%X\n", fChannelID, readIndex,
atomic_get((int32*)&fRXRing->write_index));
_ReadRX(readIndex, _buffer, length);
return B_OK;
}
status_t
VMBusDevice::ReadPacket(vmbus_pkt_header* _header, uint32* _headerLength, void* _buffer,
uint32* _length)
{
vmbus_pkt_header header;
vmbus_pkt_header* headerPtr;
if (_header != NULL) {
if (_headerLength == NULL || *_headerLength < sizeof(vmbus_pkt_header))
return B_BAD_VALUE;
headerPtr = _header;
} else {
headerPtr = &header;
}
status_t status = PeekPacket(headerPtr, sizeof(vmbus_pkt_header));
if (status != B_OK)
return status;
uint32 headerLength = headerPtr->header_length << VMBUS_PKT_SIZE_SHIFT;
uint32 totalLength = headerPtr->total_length << VMBUS_PKT_SIZE_SHIFT;
if (headerLength < sizeof(vmbus_pkt_header) || totalLength < headerLength) {
ERROR("Channel %u RX invalid pkt hdr len 0x%X tot len 0x%X\n", fChannelID, headerLength,
totalLength);
return B_IO_ERROR;
}
uint32 dataLength = totalLength - headerLength;
TRACE_RX("Channel %u RX pkt %u hdr len 0x%X tot len 0x%X\n", fChannelID, headerPtr->type,
headerLength, totalLength);
// Ensure provided buffers are large enough
if (_header != NULL) {
if (*_headerLength < headerLength) {
*_headerLength = headerLength;
return B_NO_MEMORY;
}
*_headerLength = headerLength;
}
if (*_length < dataLength) {
*_length = dataLength;
return B_NO_MEMORY;
}
*_length = dataLength;
if (*bufferLength < sizeof(vmbus_pkt_header))
return B_BAD_VALUE;
InterruptsSpinLocker locker(fRXLock);
if (_AvailableRX() < totalLength + sizeof(uint64))
// Should have at least the standard header and the shifted read index present on the ring
if (_AvailableRX() < sizeof(vmbus_pkt_header) + sizeof(uint64))
return B_DEV_NOT_READY;
uint32 readIndexNew = atomic_get((int32*)&fRXRing->read_index);
TRACE_TX("Channel %u RX old read idx 0x%X write idx 0x%X\n", fChannelID, readIndexNew,
TRACE_RX("Channel %u RX old read idx 0x%X write idx 0x%X\n", fChannelID, readIndexNew,
atomic_get((int32*)&fRXRing->write_index));
// Read the header, data, and seek past the shifted read index
if (_header != NULL && headerLength > sizeof(vmbus_pkt_header))
readIndexNew = _ReadRX(readIndexNew, _header, headerLength);
else
readIndexNew = _SeekRX(readIndexNew, headerLength);
readIndexNew = _ReadRX(readIndexNew, _buffer, dataLength);
// Read in the standard header and determine the length of the remainder of the data
vmbus_pkt_header* header = reinterpret_cast<vmbus_pkt_header*>(buffer);
readIndexNew = _ReadRX(readIndexNew, header, sizeof(*header));
uint32 headerLength = header->header_length << VMBUS_PKT_SIZE_SHIFT;
uint32 totalLength = header->total_length << VMBUS_PKT_SIZE_SHIFT;
if (headerLength < sizeof(*header) || totalLength < headerLength) {
ERROR("Channel %u RX invalid pkt hdr len 0x%X tot len 0x%X\n", fChannelID, headerLength,
totalLength);
return B_BAD_DATA;
}
void* dataBuffer = reinterpret_cast<uint8*>(buffer) + headerLength;
uint32 dataLength = totalLength - headerLength;
TRACE_RX("Channel %u RX pkt %u hdr len 0x%X data len 0x%X tran %" B_PRIu64 "\n", fChannelID,
header->type, headerLength, dataLength, header->transaction_id);
// Ensure provided buffer is large enough
if (*bufferLength < totalLength) {
*bufferLength = totalLength;
return B_NO_MEMORY;
}
*bufferLength = totalLength;
uint32 readLength = totalLength + sizeof(uint64);
if (_AvailableRX() < readLength)
return B_DEV_NOT_READY;
// Standard header was already read above; read remainder of header and data
// Shifted index is discarded
readIndexNew = _ReadRX(readIndexNew, header + 1, headerLength - sizeof(*header));
readIndexNew = _ReadRX(readIndexNew, dataBuffer, dataLength);
readIndexNew = _SeekRX(readIndexNew, sizeof(uint64));
memory_write_barrier();
atomic_set((int32*)&fRXRing->read_index, (int32)readIndexNew);
TRACE_TX("Channel %u RX new read idx 0x%X write idx 0x%X\n", fChannelID,
TRACE_RX("Channel %u RX new read idx 0x%X write idx 0x%X\n", fChannelID,
atomic_get((int32*)&fRXRing->read_index), atomic_get((int32*)&fRXRing->write_index));
locker.Unlock();
*_headerLength = headerLength;
*_dataLength = dataLength;
// Signal Hyper-V if required; signaling is only needed if the RX ring buffer was previously
// completely full, and there is now enough space to write pending data (if supported)
memory_read_barrier();
if (fRXRing->features.pending_send_size_supported) {
uint32 pendingSendLength = atomic_get((int32*)&fRXRing->pending_send_size);
if (pendingSendLength > 0) {
uint32 availableLength = _AvailableRX();
if ((availableLength - readLength) < pendingSendLength
&& availableLength > pendingSendLength) {
atomic_add64((int64*)&fRXRing->guest_to_host_interrupt_count, 1);
fVMBus->signal_channel(fVMBusCookie, fChannelID);
}
}
}
return B_OK;
}
@@ -76,11 +76,11 @@ vmbus_device_close(hyperv_device cookie)
static status_t
vmbus_device_read_packet(hyperv_device cookie, vmbus_pkt_header* _header, uint32* _headerLength,
void* _buffer, uint32* _length)
vmbus_device_read_packet(hyperv_device cookie, void* buffer, uint32* bufferLength,
uint32* _headerLength, uint32* _dataLength)
{
VMBusDevice* device = reinterpret_cast<VMBusDevice*>(cookie);
return device->ReadPacket(_header, _headerLength, _buffer, _length);
return device->ReadPacket(buffer, bufferLength, _headerLength, _dataLength);
}
@@ -57,9 +57,8 @@ public:
const vmbus_gpa_range* rangesList, uint32 rangesLength,
const void* buffer, uint32 length, bool responseRequired,
uint64 transactionID);
status_t PeekPacket(void* _buffer, uint32 length);
status_t ReadPacket(vmbus_pkt_header* _header, uint32* _headerLength,
void* _buffer, uint32* _length);
status_t ReadPacket(void* buffer, uint32* bufferLength,
uint32* _headerLength, uint32* _dataLength);
status_t AllocateGPADL(uint32 length, void** _buffer, uint32* _gpadl);
status_t FreeGPADL(uint32 gpadl);
@@ -36,7 +36,7 @@ HIDDevice::HIDDevice(hyperv_device_interface* hyperv,
fProtocolRespEvent.Init(this, "hyper-v hid protoresp");
fDeviceInfoEvent.Init(this, "hyper-v hid devinfo");
fPacket = malloc(HV_HID_RX_PKT_BUFFER_SIZE);
fPacket = static_cast<uint8*>(malloc(HV_HID_RX_PKT_BUFFER_SIZE));
if (fPacket == NULL) {
fStatus = B_NO_MEMORY;
return;
@@ -137,12 +137,12 @@ void
HIDDevice::_Callback()
{
while (true) {
vmbus_pkt_header header;
uint32 headerLength = sizeof(header);
uint32 packetLength = HV_HID_RX_PKT_BUFFER_SIZE;
uint32 length = HV_HID_RX_PKT_BUFFER_SIZE;
uint32 headerLength;
uint32 messageLength;
status_t status = fHyperV->read_packet(fHyperVCookie, &header, &headerLength,
fPacket, &packetLength);
status_t status = fHyperV->read_packet(fHyperVCookie, fPacket, &length, &headerLength,
&messageLength);
if (status == B_DEV_NOT_READY) {
break;
} else if (status != B_OK) {
@@ -151,7 +151,7 @@ HIDDevice::_Callback()
}
// Check if this is an HID pipe data message
hv_hid_pipe_in_msg* message = reinterpret_cast<hv_hid_pipe_in_msg*>(fPacket);
hv_hid_pipe_in_msg* message = reinterpret_cast<hv_hid_pipe_in_msg*>(fPacket + headerLength);
if (message->pipe_header.type != HV_HID_PIPE_MSGTYPE_DATA) {
ERROR("Non-data HID pipe message type %u received\n", message->pipe_header.type);
continue;
@@ -68,7 +68,7 @@ private:
ConditionVariable fProtocolRespEvent;
ConditionVariable fDeviceInfoEvent;
void* fPacket;
uint8* fPacket;
uint16 fLastX;
uint16 fLastY;
};