diff for duplicates of <20160224160921.GA3305@pathway.suse.cz> diff --git a/a/1.txt b/N1/1.txt index 40ee601..ae6a6b3 100644 --- a/a/1.txt +++ b/N1/1.txt @@ -18,253 +18,3 @@ On Tue 2016-02-23 00:06:50, kbuild test robot wrote: > kernel/sys.c:1: warning: no structured comments found Great catch. Please, find below a fixed version of the patch. - - ->From d20fa44b6a62a33098e4465c1bb0baf522b177f3 Mon Sep 17 00:00:00 2001 -From: Petr Mladek <pmladek@suse.com> -Date: Wed, 9 Dec 2015 15:53:03 +0100 -Subject: [PATCH 7/7] kthread: Initial support for delayed kthread work - -We are going to use kthread_worker more widely and delayed works -will be pretty useful. - -The implementation is inspired by workqueues. It uses a timer to -queue the work after the requested delay. If the delay is zero, -the work is queued immediately. - -In compare with workqueues, each work is associated with a single -worker (kthread). Therefore the implementation could be much easier. -In particular, we use the worker->lock to synchronize all the -operations with the work. We do not need any atomic operation -with a flags variable. - -In fact, we do not need any state variable at all. Instead, we -add a list of delayed works into the worker. Then the pending -work is listed either in the list of queued or delayed works. -And the existing check of pending works is the same even for -the delayed ones. - -A work must not be assigned to another worker unless reinitialized. -Therefore the timer handler might expect that dwork->work->worker -is valid and it could simply take the lock. We just add some -sanity checks to help with debugging a potential misuse. - -Signed-off-by: Petr Mladek <pmladek@suse.com> ---- - include/linux/kthread.h | 33 ++++++++++++++++ - kernel/kthread.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 135 insertions(+) - -diff --git a/include/linux/kthread.h b/include/linux/kthread.h -index c4a95a3ba500..6e37b169e8b6 100644 ---- a/include/linux/kthread.h -+++ b/include/linux/kthread.h -@@ -63,10 +63,12 @@ extern int tsk_fork_get_node(struct task_struct *tsk); - */ - struct kthread_work; - typedef void (*kthread_work_func_t)(struct kthread_work *work); -+void delayed_kthread_work_timer_fn(unsigned long __data); - - struct kthread_worker { - spinlock_t lock; - struct list_head work_list; -+ struct list_head delayed_work_list; - struct task_struct *task; - struct kthread_work *current_work; - }; -@@ -77,9 +79,15 @@ struct kthread_work { - struct kthread_worker *worker; - }; - -+struct delayed_kthread_work { -+ struct kthread_work work; -+ struct timer_list timer; -+}; -+ - #define KTHREAD_WORKER_INIT(worker) { \ - .lock = __SPIN_LOCK_UNLOCKED((worker).lock), \ - .work_list = LIST_HEAD_INIT((worker).work_list), \ -+ .delayed_work_list = LIST_HEAD_INIT((worker).delayed_work_list),\ - } - - #define KTHREAD_WORK_INIT(work, fn) { \ -@@ -87,12 +95,23 @@ struct kthread_work { - .func = (fn), \ - } - -+#define DELAYED_KTHREAD_WORK_INIT(dwork, fn) { \ -+ .work = KTHREAD_WORK_INIT((dwork).work, (fn)), \ -+ .timer = __TIMER_INITIALIZER(delayed_kthread_work_timer_fn, \ -+ 0, (unsigned long)&(dwork), \ -+ TIMER_IRQSAFE), \ -+ } -+ - #define DEFINE_KTHREAD_WORKER(worker) \ - struct kthread_worker worker = KTHREAD_WORKER_INIT(worker) - - #define DEFINE_KTHREAD_WORK(work, fn) \ - struct kthread_work work = KTHREAD_WORK_INIT(work, fn) - -+#define DEFINE_DELAYED_KTHREAD_WORK(dwork, fn) \ -+ struct delayed_kthread_work dwork = \ -+ DELAYED_KTHREAD_WORK_INIT(dwork, fn) -+ - /* - * kthread_worker.lock needs its own lockdep class key when defined on - * stack with lockdep enabled. Use the following macros in such cases. -@@ -122,6 +141,15 @@ extern void __init_kthread_worker(struct kthread_worker *worker, - (work)->func = (fn); \ - } while (0) - -+#define init_delayed_kthread_work(dwork, fn) \ -+ do { \ -+ init_kthread_work(&(dwork)->work, (fn)); \ -+ __setup_timer(&(dwork)->timer, \ -+ delayed_kthread_work_timer_fn, \ -+ (unsigned long)(dwork), \ -+ TIMER_IRQSAFE); \ -+ } while (0) -+ - int kthread_worker_fn(void *worker_ptr); - - __printf(1, 2) -@@ -133,6 +161,11 @@ create_kthread_worker_on_cpu(int cpu, const char namefmt[]); - - bool queue_kthread_work(struct kthread_worker *worker, - struct kthread_work *work); -+ -+bool queue_delayed_kthread_work(struct kthread_worker *worker, -+ struct delayed_kthread_work *dwork, -+ unsigned long delay); -+ - void flush_kthread_work(struct kthread_work *work); - void flush_kthread_worker(struct kthread_worker *worker); - -diff --git a/kernel/kthread.c b/kernel/kthread.c -index 10e3ee1df2e1..0db87d197e36 100644 ---- a/kernel/kthread.c -+++ b/kernel/kthread.c -@@ -559,6 +559,7 @@ void __init_kthread_worker(struct kthread_worker *worker, - spin_lock_init(&worker->lock); - lockdep_set_class_and_name(&worker->lock, key, name); - INIT_LIST_HEAD(&worker->work_list); -+ INIT_LIST_HEAD(&worker->delayed_work_list); - worker->task = NULL; - } - EXPORT_SYMBOL_GPL(__init_kthread_worker); -@@ -747,6 +748,107 @@ bool queue_kthread_work(struct kthread_worker *worker, - } - EXPORT_SYMBOL_GPL(queue_kthread_work); - -+/** -+ * delayed_kthread_work_timer_fn - callback that queues the associated delayed -+ * kthread work when the timer expires. -+ * @__data: pointer to the data associated with the timer -+ * -+ * The format of the function is defined by struct timer_list. -+ * It should have been called from irqsafe timer with irq already off. -+ */ -+void delayed_kthread_work_timer_fn(unsigned long __data) -+{ -+ struct delayed_kthread_work *dwork = -+ (struct delayed_kthread_work *)__data; -+ struct kthread_work *work = &dwork->work; -+ struct kthread_worker *worker = work->worker; -+ -+ /* -+ * This might happen when a pending work is reinitialized. -+ * It means that it is used a wrong way. -+ */ -+ if (WARN_ON_ONCE(!worker)) -+ return; -+ -+ spin_lock(&worker->lock); -+ /* Work must not be used with more workers, see queue_kthread_work(). */ -+ WARN_ON_ONCE(work->worker != worker); -+ -+ /* Move the work from worker->delayed_work_list. */ -+ WARN_ON_ONCE(list_empty(&work->node)); -+ list_del_init(&work->node); -+ insert_kthread_work(worker, work, &worker->work_list); -+ -+ spin_unlock(&worker->lock); -+} -+EXPORT_SYMBOL(delayed_kthread_work_timer_fn); -+ -+void __queue_delayed_kthread_work(struct kthread_worker *worker, -+ struct delayed_kthread_work *dwork, -+ unsigned long delay) -+{ -+ struct timer_list *timer = &dwork->timer; -+ struct kthread_work *work = &dwork->work; -+ -+ WARN_ON_ONCE(timer->function != delayed_kthread_work_timer_fn || -+ timer->data != (unsigned long)dwork); -+ -+ /* -+ * If @delay is 0, queue @dwork->work immediately. This is for -+ * both optimization and correctness. The earliest @timer can -+ * expire is on the closest next tick and delayed_work users depend -+ * on that there's no such delay when @delay is 0. -+ */ -+ if (!delay) { -+ insert_kthread_work(worker, work, &worker->work_list); -+ return; -+ } -+ -+ /* Be paranoid and try to detect possible races already now. */ -+ insert_kthread_work_sanity_check(worker, work); -+ -+ list_add(&work->node, &worker->delayed_work_list); -+ work->worker = worker; -+ timer_stats_timer_set_start_info(&dwork->timer); -+ timer->expires = jiffies + delay; -+ add_timer(timer); -+} -+ -+/** -+ * queue_delayed_kthread_work - queue the associated kthread work -+ * after a delay. -+ * @worker: target kthread_worker -+ * @dwork: delayed_kthread_work to queue -+ * @delay: number of jiffies to wait before queuing -+ * -+ * If the work has not been pending it starts a timer that will queue -+ * the work after the given @delay. If @delay is zero, it queues the -+ * work immediately. -+ * -+ * Return: %false if the @work has already been pending. It means that -+ * either the timer was running or the work was queued. It returns %true -+ * otherwise. -+ */ -+bool queue_delayed_kthread_work(struct kthread_worker *worker, -+ struct delayed_kthread_work *dwork, -+ unsigned long delay) -+{ -+ struct kthread_work *work = &dwork->work; -+ unsigned long flags; -+ bool ret = false; -+ -+ spin_lock_irqsave(&worker->lock, flags); -+ -+ if (list_empty(&work->node)) { -+ __queue_delayed_kthread_work(worker, dwork, delay); -+ ret = true; -+ } -+ -+ spin_unlock_irqrestore(&worker->lock, flags); -+ return ret; -+} -+EXPORT_SYMBOL_GPL(queue_delayed_kthread_work); -+ - struct kthread_flush_work { - struct kthread_work work; - struct completion done; --- -1.8.5.6 - --- -To unsubscribe, send a message with 'unsubscribe linux-mm' in -the body to majordomo@kvack.org. For more info on Linux MM, -see: http://www.linux-mm.org/ . -Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> diff --git a/a/content_digest b/N1/content_digest index c9c31a9..f644aea 100644 --- a/a/content_digest +++ b/N1/content_digest @@ -43,256 +43,6 @@ "> >> kernel/kthread.c:833: warning: Excess function parameter 'work' description in 'queue_delayed_kthread_work'\n" "> kernel/sys.c:1: warning: no structured comments found\n" "\n" - "Great catch. Please, find below a fixed version of the patch.\n" - "\n" - "\n" - ">From d20fa44b6a62a33098e4465c1bb0baf522b177f3 Mon Sep 17 00:00:00 2001\n" - "From: Petr Mladek <pmladek@suse.com>\n" - "Date: Wed, 9 Dec 2015 15:53:03 +0100\n" - "Subject: [PATCH 7/7] kthread: Initial support for delayed kthread work\n" - "\n" - "We are going to use kthread_worker more widely and delayed works\n" - "will be pretty useful.\n" - "\n" - "The implementation is inspired by workqueues. It uses a timer to\n" - "queue the work after the requested delay. If the delay is zero,\n" - "the work is queued immediately.\n" - "\n" - "In compare with workqueues, each work is associated with a single\n" - "worker (kthread). Therefore the implementation could be much easier.\n" - "In particular, we use the worker->lock to synchronize all the\n" - "operations with the work. We do not need any atomic operation\n" - "with a flags variable.\n" - "\n" - "In fact, we do not need any state variable at all. Instead, we\n" - "add a list of delayed works into the worker. Then the pending\n" - "work is listed either in the list of queued or delayed works.\n" - "And the existing check of pending works is the same even for\n" - "the delayed ones.\n" - "\n" - "A work must not be assigned to another worker unless reinitialized.\n" - "Therefore the timer handler might expect that dwork->work->worker\n" - "is valid and it could simply take the lock. We just add some\n" - "sanity checks to help with debugging a potential misuse.\n" - "\n" - "Signed-off-by: Petr Mladek <pmladek@suse.com>\n" - "---\n" - " include/linux/kthread.h | 33 ++++++++++++++++\n" - " kernel/kthread.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++\n" - " 2 files changed, 135 insertions(+)\n" - "\n" - "diff --git a/include/linux/kthread.h b/include/linux/kthread.h\n" - "index c4a95a3ba500..6e37b169e8b6 100644\n" - "--- a/include/linux/kthread.h\n" - "+++ b/include/linux/kthread.h\n" - "@@ -63,10 +63,12 @@ extern int tsk_fork_get_node(struct task_struct *tsk);\n" - " */\n" - " struct kthread_work;\n" - " typedef void (*kthread_work_func_t)(struct kthread_work *work);\n" - "+void delayed_kthread_work_timer_fn(unsigned long __data);\n" - " \n" - " struct kthread_worker {\n" - " \tspinlock_t\t\tlock;\n" - " \tstruct list_head\twork_list;\n" - "+\tstruct list_head\tdelayed_work_list;\n" - " \tstruct task_struct\t*task;\n" - " \tstruct kthread_work\t*current_work;\n" - " };\n" - "@@ -77,9 +79,15 @@ struct kthread_work {\n" - " \tstruct kthread_worker\t*worker;\n" - " };\n" - " \n" - "+struct delayed_kthread_work {\n" - "+\tstruct kthread_work work;\n" - "+\tstruct timer_list timer;\n" - "+};\n" - "+\n" - " #define KTHREAD_WORKER_INIT(worker)\t{\t\t\t\t\\\n" - " \t.lock = __SPIN_LOCK_UNLOCKED((worker).lock),\t\t\t\\\n" - " \t.work_list = LIST_HEAD_INIT((worker).work_list),\t\t\\\n" - "+\t.delayed_work_list = LIST_HEAD_INIT((worker).delayed_work_list),\\\n" - " \t}\n" - " \n" - " #define KTHREAD_WORK_INIT(work, fn)\t{\t\t\t\t\\\n" - "@@ -87,12 +95,23 @@ struct kthread_work {\n" - " \t.func = (fn),\t\t\t\t\t\t\t\\\n" - " \t}\n" - " \n" - "+#define DELAYED_KTHREAD_WORK_INIT(dwork, fn) {\t\t\t\t\\\n" - "+\t.work = KTHREAD_WORK_INIT((dwork).work, (fn)),\t\t\t\\\n" - "+\t.timer = __TIMER_INITIALIZER(delayed_kthread_work_timer_fn,\t\\\n" - "+\t\t\t\t 0, (unsigned long)&(dwork),\t\\\n" - "+\t\t\t\t TIMER_IRQSAFE),\t\t\t\\\n" - "+\t}\n" - "+\n" - " #define DEFINE_KTHREAD_WORKER(worker)\t\t\t\t\t\\\n" - " \tstruct kthread_worker worker = KTHREAD_WORKER_INIT(worker)\n" - " \n" - " #define DEFINE_KTHREAD_WORK(work, fn)\t\t\t\t\t\\\n" - " \tstruct kthread_work work = KTHREAD_WORK_INIT(work, fn)\n" - " \n" - "+#define DEFINE_DELAYED_KTHREAD_WORK(dwork, fn)\t\t\t\t\\\n" - "+\tstruct delayed_kthread_work dwork =\t\t\t\t\\\n" - "+\t\tDELAYED_KTHREAD_WORK_INIT(dwork, fn)\n" - "+\n" - " /*\n" - " * kthread_worker.lock needs its own lockdep class key when defined on\n" - " * stack with lockdep enabled. Use the following macros in such cases.\n" - "@@ -122,6 +141,15 @@ extern void __init_kthread_worker(struct kthread_worker *worker,\n" - " \t\t(work)->func = (fn);\t\t\t\t\t\\\n" - " \t} while (0)\n" - " \n" - "+#define init_delayed_kthread_work(dwork, fn)\t\t\t\t\\\n" - "+\tdo {\t\t\t\t\t\t\t\t\\\n" - "+\t\tinit_kthread_work(&(dwork)->work, (fn));\t\t\\\n" - "+\t\t__setup_timer(&(dwork)->timer,\t\t\t\t\\\n" - "+\t\t\t delayed_kthread_work_timer_fn,\t\t\\\n" - "+\t\t\t (unsigned long)(dwork),\t\t\t\\\n" - "+\t\t\t TIMER_IRQSAFE);\t\t\t\t\\\n" - "+\t} while (0)\n" - "+\n" - " int kthread_worker_fn(void *worker_ptr);\n" - " \n" - " __printf(1, 2)\n" - "@@ -133,6 +161,11 @@ create_kthread_worker_on_cpu(int cpu, const char namefmt[]);\n" - " \n" - " bool queue_kthread_work(struct kthread_worker *worker,\n" - " \t\t\tstruct kthread_work *work);\n" - "+\n" - "+bool queue_delayed_kthread_work(struct kthread_worker *worker,\n" - "+\t\t\t\tstruct delayed_kthread_work *dwork,\n" - "+\t\t\t\tunsigned long delay);\n" - "+\n" - " void flush_kthread_work(struct kthread_work *work);\n" - " void flush_kthread_worker(struct kthread_worker *worker);\n" - " \n" - "diff --git a/kernel/kthread.c b/kernel/kthread.c\n" - "index 10e3ee1df2e1..0db87d197e36 100644\n" - "--- a/kernel/kthread.c\n" - "+++ b/kernel/kthread.c\n" - "@@ -559,6 +559,7 @@ void __init_kthread_worker(struct kthread_worker *worker,\n" - " \tspin_lock_init(&worker->lock);\n" - " \tlockdep_set_class_and_name(&worker->lock, key, name);\n" - " \tINIT_LIST_HEAD(&worker->work_list);\n" - "+\tINIT_LIST_HEAD(&worker->delayed_work_list);\n" - " \tworker->task = NULL;\n" - " }\n" - " EXPORT_SYMBOL_GPL(__init_kthread_worker);\n" - "@@ -747,6 +748,107 @@ bool queue_kthread_work(struct kthread_worker *worker,\n" - " }\n" - " EXPORT_SYMBOL_GPL(queue_kthread_work);\n" - " \n" - "+/**\n" - "+ * delayed_kthread_work_timer_fn - callback that queues the associated delayed\n" - "+ *\tkthread work when the timer expires.\n" - "+ * @__data: pointer to the data associated with the timer\n" - "+ *\n" - "+ * The format of the function is defined by struct timer_list.\n" - "+ * It should have been called from irqsafe timer with irq already off.\n" - "+ */\n" - "+void delayed_kthread_work_timer_fn(unsigned long __data)\n" - "+{\n" - "+\tstruct delayed_kthread_work *dwork =\n" - "+\t\t(struct delayed_kthread_work *)__data;\n" - "+\tstruct kthread_work *work = &dwork->work;\n" - "+\tstruct kthread_worker *worker = work->worker;\n" - "+\n" - "+\t/*\n" - "+\t * This might happen when a pending work is reinitialized.\n" - "+\t * It means that it is used a wrong way.\n" - "+\t */\n" - "+\tif (WARN_ON_ONCE(!worker))\n" - "+\t\treturn;\n" - "+\n" - "+\tspin_lock(&worker->lock);\n" - "+\t/* Work must not be used with more workers, see queue_kthread_work(). */\n" - "+\tWARN_ON_ONCE(work->worker != worker);\n" - "+\n" - "+\t/* Move the work from worker->delayed_work_list. */\n" - "+\tWARN_ON_ONCE(list_empty(&work->node));\n" - "+\tlist_del_init(&work->node);\n" - "+\tinsert_kthread_work(worker, work, &worker->work_list);\n" - "+\n" - "+\tspin_unlock(&worker->lock);\n" - "+}\n" - "+EXPORT_SYMBOL(delayed_kthread_work_timer_fn);\n" - "+\n" - "+void __queue_delayed_kthread_work(struct kthread_worker *worker,\n" - "+\t\t\t\tstruct delayed_kthread_work *dwork,\n" - "+\t\t\t\tunsigned long delay)\n" - "+{\n" - "+\tstruct timer_list *timer = &dwork->timer;\n" - "+\tstruct kthread_work *work = &dwork->work;\n" - "+\n" - "+\tWARN_ON_ONCE(timer->function != delayed_kthread_work_timer_fn ||\n" - "+\t\t timer->data != (unsigned long)dwork);\n" - "+\n" - "+\t/*\n" - "+\t * If @delay is 0, queue @dwork->work immediately. This is for\n" - "+\t * both optimization and correctness. The earliest @timer can\n" - "+\t * expire is on the closest next tick and delayed_work users depend\n" - "+\t * on that there's no such delay when @delay is 0.\n" - "+\t */\n" - "+\tif (!delay) {\n" - "+\t\tinsert_kthread_work(worker, work, &worker->work_list);\n" - "+\t\treturn;\n" - "+\t}\n" - "+\n" - "+\t/* Be paranoid and try to detect possible races already now. */\n" - "+\tinsert_kthread_work_sanity_check(worker, work);\n" - "+\n" - "+\tlist_add(&work->node, &worker->delayed_work_list);\n" - "+\twork->worker = worker;\n" - "+\ttimer_stats_timer_set_start_info(&dwork->timer);\n" - "+\ttimer->expires = jiffies + delay;\n" - "+\tadd_timer(timer);\n" - "+}\n" - "+\n" - "+/**\n" - "+ * queue_delayed_kthread_work - queue the associated kthread work\n" - "+ *\tafter a delay.\n" - "+ * @worker: target kthread_worker\n" - "+ * @dwork: delayed_kthread_work to queue\n" - "+ * @delay: number of jiffies to wait before queuing\n" - "+ *\n" - "+ * If the work has not been pending it starts a timer that will queue\n" - "+ * the work after the given @delay. If @delay is zero, it queues the\n" - "+ * work immediately.\n" - "+ *\n" - "+ * Return: %false if the @work has already been pending. It means that\n" - "+ * either the timer was running or the work was queued. It returns %true\n" - "+ * otherwise.\n" - "+ */\n" - "+bool queue_delayed_kthread_work(struct kthread_worker *worker,\n" - "+\t\t\t\tstruct delayed_kthread_work *dwork,\n" - "+\t\t\t\tunsigned long delay)\n" - "+{\n" - "+\tstruct kthread_work *work = &dwork->work;\n" - "+\tunsigned long flags;\n" - "+\tbool ret = false;\n" - "+\n" - "+\tspin_lock_irqsave(&worker->lock, flags);\n" - "+\n" - "+\tif (list_empty(&work->node)) {\n" - "+\t\t__queue_delayed_kthread_work(worker, dwork, delay);\n" - "+\t\tret = true;\n" - "+\t}\n" - "+\n" - "+\tspin_unlock_irqrestore(&worker->lock, flags);\n" - "+\treturn ret;\n" - "+}\n" - "+EXPORT_SYMBOL_GPL(queue_delayed_kthread_work);\n" - "+\n" - " struct kthread_flush_work {\n" - " \tstruct kthread_work\twork;\n" - " \tstruct completion\tdone;\n" - "-- \n" - "1.8.5.6\n" - "\n" - "--\n" - "To unsubscribe, send a message with 'unsubscribe linux-mm' in\n" - "the body to majordomo@kvack.org. For more info on Linux MM,\n" - "see: http://www.linux-mm.org/ .\n" - "Don't email: <a href=mailto:\"dont@kvack.org\"> email@kvack.org </a>" + Great catch. Please, find below a fixed version of the patch. -3f90e86b61efc0195a664fea4895cff59b292315d64d063e770c9685f11b6c7f +7d82cc38739193b038c0d2cef1b432cfe113391665fcec029e117378672c4215
diff --git a/a/1.txt b/N2/1.txt index 40ee601..65b0334 100644 --- a/a/1.txt +++ b/N2/1.txt @@ -262,9 +262,3 @@ index 10e3ee1df2e1..0db87d197e36 100644 struct completion done; -- 1.8.5.6 - --- -To unsubscribe, send a message with 'unsubscribe linux-mm' in -the body to majordomo@kvack.org. For more info on Linux MM, -see: http://www.linux-mm.org/ . -Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a> diff --git a/a/content_digest b/N2/content_digest index c9c31a9..8cebd50 100644 --- a/a/content_digest +++ b/N2/content_digest @@ -287,12 +287,6 @@ " \tstruct kthread_work\twork;\n" " \tstruct completion\tdone;\n" "-- \n" - "1.8.5.6\n" - "\n" - "--\n" - "To unsubscribe, send a message with 'unsubscribe linux-mm' in\n" - "the body to majordomo@kvack.org. For more info on Linux MM,\n" - "see: http://www.linux-mm.org/ .\n" - "Don't email: <a href=mailto:\"dont@kvack.org\"> email@kvack.org </a>" + 1.8.5.6 -3f90e86b61efc0195a664fea4895cff59b292315d64d063e770c9685f11b6c7f +6fbb87231b25f14b29611dd12fea07bfde9c784816140931e02e6dfaf5a1167b
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.