Skip to content

Commit c69b985

Browse files
committed
- Update README
- Add MSI-X support - Disarm the RX/TX interrupts in HW
1 parent 5419098 commit c69b985

File tree

11 files changed

+162
-102
lines changed

11 files changed

+162
-102
lines changed

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@ The driver contains code from the Intel FreeBSD igb driver version 2.5.30.
1010

1111
Thanks to *CoolStar* for writing open source [RealTek driver](https://github.com/coolstar/if_re-win), and to *alotipac* for the [Raspberry Pi GENET Ethernet driver](https://github.com/raspberrypi/windows-drivers/). Both of them were invaluable resources that helped me bootstrap the prototype.
1212

13+
## Technical details
14+
15+
The driver supports one 1 TX queue and up to 4 RX queues. This was chosen to balance the supported send offloading methods, receive side scaling, and the number of interrupt vectors supported by the card. For transmission the NIC supports offloading of sending large IPv4, IPv6, TCP, and UDP packets. Since the NIC doesn't support receive side coalescing, we rely mostly on receive side scaling to distribute the load of RX queues.
16+
17+
### Interrupt mapping
18+
19+
In MSI/Legacy mode the RX queues are mapped into the first 4 bits of the Extended Interrupt Cause register. The TX queue uses the legacy bits.
20+
21+
In MSI-X mode we map the RX queues to the first 4 interrupts. The 5th interrupt is used for link status changes and the TX queue.
22+
1323
## TODO
1424

15-
- [ ] Efficient MSI/MSI-X interrupt handling
16-
- [x] Offloading
17-
- [x] Checksums
18-
- [x] Large send offload
1925
- [ ] Energy Efficient Ethernet
20-
- [ ] RSS
26+
- [ ] Wake on LAN
27+
- [ ] Manual Duplex/Speed setting
28+
- [ ] VLAN filtering
2129
- [ ] Enable support for more NIC models

adapter.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,17 @@ IgbAdapterSetDatapathCapabilities(
145145
NET_ADAPTER_DMA_CAPABILITIES rxDmaCapabilities;
146146
NET_ADAPTER_DMA_CAPABILITIES_INIT(&rxDmaCapabilities, adapter->DmaEnabler);
147147

148+
SIZE_T maxRxQueues = IGB_MAX_RX_QUEUES;
149+
150+
if (adapter->MsiInterrupts > 1)
151+
maxRxQueues = min(maxRxQueues, adapter->MsiInterrupts - 1);
152+
148153
NET_ADAPTER_RX_CAPABILITIES rxCapabilities;
149154
NET_ADAPTER_RX_CAPABILITIES_INIT_SYSTEM_MANAGED_DMA(
150155
&rxCapabilities,
151156
&rxDmaCapabilities,
152157
IGB_BUF_SIZE,
153-
IGB_MAX_RX_QUEUES);
158+
maxRxQueues);
154159

155160
rxCapabilities.FragmentBufferAlignment = IGB_RX_BUFFER_ALIGN;
156161
rxCapabilities.FragmentRingNumberOfElementsHint = IGB_RX_BUF_NUM;
@@ -297,12 +302,14 @@ EvtAdapterReceiveScalingSetIndirectionEntries(
297302
{
298303
const ULONG queueId = IgbGetRxQueueContext(indirectionEntries->Entries[i].PacketQueue)->QueueId;
299304
const UINT32 index = indirectionEntries->Entries[i].Index;
300-
u32 reta = E1000_READ_REG(&adapter->Hw, E1000_RETA(index >> 2));
301-
u32 shift = (index & 2) << 3;
302-
reta ^= reta & (0xff << shift);
303-
reta |= queueId << shift;
304-
E1000_WRITE_REG(&adapter->Hw, E1000_RETA(index >> 2), reta);
305-
indirectionEntries->Entries[i].Status = STATUS_SUCCESS;
305+
if (index < 128)
306+
{
307+
u32 reta = E1000_READ_REG(&adapter->Hw, E1000_RETA(index >> 2));
308+
u32 shift = (index & 2) << 3;
309+
reta ^= reta & (0xff << shift);
310+
reta |= queueId << shift;
311+
E1000_WRITE_REG(&adapter->Hw, E1000_RETA(index >> 2), reta);
312+
}
306313
}
307314

308315
return STATUS_SUCCESS;

adapter.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ extern "C"
77

88
#define IGB_MAX_TX_QUEUES 1
99
#define IGB_MAX_RX_QUEUES 4
10-
//#define IGB_MAX_QUEUE_INTERRUPTS 4
10+
#define IGB_MAX_INTERRUPTS 5
1111
#define IGB_MAX_MCAST_LIST 32
1212

1313
#define IGB_TX_BUF_NUM 1024
@@ -33,8 +33,9 @@ typedef struct _IGB_ADAPTER
3333
SIZE_T MMIOSize;
3434
BUS_INTERFACE_STANDARD PciConfig;
3535

36-
// Main interrupt
37-
IGB_INTERRUPT* Interrupt;
36+
PIGB_INTERRUPT MiscInterrupt;
37+
PIGB_INTERRUPT Interrupts[IGB_MAX_INTERRUPTS];
38+
UINT MsiInterrupts;
3839

3940
NET_PACKET_FILTER_FLAGS PacketFilterFlags;
4041
UINT MCAddressLength;

device.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,13 @@ IgbGetResources(
4747
}
4848
else if (rawDescriptor->Type == CmResourceTypeInterrupt)
4949
{
50-
/*if (translatedDescriptor->Flags & CM_RESOURCE_INTERRUPT_MESSAGE)
51-
{
52-
DBGPRINT("MSI/MSI-X interrupt %d\n", rawDescriptor->u.MessageInterrupt.Raw.MessageCount);
53-
}
54-
else
55-
{
56-
DBGPRINT("LBI interrupt\n");
57-
}*/
58-
if (intCnt == 0)
50+
if (intCnt < IGB_MAX_INTERRUPTS)
5951
{
6052
GOTO_IF_NOT_NT_SUCCESS(Exit, status,
61-
IgbInterruptCreate(adapter->WdfDevice, adapter, rawDescriptor, translatedDescriptor, &adapter->Interrupt));
53+
IgbInterruptCreate(adapter->WdfDevice, adapter, rawDescriptor, translatedDescriptor, &adapter->Interrupts[intCnt]));
54+
55+
if (translatedDescriptor->Flags & CM_RESOURCE_INTERRUPT_MESSAGE)
56+
adapter->MsiInterrupts++;
6257
}
6358
intCnt++;
6459
}
@@ -89,7 +84,6 @@ NTSTATUS
8984
IgbRegisterScatterGatherDma(
9085
_In_ IGB_ADAPTER* adapter)
9186
{
92-
//TraceEntryRtAdapter(adapter);
9387
DBGPRINT("IntelRegisterScatterGatherDma\n");
9488

9589
WDF_DMA_ENABLER_CONFIG dmaEnablerConfig;

forward.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3-
typedef struct _IGB_ADAPTER IGB_ADAPTER;
4-
typedef struct _IGB_DEVICE IGB_DEVICE;
5-
typedef struct _IGB_INTERRUPT IGB_INTERRUPT;
3+
typedef struct _IGB_ADAPTER IGB_ADAPTER, *PIGB_ADAPTER;
4+
typedef struct _IGB_DEVICE IGB_DEVICE, *PIGB_DEVICE;
5+
typedef struct _IGB_INTERRUPT IGB_INTERRUPT, *PIGB_INTERRUPT;
6+
typedef struct _IGB_QUEUE_INTERRUPT IGB_QUEUE_INTERRUPT, *PIGB_QUEUE_INTERRUPT;

interrupt.cpp

Lines changed: 109 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ IgbInterruptCreate(
3434
IGB_INTERRUPT* context = IgbGetInterruptContext(wdfInterrupt);
3535
context->Adapter = adapter;
3636
context->Handle = wdfInterrupt;
37-
context->IsMsi = translatedDescriptor->Flags & CM_RESOURCE_INTERRUPT_MESSAGE ? TRUE : FALSE;
3837
*interrupt = context;
3938

4039
Exit:
@@ -43,14 +42,44 @@ IgbInterruptCreate(
4342

4443
void
4544
IgbInterruptInitialize(
46-
_In_ IGB_INTERRUPT* interrupt)
45+
_In_ IGB_ADAPTER* adapter)
4746
{
48-
// Map RX queues to EICR bits
4947
// TODO: Handle 82575/82576 models
48+
49+
if (adapter->MsiInterrupts > 1)
50+
{
51+
UINT miscInterrupt = adapter->MsiInterrupts - 1;
52+
53+
// Enable MSI-X mode
54+
E1000_WRITE_REG(&adapter->Hw, E1000_GPIE, E1000_GPIE_NSICR | E1000_GPIE_MSIX_MODE | E1000_GPIE_EIAME | E1000_GPIE_PBA);
55+
56+
// Map link & misc interrupts
57+
E1000_WRITE_REG(&adapter->Hw, E1000_IVAR_MISC, ((miscInterrupt | E1000_IVAR_VALID) << 8));
58+
59+
// Map the TX queue to the misc interrupt
60+
u32 ivar = E1000_READ_REG_ARRAY(&adapter->Hw, E1000_IVAR0, 0);
61+
E1000_WRITE_REG_ARRAY(&adapter->Hw, E1000_IVAR0, 0, (ivar & 0xFFFF00FF) | ((miscInterrupt | E1000_IVAR_VALID) << 8));
62+
63+
// Automatically clear interrupt causes
64+
E1000_WRITE_REG(&adapter->Hw, E1000_EIAC, (1 << adapter->MsiInterrupts) - 1);
65+
// Automatically mask RX queues
66+
E1000_WRITE_REG(&adapter->Hw, E1000_EIAM, (1 << (adapter->MsiInterrupts - 1)) - 1);
67+
// Enable misc interrupt
68+
E1000_WRITE_REG(&adapter->Hw, E1000_EIMS, (1 << miscInterrupt));
69+
70+
adapter->MiscInterrupt = adapter->Interrupts[miscInterrupt];
71+
}
72+
else
73+
{
74+
E1000_WRITE_REG(&adapter->Hw, E1000_GPIE, adapter->MsiInterrupts == 1 ? E1000_GPIE_NSICR : 0);
75+
adapter->MiscInterrupt = adapter->Interrupts[0];
76+
}
77+
78+
// Map RX queues to EICR bits
5079
for (int i = 0; i < IGB_MAX_RX_QUEUES; i++)
5180
{
5281
u32 index = i >> 1;
53-
u32 ivar = E1000_READ_REG_ARRAY(&interrupt->Adapter->Hw, E1000_IVAR0, index);
82+
u32 ivar = E1000_READ_REG_ARRAY(&adapter->Hw, E1000_IVAR0, index);
5483
if (i & 1)
5584
{
5685
ivar &= 0xFF00FFFF;
@@ -61,98 +90,130 @@ IgbInterruptInitialize(
6190
ivar &= 0xFFFFFF00;
6291
ivar |= i | E1000_IVAR_VALID;
6392
}
64-
E1000_WRITE_REG_ARRAY(&interrupt->Adapter->Hw, E1000_IVAR0, index, ivar);
93+
E1000_WRITE_REG_ARRAY(&adapter->Hw, E1000_IVAR0, index, ivar);
6594
}
6695

67-
// TODO: MSI-X
68-
if (interrupt->IsMsi)
69-
{
70-
E1000_WRITE_REG(&interrupt->Adapter->Hw, E1000_GPIE, E1000_GPIE_NSICR);
71-
}
72-
else
73-
{
74-
E1000_WRITE_REG(&interrupt->Adapter->Hw, E1000_GPIE, 0);
75-
}
96+
E1000_WRITE_FLUSH(&adapter->Hw);
7697
}
7798

7899
_Use_decl_annotations_
79100
BOOLEAN
80101
EvtInterruptIsr(
81102
_In_ WDFINTERRUPT wdfInterrupt,
82-
ULONG MessageID)
103+
_In_ ULONG messageId)
83104
{
84-
UNREFERENCED_PARAMETER((MessageID));
85-
86105
IGB_INTERRUPT* interrupt = IgbGetInterruptContext(wdfInterrupt);
87106
IGB_ADAPTER* adapter = interrupt->Adapter;
88107
struct e1000_hw* hw = &adapter->Hw;
89-
u32 icr, eicr;
90-
BOOLEAN queueDPC = FALSE;
91-
92-
eicr = E1000_READ_REG(hw, E1000_EICR);
93-
94-
// Hot eject
95-
if (eicr == 0xffffffff)
96-
{
97-
return TRUE;
98-
}
99108

100-
if (eicr != 0)
109+
if (adapter->MsiInterrupts > 1)
101110
{
102-
InterlockedOr(&interrupt->EICR, eicr);
103-
104-
if ((eicr & E1000_EICR_OTHER) != 0)
111+
if (interrupt == adapter->MiscInterrupt)
105112
{
106-
icr = E1000_READ_REG(hw, E1000_ICR);
113+
// We use MSI-X for RX queues and we are forbidden to read EICR
114+
u32 icr = E1000_READ_REG(hw, E1000_ICR);
115+
116+
// Disarm the TX interrupts for queue where a transfer finished
117+
if ((icr & (E1000_ICR_TXDW | E1000_ICR_TXQE)) != 0)
118+
{
119+
E1000_WRITE_REG(&interrupt->Adapter->Hw, E1000_IMC, E1000_IMS_TXDW | E1000_ICR_TXQE);
120+
E1000_WRITE_FLUSH(&interrupt->Adapter->Hw);
121+
}
122+
107123
InterlockedOr(&interrupt->ICR, icr);
124+
InterlockedOr(&interrupt->EICR, E1000_EICR_OTHER);
125+
}
126+
else
127+
{
128+
// Mark the RX queue
129+
InterlockedOr(&interrupt->EICR, 1 << messageId);
108130
}
109131

110132
WdfInterruptQueueDpcForIsr(wdfInterrupt);
111133
return TRUE;
112134
}
135+
else
136+
{
137+
u32 eicr = E1000_READ_REG(hw, E1000_EICR);
138+
139+
// Hot eject
140+
if (eicr == 0xffffffff)
141+
{
142+
return TRUE;
143+
}
144+
145+
if (eicr != 0)
146+
{
147+
// Disarm the RX interrupts for queues where we received packets
148+
u32 eicrQueueMask = ((1 << IGB_MAX_RX_QUEUES) - 1);
149+
if ((eicr & eicrQueueMask) != 0)
150+
{
151+
E1000_WRITE_REG(&interrupt->Adapter->Hw, E1000_EIMC, eicr & eicrQueueMask);
152+
E1000_WRITE_FLUSH(&interrupt->Adapter->Hw);
153+
}
154+
155+
if ((eicr & E1000_EICR_OTHER) != 0)
156+
{
157+
u32 icr = E1000_READ_REG(hw, E1000_ICR);
158+
159+
// Disarm the TX interrupts for queue where a transfer finished
160+
if ((icr & (E1000_ICR_TXDW | E1000_ICR_TXQE)) != 0)
161+
{
162+
E1000_WRITE_REG(&interrupt->Adapter->Hw, E1000_IMC, E1000_IMS_TXDW | E1000_ICR_TXQE);
163+
E1000_WRITE_FLUSH(&interrupt->Adapter->Hw);
164+
}
165+
166+
InterlockedOr(&interrupt->ICR, icr);
167+
}
168+
169+
InterlockedOr(&interrupt->EICR, eicr);
170+
171+
WdfInterruptQueueDpcForIsr(wdfInterrupt);
172+
return TRUE;
173+
}
113174

114-
return interrupt->IsMsi;
175+
return FALSE;
176+
}
115177
}
116178

117179
_Use_decl_annotations_
118180
VOID
119181
EvtInterruptDpc(
120-
_In_ WDFINTERRUPT Interrupt,
121-
_In_ WDFOBJECT AssociatedObject)
182+
_In_ WDFINTERRUPT wdfInterrupt,
183+
_In_ WDFOBJECT associatedObject)
122184
{
123-
UNREFERENCED_PARAMETER(AssociatedObject);
185+
UNREFERENCED_PARAMETER(associatedObject);
124186

125-
IGB_INTERRUPT* interrupt = IgbGetInterruptContext(Interrupt);
187+
IGB_INTERRUPT* interrupt = IgbGetInterruptContext(wdfInterrupt);
126188
IGB_ADAPTER* adapter = interrupt->Adapter;
127-
struct e1000_hw* hw = &adapter->Hw;
128-
u32 icr = 0, eicr;
129189

130-
eicr = InterlockedExchange(&interrupt->EICR, 0);
190+
LONG eicr = InterlockedExchange(&interrupt->EICR, 0);
131191

132-
for (int i = 0; i < IGB_MAX_RX_QUEUES; i++)
192+
if ((eicr & ((1 << IGB_MAX_RX_QUEUES) - 1)) != 0)
133193
{
134-
if ((eicr & (1 << i)) != 0)
194+
for (int i = 0; i < IGB_MAX_RX_QUEUES; i++)
135195
{
136-
if (InterlockedExchange(&interrupt->RxNotifyArmed[i], FALSE))
196+
if ((eicr & (1 << i)) != 0)
197+
{
137198
NetRxQueueNotifyMoreReceivedPacketsAvailable(adapter->RxQueues[i]);
199+
}
138200
}
139201
}
140202

141203
if ((eicr & E1000_EICR_OTHER) != 0)
142204
{
143-
icr = InterlockedExchange(&interrupt->ICR, 0);
205+
LONG icr = InterlockedExchange(&interrupt->ICR, 0);
144206

145-
if ((icr & (E1000_ICR_TXDW | E1000_ICR_TXQE)) != 0 &&
146-
InterlockedExchange(&interrupt->TxNotifyArmed[0], FALSE))
207+
if ((icr & (E1000_ICR_TXDW | E1000_ICR_TXQE)) != 0)
147208
{
148209
NetTxQueueNotifyMoreCompletedPacketsAvailable(adapter->TxQueues[0]);
149210
}
150211

151212
if ((icr & E1000_ICR_LSC) != 0)
152213
{
153214
DBGPRINT("Link Interrupt!\n");
154-
hw->mac.get_link_status = 1;
215+
adapter->Hw.mac.get_link_status = 1;
155216
IgbCheckLinkStatus(adapter);
156217
}
157218
}
158-
}
219+
}

interrupt.h

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,8 @@ typedef struct _IGB_INTERRUPT
44
{
55
IGB_ADAPTER* Adapter;
66
WDFINTERRUPT Handle;
7-
8-
// Armed Notifications
9-
LONG RxNotifyArmed[IGB_MAX_RX_QUEUES];
10-
LONG TxNotifyArmed[IGB_MAX_TX_QUEUES];
11-
127
LONG EICR;
138
LONG ICR;
14-
15-
BOOLEAN IsMsi;
169
} IGB_INTERRUPT, *PIGB_INTERRUPT;
1710

1811
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(IGB_INTERRUPT, IgbGetInterruptContext);
@@ -27,7 +20,9 @@ IgbInterruptCreate(
2720

2821
void
2922
IgbInterruptInitialize(
30-
_In_ IGB_INTERRUPT* interrupt);
23+
_In_ IGB_ADAPTER* adapter);
3124

3225
EVT_WDF_INTERRUPT_ISR EvtInterruptIsr;
3326
EVT_WDF_INTERRUPT_DPC EvtInterruptDpc;
27+
EVT_WDF_INTERRUPT_ISR EvtQueueInterruptIsr;
28+
EVT_WDF_INTERRUPT_DPC EvtQueueInterruptDpc;

0 commit comments

Comments
 (0)