All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jingjing Wu <jingjing.wu@intel.com>
To: bruce.richardson@intel.com
Cc: dev@dpdk.org
Subject: [PATCH v3 2/2] i40evf: support to report pf reset event
Date: Fri, 26 Feb 2016 14:51:54 +0800	[thread overview]
Message-ID: <1456469514-9103-3-git-send-email-jingjing.wu@intel.com> (raw)
In-Reply-To: <1456469514-9103-1-git-send-email-jingjing.wu@intel.com>

When Linux PF and DPDK VF are used for i40e PMD, In case of PF reset,
interrupt will go via adminq event, VF need be informed the event,
a callback mechanism is introduced by VF. This will allow VF to
invoke callback when reset happens.
Users can register a callback for this interrupt event like:
  rte_eth_dev_callback_register(portid,
		RTE_ETH_EVENT_INTR_RESET,
		reset_event_callback,
		arg);

Signed-off-by: Jingjing Wu <jingjing.wu@intel.com>
---
 doc/guides/rel_notes/release_16_04.rst |   1 +
 drivers/net/i40e/i40e_ethdev_vf.c      | 272 +++++++++++++++++++++++++++++----
 lib/librte_ether/rte_ethdev.h          |   1 +
 3 files changed, 245 insertions(+), 29 deletions(-)

diff --git a/doc/guides/rel_notes/release_16_04.rst b/doc/guides/rel_notes/release_16_04.rst
index 3036f7d..596b6e2 100644
--- a/doc/guides/rel_notes/release_16_04.rst
+++ b/doc/guides/rel_notes/release_16_04.rst
@@ -70,6 +70,7 @@ This section should contain new features added in this release. Sample format:
 
 * **szedata2: Add functions for setting link up/down.**
 
+* **Added pf reset event reported in i40e vf PMD driver.
 
 Resolved Issues
 ---------------
diff --git a/drivers/net/i40e/i40e_ethdev_vf.c b/drivers/net/i40e/i40e_ethdev_vf.c
index 4b15ed6..b6e360b 100644
--- a/drivers/net/i40e/i40e_ethdev_vf.c
+++ b/drivers/net/i40e/i40e_ethdev_vf.c
@@ -74,8 +74,6 @@
 #define I40EVF_BUSY_WAIT_DELAY 10
 #define I40EVF_BUSY_WAIT_COUNT 50
 #define MAX_RESET_WAIT_CNT     20
-/*ITR index for NOITR*/
-#define I40E_QINT_RQCTL_MSIX_INDX_NOITR     3
 
 struct i40evf_arq_msg_info {
 	enum i40e_virtchnl_ops ops;
@@ -151,6 +149,9 @@ static int
 i40evf_dev_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id);
 static int
 i40evf_dev_rx_queue_intr_disable(struct rte_eth_dev *dev, uint16_t queue_id);
+static void i40evf_handle_pf_event(__rte_unused struct rte_eth_dev *dev,
+			   uint8_t *msg,
+			   uint16_t msglen);
 
 /* Default hash key buffer for RSS */
 static uint32_t rss_key_default[I40E_VFQF_HKEY_MAX_INDEX + 1];
@@ -355,19 +356,40 @@ i40evf_execute_vf_cmd(struct rte_eth_dev *dev, struct vf_cmd_info *args)
 		return err;
 	}
 
-	do {
-		ret = i40evf_read_pfmsg(dev, &info);
-		if (ret == I40EVF_MSG_CMD) {
-			err = 0;
-			break;
-		} else if (ret == I40EVF_MSG_ERR) {
-			err = -1;
-			break;
-		}
-		rte_delay_ms(ASQ_DELAY_MS);
-		/* If don't read msg or read sys event, continue */
-	} while (i++ < MAX_TRY_TIMES);
-	_clear_cmd(vf);
+	switch (args->ops) {
+	case I40E_VIRTCHNL_OP_RESET_VF:
+		/*no need to process in this function */
+		break;
+	case I40E_VIRTCHNL_OP_VERSION:
+	case I40E_VIRTCHNL_OP_GET_VF_RESOURCES:
+		/* for init adminq commands, need to poll the response */
+		do {
+			ret = i40evf_read_pfmsg(dev, &info);
+			if (ret == I40EVF_MSG_CMD) {
+				err = 0;
+				break;
+			} else if (ret == I40EVF_MSG_ERR) {
+				err = -1;
+				break;
+			}
+			rte_delay_ms(ASQ_DELAY_MS);
+			/* If don't read msg or read sys event, continue */
+		} while (i++ < MAX_TRY_TIMES);
+		_clear_cmd(vf);
+		break;
+
+	default:
+		/* for other adminq in running time, waiting the cmd done flag */
+		do {
+			if (vf->pend_cmd == I40E_VIRTCHNL_OP_UNKNOWN) {
+				err = 0;
+				break;
+			}
+			rte_delay_ms(ASQ_DELAY_MS);
+			/* If don't read msg or read sys event, continue */
+		} while (i++ < MAX_TRY_TIMES);
+		break;
+	}
 
 	return err | vf->cmd_retval;
 }
@@ -716,7 +738,7 @@ i40evf_config_irq_map(struct rte_eth_dev *dev)
 
 	map_info = (struct i40e_virtchnl_irq_map_info *)cmd_buffer;
 	map_info->num_vectors = 1;
-	map_info->vecmap[0].rxitr_idx = I40E_QINT_RQCTL_MSIX_INDX_NOITR;
+	map_info->vecmap[0].rxitr_idx = I40E_ITR_INDEX_DEFAULT;
 	map_info->vecmap[0].vsi_id = vf->vsi_res->vsi_id;
 	/* Alway use default dynamic MSIX interrupt */
 	map_info->vecmap[0].vector_id = vector_id;
@@ -1090,6 +1112,38 @@ i40evf_dev_atomic_write_link_status(struct rte_eth_dev *dev,
 	return 0;
 }
 
+/* Disable IRQ0 */
+static inline void
+i40evf_disable_irq0(struct i40e_hw *hw)
+{
+	/* Disable all interrupt types */
+	I40E_WRITE_REG(hw, I40E_VFINT_ICR0_ENA1, 0);
+	I40E_WRITE_REG(hw, I40E_VFINT_DYN_CTL01,
+		       I40E_VFINT_DYN_CTL01_ITR_INDX_MASK);
+	I40EVF_WRITE_FLUSH(hw);
+}
+
+/* Enable IRQ0 */
+static inline void
+i40evf_enable_irq0(struct i40e_hw *hw)
+{
+	/* Enable admin queue interrupt trigger */
+	uint32_t val;
+
+	i40evf_disable_irq0(hw);
+	val = I40E_READ_REG(hw, I40E_VFINT_ICR0_ENA1);
+	val |= I40E_VFINT_ICR0_ENA1_ADMINQ_MASK |
+		I40E_VFINT_ICR0_ENA1_LINK_STAT_CHANGE_MASK;
+	I40E_WRITE_REG(hw, I40E_VFINT_ICR0_ENA1, val);
+
+	I40E_WRITE_REG(hw, I40E_VFINT_DYN_CTL01,
+		I40E_VFINT_DYN_CTL01_INTENA_MASK |
+		I40E_VFINT_DYN_CTL01_CLEARPBA_MASK |
+		I40E_VFINT_DYN_CTL01_ITR_INDX_MASK);
+
+	I40EVF_WRITE_FLUSH(hw);
+}
+
 static int
 i40evf_reset_vf(struct i40e_hw *hw)
 {
@@ -1134,6 +1188,8 @@ i40evf_init_vf(struct rte_eth_dev *dev)
 	int i, err, bufsz;
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 	struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
+	uint16_t interval =
+		i40e_calc_itr_interval(I40E_QUEUE_ITR_INTERVAL_MAX);
 
 	vf->adapter = I40E_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 	vf->dev_data = dev->data;
@@ -1215,6 +1271,16 @@ i40evf_init_vf(struct rte_eth_dev *dev)
 	ether_addr_copy((struct ether_addr *)vf->vsi_res->default_mac_addr,
 					(struct ether_addr *)hw->mac.addr);
 
+	/* If the PF host is not DPDK, set the interval of ITR0 to max*/
+	if (vf->version_major != I40E_DPDK_VERSION_MAJOR) {
+		I40E_WRITE_REG(hw, I40E_VFINT_DYN_CTL01,
+			       (I40E_ITR_INDEX_DEFAULT <<
+				I40E_VFINT_DYN_CTL0_ITR_INDX_SHIFT) |
+			       (interval <<
+				I40E_VFINT_DYN_CTL0_INTERVAL_SHIFT));
+		I40EVF_WRITE_FLUSH(hw);
+	}
+
 	return 0;
 
 err_alloc:
@@ -1243,11 +1309,142 @@ i40evf_uninit_vf(struct rte_eth_dev *dev)
 	return 0;
 }
 
+static void
+i40evf_handle_pf_event(__rte_unused struct rte_eth_dev *dev,
+			   uint8_t *msg,
+			   __rte_unused uint16_t msglen)
+{
+	struct i40e_virtchnl_pf_event *pf_msg =
+			(struct i40e_virtchnl_pf_event *)msg;
+
+	switch (pf_msg->event) {
+	case I40E_VIRTCHNL_EVENT_RESET_IMPENDING:
+		PMD_DRV_LOG(DEBUG, "VIRTCHNL_EVENT_RESET_IMPENDING event\n");
+		_rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_RESET);
+		break;
+	case I40E_VIRTCHNL_EVENT_LINK_CHANGE:
+		PMD_DRV_LOG(DEBUG, "VIRTCHNL_EVENT_LINK_CHANGE event\n");
+		break;
+	case I40E_VIRTCHNL_EVENT_PF_DRIVER_CLOSE:
+		PMD_DRV_LOG(DEBUG, "VIRTCHNL_EVENT_PF_DRIVER_CLOSE event\n");
+		break;
+	default:
+		PMD_DRV_LOG(ERR, " unknown event received %u", pf_msg->event);
+		break;
+	}
+}
+
+static void
+i40evf_handle_aq_msg(struct rte_eth_dev *dev)
+{
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
+	struct i40e_arq_event_info info;
+	struct i40e_virtchnl_msg *v_msg;
+	uint16_t pending, opcode;
+	int ret;
+
+	info.buf_len = I40E_AQ_BUF_SZ;
+	if (!vf->aq_resp) {
+		PMD_DRV_LOG(ERR, "Buffer for adminq resp should not be NULL");
+		return;
+	}
+	info.msg_buf = vf->aq_resp;
+	v_msg = (struct i40e_virtchnl_msg *)&info.desc;
+
+	pending = 1;
+	while (pending) {
+		ret = i40e_clean_arq_element(hw, &info, &pending);
+
+		if (ret != I40E_SUCCESS) {
+			PMD_DRV_LOG(INFO, "Failed to read msg from AdminQ,"
+				    "ret: %d", ret);
+			break;
+		}
+		opcode = rte_le_to_cpu_16(info.desc.opcode);
+
+		switch (opcode) {
+		case i40e_aqc_opc_send_msg_to_vf:
+			if (v_msg->v_opcode == I40E_VIRTCHNL_OP_EVENT)
+				/* process event*/
+				i40evf_handle_pf_event(dev, info.msg_buf,
+							info.msg_len);
+			else {
+				/* read message and it's expected one */
+				if (v_msg->v_opcode == vf->pend_cmd) {
+					vf->cmd_retval = v_msg->v_retval;
+					/* prevent compiler reordering */
+					rte_compiler_barrier();
+					_clear_cmd(vf);
+				} else
+					PMD_DRV_LOG(ERR, "command mismatch,"
+						"expect %u, get %u",
+						vf->pend_cmd, v_msg->v_opcode);
+				PMD_DRV_LOG(DEBUG, "adminq response is received,"
+					     " opcode = %d\n", v_msg->v_opcode);
+			}
+			break;
+		default:
+			PMD_DRV_LOG(ERR, "Request %u is not supported yet",
+				    opcode);
+			break;
+		}
+	}
+}
+
+/**
+ * Interrupt handler triggered by NIC  for handling
+ * specific interrupt. Only adminq interrupt is processed in VF.
+ *
+ * @param handle
+ *  Pointer to interrupt handle.
+ * @param param
+ *  The address of parameter (struct rte_eth_dev *) regsitered before.
+ *
+ * @return
+ *  void
+ */
+static void
+i40evf_dev_interrupt_handler(__rte_unused struct rte_intr_handle *handle,
+			   void *param)
+{
+	struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
+	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	uint32_t icr0;
+
+	i40evf_disable_irq0(hw);
+
+	/* read out interrupt causes */
+	icr0 = I40E_READ_REG(hw, I40E_VFINT_ICR01);
+
+	/* No interrupt event indicated */
+	if (!(icr0 & I40E_VFINT_ICR01_INTEVENT_MASK)) {
+		PMD_DRV_LOG(DEBUG, "No interrupt event, nothing to do\n");
+		goto done;
+	}
+
+	if (icr0 & I40E_VFINT_ICR01_ADMINQ_MASK) {
+		PMD_DRV_LOG(DEBUG, "ICR01_ADMINQ is reported\n");
+		i40evf_handle_aq_msg(dev);
+	}
+
+	/* Link Status Change interrupt */
+	if (icr0 & I40E_VFINT_ICR01_LINK_STAT_CHANGE_MASK)
+		PMD_DRV_LOG(DEBUG, "LINK_STAT_CHANGE is reported,"
+				   " do nothing\n");
+
+done:
+	i40evf_enable_irq0(hw);
+	rte_intr_enable(&(dev->pci_dev->intr_handle));
+}
+
+
 static int
 i40evf_dev_init(struct rte_eth_dev *eth_dev)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(\
 			eth_dev->data->dev_private);
+	struct rte_pci_device *pci_dev = eth_dev->pci_dev;
 
 	PMD_INIT_FUNC_TRACE();
 
@@ -1282,6 +1479,16 @@ i40evf_dev_init(struct rte_eth_dev *eth_dev)
 		return -1;
 	}
 
+	/* register callback func to eal lib */
+	rte_intr_callback_register(&(pci_dev->intr_handle),
+		i40evf_dev_interrupt_handler, (void *)eth_dev);
+
+	/* enable uio intr after callback register */
+	rte_intr_enable(&(pci_dev->intr_handle));
+
+	/* configure and enable device interrupt */
+	i40evf_enable_irq0(hw);
+
 	/* copy mac addr */
 	eth_dev->data->mac_addrs = rte_zmalloc("i40evf_mac",
 					ETHER_ADDR_LEN, 0);
@@ -1659,7 +1866,8 @@ i40evf_enable_queues_intr(struct rte_eth_dev *dev)
 		I40E_WRITE_REG(hw,
 			       I40E_VFINT_DYN_CTL01,
 			       I40E_VFINT_DYN_CTL01_INTENA_MASK |
-			       I40E_VFINT_DYN_CTL01_CLEARPBA_MASK);
+			       I40E_VFINT_DYN_CTL01_CLEARPBA_MASK |
+			       I40E_VFINT_DYN_CTL01_ITR_INDX_MASK);
 		I40EVF_WRITE_FLUSH(hw);
 		return;
 	}
@@ -1670,11 +1878,10 @@ i40evf_enable_queues_intr(struct rte_eth_dev *dev)
 			I40E_VFINT_DYN_CTLN1(I40EVF_VSI_DEFAULT_MSIX_INTR - 1),
 			I40E_VFINT_DYN_CTLN1_INTENA_MASK |
 			I40E_VFINT_DYN_CTLN_CLEARPBA_MASK);
-	else
-		/* To support Linux PF host */
-		I40E_WRITE_REG(hw, I40E_VFINT_DYN_CTL01,
-				I40E_VFINT_DYN_CTL01_INTENA_MASK |
-				I40E_VFINT_DYN_CTL01_CLEARPBA_MASK);
+	/* If host driver is kernel driver, do nothing.
+	 * Interrupt 0 is used for rx packets, but don't set I40E_VFINT_DYN_CTL01,
+	 * because it is already done in i40evf_enable_irq0.
+	 */
 
 	I40EVF_WRITE_FLUSH(hw);
 }
@@ -1687,7 +1894,8 @@ i40evf_disable_queues_intr(struct rte_eth_dev *dev)
 	struct rte_intr_handle *intr_handle = &dev->pci_dev->intr_handle;
 
 	if (!rte_intr_allow_others(intr_handle)) {
-		I40E_WRITE_REG(hw, I40E_VFINT_DYN_CTL01, 0);
+		I40E_WRITE_REG(hw, I40E_VFINT_DYN_CTL01,
+			       I40E_VFINT_DYN_CTL01_ITR_INDX_MASK);
 		I40EVF_WRITE_FLUSH(hw);
 		return;
 	}
@@ -1697,8 +1905,10 @@ i40evf_disable_queues_intr(struct rte_eth_dev *dev)
 			       I40E_VFINT_DYN_CTLN1(I40EVF_VSI_DEFAULT_MSIX_INTR
 						    - 1),
 			       0);
-	else
-		I40E_WRITE_REG(hw, I40E_VFINT_DYN_CTL01, 0);
+	/* If host driver is kernel driver, do nothing.
+	 * Interrupt 0 is used for rx packets, but don't zero I40E_VFINT_DYN_CTL01,
+	 * because interrupt 0 is also used for adminq processing.
+	 */
 
 	I40EVF_WRITE_FLUSH(hw);
 }
@@ -1822,10 +2032,6 @@ i40evf_dev_start(struct rte_eth_dev *dev)
 		goto err_mac;
 	}
 
-	/* vf don't allow intr except for rxq intr */
-	if (dev->data->dev_conf.intr_conf.rxq != 0)
-		rte_intr_enable(intr_handle);
-
 	i40evf_enable_queues_intr(dev);
 	return 0;
 
@@ -2017,12 +2223,20 @@ static void
 i40evf_dev_close(struct rte_eth_dev *dev)
 {
 	struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct rte_pci_device *pci_dev = dev->pci_dev;
 
 	i40evf_dev_stop(dev);
 	hw->adapter_stopped = 1;
 	i40e_dev_free_queues(dev);
 	i40evf_reset_vf(hw);
 	i40e_shutdown_adminq(hw);
+	/* disable uio intr before callback unregister */
+	rte_intr_disable(&(pci_dev->intr_handle));
+
+	/* unregister callback func from eal lib */
+	rte_intr_callback_unregister(&(pci_dev->intr_handle),
+		i40evf_dev_interrupt_handler, (void *)dev);
+	i40evf_disable_irq0(hw);
 }
 
 static int
diff --git a/lib/librte_ether/rte_ethdev.h b/lib/librte_ether/rte_ethdev.h
index 16da821..cdff3b7 100644
--- a/lib/librte_ether/rte_ethdev.h
+++ b/lib/librte_ether/rte_ethdev.h
@@ -2661,6 +2661,7 @@ rte_eth_tx_burst(uint8_t port_id, uint16_t queue_id,
 enum rte_eth_event_type {
 	RTE_ETH_EVENT_UNKNOWN,  /**< unknown event type */
 	RTE_ETH_EVENT_INTR_LSC, /**< lsc interrupt event */
+	RTE_ETH_EVENT_INTR_RESET, /**< reset interrupt event */
 	RTE_ETH_EVENT_MAX       /**< max value of this enum */
 };
 
-- 
2.4.0

  parent reply	other threads:[~2016-02-26  6:52 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-13 12:31 [PATCH 0/2] i40evf: support interrupt based pf reset request Jingjing Wu
2016-01-13 12:31 ` [PATCH 1/2] i40evf: allocate virtchnl cmd buffer for each vf Jingjing Wu
2016-01-13 12:31 ` [PATCH 2/2] i40evf: support interrupt based pf reset request Jingjing Wu
2016-01-27  1:49 ` [PATCH v2 0/2] " Jingjing Wu
2016-01-27  1:49   ` [PATCH v2 1/2] i40evf: allocate virtchnl cmd buffer for each vf Jingjing Wu
2016-01-29  7:28     ` Tao, Zhe
2016-02-14  2:22       ` Wu, Jingjing
2016-02-22  7:26     ` Zhang, Helin
2016-01-27  1:49   ` [PATCH v2 2/2] i40evf: support interrupt based pf reset request Jingjing Wu
2016-01-27  8:34     ` David Marchand
2016-02-14  3:25       ` Wu, Jingjing
2016-02-15 13:16         ` David Marchand
2016-02-18  4:06           ` Zhe Tao
2016-02-19  5:51           ` Wu, Jingjing
2016-01-28  7:03     ` Tao, Zhe
2016-02-14  2:12       ` Wu, Jingjing
2016-01-29  8:50     ` Tao, Zhe
2016-02-14  3:04       ` Wu, Jingjing
2016-02-22  8:26     ` Zhang, Helin
2016-02-26  6:51   ` [PATCH v3 0/2] i40evf: pf reset event report Jingjing Wu
2016-02-26  6:51     ` [PATCH v3 1/2] i40evf: allocate virtchnl cmd buffer for each vf Jingjing Wu
2016-02-26  8:12       ` Zhang, Helin
2016-02-26  6:51     ` Jingjing Wu [this message]
2016-02-26  8:13       ` [PATCH v3 2/2] i40evf: support to report pf reset event Zhang, Helin
2016-03-08 17:44     ` [PATCH v3 0/2] i40evf: pf reset event report Bruce Richardson
2016-03-09  3:08     ` Zhe Tao
2016-03-09  6:00     ` [PATCH v4 " Jingjing Wu
2016-03-09  6:00       ` [PATCH v4 1/2] i40evf: allocate virtchnl cmd buffer for each vf Jingjing Wu
2016-03-09  6:00       ` [PATCH v4 2/2] i40evf: support to report pf reset event Jingjing Wu
2016-03-09  9:59         ` Thomas Monjalon
2016-03-09 11:01           ` Bruce Richardson
2016-03-09 11:26             ` expectations on maintainer's review Thomas Monjalon
2016-03-09 14:15               ` Bruce Richardson
2016-03-09 14:19                 ` Thomas Monjalon
2016-03-10  3:41       ` [PATCH v5 0/2] i40evf: pf reset event report Jingjing Wu
2016-03-10  3:41         ` [PATCH v5 1/2] i40evf: allocate virtchnl cmd buffer for each vf Jingjing Wu
2016-03-14 12:21           ` Bruce Richardson
2016-03-15  1:01             ` Wu, Jingjing
2016-03-10  3:41         ` [PATCH v5 2/2] i40evf: support to report pf reset event Jingjing Wu
2016-03-15  1:59         ` [PATCH v6 0/2] i40evf: pf reset event report Jingjing Wu
2016-03-15  1:59           ` [PATCH v6 1/2] i40evf: allocate virtchnl cmd buffer for each vf Jingjing Wu
2016-03-15  1:59           ` [PATCH v6 2/2] i40evf: support to report pf reset event Jingjing Wu
2016-03-22 15:13           ` [PATCH v6 0/2] i40evf: pf reset event report Bruce Richardson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1456469514-9103-3-git-send-email-jingjing.wu@intel.com \
    --to=jingjing.wu@intel.com \
    --cc=bruce.richardson@intel.com \
    --cc=dev@dpdk.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.