Browse Source

[intelxl] Choose to operate in non-PXE mode

The physical function defaults to operating in "PXE mode" after a
power-on reset.  In this mode, receive descriptors are fetched and
written back as single descriptors.  In normal (non-PXE mode)
operation, receive descriptors are fetched and written back only as
complete cachelines unless an interrupt is raised.

There is no way to return to PXE mode from non-PXE mode, and there is
no way for the virtual function driver to operate in PXE mode.

Choose to operate in non-PXE mode.  This requires us to trick the
hardware into believing that it is raising an interrupt, so that it
will not defer writing back receive descriptors until a complete
cacheline (i.e. four packets) have been consumed.  We do so by
configuring the hardware to use MSI-X with a dummy target location in
place of the usual APIC register.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 5 years ago
parent
commit
92b46b7858
2 changed files with 155 additions and 28 deletions
  1. 113
    20
      src/drivers/net/intelxl.c
  2. 42
    8
      src/drivers/net/intelxl.h

+ 113
- 20
src/drivers/net/intelxl.c View File

116
 	return 0;
116
 	return 0;
117
 }
117
 }
118
 
118
 
119
+/******************************************************************************
120
+ *
121
+ * MSI-X interrupts
122
+ *
123
+ ******************************************************************************
124
+ */
125
+
126
+/**
127
+ * Enable MSI-X dummy interrupt
128
+ *
129
+ * @v intelxl		Intel device
130
+ * @v pci		PCI device
131
+ * @ret rc		Return status code
132
+ */
133
+int intelxl_msix_enable ( struct intelxl_nic *intelxl,
134
+			  struct pci_device *pci ) {
135
+	int rc;
136
+
137
+	/* Enable MSI-X capability */
138
+	if ( ( rc = pci_msix_enable ( pci, &intelxl->msix ) ) != 0 ) {
139
+		DBGC ( intelxl, "INTELXL %p could not enable MSI-X: %s\n",
140
+		       intelxl, strerror ( rc ) );
141
+		return rc;
142
+	}
143
+
144
+	/* Configure interrupt zero to write to dummy location */
145
+	pci_msix_map ( &intelxl->msix, 0, virt_to_bus ( &intelxl->msg ), 0 );
146
+
147
+	/* Enable dummy interrupt zero */
148
+	pci_msix_unmask ( &intelxl->msix, 0 );
149
+
150
+	return 0;
151
+}
152
+
153
+/**
154
+ * Disable MSI-X dummy interrupt
155
+ *
156
+ * @v intelxl		Intel device
157
+ * @v pci		PCI device
158
+ */
159
+void intelxl_msix_disable ( struct intelxl_nic *intelxl,
160
+			    struct pci_device *pci ) {
161
+
162
+	/* Disable dummy interrupt zero */
163
+	pci_msix_mask ( &intelxl->msix, 0 );
164
+
165
+	/* Disable MSI-X capability */
166
+	pci_msix_disable ( pci, &intelxl->msix );
167
+}
168
+
119
 /******************************************************************************
169
 /******************************************************************************
120
  *
170
  *
121
  * Admin queue
171
  * Admin queue
480
 	return 0;
530
 	return 0;
481
 }
531
 }
482
 
532
 
533
+/**
534
+ * Clear PXE mode
535
+ *
536
+ * @v intelxl		Intel device
537
+ * @ret rc		Return status code
538
+ */
539
+static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) {
540
+	struct intelxl_admin_descriptor *cmd;
541
+	struct intelxl_admin_clear_pxe_params *pxe;
542
+	uint32_t gllan_rctl_0;
543
+	int rc;
544
+
545
+	/* Do nothing if device is already out of PXE mode */
546
+	gllan_rctl_0 = readl ( intelxl->regs + INTELXL_GLLAN_RCTL_0 );
547
+	if ( ! ( gllan_rctl_0 & INTELXL_GLLAN_RCTL_0_PXE_MODE ) ) {
548
+		DBGC2 ( intelxl, "INTELXL %p already in non-PXE mode\n",
549
+			intelxl );
550
+		return 0;
551
+	}
552
+
553
+	/* Populate descriptor */
554
+	cmd = intelxl_admin_command_descriptor ( intelxl );
555
+	cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_CLEAR_PXE );
556
+	pxe = &cmd->params.pxe;
557
+	pxe->magic = INTELXL_ADMIN_CLEAR_PXE_MAGIC;
558
+
559
+	/* Issue command */
560
+	if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
561
+		return rc;
562
+
563
+	return 0;
564
+}
565
+
483
 /**
566
 /**
484
  * Get switch configuration
567
  * Get switch configuration
485
  *
568
  *
1504
 void intelxl_poll ( struct net_device *netdev ) {
1587
 void intelxl_poll ( struct net_device *netdev ) {
1505
 	struct intelxl_nic *intelxl = netdev->priv;
1588
 	struct intelxl_nic *intelxl = netdev->priv;
1506
 
1589
 
1507
-	/* Acknowledge interrupts, if applicable */
1508
-	if ( netdev_irq_enabled ( netdev ) ) {
1509
-		writel ( ( INTELXL_INT_DYN_CTL_CLEARPBA |
1510
-			   INTELXL_INT_DYN_CTL_INTENA_MASK ),
1511
-			 ( intelxl->regs + intelxl->intr ) );
1512
-	}
1513
-
1514
 	/* Poll for completed packets */
1590
 	/* Poll for completed packets */
1515
 	intelxl_poll_tx ( netdev );
1591
 	intelxl_poll_tx ( netdev );
1516
 
1592
 
1522
 
1598
 
1523
 	/* Refill RX ring */
1599
 	/* Refill RX ring */
1524
 	intelxl_refill_rx ( intelxl );
1600
 	intelxl_refill_rx ( intelxl );
1525
-}
1526
-
1527
-/**
1528
- * Enable or disable interrupts
1529
- *
1530
- * @v netdev		Network device
1531
- * @v enable		Interrupts should be enabled
1532
- */
1533
-static void intelxl_irq ( struct net_device *netdev, int enable ) {
1534
-	struct intelxl_nic *intelxl = netdev->priv;
1535
 
1601
 
1536
-	writel ( ( enable ? INTELXL_INT_DYN_CTL_INTENA : 0 ),
1537
-		 ( intelxl->regs + intelxl->intr ) );
1602
+	/* Rearm interrupt, since otherwise receive descriptors will
1603
+	 * be written back only after a complete cacheline (four
1604
+	 * packets) have been received.
1605
+	 *
1606
+	 * There is unfortunately no efficient way to determine
1607
+	 * whether or not rearming the interrupt is necessary.  If we
1608
+	 * are running inside a hypervisor (e.g. using a VF or PF as a
1609
+	 * passed-through PCI device), then the MSI-X write is
1610
+	 * redirected by the hypervisor to the real host APIC and the
1611
+	 * host ISR then raises an interrupt within the guest.  We
1612
+	 * therefore cannot poll the nominal MSI-X target location to
1613
+	 * watch for the value being written.  We could read from the
1614
+	 * INT_DYN_CTL register, but this is even less efficient than
1615
+	 * just unconditionally rearming the interrupt.
1616
+	 */
1617
+	writel ( INTELXL_INT_DYN_CTL_INTENA, intelxl->regs + intelxl->intr );
1538
 }
1618
 }
1539
 
1619
 
1540
 /** Network device operations */
1620
 /** Network device operations */
1543
 	.close		= intelxl_close,
1623
 	.close		= intelxl_close,
1544
 	.transmit	= intelxl_transmit,
1624
 	.transmit	= intelxl_transmit,
1545
 	.poll		= intelxl_poll,
1625
 	.poll		= intelxl_poll,
1546
-	.irq		= intelxl_irq,
1547
 };
1626
 };
1548
 
1627
 
1549
 /******************************************************************************
1628
 /******************************************************************************
1617
 	if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 )
1696
 	if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 )
1618
 		goto err_fetch_mac;
1697
 		goto err_fetch_mac;
1619
 
1698
 
1699
+	/* Enable MSI-X dummy interrupt */
1700
+	if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 )
1701
+		goto err_msix;
1702
+
1620
 	/* Open admin queues */
1703
 	/* Open admin queues */
1621
 	if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 )
1704
 	if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 )
1622
 		goto err_open_admin;
1705
 		goto err_open_admin;
1623
 
1706
 
1707
+	/* Clear PXE mode */
1708
+	if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 )
1709
+		goto err_admin_clear_pxe;
1710
+
1624
 	/* Get switch configuration */
1711
 	/* Get switch configuration */
1625
 	if ( ( rc = intelxl_admin_switch ( intelxl ) ) != 0 )
1712
 	if ( ( rc = intelxl_admin_switch ( intelxl ) ) != 0 )
1626
 		goto err_admin_switch;
1713
 		goto err_admin_switch;
1667
  err_admin_promisc:
1754
  err_admin_promisc:
1668
  err_admin_vsi:
1755
  err_admin_vsi:
1669
  err_admin_switch:
1756
  err_admin_switch:
1757
+ err_admin_clear_pxe:
1670
 	intelxl_close_admin ( intelxl );
1758
 	intelxl_close_admin ( intelxl );
1671
  err_open_admin:
1759
  err_open_admin:
1760
+	intelxl_msix_disable ( intelxl, pci );
1761
+ err_msix:
1672
  err_fetch_mac:
1762
  err_fetch_mac:
1673
 	intelxl_reset ( intelxl );
1763
 	intelxl_reset ( intelxl );
1674
  err_reset:
1764
  err_reset:
1695
 	/* Close admin queues */
1785
 	/* Close admin queues */
1696
 	intelxl_close_admin ( intelxl );
1786
 	intelxl_close_admin ( intelxl );
1697
 
1787
 
1788
+	/* Disable MSI-X dummy interrupt */
1789
+	intelxl_msix_disable ( intelxl, pci );
1790
+
1698
 	/* Reset the NIC */
1791
 	/* Reset the NIC */
1699
 	intelxl_reset ( intelxl );
1792
 	intelxl_reset ( intelxl );
1700
 
1793
 

+ 42
- 8
src/drivers/net/intelxl.h View File

11
 
11
 
12
 #include <stdint.h>
12
 #include <stdint.h>
13
 #include <ipxe/if_ether.h>
13
 #include <ipxe/if_ether.h>
14
+#include <ipxe/pcimsix.h>
14
 
15
 
15
 struct intelxl_nic;
16
 struct intelxl_nic;
16
 
17
 
143
 /** Driver is unloading */
144
 /** Driver is unloading */
144
 #define INTELXL_ADMIN_SHUTDOWN_UNLOADING 0x01
145
 #define INTELXL_ADMIN_SHUTDOWN_UNLOADING 0x01
145
 
146
 
147
+/** Admin queue Clear PXE Mode command */
148
+#define INTELXL_ADMIN_CLEAR_PXE 0x0110
149
+
150
+/** Admin queue Clear PXE Mode command parameters */
151
+struct intelxl_admin_clear_pxe_params {
152
+	/** Magic value */
153
+	uint8_t magic;
154
+	/** Reserved */
155
+	uint8_t reserved[15];
156
+} __attribute__ (( packed ));
157
+
158
+/** Clear PXE Mode magic value */
159
+#define INTELXL_ADMIN_CLEAR_PXE_MAGIC 0x02
160
+
146
 /** Admin queue Get Switch Configuration command */
161
 /** Admin queue Get Switch Configuration command */
147
 #define INTELXL_ADMIN_SWITCH 0x0200
162
 #define INTELXL_ADMIN_SWITCH 0x0200
148
 
163
 
305
 	struct intelxl_admin_driver_params driver;
320
 	struct intelxl_admin_driver_params driver;
306
 	/** Shutdown command parameters */
321
 	/** Shutdown command parameters */
307
 	struct intelxl_admin_shutdown_params shutdown;
322
 	struct intelxl_admin_shutdown_params shutdown;
323
+	/** Clear PXE Mode command parameters */
324
+	struct intelxl_admin_clear_pxe_params pxe;
308
 	/** Get Switch Configuration command parameters */
325
 	/** Get Switch Configuration command parameters */
309
 	struct intelxl_admin_switch_params sw;
326
 	struct intelxl_admin_switch_params sw;
310
 	/** Get VSI Parameters command parameters */
327
 	/** Get VSI Parameters command parameters */
563
 /** Queue Tail Pointer Register (offset) */
580
 /** Queue Tail Pointer Register (offset) */
564
 #define INTELXL_QXX_TAIL 0x8000
581
 #define INTELXL_QXX_TAIL 0x8000
565
 
582
 
583
+/** Global RLAN Control 0 register */
584
+#define INTELXL_GLLAN_RCTL_0 0x12a500
585
+#define INTELXL_GLLAN_RCTL_0_PXE_MODE	0x00000001UL	/**< PXE mode */
586
+
566
 /** Transmit data descriptor */
587
 /** Transmit data descriptor */
567
 struct intelxl_tx_data_descriptor {
588
 struct intelxl_tx_data_descriptor {
568
 	/** Buffer address */
589
 	/** Buffer address */
709
 	ring->context = context;
730
 	ring->context = context;
710
 }
731
 }
711
 
732
 
712
-/** Number of transmit descriptors */
713
-#define INTELXL_TX_NUM_DESC 16
733
+/** Number of transmit descriptors
734
+ *
735
+ * Chosen to exceed the receive ring fill level, in order to avoid
736
+ * running out of transmit descriptors when sending TCP ACKs.
737
+ */
738
+#define INTELXL_TX_NUM_DESC 64
714
 
739
 
715
 /** Transmit descriptor ring maximum fill level */
740
 /** Transmit descriptor ring maximum fill level */
716
 #define INTELXL_TX_FILL ( INTELXL_TX_NUM_DESC - 1 )
741
 #define INTELXL_TX_FILL ( INTELXL_TX_NUM_DESC - 1 )
717
 
742
 
718
 /** Number of receive descriptors
743
 /** Number of receive descriptors
719
  *
744
  *
720
- * In PXE mode (i.e. able to post single receive descriptors), 8
721
- * descriptors is the only permitted value covering all possible
722
- * numbers of PFs.
745
+ * Must be a multiple of 32.
723
  */
746
  */
724
-#define INTELXL_RX_NUM_DESC 8
747
+#define INTELXL_RX_NUM_DESC 32
725
 
748
 
726
-/** Receive descriptor ring fill level */
727
-#define INTELXL_RX_FILL ( INTELXL_RX_NUM_DESC - 1 )
749
+/** Receive descriptor ring fill level
750
+ *
751
+ * Must be a multiple of 8 and greater than 8.
752
+ */
753
+#define INTELXL_RX_FILL 16
728
 
754
 
729
 /******************************************************************************
755
 /******************************************************************************
730
  *
756
  *
837
 	unsigned int qset;
863
 	unsigned int qset;
838
 	/** Interrupt control register */
864
 	/** Interrupt control register */
839
 	unsigned int intr;
865
 	unsigned int intr;
866
+	/** MSI-X capability */
867
+	struct pci_msix msix;
868
+	/** MSI-X dummy interrupt target */
869
+	uint32_t msg;
840
 
870
 
841
 	/** Admin command queue */
871
 	/** Admin command queue */
842
 	struct intelxl_admin command;
872
 	struct intelxl_admin command;
851
 	struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC];
881
 	struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC];
852
 };
882
 };
853
 
883
 
884
+extern int intelxl_msix_enable ( struct intelxl_nic *intelxl,
885
+				 struct pci_device *pci );
886
+extern void intelxl_msix_disable ( struct intelxl_nic *intelxl,
887
+				   struct pci_device *pci );
854
 extern struct intelxl_admin_descriptor *
888
 extern struct intelxl_admin_descriptor *
855
 intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl );
889
 intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl );
856
 extern union intelxl_admin_buffer *
890
 extern union intelxl_admin_buffer *

Loading…
Cancel
Save