All of lore.kernel.org
 help / color / mirror / Atom feed
From: Bart Van Assche <bvanassche@acm.org>
To: Jens Axboe <axboe@kernel.dk>
Cc: Christoph Hellwig <hch@lst.de>,
	Damien Le Moal <dlemoal@kernel.org>,
	Marco Elver <elver@google.com>,
	linux-block@vger.kernel.org, Bart Van Assche <bvanassche@acm.org>,
	Tejun Heo <tj@kernel.org>, Josef Bacik <josef@toxicpanda.com>,
	Alexei Starovoitov <ast@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Andrii Nakryiko <andrii@kernel.org>,
	Nathan Chancellor <nathan@kernel.org>,
	Miklos Szeredi <mszeredi@redhat.com>,
	Christian Brauner <brauner@kernel.org>,
	Andreas Gruenbacher <agruenba@redhat.com>,
	Joanne Koong <joannelkoong@gmail.com>,
	Mateusz Guzik <mjguzik@gmail.com>
Subject: [PATCH 03/14] block: Make the lock context annotations compatible with Clang
Date: Wed,  4 Mar 2026 11:48:22 -0800	[thread overview]
Message-ID: <20260304194843.760669-4-bvanassche@acm.org> (raw)
In-Reply-To: <20260304194843.760669-1-bvanassche@acm.org>

Clang is more strict than sparse with regard to lock context annotation
checking. Hence this patch that makes the lock context annotations
compatible with Clang. __release() annotations have been added below
invocations of indirect calls that unlock a mutex because Clang does not
support annotating function pointers with __releases().

Enable context analysis in the block layer Makefile.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 block/Makefile              |  2 ++
 block/bdev.c                |  7 +++++--
 block/blk-cgroup.c          |  7 ++++---
 block/blk-crypto-profile.c  |  2 ++
 block/blk-iocost.c          |  2 ++
 block/blk-mq-debugfs.c      | 12 ++++++------
 block/blk-zoned.c           |  1 +
 block/blk.h                 |  4 ++++
 block/ioctl.c               |  1 +
 block/kyber-iosched.c       |  4 ++--
 block/mq-deadline.c         |  8 ++++----
 include/linux/backing-dev.h |  2 ++
 include/linux/blkdev.h      | 11 ++++++++---
 include/linux/bpf.h         |  1 +
 14 files changed, 44 insertions(+), 20 deletions(-)

diff --git a/block/Makefile b/block/Makefile
index c65f4da93702..407ea53e39b2 100644
--- a/block/Makefile
+++ b/block/Makefile
@@ -3,6 +3,8 @@
 # Makefile for the kernel block layer
 #
 
+CONTEXT_ANALYSIS := y
+
 obj-y		:= bdev.o fops.o bio.o elevator.o blk-core.o blk-sysfs.o \
 			blk-flush.o blk-settings.o blk-ioc.o blk-map.o \
 			blk-merge.o blk-timeout.o blk-lib.o blk-mq.o \
diff --git a/block/bdev.c b/block/bdev.c
index ed022f8c48c7..367f0f09a2e4 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -313,6 +313,7 @@ int bdev_freeze(struct block_device *bdev)
 	if (bdev->bd_holder_ops && bdev->bd_holder_ops->freeze) {
 		error = bdev->bd_holder_ops->freeze(bdev);
 		lockdep_assert_not_held(&bdev->bd_holder_lock);
+		__release(&bdev->bd_holder_lock);
 	} else {
 		mutex_unlock(&bdev->bd_holder_lock);
 		error = sync_blockdev(bdev);
@@ -356,6 +357,7 @@ int bdev_thaw(struct block_device *bdev)
 	if (bdev->bd_holder_ops && bdev->bd_holder_ops->thaw) {
 		error = bdev->bd_holder_ops->thaw(bdev);
 		lockdep_assert_not_held(&bdev->bd_holder_lock);
+		__release(&bdev->bd_holder_lock);
 	} else {
 		mutex_unlock(&bdev->bd_holder_lock);
 	}
@@ -1254,9 +1256,10 @@ EXPORT_SYMBOL(lookup_bdev);
 void bdev_mark_dead(struct block_device *bdev, bool surprise)
 {
 	mutex_lock(&bdev->bd_holder_lock);
-	if (bdev->bd_holder_ops && bdev->bd_holder_ops->mark_dead)
+	if (bdev->bd_holder_ops && bdev->bd_holder_ops->mark_dead) {
 		bdev->bd_holder_ops->mark_dead(bdev, surprise);
-	else {
+		__release(&bdev->bd_holder_lock);
+	} else {
 		mutex_unlock(&bdev->bd_holder_lock);
 		sync_blockdev(bdev);
 	}
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index b70096497d38..5aec000d3da6 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -774,6 +774,7 @@ EXPORT_SYMBOL_GPL(blkg_conf_init);
  * of @ctx->input. Returns -errno on error.
  */
 int blkg_conf_open_bdev(struct blkg_conf_ctx *ctx)
+	__no_context_analysis /* conditional locking */
 {
 	char *input = ctx->input;
 	unsigned int major, minor;
@@ -819,6 +820,7 @@ int blkg_conf_open_bdev(struct blkg_conf_ctx *ctx)
  * for restoring the memalloc scope.
  */
 unsigned long __must_check blkg_conf_open_bdev_frozen(struct blkg_conf_ctx *ctx)
+	__must_hold(&ctx->bdev->bd_queue->rq_qos_mutex)
 {
 	int ret;
 	unsigned long memflags;
@@ -860,7 +862,7 @@ unsigned long __must_check blkg_conf_open_bdev_frozen(struct blkg_conf_ctx *ctx)
  */
 int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
 		   struct blkg_conf_ctx *ctx)
-	__acquires(&bdev->bd_queue->queue_lock)
+	__cond_acquires(0, &ctx->bdev->bd_disk->queue->queue_lock)
 {
 	struct gendisk *disk;
 	struct request_queue *q;
@@ -974,8 +976,7 @@ EXPORT_SYMBOL_GPL(blkg_conf_prep);
  * blkg_conf_ctx's initialized with blkg_conf_init().
  */
 void blkg_conf_exit(struct blkg_conf_ctx *ctx)
-	__releases(&ctx->bdev->bd_queue->queue_lock)
-	__releases(&ctx->bdev->bd_queue->rq_qos_mutex)
+	__no_context_analysis /* conditional unlocking */
 {
 	if (ctx->blkg) {
 		spin_unlock_irq(&bdev_get_queue(ctx->bdev)->queue_lock);
diff --git a/block/blk-crypto-profile.c b/block/blk-crypto-profile.c
index 4ac74443687a..cf447ba4a66e 100644
--- a/block/blk-crypto-profile.c
+++ b/block/blk-crypto-profile.c
@@ -43,6 +43,7 @@ struct blk_crypto_keyslot {
 };
 
 static inline void blk_crypto_hw_enter(struct blk_crypto_profile *profile)
+	__acquires(&profile->lock)
 {
 	/*
 	 * Calling into the driver requires profile->lock held and the device
@@ -55,6 +56,7 @@ static inline void blk_crypto_hw_enter(struct blk_crypto_profile *profile)
 }
 
 static inline void blk_crypto_hw_exit(struct blk_crypto_profile *profile)
+	__releases(&profile->lock)
 {
 	up_write(&profile->lock);
 	if (profile->dev)
diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index d145db61e5c3..081054ca8111 100644
--- a/block/blk-iocost.c
+++ b/block/blk-iocost.c
@@ -728,6 +728,7 @@ static void iocg_commit_bio(struct ioc_gq *iocg, struct bio *bio,
 }
 
 static void iocg_lock(struct ioc_gq *iocg, bool lock_ioc, unsigned long *flags)
+	__no_context_analysis /* conditional locking */
 {
 	if (lock_ioc) {
 		spin_lock_irqsave(&iocg->ioc->lock, *flags);
@@ -738,6 +739,7 @@ static void iocg_lock(struct ioc_gq *iocg, bool lock_ioc, unsigned long *flags)
 }
 
 static void iocg_unlock(struct ioc_gq *iocg, bool unlock_ioc, unsigned long *flags)
+	__no_context_analysis /* conditional locking */
 {
 	if (unlock_ioc) {
 		spin_unlock(&iocg->waitq.lock);
diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c
index 047ec887456b..5c168e82273e 100644
--- a/block/blk-mq-debugfs.c
+++ b/block/blk-mq-debugfs.c
@@ -20,7 +20,7 @@ static int queue_poll_stat_show(void *data, struct seq_file *m)
 }
 
 static void *queue_requeue_list_start(struct seq_file *m, loff_t *pos)
-	__acquires(&q->requeue_lock)
+	__acquires(&((struct request_queue *)m->private)->requeue_lock)
 {
 	struct request_queue *q = m->private;
 
@@ -36,7 +36,7 @@ static void *queue_requeue_list_next(struct seq_file *m, void *v, loff_t *pos)
 }
 
 static void queue_requeue_list_stop(struct seq_file *m, void *v)
-	__releases(&q->requeue_lock)
+	__releases(&((struct request_queue *)m->private)->requeue_lock)
 {
 	struct request_queue *q = m->private;
 
@@ -298,7 +298,7 @@ int blk_mq_debugfs_rq_show(struct seq_file *m, void *v)
 EXPORT_SYMBOL_GPL(blk_mq_debugfs_rq_show);
 
 static void *hctx_dispatch_start(struct seq_file *m, loff_t *pos)
-	__acquires(&hctx->lock)
+	__acquires(&((struct blk_mq_hw_ctx *)m->private)->lock)
 {
 	struct blk_mq_hw_ctx *hctx = m->private;
 
@@ -314,7 +314,7 @@ static void *hctx_dispatch_next(struct seq_file *m, void *v, loff_t *pos)
 }
 
 static void hctx_dispatch_stop(struct seq_file *m, void *v)
-	__releases(&hctx->lock)
+	__releases(&((struct blk_mq_hw_ctx *)m->private)->lock)
 {
 	struct blk_mq_hw_ctx *hctx = m->private;
 
@@ -486,7 +486,7 @@ static int hctx_dispatch_busy_show(void *data, struct seq_file *m)
 
 #define CTX_RQ_SEQ_OPS(name, type)					\
 static void *ctx_##name##_rq_list_start(struct seq_file *m, loff_t *pos) \
-	__acquires(&ctx->lock)						\
+	__acquires(&((struct blk_mq_ctx *)m->private)->lock)		\
 {									\
 	struct blk_mq_ctx *ctx = m->private;				\
 									\
@@ -503,7 +503,7 @@ static void *ctx_##name##_rq_list_next(struct seq_file *m, void *v,	\
 }									\
 									\
 static void ctx_##name##_rq_list_stop(struct seq_file *m, void *v)	\
-	__releases(&ctx->lock)						\
+	__releases(&((struct blk_mq_ctx *)m->private)->lock)		\
 {									\
 	struct blk_mq_ctx *ctx = m->private;				\
 									\
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index e1a23c8b676d..df0800e69ad7 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -439,6 +439,7 @@ static int blkdev_truncate_zone_range(struct block_device *bdev,
  */
 int blkdev_zone_mgmt_ioctl(struct block_device *bdev, blk_mode_t mode,
 			   unsigned int cmd, unsigned long arg)
+	__cond_acquires(0, bdev->bd_mapping->host->i_rwsem)
 {
 	void __user *argp = (void __user *)arg;
 	struct blk_zone_range zrange;
diff --git a/block/blk.h b/block/blk.h
index f6053e9dd2aa..59321957f54b 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -736,16 +736,19 @@ static inline void blk_unfreeze_release_lock(struct request_queue *q)
  * reclaim from triggering block I/O.
  */
 static inline void blk_debugfs_lock_nomemsave(struct request_queue *q)
+	__acquires(&q->debugfs_mutex)
 {
 	mutex_lock(&q->debugfs_mutex);
 }
 
 static inline void blk_debugfs_unlock_nomemrestore(struct request_queue *q)
+	__releases(&q->debugfs_mutex)
 {
 	mutex_unlock(&q->debugfs_mutex);
 }
 
 static inline unsigned int __must_check blk_debugfs_lock(struct request_queue *q)
+	__acquires(&q->debugfs_mutex)
 {
 	unsigned int memflags = memalloc_noio_save();
 
@@ -755,6 +758,7 @@ static inline unsigned int __must_check blk_debugfs_lock(struct request_queue *q
 
 static inline void blk_debugfs_unlock(struct request_queue *q,
 				      unsigned int memflags)
+	__releases(&q->debugfs_mutex)
 {
 	blk_debugfs_unlock_nomemrestore(q);
 	memalloc_noio_restore(memflags);
diff --git a/block/ioctl.c b/block/ioctl.c
index 0b04661ac809..784f2965f8bd 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -518,6 +518,7 @@ static int blkdev_pr_read_reservation(struct block_device *bdev,
 
 static int blkdev_flushbuf(struct block_device *bdev, unsigned cmd,
 		unsigned long arg)
+	__cond_acquires(0, bdev->bd_holder_lock)
 {
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
diff --git a/block/kyber-iosched.c b/block/kyber-iosched.c
index b84163d1f851..874791838cbc 100644
--- a/block/kyber-iosched.c
+++ b/block/kyber-iosched.c
@@ -894,7 +894,7 @@ static int kyber_##name##_tokens_show(void *data, struct seq_file *m)	\
 }									\
 									\
 static void *kyber_##name##_rqs_start(struct seq_file *m, loff_t *pos)	\
-	__acquires(&khd->lock)						\
+	__acquires(((struct kyber_hctx_data *)((struct blk_mq_hw_ctx *)m->private)->sched_data)->lock) \
 {									\
 	struct blk_mq_hw_ctx *hctx = m->private;			\
 	struct kyber_hctx_data *khd = hctx->sched_data;			\
@@ -913,7 +913,7 @@ static void *kyber_##name##_rqs_next(struct seq_file *m, void *v,	\
 }									\
 									\
 static void kyber_##name##_rqs_stop(struct seq_file *m, void *v)	\
-	__releases(&khd->lock)						\
+	__releases(((struct kyber_hctx_data *)((struct blk_mq_hw_ctx *)m->private)->sched_data)->lock)						\
 {									\
 	struct blk_mq_hw_ctx *hctx = m->private;			\
 	struct kyber_hctx_data *khd = hctx->sched_data;			\
diff --git a/block/mq-deadline.c b/block/mq-deadline.c
index 95917a88976f..b812708a86ee 100644
--- a/block/mq-deadline.c
+++ b/block/mq-deadline.c
@@ -798,7 +798,7 @@ static const struct elv_fs_entry deadline_attrs[] = {
 #define DEADLINE_DEBUGFS_DDIR_ATTRS(prio, data_dir, name)		\
 static void *deadline_##name##_fifo_start(struct seq_file *m,		\
 					  loff_t *pos)			\
-	__acquires(&dd->lock)						\
+	__acquires(&((struct deadline_data *)((struct request_queue *)m->private)->elevator->elevator_data)->lock)						\
 {									\
 	struct request_queue *q = m->private;				\
 	struct deadline_data *dd = q->elevator->elevator_data;		\
@@ -819,7 +819,7 @@ static void *deadline_##name##_fifo_next(struct seq_file *m, void *v,	\
 }									\
 									\
 static void deadline_##name##_fifo_stop(struct seq_file *m, void *v)	\
-	__releases(&dd->lock)						\
+	__releases(&((struct deadline_data *)((struct request_queue *)m->private)->elevator->elevator_data)->lock)						\
 {									\
 	struct request_queue *q = m->private;				\
 	struct deadline_data *dd = q->elevator->elevator_data;		\
@@ -921,7 +921,7 @@ static int dd_owned_by_driver_show(void *data, struct seq_file *m)
 }
 
 static void *deadline_dispatch_start(struct seq_file *m, loff_t *pos)
-	__acquires(&dd->lock)
+	__acquires(&((struct deadline_data *)((struct request_queue *)m->private)->elevator->elevator_data)->lock)
 {
 	struct request_queue *q = m->private;
 	struct deadline_data *dd = q->elevator->elevator_data;
@@ -939,7 +939,7 @@ static void *deadline_dispatch_next(struct seq_file *m, void *v, loff_t *pos)
 }
 
 static void deadline_dispatch_stop(struct seq_file *m, void *v)
-	__releases(&dd->lock)
+	__releases(&((struct deadline_data *)((struct request_queue *)m->private)->elevator->elevator_data)->lock)
 {
 	struct request_queue *q = m->private;
 	struct deadline_data *dd = q->elevator->elevator_data;
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index 0c8342747cab..34571d8b9dce 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -273,6 +273,7 @@ static inline struct bdi_writeback *inode_to_wb_wbc(
  */
 static inline struct bdi_writeback *
 unlocked_inode_to_wb_begin(struct inode *inode, struct wb_lock_cookie *cookie)
+	__no_context_analysis /* conditional locking */
 {
 	rcu_read_lock();
 
@@ -300,6 +301,7 @@ unlocked_inode_to_wb_begin(struct inode *inode, struct wb_lock_cookie *cookie)
  */
 static inline void unlocked_inode_to_wb_end(struct inode *inode,
 					    struct wb_lock_cookie *cookie)
+	__no_context_analysis /* conditional locking */
 {
 	if (unlikely(cookie->locked))
 		xa_unlock_irqrestore(&inode->i_mapping->i_pages, cookie->flags);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 8d93d8e356d8..7b05ea282435 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1092,15 +1092,19 @@ static inline unsigned int blk_boundary_sectors_left(sector_t offset,
  */
 static inline struct queue_limits
 queue_limits_start_update(struct request_queue *q)
+	__acquires(&q->limits_lock)
 {
 	mutex_lock(&q->limits_lock);
 	return q->limits;
 }
 int queue_limits_commit_update_frozen(struct request_queue *q,
-		struct queue_limits *lim);
+		struct queue_limits *lim)
+	__releases(&q->limits_lock);
 int queue_limits_commit_update(struct request_queue *q,
-		struct queue_limits *lim);
-int queue_limits_set(struct request_queue *q, struct queue_limits *lim);
+		struct queue_limits *lim)
+	__releases(&q->limits_lock);
+int queue_limits_set(struct request_queue *q, struct queue_limits *lim)
+	__must_not_hold(&q->limits_lock);
 int blk_validate_limits(struct queue_limits *lim);
 
 /**
@@ -1112,6 +1116,7 @@ int blk_validate_limits(struct queue_limits *lim);
  * starting update.
  */
 static inline void queue_limits_cancel_update(struct request_queue *q)
+	__releases(&q->limits_lock)
 {
 	mutex_unlock(&q->limits_lock);
 }
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 05b34a6355b0..a3277bcf8d1d 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2489,6 +2489,7 @@ bpf_prog_run_array(const struct bpf_prog_array *array,
 static __always_inline u32
 bpf_prog_run_array_uprobe(const struct bpf_prog_array *array,
 			  const void *ctx, bpf_prog_run_fn run_prog)
+	__no_context_analysis /* conditional locking */
 {
 	const struct bpf_prog_array_item *item;
 	const struct bpf_prog *prog;

  parent reply	other threads:[~2026-03-04 19:49 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-04 19:48 [PATCH 00/14] Enable lock context analysis Bart Van Assche
2026-03-04 19:48 ` [PATCH 01/14] drbd: Balance RCU calls in drbd_adm_dump_devices() Bart Van Assche
2026-03-04 20:25   ` Damien Le Moal
2026-03-04 20:59     ` Bart Van Assche
2026-03-04 19:48 ` [PATCH 02/14] blk-ioc: Prepare for enabling thread-safety analysis Bart Van Assche
2026-03-05 10:10   ` Jan Kara
2026-03-05 12:46     ` Bart Van Assche
2026-03-05 13:18       ` Marco Elver
2026-03-05 14:35         ` Bart Van Assche
2026-03-05 20:30           ` Marco Elver
2026-03-04 19:48 ` Bart Van Assche [this message]
2026-03-04 20:03   ` [PATCH 03/14] block: Make the lock context annotations compatible with Clang Tejun Heo
2026-03-04 20:29     ` Bart Van Assche
2026-03-04 20:58       ` Tejun Heo
2026-03-04 21:34         ` Bart Van Assche
2026-03-04 21:45           ` Tejun Heo
2026-03-04 21:46             ` Tejun Heo
2026-03-04 19:48 ` [PATCH 04/14] aoe: Add a lock context annotation Bart Van Assche
2026-03-04 19:48 ` [PATCH 05/14] drbd: Make the lock context annotations compatible with Clang Bart Van Assche
2026-03-09 10:08   ` Christoph Böhmwalder
2026-03-09 23:15     ` Bart Van Assche
2026-03-11 20:42       ` Christoph Böhmwalder
2026-03-04 19:48 ` [PATCH 06/14] loop: Add lock context annotations Bart Van Assche
2026-03-04 19:48 ` [PATCH 07/14] nbd: " Bart Van Assche
2026-03-04 19:48 ` [PATCH 08/14] null_blk: Add more " Bart Van Assche
2026-03-04 19:48 ` [PATCH 09/14] rbd: Add " Bart Van Assche
2026-03-04 19:48 ` [PATCH 10/14] rnbd: Add more " Bart Van Assche
2026-03-06 13:09   ` Marco Elver
2026-03-06 14:11     ` Bart Van Assche
2026-03-04 19:48 ` [PATCH 11/14] ublk: Fix the " Bart Van Assche
2026-03-04 20:43   ` Caleb Sander Mateos
2026-03-04 20:55     ` Bart Van Assche
2026-03-04 21:03       ` Caleb Sander Mateos
2026-03-04 21:36         ` Bart Van Assche
2026-03-04 19:48 ` [PATCH 12/14] zloop: Add a " Bart Van Assche
2026-03-04 19:48 ` [PATCH 13/14] zram: Add " Bart Van Assche
2026-03-05  1:23   ` Sergey Senozhatsky
2026-03-04 19:48 ` [PATCH 14/14] block: Enable lock context analysis for all block drivers Bart Van Assche
2026-03-05  1:33   ` Sergey Senozhatsky

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=20260304194843.760669-4-bvanassche@acm.org \
    --to=bvanassche@acm.org \
    --cc=agruenba@redhat.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=axboe@kernel.dk \
    --cc=brauner@kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=dlemoal@kernel.org \
    --cc=elver@google.com \
    --cc=hch@lst.de \
    --cc=joannelkoong@gmail.com \
    --cc=josef@toxicpanda.com \
    --cc=linux-block@vger.kernel.org \
    --cc=mjguzik@gmail.com \
    --cc=mszeredi@redhat.com \
    --cc=nathan@kernel.org \
    --cc=tj@kernel.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.