From: Tejun Heo <tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
To: axboe-tSWWG44O7X1aa/9Udqfwiw@public.gmane.org
Cc: containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Tejun Heo <tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
cgroups-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
vgoyal-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org
Subject: [PATCH 13/31] blk-throttle: add backlink pointer from throtl_grp to throtl_data
Date: Wed, 1 May 2013 17:39:31 -0700 [thread overview]
Message-ID: <1367455189-6957-14-git-send-email-tj@kernel.org> (raw)
In-Reply-To: <1367455189-6957-1-git-send-email-tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Add throtl_grp->td so that the td (throtl_data) a given tg
(throtl_grp) belongs to can be determined, and remove @td argument
from functions which take both @td and @tg as the former now can be
determined from the latter.
This generally simplifies the code and removes a number of cases where
@td is passed as an argument without being actually used. This will
also help hierarchy support implementation.
While at it, in multi-line conditions, move the logical operators
leading broken lines to the end of the previous line.
Signed-off-by: Tejun Heo <tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
block/blk-throttle.c | 106 +++++++++++++++++++++++++--------------------------
1 file changed, 53 insertions(+), 53 deletions(-)
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index fc55dda..6aae239 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -57,6 +57,9 @@ struct throtl_grp {
/* active throtl group service_queue member */
struct rb_node rb_node;
+ /* throtl_data this group belongs to */
+ struct throtl_data *td;
+
/*
* Dispatch time in jiffies. This is the estimated time when group
* will unthrottle and is ready to dispatch more bio. It is used as
@@ -140,11 +143,11 @@ static inline struct throtl_grp *td_root_tg(struct throtl_data *td)
return blkg_to_tg(td->queue->root_blkg);
}
-#define throtl_log_tg(td, tg, fmt, args...) do { \
+#define throtl_log_tg(tg, fmt, args...) do { \
char __pbuf[128]; \
\
blkg_path(tg_to_blkg(tg), __pbuf, sizeof(__pbuf)); \
- blk_add_trace_msg((td)->queue, "throtl %s " fmt, __pbuf, ##args); \
+ blk_add_trace_msg((tg)->td->queue, "throtl %s " fmt, __pbuf, ##args); \
} while (0)
#define throtl_log(td, fmt, args...) \
@@ -193,6 +196,7 @@ static void throtl_pd_init(struct blkcg_gq *blkg)
unsigned long flags;
RB_CLEAR_NODE(&tg->rb_node);
+ tg->td = blkg->q->td;
bio_list_init(&tg->bio_lists[0]);
bio_list_init(&tg->bio_lists[1]);
@@ -401,36 +405,34 @@ static void throtl_schedule_next_dispatch(struct throtl_data *td)
throtl_schedule_delayed_work(td, sq->first_pending_disptime - jiffies);
}
-static inline void
-throtl_start_new_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
+static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw)
{
tg->bytes_disp[rw] = 0;
tg->io_disp[rw] = 0;
tg->slice_start[rw] = jiffies;
tg->slice_end[rw] = jiffies + throtl_slice;
- throtl_log_tg(td, tg, "[%c] new slice start=%lu end=%lu jiffies=%lu",
+ throtl_log_tg(tg, "[%c] new slice start=%lu end=%lu jiffies=%lu",
rw == READ ? 'R' : 'W', tg->slice_start[rw],
tg->slice_end[rw], jiffies);
}
-static inline void throtl_set_slice_end(struct throtl_data *td,
- struct throtl_grp *tg, bool rw, unsigned long jiffy_end)
+static inline void throtl_set_slice_end(struct throtl_grp *tg, bool rw,
+ unsigned long jiffy_end)
{
tg->slice_end[rw] = roundup(jiffy_end, throtl_slice);
}
-static inline void throtl_extend_slice(struct throtl_data *td,
- struct throtl_grp *tg, bool rw, unsigned long jiffy_end)
+static inline void throtl_extend_slice(struct throtl_grp *tg, bool rw,
+ unsigned long jiffy_end)
{
tg->slice_end[rw] = roundup(jiffy_end, throtl_slice);
- throtl_log_tg(td, tg, "[%c] extend slice start=%lu end=%lu jiffies=%lu",
+ throtl_log_tg(tg, "[%c] extend slice start=%lu end=%lu jiffies=%lu",
rw == READ ? 'R' : 'W', tg->slice_start[rw],
tg->slice_end[rw], jiffies);
}
/* Determine if previously allocated or extended slice is complete or not */
-static bool
-throtl_slice_used(struct throtl_data *td, struct throtl_grp *tg, bool rw)
+static bool throtl_slice_used(struct throtl_grp *tg, bool rw)
{
if (time_in_range(jiffies, tg->slice_start[rw], tg->slice_end[rw]))
return 0;
@@ -439,8 +441,7 @@ throtl_slice_used(struct throtl_data *td, struct throtl_grp *tg, bool rw)
}
/* Trim the used slices and adjust slice start accordingly */
-static inline void
-throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
+static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw)
{
unsigned long nr_slices, time_elapsed, io_trim;
u64 bytes_trim, tmp;
@@ -452,7 +453,7 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
* renewed. Don't try to trim the slice if slice is used. A new
* slice will start when appropriate.
*/
- if (throtl_slice_used(td, tg, rw))
+ if (throtl_slice_used(tg, rw))
return;
/*
@@ -463,7 +464,7 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
* is bad because it does not allow new slice to start.
*/
- throtl_set_slice_end(td, tg, rw, jiffies + throtl_slice);
+ throtl_set_slice_end(tg, rw, jiffies + throtl_slice);
time_elapsed = jiffies - tg->slice_start[rw];
@@ -492,14 +493,14 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
tg->slice_start[rw] += nr_slices * throtl_slice;
- throtl_log_tg(td, tg, "[%c] trim slice nr=%lu bytes=%llu io=%lu"
+ throtl_log_tg(tg, "[%c] trim slice nr=%lu bytes=%llu io=%lu"
" start=%lu end=%lu jiffies=%lu",
rw == READ ? 'R' : 'W', nr_slices, bytes_trim, io_trim,
tg->slice_start[rw], tg->slice_end[rw], jiffies);
}
-static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
- struct bio *bio, unsigned long *wait)
+static bool tg_with_in_iops_limit(struct throtl_grp *tg, struct bio *bio,
+ unsigned long *wait)
{
bool rw = bio_data_dir(bio);
unsigned int io_allowed;
@@ -548,8 +549,8 @@ static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
return 0;
}
-static bool tg_with_in_bps_limit(struct throtl_data *td, struct throtl_grp *tg,
- struct bio *bio, unsigned long *wait)
+static bool tg_with_in_bps_limit(struct throtl_grp *tg, struct bio *bio,
+ unsigned long *wait)
{
bool rw = bio_data_dir(bio);
u64 bytes_allowed, extra_bytes, tmp;
@@ -600,8 +601,8 @@ static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) {
* Returns whether one can dispatch a bio or not. Also returns approx number
* of jiffies to wait before this bio is with-in IO rate and can be dispatched
*/
-static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
- struct bio *bio, unsigned long *wait)
+static bool tg_may_dispatch(struct throtl_grp *tg, struct bio *bio,
+ unsigned long *wait)
{
bool rw = bio_data_dir(bio);
unsigned long bps_wait = 0, iops_wait = 0, max_wait = 0;
@@ -626,15 +627,15 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
* existing slice to make sure it is at least throtl_slice interval
* long since now.
*/
- if (throtl_slice_used(td, tg, rw))
- throtl_start_new_slice(td, tg, rw);
+ if (throtl_slice_used(tg, rw))
+ throtl_start_new_slice(tg, rw);
else {
if (time_before(tg->slice_end[rw], jiffies + throtl_slice))
- throtl_extend_slice(td, tg, rw, jiffies + throtl_slice);
+ throtl_extend_slice(tg, rw, jiffies + throtl_slice);
}
- if (tg_with_in_bps_limit(td, tg, bio, &bps_wait)
- && tg_with_in_iops_limit(td, tg, bio, &iops_wait)) {
+ if (tg_with_in_bps_limit(tg, bio, &bps_wait) &&
+ tg_with_in_iops_limit(tg, bio, &iops_wait)) {
if (wait)
*wait = 0;
return 1;
@@ -646,7 +647,7 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
*wait = max_wait;
if (time_before(tg->slice_end[rw], jiffies + max_wait))
- throtl_extend_slice(td, tg, rw, jiffies + max_wait);
+ throtl_extend_slice(tg, rw, jiffies + max_wait);
return 0;
}
@@ -707,10 +708,10 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
struct bio *bio;
if ((bio = bio_list_peek(&tg->bio_lists[READ])))
- tg_may_dispatch(td, tg, bio, &read_wait);
+ tg_may_dispatch(tg, bio, &read_wait);
if ((bio = bio_list_peek(&tg->bio_lists[WRITE])))
- tg_may_dispatch(td, tg, bio, &write_wait);
+ tg_may_dispatch(tg, bio, &write_wait);
min_wait = min(read_wait, write_wait);
disptime = jiffies + min_wait;
@@ -721,8 +722,8 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
throtl_enqueue_tg(td, tg);
}
-static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg,
- bool rw, struct bio_list *bl)
+static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw,
+ struct bio_list *bl)
{
struct bio *bio;
@@ -731,18 +732,17 @@ static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg,
/* Drop bio reference on blkg */
blkg_put(tg_to_blkg(tg));
- BUG_ON(td->nr_queued[rw] <= 0);
- td->nr_queued[rw]--;
+ BUG_ON(tg->td->nr_queued[rw] <= 0);
+ tg->td->nr_queued[rw]--;
throtl_charge_bio(tg, bio);
bio_list_add(bl, bio);
bio->bi_rw |= REQ_THROTTLED;
- throtl_trim_slice(td, tg, rw);
+ throtl_trim_slice(tg, rw);
}
-static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
- struct bio_list *bl)
+static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl)
{
unsigned int nr_reads = 0, nr_writes = 0;
unsigned int max_nr_reads = throtl_grp_quantum*3/4;
@@ -751,20 +751,20 @@ static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
/* Try to dispatch 75% READS and 25% WRITES */
- while ((bio = bio_list_peek(&tg->bio_lists[READ]))
- && tg_may_dispatch(td, tg, bio, NULL)) {
+ while ((bio = bio_list_peek(&tg->bio_lists[READ])) &&
+ tg_may_dispatch(tg, bio, NULL)) {
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
+ tg_dispatch_one_bio(tg, bio_data_dir(bio), bl);
nr_reads++;
if (nr_reads >= max_nr_reads)
break;
}
- while ((bio = bio_list_peek(&tg->bio_lists[WRITE]))
- && tg_may_dispatch(td, tg, bio, NULL)) {
+ while ((bio = bio_list_peek(&tg->bio_lists[WRITE])) &&
+ tg_may_dispatch(tg, bio, NULL)) {
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
+ tg_dispatch_one_bio(tg, bio_data_dir(bio), bl);
nr_writes++;
if (nr_writes >= max_nr_writes)
@@ -791,7 +791,7 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl)
throtl_dequeue_tg(td, tg);
- nr_disp += throtl_dispatch_tg(td, tg, bl);
+ nr_disp += throtl_dispatch_tg(tg, bl);
if (tg->nr_queued[0] || tg->nr_queued[1])
tg_update_disptime(td, tg);
@@ -933,7 +933,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf,
else
*(unsigned int *)((void *)tg + cft->private) = ctx.v;
- throtl_log_tg(td, tg, "limit change rbps=%llu wbps=%llu riops=%u wiops=%u",
+ throtl_log_tg(tg, "limit change rbps=%llu wbps=%llu riops=%u wiops=%u",
tg->bps[READ], tg->bps[WRITE],
tg->iops[READ], tg->iops[WRITE]);
@@ -945,8 +945,8 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf,
* that a group's limit are dropped suddenly and we don't want to
* account recently dispatched IO with new low rate.
*/
- throtl_start_new_slice(td, tg, 0);
- throtl_start_new_slice(td, tg, 1);
+ throtl_start_new_slice(tg, 0);
+ throtl_start_new_slice(tg, 1);
if (tg->flags & THROTL_TG_PENDING) {
tg_update_disptime(td, tg);
@@ -1079,7 +1079,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
}
/* Bio is with-in rate limit of group */
- if (tg_may_dispatch(td, tg, bio, NULL)) {
+ if (tg_may_dispatch(tg, bio, NULL)) {
throtl_charge_bio(tg, bio);
/*
@@ -1093,12 +1093,12 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
*
* So keep on trimming slice even if bio is not queued.
*/
- throtl_trim_slice(td, tg, rw);
+ throtl_trim_slice(tg, rw);
goto out_unlock;
}
queue_bio:
- throtl_log_tg(td, tg, "[%c] bio. bdisp=%llu sz=%u bps=%llu"
+ throtl_log_tg(tg, "[%c] bio. bdisp=%llu sz=%u bps=%llu"
" iodisp=%u iops=%u queued=%d/%d",
rw == READ ? 'R' : 'W',
tg->bytes_disp[rw], bio->bi_size, tg->bps[rw],
@@ -1145,9 +1145,9 @@ void blk_throtl_drain(struct request_queue *q)
throtl_dequeue_tg(td, tg);
while ((bio = bio_list_peek(&tg->bio_lists[READ])))
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), &bl);
+ tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl);
while ((bio = bio_list_peek(&tg->bio_lists[WRITE])))
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), &bl);
+ tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl);
}
spin_unlock_irq(q->queue_lock);
--
1.8.1.4
WARNING: multiple messages have this Message-ID (diff)
From: Tejun Heo <tj@kernel.org>
To: axboe@kernel.dk
Cc: linux-kernel@vger.kernel.org, lizefan@huawei.com,
containers@lists.linux-foundation.org, cgroups@vger.kernel.org,
vgoyal@redhat.com, Tejun Heo <tj@kernel.org>
Subject: [PATCH 13/31] blk-throttle: add backlink pointer from throtl_grp to throtl_data
Date: Wed, 1 May 2013 17:39:31 -0700 [thread overview]
Message-ID: <1367455189-6957-14-git-send-email-tj@kernel.org> (raw)
In-Reply-To: <1367455189-6957-1-git-send-email-tj@kernel.org>
Add throtl_grp->td so that the td (throtl_data) a given tg
(throtl_grp) belongs to can be determined, and remove @td argument
from functions which take both @td and @tg as the former now can be
determined from the latter.
This generally simplifies the code and removes a number of cases where
@td is passed as an argument without being actually used. This will
also help hierarchy support implementation.
While at it, in multi-line conditions, move the logical operators
leading broken lines to the end of the previous line.
Signed-off-by: Tejun Heo <tj@kernel.org>
---
block/blk-throttle.c | 106 +++++++++++++++++++++++++--------------------------
1 file changed, 53 insertions(+), 53 deletions(-)
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index fc55dda..6aae239 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -57,6 +57,9 @@ struct throtl_grp {
/* active throtl group service_queue member */
struct rb_node rb_node;
+ /* throtl_data this group belongs to */
+ struct throtl_data *td;
+
/*
* Dispatch time in jiffies. This is the estimated time when group
* will unthrottle and is ready to dispatch more bio. It is used as
@@ -140,11 +143,11 @@ static inline struct throtl_grp *td_root_tg(struct throtl_data *td)
return blkg_to_tg(td->queue->root_blkg);
}
-#define throtl_log_tg(td, tg, fmt, args...) do { \
+#define throtl_log_tg(tg, fmt, args...) do { \
char __pbuf[128]; \
\
blkg_path(tg_to_blkg(tg), __pbuf, sizeof(__pbuf)); \
- blk_add_trace_msg((td)->queue, "throtl %s " fmt, __pbuf, ##args); \
+ blk_add_trace_msg((tg)->td->queue, "throtl %s " fmt, __pbuf, ##args); \
} while (0)
#define throtl_log(td, fmt, args...) \
@@ -193,6 +196,7 @@ static void throtl_pd_init(struct blkcg_gq *blkg)
unsigned long flags;
RB_CLEAR_NODE(&tg->rb_node);
+ tg->td = blkg->q->td;
bio_list_init(&tg->bio_lists[0]);
bio_list_init(&tg->bio_lists[1]);
@@ -401,36 +405,34 @@ static void throtl_schedule_next_dispatch(struct throtl_data *td)
throtl_schedule_delayed_work(td, sq->first_pending_disptime - jiffies);
}
-static inline void
-throtl_start_new_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
+static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw)
{
tg->bytes_disp[rw] = 0;
tg->io_disp[rw] = 0;
tg->slice_start[rw] = jiffies;
tg->slice_end[rw] = jiffies + throtl_slice;
- throtl_log_tg(td, tg, "[%c] new slice start=%lu end=%lu jiffies=%lu",
+ throtl_log_tg(tg, "[%c] new slice start=%lu end=%lu jiffies=%lu",
rw == READ ? 'R' : 'W', tg->slice_start[rw],
tg->slice_end[rw], jiffies);
}
-static inline void throtl_set_slice_end(struct throtl_data *td,
- struct throtl_grp *tg, bool rw, unsigned long jiffy_end)
+static inline void throtl_set_slice_end(struct throtl_grp *tg, bool rw,
+ unsigned long jiffy_end)
{
tg->slice_end[rw] = roundup(jiffy_end, throtl_slice);
}
-static inline void throtl_extend_slice(struct throtl_data *td,
- struct throtl_grp *tg, bool rw, unsigned long jiffy_end)
+static inline void throtl_extend_slice(struct throtl_grp *tg, bool rw,
+ unsigned long jiffy_end)
{
tg->slice_end[rw] = roundup(jiffy_end, throtl_slice);
- throtl_log_tg(td, tg, "[%c] extend slice start=%lu end=%lu jiffies=%lu",
+ throtl_log_tg(tg, "[%c] extend slice start=%lu end=%lu jiffies=%lu",
rw == READ ? 'R' : 'W', tg->slice_start[rw],
tg->slice_end[rw], jiffies);
}
/* Determine if previously allocated or extended slice is complete or not */
-static bool
-throtl_slice_used(struct throtl_data *td, struct throtl_grp *tg, bool rw)
+static bool throtl_slice_used(struct throtl_grp *tg, bool rw)
{
if (time_in_range(jiffies, tg->slice_start[rw], tg->slice_end[rw]))
return 0;
@@ -439,8 +441,7 @@ throtl_slice_used(struct throtl_data *td, struct throtl_grp *tg, bool rw)
}
/* Trim the used slices and adjust slice start accordingly */
-static inline void
-throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
+static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw)
{
unsigned long nr_slices, time_elapsed, io_trim;
u64 bytes_trim, tmp;
@@ -452,7 +453,7 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
* renewed. Don't try to trim the slice if slice is used. A new
* slice will start when appropriate.
*/
- if (throtl_slice_used(td, tg, rw))
+ if (throtl_slice_used(tg, rw))
return;
/*
@@ -463,7 +464,7 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
* is bad because it does not allow new slice to start.
*/
- throtl_set_slice_end(td, tg, rw, jiffies + throtl_slice);
+ throtl_set_slice_end(tg, rw, jiffies + throtl_slice);
time_elapsed = jiffies - tg->slice_start[rw];
@@ -492,14 +493,14 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw)
tg->slice_start[rw] += nr_slices * throtl_slice;
- throtl_log_tg(td, tg, "[%c] trim slice nr=%lu bytes=%llu io=%lu"
+ throtl_log_tg(tg, "[%c] trim slice nr=%lu bytes=%llu io=%lu"
" start=%lu end=%lu jiffies=%lu",
rw == READ ? 'R' : 'W', nr_slices, bytes_trim, io_trim,
tg->slice_start[rw], tg->slice_end[rw], jiffies);
}
-static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
- struct bio *bio, unsigned long *wait)
+static bool tg_with_in_iops_limit(struct throtl_grp *tg, struct bio *bio,
+ unsigned long *wait)
{
bool rw = bio_data_dir(bio);
unsigned int io_allowed;
@@ -548,8 +549,8 @@ static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg,
return 0;
}
-static bool tg_with_in_bps_limit(struct throtl_data *td, struct throtl_grp *tg,
- struct bio *bio, unsigned long *wait)
+static bool tg_with_in_bps_limit(struct throtl_grp *tg, struct bio *bio,
+ unsigned long *wait)
{
bool rw = bio_data_dir(bio);
u64 bytes_allowed, extra_bytes, tmp;
@@ -600,8 +601,8 @@ static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) {
* Returns whether one can dispatch a bio or not. Also returns approx number
* of jiffies to wait before this bio is with-in IO rate and can be dispatched
*/
-static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
- struct bio *bio, unsigned long *wait)
+static bool tg_may_dispatch(struct throtl_grp *tg, struct bio *bio,
+ unsigned long *wait)
{
bool rw = bio_data_dir(bio);
unsigned long bps_wait = 0, iops_wait = 0, max_wait = 0;
@@ -626,15 +627,15 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
* existing slice to make sure it is at least throtl_slice interval
* long since now.
*/
- if (throtl_slice_used(td, tg, rw))
- throtl_start_new_slice(td, tg, rw);
+ if (throtl_slice_used(tg, rw))
+ throtl_start_new_slice(tg, rw);
else {
if (time_before(tg->slice_end[rw], jiffies + throtl_slice))
- throtl_extend_slice(td, tg, rw, jiffies + throtl_slice);
+ throtl_extend_slice(tg, rw, jiffies + throtl_slice);
}
- if (tg_with_in_bps_limit(td, tg, bio, &bps_wait)
- && tg_with_in_iops_limit(td, tg, bio, &iops_wait)) {
+ if (tg_with_in_bps_limit(tg, bio, &bps_wait) &&
+ tg_with_in_iops_limit(tg, bio, &iops_wait)) {
if (wait)
*wait = 0;
return 1;
@@ -646,7 +647,7 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg,
*wait = max_wait;
if (time_before(tg->slice_end[rw], jiffies + max_wait))
- throtl_extend_slice(td, tg, rw, jiffies + max_wait);
+ throtl_extend_slice(tg, rw, jiffies + max_wait);
return 0;
}
@@ -707,10 +708,10 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
struct bio *bio;
if ((bio = bio_list_peek(&tg->bio_lists[READ])))
- tg_may_dispatch(td, tg, bio, &read_wait);
+ tg_may_dispatch(tg, bio, &read_wait);
if ((bio = bio_list_peek(&tg->bio_lists[WRITE])))
- tg_may_dispatch(td, tg, bio, &write_wait);
+ tg_may_dispatch(tg, bio, &write_wait);
min_wait = min(read_wait, write_wait);
disptime = jiffies + min_wait;
@@ -721,8 +722,8 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg)
throtl_enqueue_tg(td, tg);
}
-static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg,
- bool rw, struct bio_list *bl)
+static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw,
+ struct bio_list *bl)
{
struct bio *bio;
@@ -731,18 +732,17 @@ static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg,
/* Drop bio reference on blkg */
blkg_put(tg_to_blkg(tg));
- BUG_ON(td->nr_queued[rw] <= 0);
- td->nr_queued[rw]--;
+ BUG_ON(tg->td->nr_queued[rw] <= 0);
+ tg->td->nr_queued[rw]--;
throtl_charge_bio(tg, bio);
bio_list_add(bl, bio);
bio->bi_rw |= REQ_THROTTLED;
- throtl_trim_slice(td, tg, rw);
+ throtl_trim_slice(tg, rw);
}
-static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
- struct bio_list *bl)
+static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl)
{
unsigned int nr_reads = 0, nr_writes = 0;
unsigned int max_nr_reads = throtl_grp_quantum*3/4;
@@ -751,20 +751,20 @@ static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg,
/* Try to dispatch 75% READS and 25% WRITES */
- while ((bio = bio_list_peek(&tg->bio_lists[READ]))
- && tg_may_dispatch(td, tg, bio, NULL)) {
+ while ((bio = bio_list_peek(&tg->bio_lists[READ])) &&
+ tg_may_dispatch(tg, bio, NULL)) {
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
+ tg_dispatch_one_bio(tg, bio_data_dir(bio), bl);
nr_reads++;
if (nr_reads >= max_nr_reads)
break;
}
- while ((bio = bio_list_peek(&tg->bio_lists[WRITE]))
- && tg_may_dispatch(td, tg, bio, NULL)) {
+ while ((bio = bio_list_peek(&tg->bio_lists[WRITE])) &&
+ tg_may_dispatch(tg, bio, NULL)) {
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl);
+ tg_dispatch_one_bio(tg, bio_data_dir(bio), bl);
nr_writes++;
if (nr_writes >= max_nr_writes)
@@ -791,7 +791,7 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl)
throtl_dequeue_tg(td, tg);
- nr_disp += throtl_dispatch_tg(td, tg, bl);
+ nr_disp += throtl_dispatch_tg(tg, bl);
if (tg->nr_queued[0] || tg->nr_queued[1])
tg_update_disptime(td, tg);
@@ -933,7 +933,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf,
else
*(unsigned int *)((void *)tg + cft->private) = ctx.v;
- throtl_log_tg(td, tg, "limit change rbps=%llu wbps=%llu riops=%u wiops=%u",
+ throtl_log_tg(tg, "limit change rbps=%llu wbps=%llu riops=%u wiops=%u",
tg->bps[READ], tg->bps[WRITE],
tg->iops[READ], tg->iops[WRITE]);
@@ -945,8 +945,8 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf,
* that a group's limit are dropped suddenly and we don't want to
* account recently dispatched IO with new low rate.
*/
- throtl_start_new_slice(td, tg, 0);
- throtl_start_new_slice(td, tg, 1);
+ throtl_start_new_slice(tg, 0);
+ throtl_start_new_slice(tg, 1);
if (tg->flags & THROTL_TG_PENDING) {
tg_update_disptime(td, tg);
@@ -1079,7 +1079,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
}
/* Bio is with-in rate limit of group */
- if (tg_may_dispatch(td, tg, bio, NULL)) {
+ if (tg_may_dispatch(tg, bio, NULL)) {
throtl_charge_bio(tg, bio);
/*
@@ -1093,12 +1093,12 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
*
* So keep on trimming slice even if bio is not queued.
*/
- throtl_trim_slice(td, tg, rw);
+ throtl_trim_slice(tg, rw);
goto out_unlock;
}
queue_bio:
- throtl_log_tg(td, tg, "[%c] bio. bdisp=%llu sz=%u bps=%llu"
+ throtl_log_tg(tg, "[%c] bio. bdisp=%llu sz=%u bps=%llu"
" iodisp=%u iops=%u queued=%d/%d",
rw == READ ? 'R' : 'W',
tg->bytes_disp[rw], bio->bi_size, tg->bps[rw],
@@ -1145,9 +1145,9 @@ void blk_throtl_drain(struct request_queue *q)
throtl_dequeue_tg(td, tg);
while ((bio = bio_list_peek(&tg->bio_lists[READ])))
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), &bl);
+ tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl);
while ((bio = bio_list_peek(&tg->bio_lists[WRITE])))
- tg_dispatch_one_bio(td, tg, bio_data_dir(bio), &bl);
+ tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl);
}
spin_unlock_irq(q->queue_lock);
--
1.8.1.4
next prev parent reply other threads:[~2013-05-02 0:39 UTC|newest]
Thread overview: 154+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-05-02 0:39 [PATCHSET] blk-throttle: implement proper hierarchy support Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 11/31] blk-throttle: rename throtl_rb_root to throtl_service_queue Tejun Heo
2013-05-02 0:39 ` [PATCH 17/31] blk-throttle: move bio_lists[] and friends " Tejun Heo
2013-05-02 0:39 ` [PATCH 20/31] blk-throttle: add throtl_service_queue->parent_sq Tejun Heo
2013-05-02 0:39 ` [PATCH 21/31] blk-throttle: implement sq_to_tg(), sq_to_td() and throtl_log() Tejun Heo
[not found] ` <1367455189-6957-22-git-send-email-tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2013-05-06 17:36 ` Vivek Goyal
2013-05-06 17:36 ` Vivek Goyal
[not found] ` <20130506173644.GD11731-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-06 18:38 ` Tejun Heo
2013-05-06 18:38 ` Tejun Heo
2013-05-06 20:38 ` Tejun Heo
2013-05-06 20:38 ` Tejun Heo
[not found] ` <20130506203827.GE800-9pTldWuhBndy/B6EtB590w@public.gmane.org>
2013-05-06 20:39 ` Tejun Heo
2013-05-06 20:39 ` Tejun Heo
2013-05-06 20:39 ` Tejun Heo
2013-05-06 20:41 ` Vivek Goyal
2013-05-06 20:41 ` Vivek Goyal
[not found] ` <20130506204141.GF11731-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-06 20:43 ` Tejun Heo
2013-05-06 20:43 ` Tejun Heo
2013-05-06 20:43 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 22/31] blk-throttle: set REQ_THROTTLED from throtl_charge_bio() and gate stats update with it Tejun Heo
2013-05-02 0:39 ` [PATCH 23/31] blk-throttle: separate out throtl_service_queue->pending_timer from throtl_data->dispatch_work Tejun Heo
2013-05-02 0:39 ` [PATCH 24/31] blk-throttle: implement dispatch looping Tejun Heo
2013-05-02 0:39 ` [PATCH 26/31] blk-throttle: make blk_throtl_drain() ready for hierarchy Tejun Heo
2013-05-02 0:39 ` [PATCH 27/31] blk-throttle: make blk_throtl_bio() " Tejun Heo
2013-05-02 0:39 ` [PATCH 29/31] blk-throttle: make throtl_pending_timer_fn() " Tejun Heo
2013-05-02 0:39 ` [PATCH 30/31] blk-throttle: implement throtl_grp->has_rules[] Tejun Heo
2013-05-02 0:39 ` [PATCH 31/31] blk-throttle: implement proper hierarchy support Tejun Heo
[not found] ` <1367455189-6957-1-git-send-email-tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2013-05-02 0:39 ` [PATCH 01/31] blkcg: fix error return path in blkg_create() Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 02/31] blkcg: move blkg_for_each_descendant_pre() to block/blk-cgroup.h Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 03/31] blkcg: implement blkg_for_each_descendant_post() Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 04/31] blkcg: invoke blkcg_policy->pd_init() after parent is linked Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 05/31] blkcg: move bulk of blkcg_gq release operations to the RCU callback Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 06/31] blk-throttle: remove spurious throtl_enqueue_tg() call from throtl_select_dispatch() Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 07/31] blk-throttle: removed deferred config application mechanism Tejun Heo
2013-05-02 0:39 ` Tejun Heo
[not found] ` <1367455189-6957-8-git-send-email-tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2013-05-02 14:49 ` Vivek Goyal
2013-05-02 14:49 ` Vivek Goyal
[not found] ` <20130502144912.GE30020-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-02 17:27 ` Tejun Heo
2013-05-02 17:27 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 08/31] blk-throttle: collapse throtl_dispatch() into the work function Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 09/31] blk-throttle: relocate throtl_schedule_delayed_work() Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 10/31] blk-throttle: remove pointless throtl_nr_queued() optimizations Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 11/31] blk-throttle: rename throtl_rb_root to throtl_service_queue Tejun Heo
2013-05-02 0:39 ` [PATCH 12/31] blk-throttle: simplify throtl_grp flag handling Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` Tejun Heo [this message]
2013-05-02 0:39 ` [PATCH 13/31] blk-throttle: add backlink pointer from throtl_grp to throtl_data Tejun Heo
2013-05-02 0:39 ` [PATCH 14/31] blk-throttle: pass around throtl_service_queue instead of throtl_data Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 15/31] blk-throttle: reorganize throtl_service_queue passed around as argument Tejun Heo
2013-05-02 0:39 ` Tejun Heo
[not found] ` <1367455189-6957-16-git-send-email-tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
2013-05-02 15:21 ` Vivek Goyal
2013-05-02 15:21 ` Vivek Goyal
[not found] ` <20130502152148.GF30020-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-02 17:29 ` Tejun Heo
2013-05-02 17:29 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 16/31] blk-throttle: add throtl_grp->service_queue Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 17/31] blk-throttle: move bio_lists[] and friends to throtl_service_queue Tejun Heo
2013-05-02 0:39 ` [PATCH 18/31] blk-throttle: dispatch to throtl_data->service_queue.bio_lists[] Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 19/31] blk-throttle: generalize update_disptime optimization in blk_throtl_bio() Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 20/31] blk-throttle: add throtl_service_queue->parent_sq Tejun Heo
2013-05-02 0:39 ` [PATCH 21/31] blk-throttle: implement sq_to_tg(), sq_to_td() and throtl_log() Tejun Heo
2013-05-02 0:39 ` [PATCH 22/31] blk-throttle: set REQ_THROTTLED from throtl_charge_bio() and gate stats update with it Tejun Heo
2013-05-02 0:39 ` [PATCH 23/31] blk-throttle: separate out throtl_service_queue->pending_timer from throtl_data->dispatch_work Tejun Heo
2013-05-02 0:39 ` [PATCH 24/31] blk-throttle: implement dispatch looping Tejun Heo
2013-05-02 0:39 ` [PATCH 25/31] blk-throttle: dispatch from throtl_pending_timer_fn() Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 26/31] blk-throttle: make blk_throtl_drain() ready for hierarchy Tejun Heo
2013-05-02 0:39 ` [PATCH 27/31] blk-throttle: make blk_throtl_bio() " Tejun Heo
2013-05-02 0:39 ` [PATCH 28/31] blk-throttle: make tg_dispatch_one_bio() " Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` Tejun Heo
2013-05-02 0:39 ` [PATCH 29/31] blk-throttle: make throtl_pending_timer_fn() " Tejun Heo
2013-05-02 0:39 ` [PATCH 30/31] blk-throttle: implement throtl_grp->has_rules[] Tejun Heo
2013-05-02 0:39 ` [PATCH 31/31] blk-throttle: implement proper hierarchy support Tejun Heo
2013-05-02 17:34 ` [PATCHSET] " Vivek Goyal
2013-05-02 17:34 ` Vivek Goyal
[not found] ` <20130502173428.GA4771-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-02 17:57 ` Tejun Heo
2013-05-02 17:57 ` Tejun Heo
[not found] ` <20130502175701.GL19814-9pTldWuhBndy/B6EtB590w@public.gmane.org>
2013-05-02 18:17 ` Vivek Goyal
2013-05-02 18:17 ` Vivek Goyal
[not found] ` <20130502181747.GH30020-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-02 18:29 ` Tejun Heo
2013-05-02 18:29 ` Tejun Heo
[not found] ` <20130502182933.GN19814-9pTldWuhBndy/B6EtB590w@public.gmane.org>
2013-05-02 18:45 ` Vivek Goyal
2013-05-02 18:45 ` Vivek Goyal
[not found] ` <20130502184514.GI30020-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-02 18:49 ` Tejun Heo
2013-05-02 18:49 ` Tejun Heo
[not found] ` <20130502184953.GP19814-9pTldWuhBndy/B6EtB590w@public.gmane.org>
2013-05-02 19:07 ` Vivek Goyal
2013-05-02 19:07 ` Vivek Goyal
[not found] ` <20130502190732.GK30020-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-02 19:11 ` Tejun Heo
2013-05-02 19:11 ` Tejun Heo
[not found] ` <CAOS58YOk7G=dBG1v5Ed2z3biMMyKkkutp30vH5XC72z0_Z85cw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2013-05-02 19:31 ` Vivek Goyal
2013-05-02 19:31 ` Vivek Goyal
2013-05-02 19:31 ` Vivek Goyal
[not found] ` <20130502193139.GL30020-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-02 23:13 ` Tejun Heo
2013-05-02 23:13 ` Tejun Heo
[not found] ` <20130502231307.GT19814-9pTldWuhBndy/B6EtB590w@public.gmane.org>
2013-05-03 17:56 ` Vivek Goyal
2013-05-03 17:56 ` Vivek Goyal
[not found] ` <20130503175652.GB6062-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-03 18:57 ` Tejun Heo
2013-05-03 18:57 ` Tejun Heo
[not found] ` <20130503185751.GA22860-9pTldWuhBndy/B6EtB590w@public.gmane.org>
2013-05-03 18:58 ` Tejun Heo
2013-05-03 18:58 ` Tejun Heo
2013-05-03 18:58 ` Tejun Heo
2013-05-03 19:08 ` Vivek Goyal
2013-05-03 19:08 ` Vivek Goyal
2013-05-03 19:08 ` Vivek Goyal
[not found] ` <20130503190823.GC6062-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-03 19:14 ` Tejun Heo
2013-05-03 19:14 ` Tejun Heo
[not found] ` <20130503191418.GC22860-9pTldWuhBndy/B6EtB590w@public.gmane.org>
2013-05-03 19:26 ` Vivek Goyal
2013-05-03 19:26 ` Vivek Goyal
2013-05-03 21:05 ` Vivek Goyal
2013-05-03 21:05 ` Vivek Goyal
2013-05-03 21:05 ` Vivek Goyal
[not found] ` <20130503210513.GE6062-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-03 23:54 ` Tejun Heo
2013-05-03 23:54 ` Tejun Heo
[not found] ` <20130503235455.GE22860-9pTldWuhBndy/B6EtB590w@public.gmane.org>
2013-05-06 17:33 ` Vivek Goyal
2013-05-06 17:33 ` Vivek Goyal
2013-05-03 19:14 ` Tejun Heo
2013-05-03 18:57 ` Tejun Heo
2013-05-03 17:56 ` Vivek Goyal
2013-05-02 18:49 ` Tejun Heo
2013-05-02 18:29 ` Tejun Heo
2013-05-02 17:57 ` Tejun Heo
2013-05-02 18:08 ` Vivek Goyal
2013-05-02 18:08 ` Vivek Goyal
[not found] ` <20130502180815.GG30020-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-02 18:44 ` Tejun Heo
2013-05-02 18:44 ` Tejun Heo
[not found] ` <20130502184426.GO19814-9pTldWuhBndy/B6EtB590w@public.gmane.org>
2013-05-02 18:59 ` Vivek Goyal
2013-05-02 18:59 ` Vivek Goyal
2013-05-02 18:59 ` Vivek Goyal
2013-05-04 0:50 ` [PATCH 29.5/32] blk-throttle: add throtl_qnode for dispatch fairness Tejun Heo
2013-05-04 0:50 ` Tejun Heo
[not found] ` <20130504005044.GF22860-9pTldWuhBndy/B6EtB590w@public.gmane.org>
2013-05-04 0:53 ` Tejun Heo
2013-05-04 0:53 ` Tejun Heo
2013-05-06 16:00 ` Vivek Goyal
2013-05-06 16:00 ` Vivek Goyal
[not found] ` <20130506160006.GA11731-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2013-05-06 18:35 ` Tejun Heo
2013-05-06 18:35 ` Tejun Heo
2013-05-06 16:00 ` Vivek Goyal
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=1367455189-6957-14-git-send-email-tj@kernel.org \
--to=tj-dgejt+ai2ygdnm+yrofe0a@public.gmane.org \
--cc=axboe-tSWWG44O7X1aa/9Udqfwiw@public.gmane.org \
--cc=cgroups-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org \
--cc=linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=vgoyal-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.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.