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,6 +116,56 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl,
116 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 171
  * Admin queue
@@ -480,6 +530,39 @@ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) {
480 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 567
  * Get switch configuration
485 568
  *
@@ -1504,13 +1587,6 @@ static void intelxl_poll_rx ( struct net_device *netdev ) {
1504 1587
 void intelxl_poll ( struct net_device *netdev ) {
1505 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 1590
 	/* Poll for completed packets */
1515 1591
 	intelxl_poll_tx ( netdev );
1516 1592
 
@@ -1522,19 +1598,23 @@ void intelxl_poll ( struct net_device *netdev ) {
1522 1598
 
1523 1599
 	/* Refill RX ring */
1524 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 1620
 /** Network device operations */
@@ -1543,7 +1623,6 @@ static struct net_device_operations intelxl_operations = {
1543 1623
 	.close		= intelxl_close,
1544 1624
 	.transmit	= intelxl_transmit,
1545 1625
 	.poll		= intelxl_poll,
1546
-	.irq		= intelxl_irq,
1547 1626
 };
1548 1627
 
1549 1628
 /******************************************************************************
@@ -1617,10 +1696,18 @@ static int intelxl_probe ( struct pci_device *pci ) {
1617 1696
 	if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 )
1618 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 1703
 	/* Open admin queues */
1621 1704
 	if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 )
1622 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 1711
 	/* Get switch configuration */
1625 1712
 	if ( ( rc = intelxl_admin_switch ( intelxl ) ) != 0 )
1626 1713
 		goto err_admin_switch;
@@ -1667,8 +1754,11 @@ static int intelxl_probe ( struct pci_device *pci ) {
1667 1754
  err_admin_promisc:
1668 1755
  err_admin_vsi:
1669 1756
  err_admin_switch:
1757
+ err_admin_clear_pxe:
1670 1758
 	intelxl_close_admin ( intelxl );
1671 1759
  err_open_admin:
1760
+	intelxl_msix_disable ( intelxl, pci );
1761
+ err_msix:
1672 1762
  err_fetch_mac:
1673 1763
 	intelxl_reset ( intelxl );
1674 1764
  err_reset:
@@ -1695,6 +1785,9 @@ static void intelxl_remove ( struct pci_device *pci ) {
1695 1785
 	/* Close admin queues */
1696 1786
 	intelxl_close_admin ( intelxl );
1697 1787
 
1788
+	/* Disable MSI-X dummy interrupt */
1789
+	intelxl_msix_disable ( intelxl, pci );
1790
+
1698 1791
 	/* Reset the NIC */
1699 1792
 	intelxl_reset ( intelxl );
1700 1793
 

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

@@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11 11
 
12 12
 #include <stdint.h>
13 13
 #include <ipxe/if_ether.h>
14
+#include <ipxe/pcimsix.h>
14 15
 
15 16
 struct intelxl_nic;
16 17
 
@@ -143,6 +144,20 @@ struct intelxl_admin_shutdown_params {
143 144
 /** Driver is unloading */
144 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 161
 /** Admin queue Get Switch Configuration command */
147 162
 #define INTELXL_ADMIN_SWITCH 0x0200
148 163
 
@@ -305,6 +320,8 @@ union intelxl_admin_params {
305 320
 	struct intelxl_admin_driver_params driver;
306 321
 	/** Shutdown command parameters */
307 322
 	struct intelxl_admin_shutdown_params shutdown;
323
+	/** Clear PXE Mode command parameters */
324
+	struct intelxl_admin_clear_pxe_params pxe;
308 325
 	/** Get Switch Configuration command parameters */
309 326
 	struct intelxl_admin_switch_params sw;
310 327
 	/** Get VSI Parameters command parameters */
@@ -563,6 +580,10 @@ struct intelxl_context_rx {
563 580
 /** Queue Tail Pointer Register (offset) */
564 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 587
 /** Transmit data descriptor */
567 588
 struct intelxl_tx_data_descriptor {
568 589
 	/** Buffer address */
@@ -709,22 +730,27 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len,
709 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 740
 /** Transmit descriptor ring maximum fill level */
716 741
 #define INTELXL_TX_FILL ( INTELXL_TX_NUM_DESC - 1 )
717 742
 
718 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,6 +863,10 @@ struct intelxl_nic {
837 863
 	unsigned int qset;
838 864
 	/** Interrupt control register */
839 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 871
 	/** Admin command queue */
842 872
 	struct intelxl_admin command;
@@ -851,6 +881,10 @@ struct intelxl_nic {
851 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 888
 extern struct intelxl_admin_descriptor *
855 889
 intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl );
856 890
 extern union intelxl_admin_buffer *

Loading…
Cancel
Save