Skip to content

RHEL-149878: viostor: Cleanups around msix_one_vector and MessageId#1503

Merged
YanVugenfirer merged 5 commits intovirtio-win:masterfrom
kevmw:msg-cleanup
Feb 11, 2026
Merged

RHEL-149878: viostor: Cleanups around msix_one_vector and MessageId#1503
YanVugenfirer merged 5 commits intovirtio-win:masterfrom
kevmw:msg-cleanup

Conversation

@kevmw
Copy link
Contributor

@kevmw kevmw commented Jan 27, 2026

As previous discussions have shown, the way msix_one_vector works (and especially that it uses MessageId = 1 in the code while it actually uses vector 0) is quite confusing. In fact, it almost looks like it works only accidentally, not by design. This PR cleans up the code to make it more obvious how things work. A functional change is not intended.

There is a clear next step after this: Add support for mapping multiple queues to a single vector (primarily to VioStorCompleteRequest()) and update VirtIoHwInitialize() to make use of all available vectors (and give up the config vector first) instead of immediately falling back to a single queue mode if they don't match. However, as this is essentially feature work, I decided to keep it separate from the cleanups. If you'd prefer to have a single PR for the full thing, let me know.

This is a draft because it's based on the current, non-final version of #1493

@YanVugenfirer
Copy link
Collaborator

[Jenkins CI]: Can one of the admins verify this patch?

@gemini-code-assist
Copy link

Summary of Changes

Hello @kevmw, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request focuses on enhancing the clarity and robustness of the viostor driver, particularly concerning MSI-X interrupt handling and device reset procedures. It refactors the logic for assigning MSI-X vectors to virtual queues, replacing a potentially confusing boolean flag with a more explicit indicator for the presence of a dedicated configuration vector. A significant change is the implementation of a new, comprehensive device reset mechanism (VioStorResetBus) that ensures proper shutdown, cleanup, and re-initialization of the VirtIO device, while also managing in-flight I/O requests gracefully during a reset event. These changes aim to make the driver's interrupt and error recovery paths more understandable and reliable.

Highlights

  • MSI-X Vector Logic Clarification: Replaced the ambiguous msix_one_vector flag with msix_config_vector and updated related logic to clearly distinguish between queue and configuration vectors, improving code readability and maintainability.
  • Robust Device Reset Mechanism: Introduced a comprehensive VioStorResetBus function that orchestrates a full VirtIO device reset, including proper handling of pending I/O requests, queue deletion, and re-initialization, making the driver more resilient to device hangs.
  • Improved Reset State Management: Replaced a simple boolean reset_in_progress with a reset_in_progress_count to manage the device reset state more robustly, preventing new I/O from being processed during a reset and safely ignoring DPC calls.
  • Consistent MessageID/QueueNumber Handling: Introduced helper functions QueueToMessageId and MessageToDpcIdx and updated SRB extensions to use queue_number, ensuring a consistent and less error-prone way to map between virtual queues, MessageIDs, and DPC array indices.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly refactors MSIX vector handling and the device reset mechanism, replacing msix_one_vector with msix_config_vector and introducing helper functions like QueueToMessageId and MessageToDpcIdx for better maintainability. The reset logic has been improved with reset_in_progress_count and VioStorResetBus to prevent I/O during resets. However, a critical security regression was identified: the validation of MessageID in the interrupt handling logic is too loose when the configuration vector is disabled. This could lead to out-of-bounds memory access or NULL pointer dereferences. It is crucial to tighten the validation in VirtIoMSInterruptRoutine to ensure MessageID is strictly within the expected range for all configuration modes to prevent this vulnerability and ensure the overall stability and correctness of the driver.

@benyamin-codez
Copy link
Contributor

I like the approach here, Kevin.
It got past me, so only just noticed it now.
Maybe it is indeed best for me to rebase some pickings of mine on top of this.

Could you consider my VioStorVQLock()/VioStorVQUnlock() routines in PR #1499...?
We generally use InterruptSynchronizePerMessage so should prefer StorPort*MSISpinLock()...

VOID VioStorVQLock(PVOID DeviceExtension, ULONG MessageId, PSTOR_LOCK_HANDLE LockHandle)
{
ENTER_FN();
PADAPTER_EXTENSION adaptExt = (PADAPTER_EXTENSION)DeviceExtension;
if (adaptExt->msix_enabled)
{
if (adaptExt->msix_sync_mode == InterruptSynchronizePerMessage)
{
RhelDbgPrint(TRACE_LOCKS, " Processing StorPortAcquireMSISpinLock() for MessageId : %d \n", MessageId);
ULONG oldIrql = 0;
StorPortAcquireMSISpinLock(DeviceExtension, MessageId, &oldIrql);
LockHandle->Context.OldIrql = (KIRQL)oldIrql;
}
else
{
// Unused legacy pathway for when adaptExt->msix_sync_mode == InterruptSynchronizeAll
ULONG QueueNumber;
if (adaptExt->msix_one_vector)
{
NT_ASSERT(MessageId == VIRTIO_BLK_MSIX_1_VECTOR_MSG_ID);
NT_ASSERT(MessageId <= adaptExt->num_queues);
QueueNumber = MESSAGE_TO_QUEUE_1_VECTOR(MessageId);
}
else
{
NT_ASSERT(MessageId >= QUEUE_TO_MESSAGE(VIRTIO_BLK_REQUEST_QUEUE_0));
NT_ASSERT(MessageId <= adaptExt->num_queues);
QueueNumber = MESSAGE_TO_QUEUE(MessageId);
}
if (QueueNumber >= (adaptExt->num_queues + VIRTIO_BLK_REQUEST_QUEUE_0))
{
QueueNumber %= adaptExt->num_queues;
}
RhelDbgPrint(TRACE_LOCKS,
" Processing StorPortAcquireSpinLock() using DpcLock for VQ : %d \n",
QueueNumber - VIRTIO_BLK_REQUEST_QUEUE_0);
StorPortAcquireSpinLock(DeviceExtension,
DpcLock,
&adaptExt->dpc[QueueNumber - VIRTIO_BLK_REQUEST_QUEUE_0],
LockHandle);
}
}
else
{
RhelDbgPrint(TRACE_LOCKS, " Processing StorPortAcquireSpinLock() using InterruptLock.\n");
StorPortAcquireSpinLock(DeviceExtension, InterruptLock, NULL, LockHandle);
}
EXIT_FN();
}
VOID VioStorVQUnlock(PVOID DeviceExtension, ULONG MessageId, PSTOR_LOCK_HANDLE LockHandle)
{
ENTER_FN();
PADAPTER_EXTENSION adaptExt = (PADAPTER_EXTENSION)DeviceExtension;
if (adaptExt->msix_enabled && adaptExt->msix_sync_mode == InterruptSynchronizePerMessage)
{
RhelDbgPrint(TRACE_LOCKS, " Processing StorPortReleaseMSISpinLock() for MessageId : %d \n", MessageId);
StorPortReleaseMSISpinLock(DeviceExtension, MessageId, LockHandle->Context.OldIrql);
}
else
{
RhelDbgPrint(TRACE_LOCKS, " Processing StorPortReleaseSpinLock()...\n");
StorPortReleaseSpinLock(DeviceExtension, LockHandle);
}
EXIT_FN();
}

Anyway, I'm off to the land of nod...

@kevmw
Copy link
Contributor Author

kevmw commented Jan 28, 2026

@benyamin-codez I don't think your changes are in conflict with mine, except that the QueueNumber part should just be a MessageToDpcIdx() call now. They are also unrelated to my cleanups, so I think it's best to have them in a separate PR. But let's discuss the details in #1499.

kevmw added 3 commits February 2, 2026 15:29
For adaptExt->msix_one_vector = TRUE, the current implementation relies
on the fact that effectively only the first queue is used:
VioStorCompleteRequest() is only ever called with MessageID = 1, so it
checks for completions only in the first queue.

The reason why this assumption actually holds true is not obvious:
num_queues will match the number of available msix_vectors, which may be
more than 1. However, the StorPortInitializePerfOpts() call that would
tell StorPort to use all of these vectors fails because
perfData.LastRedirectionMessageNumber is msix_vectors + 1 and therefore
out of bounds. Without a successful StorPortInitializePerfOpts() call,
StorPortGetStartIoPerfParams() will just use MessageNumber = 0 for all
requests.

Don't rely on such subtle behaviour and instead make it explicit that we
can only use a single queue for msix_one_vector = TRUE. (This
restriction may be lifted later, but it matches what the code does
currently.)

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
The queue number is where everything starts, MessageID is just derived
from it. The reverse mapping won't be as trivial as the current
QueueNumber = MessageID - 1 once we start using the real vector number
for msix_one_vector or even map multiple queues to a single vector, so
store the queue number instead.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Commit b1ed558 move an '#include "virtio_stor_hw_helper.h"' from
virtio_stor.c to the main header file virtio_stor.h for no real reason.
Unfortunately, virtio_stor_hw_helper.h already included virtio_stor.h,
so depending on the inclusion order, declarations from one header file
are unavailable in the other.

Move the #include back to virtio_stor.c so that virtio_stor_hw_helper.h
can use declarations from virtio_stor.h again.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Comment on lines 1496 to 1590
if (adaptExt->msix_one_vector)
if (adaptExt->msix_config_vector && MessageID == VIRTIO_BLK_MSIX_CONFIG_VECTOR)
{
MessageID = 1;
}
else
{
if (MessageID == VIRTIO_BLK_MSIX_CONFIG_VECTOR)
{
RhelGetDiskGeometry(DeviceExtension);
adaptExt->sense_info.senseKey = SCSI_SENSE_UNIT_ATTENTION;
adaptExt->sense_info.additionalSenseCode = SCSI_ADSENSE_PARAMETERS_CHANGED;
adaptExt->sense_info.additionalSenseCodeQualifier = SCSI_SENSEQ_CAPACITY_DATA_CHANGED;
adaptExt->check_condition = TRUE;
DeviceChangeNotification(DeviceExtension, TRUE);
return TRUE;
}
RhelGetDiskGeometry(DeviceExtension);
adaptExt->sense_info.senseKey = SCSI_SENSE_UNIT_ATTENTION;
adaptExt->sense_info.additionalSenseCode = SCSI_ADSENSE_PARAMETERS_CHANGED;
adaptExt->sense_info.additionalSenseCodeQualifier = SCSI_SENSEQ_CAPACITY_DATA_CHANGED;
adaptExt->check_condition = TRUE;
DeviceChangeNotification(DeviceExtension, TRUE);
return TRUE;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been very curious how we do this when msix_config_vector = FALSE.
Is it just not possible? A limitation of running with only a single vector?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't explicitly test it, but I think it will just fall back to the non-MSI codepath in VirtIoInterrupt().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I'll do some explicit testing to confirm.

Copy link
Contributor

@benyamin-codez benyamin-codez Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears broken for me, even on master - in vioscsi too (nothing hits the events queue)...

When trying to do a size change, I don't get an interrupt at all, definitely not on MessageId 0 anyway. Disk refresh and rescan in diskmgmt.msc don't produce any changes. The only way I can hit DeviceChangeNotification() is via the VirtIoStartIo() pathway on surprise removal. On reattaching the disk, the capacity changes.

This is on Proxmox at least... I noticed the same thing on a FreeBSD guest too in the last 12 hours.

# pveversion -v | grep qemu | grep -v backup
pve-qemu-kvm: 10.1.2-5
qemu-server: 9.1.3

# kvm --version
QEMU emulator version 10.1.2 (pve-qemu-kvm_10.1.2-5)

# qm showcmd <VMID> --pretty | grep machine
  -machine 'hpet=off,type=pc-q35-10.1+pve0' \

Maybe this is a QEMU bug. Any chance you could please check on a RH flavour?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevmw - Just to be clear, this was with the config vector enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like a lot of noise around the real change. But more importantly, does this really work? You still seem to use NO_VECTOR for config changes, so I don't think we'd ever get the config interrupt. Ah, or does it work only accidentally because the next queue interrupt might still see the old ISR bit and do the config update then?

This is the fix I have: kevmw/kvm-guest-drivers-windows@msg-cleanup...notify-config (based on this PR, so I haven't created a PR yet - still not sure how to handle dependent PRs best in Github, it doesn't seem to be supported very well).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevmw

Yeah, mine works too because it still is MessageId = 0...
... and then I only skip config update if the ISR bit-field says it's for a queue vector.

In any case, I like your proposal:

  • Proper implementation of the ISR Status bit-field checks avoids potential 0x01b \ 0x10b problems.
  • The u8 for isr in VirtIoMSI...() is also the right type (although we seem to prefer UCHAR here).
  • The new VirtIoConfigUpdated() routine you carved out cleans it up too.

If you want to run with this, I might do a relocation refactor (to group the ISR routines together) plus introduce an MSI-X ISR worker routine on top of your PR. I can also update intReason in VirtIoInterrupt() to use the UCHAR instead of ULONG - unless you want to do it here. Some standardisation on relevant local variable names would be good, e.g. I used isr_status for intReason / isr and then irq_serviced rather than isInterruptServiced.

Alternatively, I could introduce your improvements into mine and do it in one step.
Let me know what you prefer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd like to go with my notify-config branch, once this PR is merged.

For the additional changes, maybe let's ask @YanVugenfirer first, because I understood that he doesn't like commits just for renaming things etc. My own approach is to just clean up the code that I'm touching anyway. But if there is something small we agree on (like moving both interrupt handlers next to each other), I can include it in my branch, too. In this case, you could rebase and then I could cherry-pick from your branch so that you're still credited as the author, but we get everything done in a single PR.

As for VirtIoMSInterruptWorker(), I'm not sure what the purpose of that one is. To me it looks like an arbitrary split of VirtIoMSInterruptRoutine(), which wasn't even a very long function?

In general, instead of trying to make the code a bit prettier here and there, I feel that maybe we should focus more on the things that have practical effects e.g. on performance. The locking changes you mentioned the other day would be something that seems interesting to get separated into a commit and benchmarked. Maybe we should try and coordinate on Slack which things to address next?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am OK with commits that renames stuff, but let's make them part of something useful. I agree regarding the coordination first.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will rebase ASAP, and now this has merged, my other PRs too.

@kevmw
Copy link
Contributor Author

kevmw commented Feb 2, 2026

I pushed a rebased version now that #1493 has been merged and it's no longer just a draft.

@kevmw kevmw marked this pull request as ready for review February 2, 2026 21:37
benyamin-codez added a commit to benyamin-codez/kvm-guest-drivers-windows that referenced this pull request Feb 3, 2026
PR virtio-win#1502 - [viostor] Use ULONG for num_queues
PR virtio-win#1503 - viostor: Cleanups around msix_one_vector and MessageId

These PRs will be dropped in rebase once they merge.

Signed-off-by: benyamin-codez <115509179+benyamin-codez@users.noreply.github.com>
benyamin-codez added a commit to benyamin-codez/kvm-guest-drivers-windows that referenced this pull request Feb 7, 2026
PR virtio-win#1502 - [viostor] Use ULONG for num_queues
PR virtio-win#1503 - viostor: Cleanups around msix_one_vector and MessageId

These PRs will be dropped in rebase once they merge.

Signed-off-by: benyamin-codez <115509179+benyamin-codez@users.noreply.github.com>
kevmw added 2 commits February 9, 2026 12:24
MSI vector 0 is the vector that is actually used for msix_one_vector,
but the code artificially used MessageId = 1 instead so that conversions
between vector, queue number and index in the adaptExt->dpc array can
stay the same both when msix_one_vector is TRUE and when it's FALSE.

This convention is very confusing, though, because it misleads the
reader into thinking that vector 1 is what is actually used.

Switch to the more accurate MessageId = 0 instead and add helper
functions for the conversions.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
What makes msix_one_vector really different from other cases in the code
is just that it means that MSI isn't used for config changes and so the
vector indices move by one (because the config vector is 0 and all the
queue vectors start from 1 then, whereas without the config vector, the
vectors for queues already start from 0).

If we wanted, we could have multiple vectors in use even without a
config vector, and that's actually a very good idea when we have less
vectors than num_queues + 1. msix_one_vector isn't a very good name any
more then.

So let's have an adaptExt->msix_has_config_vector instead.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
@kevmw
Copy link
Contributor Author

kevmw commented Feb 11, 2026

The only active conversations here don't seem to be about this PR itself any more, but more about possible future work on top of it.

Does this mean we should move ahead and merge this?

@YanVugenfirer YanVugenfirer merged commit 326c6e3 into virtio-win:master Feb 11, 2026
4 of 8 checks passed
@kostyanf14 kostyanf14 changed the title viostor: Cleanups around msix_one_vector and MessageId RHEL-149878: viostor: Cleanups around msix_one_vector and MessageId Feb 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Comments