netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
To: rdreier@cisco.com, general@lists.openfabrics.org, netdev@vger.kernel.org
Cc: amar.mudrankit@qlogic.com, poornima.kamath@qlogic.com
Subject: [ofa-general] [PATCH v4 09/14] QLogic VNIC: IB Multicast for Ethernet broadcast/multicast
Date: Tue, 10 Jun 2008 17:06:47 -0400	[thread overview]
Message-ID: <20080610210647.11186.25121.stgit@dale> (raw)
In-Reply-To: <20080610205633.11186.45499.stgit@dale>

From: Usha Srinivasan <usha.srinivasan@qlogic.com>

Implementation of ethernet broadcasting and multicasting for QLogic
VNIC interface by making use of underlying IB multicasting. 

Signed-off-by: Usha Srinivasan <usha.srinivasan@qlogic.com>
Signed-off-by: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c |  319 +++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h |   77 +++++
 2 files changed, 396 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c
new file mode 100644
index 0000000..f40ea20
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2008 QLogic, Inc.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/jiffies.h>
+#include <rdma/ib_sa.h>
+#include "vnic_viport.h"
+#include "vnic_main.h"
+#include "vnic_util.h"
+
+static inline void vnic_set_multicast_state_invalid(struct viport *viport)
+{
+	viport->mc_info.state = MCAST_STATE_INVALID;
+	viport->mc_info.mc = NULL;
+	memset(&viport->mc_info.mgid, 0, sizeof(union ib_gid));
+}
+
+int vnic_mc_init(struct viport *viport)
+{
+	MCAST_FUNCTION("vnic_mc_init %p\n", viport);
+	vnic_set_multicast_state_invalid(viport);
+	viport->mc_info.retries = 0;
+	spin_lock_init(&viport->mc_info.lock);
+
+	return 0;
+}
+
+void vnic_mc_uninit(struct viport *viport)
+{
+	unsigned long flags;
+	MCAST_FUNCTION("vnic_mc_uninit %p\n", viport);
+
+	spin_lock_irqsave(&viport->mc_info.lock, flags);
+	if ((viport->mc_info.state != MCAST_STATE_INVALID) &&
+	    (viport->mc_info.state != MCAST_STATE_RETRIED)) {
+		MCAST_ERROR("%s mcast state is not INVALID or RETRIED %d\n",
+				control_ifcfg_name(&viport->control),
+				viport->mc_info.state);
+	}
+	spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+	MCAST_FUNCTION("vnic_mc_uninit done\n");
+}
+
+
+/* This function is called when NEED_MCAST_COMPLETION is set.
+ * It finishes off the join multicast work.
+ */
+int vnic_mc_join_handle_completion(struct viport *viport)
+{
+	unsigned int ret = 0;
+
+	MCAST_FUNCTION("vnic_mc_join_handle_completion()\n");
+	if (viport->mc_info.state != MCAST_STATE_JOINING) {
+		MCAST_ERROR("%s unexpected mcast state in handle_completion: "
+				" %d\n", control_ifcfg_name(&viport->control),
+				viport->mc_info.state);
+		ret = -1;
+		goto out;
+	}
+	viport->mc_info.state = MCAST_STATE_ATTACHING;
+	MCAST_INFO("%s Attaching QP %lx mgid:"
+			VNIC_GID_FMT " mlid:%x\n",
+			control_ifcfg_name(&viport->control), jiffies,
+			VNIC_GID_RAW_ARG(viport->mc_info.mgid.raw),
+					 viport->mc_info.mlid);
+	ret = ib_attach_mcast(viport->mc_data.ib_conn.qp, &viport->mc_info.mgid,
+			viport->mc_info.mlid);
+	if (ret) {
+		MCAST_ERROR("%s Attach mcast qp failed %d\n",
+				control_ifcfg_name(&viport->control), ret);
+		ret = -1;
+		goto out;
+	}
+	viport->mc_info.state = MCAST_STATE_JOINED_ATTACHED;
+	MCAST_INFO("%s UD QP successfully attached to mcast group\n",
+			control_ifcfg_name(&viport->control));
+
+out:
+	return ret;
+}
+
+/* NOTE: ib_sa.h says "returning a non-zero value from this callback will
+ * result in destroying the multicast tracking structure.
+ */
+static int vnic_mc_join_complete(int status,
+				struct ib_sa_multicast *multicast)
+{
+	struct viport *viport = (struct viport *)multicast->context;
+	unsigned long flags;
+
+	MCAST_FUNCTION("vnic_mc_join_complete() status:%x\n", status);
+	if (status) {
+		spin_lock_irqsave(&viport->mc_info.lock, flags);
+		if (status == -ENETRESET) {
+			vnic_set_multicast_state_invalid(viport);
+			viport->mc_info.retries = 0;
+			spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+			MCAST_ERROR("%s got ENETRESET\n",
+					control_ifcfg_name(&viport->control));
+			goto out;
+		}
+		/* perhaps the mcgroup hasn't yet been created - retry */
+		viport->mc_info.retries++;
+		viport->mc_info.mc = NULL;
+		if (viport->mc_info.retries > MAX_MCAST_JOIN_RETRIES) {
+			viport->mc_info.state = MCAST_STATE_RETRIED;
+			spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+			MCAST_ERROR("%s join failed 0x%x - max retries:%d "
+					"exceeded\n",
+					control_ifcfg_name(&viport->control),
+					status, viport->mc_info.retries);
+		} else {
+			viport->mc_info.state = MCAST_STATE_INVALID;
+			spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+			spin_lock_irqsave(&viport->lock, flags);
+			viport->updates |= NEED_MCAST_JOIN;
+			spin_unlock_irqrestore(&viport->lock, flags);
+			viport_kick(viport);
+			MCAST_ERROR("%s join failed 0x%x - retrying; "
+					"retries:%d\n",
+					control_ifcfg_name(&viport->control),
+					status, viport->mc_info.retries);
+		}
+		goto out;
+	}
+
+	/* finish join work from main state loop for viport - in case
+	 * the work itself cannot be done in a callback environment */
+	spin_lock_irqsave(&viport->lock, flags);
+	viport->mc_info.mlid = be16_to_cpu(multicast->rec.mlid);
+	viport->updates |= NEED_MCAST_COMPLETION;
+	spin_unlock_irqrestore(&viport->lock, flags);
+	viport_kick(viport);
+	MCAST_INFO("%s setting NEED_MCAST_COMPLETION %x %x\n",
+			control_ifcfg_name(&viport->control),
+			multicast->rec.mlid, viport->mc_info.mlid);
+out:
+	return status;
+}
+
+void vnic_mc_join_setup(struct viport *viport, union ib_gid *mgid)
+{
+	unsigned long flags;
+
+	MCAST_FUNCTION("in vnic_mc_join_setup\n");
+	spin_lock_irqsave(&viport->mc_info.lock, flags);
+	if (viport->mc_info.state != MCAST_STATE_INVALID) {
+		if (viport->mc_info.state == MCAST_STATE_DETACHING)
+			MCAST_ERROR("%s detach in progress\n",
+					control_ifcfg_name(&viport->control));
+		else if (viport->mc_info.state == MCAST_STATE_RETRIED)
+			MCAST_ERROR("%s max join retries exceeded\n",
+					control_ifcfg_name(&viport->control));
+		else {
+			/* join/attach in progress or done */
+			/* verify that the current mgid is same as prev mgid */
+			if (memcmp(mgid, &viport->mc_info.mgid, sizeof(union ib_gid)) != 0) {
+				/* Separate MGID for each IOC */
+				MCAST_ERROR("%s Multicast Group MGIDs not "
+					"unique; mgids: " VNIC_GID_FMT
+					 " " VNIC_GID_FMT "\n",
+					control_ifcfg_name(&viport->control),
+					VNIC_GID_RAW_ARG(mgid->raw),
+					VNIC_GID_RAW_ARG(viport->mc_info.mgid.raw));
+			} else
+				MCAST_INFO("%s join already issued: %d\n",
+					control_ifcfg_name(&viport->control),
+					viport->mc_info.state);
+
+		}
+		spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+		return;
+	}
+	viport->mc_info.mgid = *mgid;
+	spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+	spin_lock_irqsave(&viport->lock, flags);
+	viport->updates |= NEED_MCAST_JOIN;
+	spin_unlock_irqrestore(&viport->lock, flags);
+	viport_kick(viport);
+	MCAST_INFO("%s setting NEED_MCAST_JOIN \n",
+			control_ifcfg_name(&viport->control));
+}
+
+int vnic_mc_join(struct viport *viport)
+{
+	struct ib_sa_mcmember_rec rec;
+	ib_sa_comp_mask comp_mask;
+	unsigned long flags;
+	int ret = 0;
+
+	MCAST_FUNCTION("vnic_mc_join()\n");
+	if (!viport->mc_data.ib_conn.qp) {
+		MCAST_ERROR("%s qp is NULL\n",
+				control_ifcfg_name(&viport->control));
+		ret = -1;
+		goto out;
+	}
+	spin_lock_irqsave(&viport->mc_info.lock, flags);
+	if (viport->mc_info.state != MCAST_STATE_INVALID) {
+		spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+		MCAST_INFO("%s Multicast join already issued\n",
+				control_ifcfg_name(&viport->control));
+		goto out;
+	}
+	viport->mc_info.state = MCAST_STATE_JOINING;
+	spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+
+	memset(&rec, 0, sizeof(rec));
+	rec.join_state = 2; /* bit 1 is Nonmember */
+	rec.mgid = viport->mc_info.mgid;
+	rec.port_gid = viport->config->path_info.path.sgid;
+
+	comp_mask = 	IB_SA_MCMEMBER_REC_MGID     |
+			IB_SA_MCMEMBER_REC_PORT_GID |
+			IB_SA_MCMEMBER_REC_JOIN_STATE;
+
+	MCAST_INFO("%s Joining Multicast group%lx mgid:"
+			VNIC_GID_FMT " port_gid: " VNIC_GID_FMT "\n",
+			control_ifcfg_name(&viport->control), jiffies,
+			VNIC_GID_RAW_ARG(rec.mgid.raw),
+			VNIC_GID_RAW_ARG(rec.port_gid.raw));
+
+	viport->mc_info.mc = ib_sa_join_multicast(&vnic_sa_client,
+			viport->config->ibdev, viport->config->port,
+			&rec, comp_mask, GFP_KERNEL,
+			vnic_mc_join_complete, viport);
+
+	if (IS_ERR(viport->mc_info.mc)) {
+		MCAST_ERROR("%s Multicast joining failed " VNIC_GID_FMT
+				".\n",
+				control_ifcfg_name(&viport->control),
+				VNIC_GID_RAW_ARG(rec.mgid.raw));
+		viport->mc_info.state = MCAST_STATE_INVALID;
+		ret = -1;
+		goto out;
+	}
+	MCAST_INFO("%s Multicast group join issued mgid:"
+			VNIC_GID_FMT " port_gid: " VNIC_GID_FMT "\n",
+			control_ifcfg_name(&viport->control),
+			VNIC_GID_RAW_ARG(rec.mgid.raw),
+			VNIC_GID_RAW_ARG(rec.port_gid.raw));
+out:
+	return ret;
+}
+
+void vnic_mc_leave(struct viport *viport)
+{
+	unsigned long flags;
+	unsigned int ret;
+	struct ib_sa_multicast *mc;
+
+	MCAST_FUNCTION("vnic_mc_leave()\n");
+
+	spin_lock_irqsave(&viport->mc_info.lock, flags);
+	if ((viport->mc_info.state == MCAST_STATE_INVALID) ||
+	    (viport->mc_info.state == MCAST_STATE_RETRIED)) {
+		spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+		return;
+	}
+
+	if (viport->mc_info.state == MCAST_STATE_JOINED_ATTACHED) {
+
+		viport->mc_info.state = MCAST_STATE_DETACHING;
+		spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+		ret = ib_detach_mcast(viport->mc_data.ib_conn.qp,
+					 &viport->mc_info.mgid,
+					viport->mc_info.mlid);
+		if (ret) {
+			MCAST_ERROR("%s UD QP Detach failed %d\n",
+				control_ifcfg_name(&viport->control), ret);
+			return;
+		}
+		MCAST_INFO("%s UD QP detached succesfully\n",
+				control_ifcfg_name(&viport->control));
+		spin_lock_irqsave(&viport->mc_info.lock, flags);
+	}
+	mc = viport->mc_info.mc;
+	vnic_set_multicast_state_invalid(viport);
+	viport->mc_info.retries = 0;
+	spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+
+	if (mc) {
+		MCAST_INFO("%s Freeing up multicast structure.\n",
+				control_ifcfg_name(&viport->control));
+		ib_sa_free_multicast(mc);
+	}
+	MCAST_FUNCTION("vnic_mc_leave done\n");
+	return;
+}
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h
new file mode 100644
index 0000000..e049180
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2008 QLogic, Inc.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __VNIC_MULTICAST_H__
+#define __VNIC_MULTTCAST_H__
+
+enum {
+	MCAST_STATE_INVALID         = 0x00, /* join not attempted or failed */
+	MCAST_STATE_JOINING         = 0x01, /* join mcgroup in progress */
+	MCAST_STATE_ATTACHING       = 0x02, /* join completed with success,
+					     * attach qp to mcgroup in progress
+					     */
+	MCAST_STATE_JOINED_ATTACHED = 0x03, /* join completed with success */
+	MCAST_STATE_DETACHING       = 0x04, /* detach qp in progress */
+	MCAST_STATE_RETRIED         = 0x05, /* retried join and failed */
+};
+
+#define MAX_MCAST_JOIN_RETRIES 	       5 /* used to retry join */
+
+struct mc_info {
+	u8  			state;
+	spinlock_t 		lock;
+	union ib_gid 		mgid;
+	u16 			mlid;
+	struct ib_sa_multicast 	*mc;
+	u8 			retries;
+};
+
+
+int vnic_mc_init(struct viport *viport);
+void vnic_mc_uninit(struct viport *viport);
+extern char *control_ifcfg_name(struct control *control);
+
+/* This function is called when a viport gets a multicast mgid from EVIC
+   and must join the multicast group. It sets up NEED_MCAST_JOIN flag, which
+   results in vnic_mc_join being called later. */
+void vnic_mc_join_setup(struct viport *viport, union ib_gid *mgid);
+
+/* This function is called when NEED_MCAST_JOIN flag is set. */
+int vnic_mc_join(struct viport *viport);
+
+/* This function is called when NEED_MCAST_COMPLETION is set.
+   It finishes off the join multicast work. */
+int vnic_mc_join_handle_completion(struct viport *viport);
+
+void vnic_mc_leave(struct viport *viport);
+
+#endif /* __VNIC_MULTICAST_H__ */

  parent reply	other threads:[~2008-06-10 21:06 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-06-10 21:02 [ofa-general] [PATCH v4 00/14] QLogic VNIC Driver Ramachandra K
2008-06-10 21:02 ` [ofa-general] [PATCH v4 01/14] QLogic VNIC: Driver - netdev implementation Ramachandra K
2008-06-10 21:03 ` [ofa-general] [PATCH v4 02/14] QLogic VNIC: Netpath - abstraction of connection to EVIC/VEx Ramachandra K
2008-06-10 21:03 ` [ofa-general] [PATCH v4 03/14] QLogic VNIC: Implementation of communication protocol with EVIC/VEx Ramachandra K
2008-06-10 21:04 ` [ofa-general] [PATCH v4 04/14] QLogic VNIC: Implementation of Control path of communication protocol Ramachandra K
2008-06-10 22:21   ` Stephen Hemminger
2008-06-10 21:04 ` [ofa-general] [PATCH v4 05/14] QLogic VNIC: Implementation of Data " Ramachandra K
2008-06-10 21:05 ` [ofa-general] [PATCH v4 06/14] QLogic VNIC: IB core stack interaction Ramachandra K
2008-06-10 21:05 ` [ofa-general] [PATCH v4 07/14] QLogic VNIC: Handling configurable parameters of the driver Ramachandra K
2008-06-10 21:06 ` [ofa-general] [PATCH v4 08/14] QLogic VNIC: sysfs interface implementation for " Ramachandra K
2008-06-10 21:06 ` Ramachandra K [this message]
2008-06-10 21:07 ` [ofa-general] [PATCH v4 10/14] QLogic VNIC: Driver Statistics collection Ramachandra K
2008-06-10 21:07 ` [PATCH v4 11/14] QLogic VNIC: Driver utility file - implements various utility macros Ramachandra K
2008-06-10 21:08 ` [PATCH v4 12/14] QLogic VNIC: Driver Kconfig and Makefile Ramachandra K
2008-06-10 21:08 ` [ofa-general] [PATCH v4 13/14] QLogic VNIC: Modifications to IB " Ramachandra K
2008-06-10 21:09 ` [ofa-general] [PATCH v4 14/14] QLogic VNIC: sysfs Documentation Ramachandra K
2008-06-11  6:47   ` Patrick McHardy
2008-06-12 15:13     ` [ofa-general] " Amar Mudrankit
2008-06-12 15:18       ` Patrick McHardy
2008-06-12 15:29         ` Ramachandra K
2008-06-12 15:34           ` Patrick McHardy
2008-06-12 20:22           ` Jeff Garzik
2008-06-14 18:03         ` Roland Dreier
2008-06-14 19:03           ` Jason Gunthorpe
2008-06-16  8:54             ` Patrick McHardy
2008-06-18 12:32               ` Ramachandra K
2008-06-18 12:38                 ` Patrick McHardy
2008-06-18 18:21                   ` Jason Gunthorpe
2008-06-19  1:19                     ` Patrick McHardy
2008-06-19  1:26                       ` Patrick McHardy
2008-06-12 15:50     ` Ramachandra K
2008-06-12 16:03       ` Patrick McHardy
2008-06-12 21:09         ` Amar Mudrankit
2008-06-13 15:20           ` Patrick McHardy
2008-06-13 21:47             ` Amar Mudrankit
2008-06-14  8:08               ` Patrick McHardy
2008-06-16 19:44                 ` Amar Mudrankit
2008-06-16 20:39                   ` Patrick McHardy
2008-06-12 16:04       ` Karen Shaeffer
2008-06-12 15:21 ` [ofa-general] [PATCH v4 00/14] QLogic VNIC Driver Ramachandra K
2008-06-12 15:35   ` Patrick McHardy

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=20080610210647.11186.25121.stgit@dale \
    --to=ramachandra.kuchimanchi@qlogic.com \
    --cc=amar.mudrankit@qlogic.com \
    --cc=general@lists.openfabrics.org \
    --cc=netdev@vger.kernel.org \
    --cc=poornima.kamath@qlogic.com \
    --cc=rdreier@cisco.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).