1 /* 2 * PROJECT: ReactOS nVidia nForce Ethernet Controller Driver 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Interrupt handling 5 * COPYRIGHT: Copyright 2021-2022 Dmitry Borisov <di.sean@protonmail.com> 6 */ 7 8 /* INCLUDES *******************************************************************/ 9 10 #include "nvnet.h" 11 12 #define NDEBUG 13 #include "debug.h" 14 15 /* FUNCTIONS ******************************************************************/ 16 17 ULONG 18 ProcessTransmitDescriptorsLegacy( 19 _In_ PNVNET_ADAPTER Adapter, 20 _Inout_ PLIST_ENTRY SendReadyList) 21 { 22 PNVNET_TCB Tcb = Adapter->Send.LastTcb; 23 ULONG TcbProcessed = 0; 24 25 while (Tcb != Adapter->Send.CurrentTcb) 26 { 27 NVNET_TBD Tbd = Tcb->Tbd; 28 ULONG Flags = Tbd.x32->FlagsLength; 29 30 if (Flags & NV_TX_VALID) 31 break; 32 33 NDIS_DbgPrint(MIN_TRACE, ("Packet transmitted (flags %lx)\n", 34 Flags & FLAG_MASK_V1)); 35 36 if (Flags & NV_TX_ERROR) 37 { 38 ++Adapter->Statistics.TransmitErrors; 39 40 if (Flags & NV_TX_UNDERFLOW) 41 ++Adapter->Statistics.TransmitUnderrunErrors; 42 if (Flags & NV_TX_LATECOLLISION) 43 ++Adapter->Statistics.TransmitLateCollisions; 44 if (Flags & NV_TX_CARRIERLOST) 45 ++Adapter->Statistics.TransmitLostCarrierSense; 46 if (Flags & NV_TX_RETRYERROR) 47 { 48 ++Adapter->Statistics.TransmitExcessiveCollisions; 49 50 if (!(Flags & NV_TX_RETRYCOUNT_MASK)) 51 { 52 NvNetBackoffReseed(Adapter); 53 } 54 } 55 } 56 else 57 { 58 ++Adapter->Statistics.TransmitOk; 59 60 if (Flags & NV_TX_DEFERRED) 61 { 62 ++Adapter->Statistics.TransmitDeferred; 63 } 64 if (!(Flags & NV_TX_RETRYCOUNT_MASK)) 65 ++Adapter->Statistics.TransmitZeroRetry; 66 else if ((Flags & NV_TX_RETRYCOUNT_MASK) == NV_TX_ONE_RETRY) 67 ++Adapter->Statistics.TransmitOneRetry; 68 } 69 70 InsertTailList(SendReadyList, PACKET_ENTRY(Tcb->Packet)); 71 72 NV_RELEASE_TCB(Adapter, Tcb); 73 74 ++TcbProcessed; 75 76 Tcb = NV_NEXT_TCB(Adapter, Tcb); 77 } 78 79 Adapter->Send.LastTcb = Tcb; 80 81 return TcbProcessed; 82 } 83 84 ULONG 85 ProcessTransmitDescriptors32( 86 _In_ PNVNET_ADAPTER Adapter, 87 _Inout_ PLIST_ENTRY SendReadyList) 88 { 89 PNVNET_TCB Tcb = Adapter->Send.LastTcb; 90 ULONG TcbProcessed = 0; 91 92 while (Tcb != Adapter->Send.CurrentTcb) 93 { 94 NVNET_TBD Tbd = Tcb->Tbd; 95 ULONG Flags = Tbd.x32->FlagsLength; 96 97 if (Flags & NV_TX_VALID) 98 break; 99 100 NDIS_DbgPrint(MIN_TRACE, ("Packet transmitted (flags %lx)\n", 101 Flags & FLAG_MASK_V2)); 102 103 if (Flags & NV_TX2_ERROR) 104 { 105 if ((Flags & NV_TX2_RETRYERROR) && !(Flags & NV_TX2_RETRYCOUNT_MASK)) 106 { 107 if (Adapter->Features & DEV_HAS_GEAR_MODE) 108 NvNetBackoffReseedEx(Adapter); 109 else 110 NvNetBackoffReseed(Adapter); 111 } 112 } 113 114 InsertTailList(SendReadyList, PACKET_ENTRY(Tcb->Packet)); 115 116 NV_RELEASE_TCB(Adapter, Tcb); 117 118 ++TcbProcessed; 119 120 Tcb = NV_NEXT_TCB(Adapter, Tcb); 121 } 122 123 Adapter->Send.LastTcb = Tcb; 124 125 return TcbProcessed; 126 } 127 128 ULONG 129 ProcessTransmitDescriptors64( 130 _In_ PNVNET_ADAPTER Adapter, 131 _Inout_ PLIST_ENTRY SendReadyList) 132 { 133 PNVNET_TCB Tcb = Adapter->Send.LastTcb; 134 ULONG TcbProcessed = 0; 135 136 while (Tcb != Adapter->Send.CurrentTcb) 137 { 138 NVNET_TBD Tbd = Tcb->Tbd; 139 ULONG Flags = Tbd.x64->FlagsLength; 140 141 if (Flags & NV_TX_VALID) 142 break; 143 144 if (Adapter->Flags & NV_SEND_ERRATA_PRESENT) 145 { 146 PNVNET_TCB DeferredTcb; 147 148 --Adapter->Send.PacketsCount; 149 150 DeferredTcb = Adapter->Send.DeferredTcb; 151 152 if (DeferredTcb) 153 { 154 DeferredTcb->DeferredTbd.x64->FlagsLength |= NV_TX2_VALID; 155 156 ++Adapter->Send.PacketsCount; 157 158 Adapter->Send.DeferredTcb = NV_NEXT_TCB(Adapter, DeferredTcb); 159 if (Adapter->Send.DeferredTcb == Adapter->Send.CurrentTcb) 160 { 161 Adapter->Send.DeferredTcb = NULL; 162 } 163 164 NV_WRITE(Adapter, NvRegTxRxControl, Adapter->TxRxControl | NVREG_TXRXCTL_KICK); 165 } 166 } 167 168 NDIS_DbgPrint(MIN_TRACE, ("Packet transmitted (flags %lx)\n", 169 Flags & FLAG_MASK_V2)); 170 171 if (Flags & NV_TX2_ERROR) 172 { 173 if ((Flags & NV_TX2_RETRYERROR) && !(Flags & NV_TX2_RETRYCOUNT_MASK)) 174 { 175 if (Adapter->Features & DEV_HAS_GEAR_MODE) 176 NvNetBackoffReseedEx(Adapter); 177 else 178 NvNetBackoffReseed(Adapter); 179 } 180 } 181 182 InsertTailList(SendReadyList, PACKET_ENTRY(Tcb->Packet)); 183 184 NV_RELEASE_TCB(Adapter, Tcb); 185 186 ++TcbProcessed; 187 188 Tcb = NV_NEXT_TCB(Adapter, Tcb); 189 } 190 191 Adapter->Send.LastTcb = Tcb; 192 193 return TcbProcessed; 194 } 195 196 static 197 BOOLEAN 198 HandleLengthError( 199 _In_ PVOID EthHeader, 200 _Inout_ PUSHORT Length) 201 { 202 PUCHAR Buffer = EthHeader; 203 ULONG i; 204 205 DBG_UNREFERENCED_LOCAL_VARIABLE(Buffer); 206 207 /* TODO */ 208 NDIS_DbgPrint(MAX_TRACE, ("() Length error detected (%u): \n", *Length)); 209 for (i = 0; i < *Length; ++i) 210 { 211 NDIS_DbgPrint(MAX_TRACE, ("%02x ", Buffer[i])); 212 } 213 NDIS_DbgPrint(MAX_TRACE, ("\n\n*** Please report it to the team! ***\n\n")); 214 215 return FALSE; 216 } 217 218 /* TODO: This need to be rewritten. I leave it as-is for now */ 219 static 220 ULONG 221 ProcessReceiveDescriptors( 222 _In_ PNVNET_ADAPTER Adapter, 223 _In_ ULONG TotalRxProcessed) 224 { 225 ULONG i, RxProcessed = 0; 226 BOOLEAN IndicateComplete = FALSE; 227 228 for (i = 0; i < NVNET_RECEIVE_DESCRIPTORS; ++i) 229 { 230 ULONG Flags; 231 USHORT Length; 232 PUCHAR EthHeader; 233 NV_RBD NvRbd; 234 235 if (Adapter->Features & DEV_HAS_HIGH_DMA) 236 { 237 NvRbd.x64 = &Adapter->Receive.NvRbd.x64[Adapter->CurrentRx]; 238 Flags = NvRbd.x64->FlagsLength; 239 } 240 else 241 { 242 NvRbd.x32 = &Adapter->Receive.NvRbd.x32[Adapter->CurrentRx]; 243 Flags = NvRbd.x32->FlagsLength; 244 } 245 246 if (Flags & NV_RX_AVAIL) 247 break; 248 249 if (TotalRxProcessed + RxProcessed >= NVNET_RECEIVE_PROCESSING_LIMIT) 250 break; 251 252 if (!Adapter->PacketFilter) 253 goto NextDescriptor; 254 255 if (Adapter->Features & (DEV_HAS_HIGH_DMA | DEV_HAS_LARGEDESC)) 256 { 257 if (!(Flags & NV_RX2_DESCRIPTORVALID)) 258 goto NextDescriptor; 259 260 Length = Flags & LEN_MASK_V2; 261 EthHeader = &Adapter->ReceiveBuffer[Adapter->CurrentRx * NVNET_RECEIVE_BUFFER_SIZE]; 262 263 if (Flags & NV_RX2_ERROR) 264 { 265 if ((Flags & NV_RX2_ERROR_MASK) == NV_RX2_ERROR4) 266 { 267 if (!HandleLengthError(EthHeader, &Length)) 268 goto NextDescriptor; 269 } 270 else if ((Flags & NV_RX2_ERROR_MASK) == NV_RX2_FRAMINGERR) 271 { 272 if (Flags & NV_RX2_SUBTRACT1) 273 --Length; 274 } 275 else 276 { 277 goto NextDescriptor; 278 } 279 } 280 281 NDIS_DbgPrint(MIN_TRACE, ("Packet %d received (length %d, flags %lx)\n", 282 Adapter->CurrentRx, Length, Flags & FLAG_MASK_V2)); 283 } 284 else 285 { 286 if (!(Flags & NV_RX_DESCRIPTORVALID)) 287 goto NextDescriptor; 288 289 Length = Flags & LEN_MASK_V1; 290 EthHeader = &Adapter->ReceiveBuffer[Adapter->CurrentRx * NVNET_RECEIVE_BUFFER_SIZE]; 291 292 if (Flags & NV_RX_ERROR) 293 { 294 if ((Flags & NV_RX_ERROR_MASK) == NV_RX_ERROR4) 295 { 296 if (!HandleLengthError(EthHeader, &Length)) 297 goto NextDescriptor; 298 } 299 else if ((Flags & NV_RX_ERROR_MASK) == NV_RX_FRAMINGERR) 300 { 301 if (Flags & NV_RX_SUBTRACT1) 302 --Length; 303 } 304 else 305 { 306 ++Adapter->Statistics.ReceiveErrors; 307 308 if (Flags & NV_RX_MISSEDFRAME) 309 ++Adapter->Statistics.ReceiveNoBuffers; 310 if (Flags & NV_RX_FRAMINGERR) 311 ++Adapter->Statistics.ReceiveAlignmentErrors; 312 if (Flags & NV_RX_OVERFLOW) 313 ++Adapter->Statistics.ReceiveOverrunErrors; 314 if (Flags & NV_RX_CRCERR) 315 ++Adapter->Statistics.ReceiveCrcErrors; 316 317 goto NextDescriptor; 318 } 319 } 320 ++Adapter->Statistics.ReceiveOk; 321 322 NDIS_DbgPrint(MIN_TRACE, ("Packet %d received (length %d, flags %lx)\n", 323 Adapter->CurrentRx, Length, Flags & FLAG_MASK_V1)); 324 } 325 326 NdisMEthIndicateReceive(Adapter->AdapterHandle, 327 NULL, 328 (PCHAR)EthHeader, 329 sizeof(ETH_HEADER), 330 EthHeader + sizeof(ETH_HEADER), 331 Length - sizeof(ETH_HEADER), 332 Length - sizeof(ETH_HEADER)); 333 IndicateComplete = TRUE; 334 335 NextDescriptor: 336 /* Invalidate the buffer length and release the descriptor */ 337 if (Adapter->Features & DEV_HAS_HIGH_DMA) 338 NvRbd.x64->FlagsLength = NV_RX2_AVAIL | NVNET_RECEIVE_BUFFER_SIZE; 339 else 340 NvRbd.x32->FlagsLength = NV_RX_AVAIL | NVNET_RECEIVE_BUFFER_SIZE; 341 342 Adapter->CurrentRx = (Adapter->CurrentRx + 1) % NVNET_RECEIVE_DESCRIPTORS; 343 ++RxProcessed; 344 } 345 346 if (IndicateComplete) 347 { 348 NdisMEthIndicateReceiveComplete(Adapter->AdapterHandle); 349 } 350 351 return RxProcessed; 352 } 353 354 static 355 inline 356 VOID 357 ChangeInterruptMode( 358 _In_ PNVNET_ADAPTER Adapter, 359 _In_ ULONG Workload) 360 { 361 if (Workload > NVNET_IM_THRESHOLD) 362 { 363 Adapter->InterruptIdleCount = 0; 364 365 /* High activity, polling based strategy */ 366 Adapter->InterruptMask = NVREG_IRQMASK_CPU; 367 } 368 else 369 { 370 if (Adapter->InterruptIdleCount < NVNET_IM_MAX_IDLE) 371 { 372 ++Adapter->InterruptIdleCount; 373 } 374 else 375 { 376 /* Low activity, 1 interrupt per packet */ 377 Adapter->InterruptMask = NVREG_IRQMASK_THROUGHPUT; 378 } 379 } 380 } 381 382 static 383 VOID 384 HandleLinkStateChange( 385 _In_ PNVNET_ADAPTER Adapter) 386 { 387 ULONG MiiStatus; 388 BOOLEAN Connected, Report = FALSE; 389 390 NDIS_DbgPrint(MIN_TRACE, ("()\n")); 391 392 NdisDprAcquireSpinLock(&Adapter->Lock); 393 394 MiiStatus = NV_READ(Adapter, NvRegMIIStatus); 395 396 /* Clear the link change interrupt */ 397 NV_WRITE(Adapter, NvRegMIIStatus, NVREG_MIISTAT_LINKCHANGE); 398 399 if (MiiStatus & NVREG_MIISTAT_LINKCHANGE) 400 { 401 Connected = NvNetUpdateLinkSpeed(Adapter); 402 if (Adapter->Connected != Connected) 403 { 404 Adapter->Connected = Connected; 405 Report = TRUE; 406 407 if (Connected) 408 { 409 /* Link up */ 410 NvNetToggleClockPowerGating(Adapter, FALSE); 411 NdisDprAcquireSpinLock(&Adapter->Receive.Lock); 412 NvNetStartReceiver(Adapter); 413 } 414 else 415 { 416 /* Link down */ 417 NvNetToggleClockPowerGating(Adapter, TRUE); 418 NdisDprAcquireSpinLock(&Adapter->Receive.Lock); 419 NvNetStopReceiver(Adapter); 420 } 421 422 NdisDprReleaseSpinLock(&Adapter->Receive.Lock); 423 } 424 } 425 426 NdisDprReleaseSpinLock(&Adapter->Lock); 427 428 if (Report) 429 { 430 NdisMIndicateStatus(Adapter->AdapterHandle, 431 Connected ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT, 432 NULL, 433 0); 434 NdisMIndicateStatusComplete(Adapter->AdapterHandle); 435 } 436 } 437 438 static 439 VOID 440 HandleRecoverableError( 441 _In_ PNVNET_ADAPTER Adapter) 442 { 443 /* TODO */ 444 NDIS_DbgPrint(MAX_TRACE, ("() Recoverable error detected\n")); 445 } 446 447 VOID 448 NTAPI 449 MiniportHandleInterrupt( 450 _In_ NDIS_HANDLE MiniportAdapterContext) 451 { 452 PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; 453 ULONG InterruptStatus = Adapter->InterruptStatus; 454 ULONG RxProcessed, TotalTxProcessed = 0, TotalRxProcessed = 0; 455 LIST_ENTRY SendReadyList; 456 457 NDIS_DbgPrint(MIN_TRACE, ("() Events 0x%lx\n", InterruptStatus)); 458 459 if (!(Adapter->Flags & NV_ACTIVE)) 460 return; 461 462 InitializeListHead(&SendReadyList); 463 464 /* Process the rings and measure network activity */ 465 while (TotalRxProcessed < NVNET_RECEIVE_PROCESSING_LIMIT) 466 { 467 NdisDprAcquireSpinLock(&Adapter->Send.Lock); 468 469 TotalTxProcessed += Adapter->ProcessTransmit(Adapter, &SendReadyList); 470 471 NdisDprReleaseSpinLock(&Adapter->Send.Lock); 472 473 while (!IsListEmpty(&SendReadyList)) 474 { 475 PLIST_ENTRY Entry = RemoveHeadList(&SendReadyList); 476 477 NdisMSendComplete(Adapter->AdapterHandle, 478 CONTAINING_RECORD(Entry, NDIS_PACKET, MiniportReserved), 479 NDIS_STATUS_SUCCESS); 480 } 481 482 RxProcessed = ProcessReceiveDescriptors(Adapter, TotalRxProcessed); 483 if (!RxProcessed) 484 break; 485 486 TotalRxProcessed += RxProcessed; 487 } 488 489 NDIS_DbgPrint(MIN_TRACE, ("Total TX: %d, RX: %d\n", TotalTxProcessed, TotalRxProcessed)); 490 491 /* Moderate the interrupts */ 492 if (Adapter->OptimizationMode == NV_OPTIMIZATION_MODE_DYNAMIC) 493 { 494 ChangeInterruptMode(Adapter, TotalTxProcessed + TotalRxProcessed); 495 } 496 497 if (InterruptStatus & NVREG_IRQ_RX_NOBUF) 498 { 499 ++Adapter->Statistics.ReceiveIrqNoBuffers; 500 } 501 502 if (InterruptStatus & NVREG_IRQ_LINK) 503 { 504 HandleLinkStateChange(Adapter); 505 } 506 507 if (InterruptStatus & NVREG_IRQ_RECOVER_ERROR) 508 { 509 HandleRecoverableError(Adapter); 510 } 511 512 /* Enable interrupts on the NIC */ 513 NvNetApplyInterruptMask(Adapter); 514 } 515 516 VOID 517 NTAPI 518 MiniportISR( 519 _Out_ PBOOLEAN InterruptRecognized, 520 _Out_ PBOOLEAN QueueMiniportHandleInterrupt, 521 _In_ NDIS_HANDLE MiniportAdapterContext) 522 { 523 PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext; 524 ULONG InterruptStatus; 525 526 NDIS_DbgPrint(MIN_TRACE, ("()\n")); 527 528 InterruptStatus = NV_READ(Adapter, NvRegIrqStatus); 529 530 /* Clear any interrupt events */ 531 NV_WRITE(Adapter, NvRegIrqStatus, InterruptStatus); 532 533 if (InterruptStatus & Adapter->InterruptMask) 534 { 535 /* Disable further interrupts */ 536 NvNetDisableInterrupts(Adapter); 537 538 Adapter->InterruptStatus = InterruptStatus; 539 540 *InterruptRecognized = TRUE; 541 *QueueMiniportHandleInterrupt = TRUE; 542 } 543 else 544 { 545 /* This interrupt is not ours */ 546 *InterruptRecognized = FALSE; 547 *QueueMiniportHandleInterrupt = FALSE; 548 } 549 } 550