* [PATCH 0/4] block: Separating discards from writes in Linux IO statistics
@ 2016-06-08 20:50 Michael Callahan
2016-06-08 20:50 ` [PATCH 1/4] block: Add part_stat_read_accum to read across field entries Michael Callahan
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Michael Callahan @ 2016-06-08 20:50 UTC (permalink / raw)
To: linux-block; +Cc: Michael Callahan, axboe, coder.callahan, kernel-team
This patch set separates block layer statistics for discards from
writes. Discards are currently bundled with writes in the
various /sys/block/*/stat files as well as in /proc/diskstats.
However discards are nearly always used to mark storage that is
no longer in use. There are many reasons having discard not
counted with writes is useful:
1) For many non volatile memory devices it is just nice to know
that discards are enabled and working properly.
2) Discards have different performance characteristics than
writes. They are generally much faster and larger and bundling
them makes performance statistics less meaningful.
3) Discards are not writes in terms of tracking device lifetime.
If a device supports six device writes per day it is nice to know
how many writes have actually been written to the device as
discards do not count against that total.
Separation of discard statistics is accomplished by expanding the
struct diskstat arrays to 3 entries for STAT_READ, STAT_WRITE,
and STAT_DISCARD. A new rw_stat_group function is then used to
convert from rw_flags (cmd_flags from requests, bi_rw from bios)
into the appropriate stat group which is then tracked as before.
Lastly the new statistics are appended to the current
/sys/bloc/*/stat and /proc/diskstats on output such that they are
the last four entries of each. These are analogous to the four
read and write statistics.
* Number of discard ios completed
* Number of discard ios merged
* Number of discard sectors completed
* Milliseconds spent on discard requests.
[before ~]# cat sys/block/nvme0/stat
296550701 0 2372405688 67317193 19672752 0 7972237312
9375167 0 2787238 79718726
[after ~]# cat sys/block/nvme0/stat
296550701 0 2372405688 67317193 18034352 0 4616794112
9125902 0 2787238 79718726 1638400 0 3355443200
249265
Note that the discards have moved out of the write fields to the
end and that the write fields are now smaller by the difference.
Adding the new statistics to the end of /sys/block/*/stat and
/proc/diskstats is backwards compatible with both iostat and
vmstat which pick up just the old fields:
[root@after ~]# iostat -x
Linux 4.5.0_68319_ge5065f4-dirty (##hostname###) 05/17/2016
_x86_64_ (48 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
0.41 0.00 0.23 0.01 0.00 99.35
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s
avgrq-sz avgqu-sz await svctm %util
sda 0.01 7.50 0.20 3.61 16.65 708.41
190.03 0.09 22.53 1.16 0.44
nvme0n1 0.00 0.00 587.03 35.70 4696.25 9139.08
22.22 0.16 0.24 0.01 0.55
[root@after ~]# vmstat -d
disk- ------------reads------------ ------------writes----------- -----IO------
total merged sectors ms total merged sectors ms cur sec
ram0 0 0 0 0 0 0 0 0 0 0
ram1 0 0 0 0 0 0 0 0 0 0
ram2 0 0 0 0 0 0 0 0 0 0
ram3 0 0 0 0 0 0 0 0 0 0
ram4 0 0 0 0 0 0 0 0 0 0
ram5 0 0 0 0 0 0 0 0 0 0
ram6 0 0 0 0 0 0 0 0 0 0
ram7 0 0 0 0 0 0 0 0 0 0
ram8 0 0 0 0 0 0 0 0 0 0
ram9 0 0 0 0 0 0 0 0 0 0
ram10 0 0 0 0 0 0 0 0 0 0
ram11 0 0 0 0 0 0 0 0 0 0
ram12 0 0 0 0 0 0 0 0 0 0
ram13 0 0 0 0 0 0 0 0 0 0
ram14 0 0 0 0 0 0 0 0 0 0
ram15 0 0 0 0 0 0 0 0 0 0
sda 102903 5247 8408420 47424 1820306 3782041 357153613 43320121
0 2228
nvme0n1 145633279 0 1165066312 18981333 13107200 0
3355443200 6663655 0 1796
loop0 0 0 0 0 0 0 0 0 0 0
loop1 0 0 0 0 0 0 0 0 0 0
[chop rest of loop devices]
Comments and suggestions welcome.
Michael Callahan (4):
block: Add part_stat_read_accum to read across field entries.
block: Define and use STAT_READ and STAT_WRITE
block: Add and use a rw_stat_group function for indexing disk_stat
fields.
block: Track DISCARD statistics and output them in stat and diskstat.
Documentation/ABI/testing/procfs-diskstats | 10 ++++++++++
Documentation/block/stat.txt | 28 ++++++++++++++++------------
Documentation/iostats.txt | 15 ++++++++++++++-
block/bio.c | 14 +++++++++-----
block/blk-core.c | 9 +++++----
block/genhd.c | 30 +++++++++++++++++++-----------
block/partition-generic.c | 25 +++++++++++++++----------
drivers/block/drbd/drbd_receiver.c | 3 +--
drivers/block/drbd/drbd_req.c | 4 ++--
drivers/block/drbd/drbd_worker.c | 4 +---
drivers/block/rsxx/dev.c | 5 ++---
drivers/block/zram/zram_drv.c | 14 +++++++-------
drivers/md/bcache/request.c | 10 +++++-----
drivers/md/dm.c | 7 +++----
drivers/md/md.c | 8 ++++----
drivers/nvdimm/core.c | 8 +++++---
fs/ext4/super.c | 5 +++--
fs/ext4/sysfs.c | 6 ++++--
fs/f2fs/f2fs.h | 2 +-
fs/f2fs/super.c | 3 ++-
include/linux/bio.h | 4 ++--
include/linux/genhd.h | 24 ++++++++++++++++++++----
22 files changed, 150 insertions(+), 88 deletions(-)
--
1.9.3
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/4] block: Add part_stat_read_accum to read across field entries.
2016-06-08 20:50 [PATCH 0/4] block: Separating discards from writes in Linux IO statistics Michael Callahan
@ 2016-06-08 20:50 ` Michael Callahan
2016-06-08 20:50 ` [PATCH 2/4] block: Define and use STAT_READ and STAT_WRITE Michael Callahan
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Michael Callahan @ 2016-06-08 20:50 UTC (permalink / raw)
To: linux-block; +Cc: Michael Callahan, axboe, coder.callahan, kernel-team
Add a part_stat_read_accum macro to genhd.h to read and sum across field
entries. For example to sum up the number read and write sectors
completed. In addition to being ar reasonable cleanup by itself this
will make it easier to add new stat fields in the future.
Signed-off-by: Michael Callahan <michaelcallahan@fb.com>
---
drivers/block/drbd/drbd_receiver.c | 3 +--
drivers/block/drbd/drbd_worker.c | 4 +---
drivers/md/md.c | 3 +--
include/linux/genhd.h | 4 ++++
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index 050aaa1..3d490a7 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -2504,8 +2504,7 @@ bool drbd_rs_c_min_rate_throttle(struct drbd_device *device)
if (c_min_rate == 0)
return false;
- curr_events = (int)part_stat_read(&disk->part0, sectors[0]) +
- (int)part_stat_read(&disk->part0, sectors[1]) -
+ curr_events = (int)part_stat_read_accum(&disk->part0, sectors) -
atomic_read(&device->rs_sect_ev);
if (atomic_read(&device->ap_actlog_cnt)
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c
index 4d87499..3e56209 100644
--- a/drivers/block/drbd/drbd_worker.c
+++ b/drivers/block/drbd/drbd_worker.c
@@ -1591,9 +1591,7 @@ void drbd_rs_controller_reset(struct drbd_device *device)
atomic_set(&device->rs_sect_in, 0);
atomic_set(&device->rs_sect_ev, 0);
device->rs_in_flight = 0;
- device->rs_last_events =
- (int)part_stat_read(&disk->part0, sectors[0]) +
- (int)part_stat_read(&disk->part0, sectors[1]);
+ device->rs_last_events = (int)part_stat_read_accum(&disk->part0, sectors);
/* Updating the RCU protected object in place is necessary since
this function gets called from atomic context.
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 866825f..38f819c 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -7635,8 +7635,7 @@ static int is_mddev_idle(struct mddev *mddev, int init)
rcu_read_lock();
rdev_for_each_rcu(rdev, mddev) {
struct gendisk *disk = rdev->bdev->bd_contains->bd_disk;
- curr_events = (int)part_stat_read(&disk->part0, sectors[0]) +
- (int)part_stat_read(&disk->part0, sectors[1]) -
+ curr_events = (int)part_stat_read_accum(&disk->part0, sectors) -
atomic_read(&disk->sync_io);
/* sync IO will cause sync_io to increase before the disk_stats
* as sync_io is counted when a request starts, and
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 359a8e4..6ab71b6 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -364,6 +364,10 @@ static inline void free_part_stats(struct hd_struct *part)
#endif /* CONFIG_SMP */
+#define part_stat_read_accum(part, field) \
+ (part_stat_read(part, field[0]) + \
+ part_stat_read(part, field[1]))
+
#define part_stat_add(cpu, part, field, addnd) do { \
__part_stat_add((cpu), (part), field, addnd); \
if ((part)->partno) \
--
1.9.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/4] block: Define and use STAT_READ and STAT_WRITE
2016-06-08 20:50 [PATCH 0/4] block: Separating discards from writes in Linux IO statistics Michael Callahan
2016-06-08 20:50 ` [PATCH 1/4] block: Add part_stat_read_accum to read across field entries Michael Callahan
@ 2016-06-08 20:50 ` Michael Callahan
2016-06-08 20:50 ` [PATCH 3/4] block: Add and use a rw_stat_group function for indexing disk_stat fields Michael Callahan
2016-06-08 20:50 ` [PATCH 4/4] block: Track DISCARD statistics and output them in stat and diskstat Michael Callahan
3 siblings, 0 replies; 5+ messages in thread
From: Michael Callahan @ 2016-06-08 20:50 UTC (permalink / raw)
To: linux-block; +Cc: Michael Callahan, axboe, coder.callahan, kernel-team
Add defines for STAT_READ and STAT_WRITE for indexing the partition stat
entries. This clarifies some fs/ code which has hardcoded 1 for
STAT_WRITE and will make it easier to extend the stats with additional
fields.
Signed-off-by: Michael Callahan <michaelcallahan@fb.com>
---
block/genhd.c | 16 ++++++++--------
block/partition-generic.c | 16 ++++++++--------
fs/ext4/super.c | 5 +++--
fs/ext4/sysfs.c | 6 ++++--
fs/f2fs/f2fs.h | 2 +-
fs/f2fs/super.c | 3 ++-
include/linux/genhd.h | 9 ++++++---
7 files changed, 32 insertions(+), 25 deletions(-)
diff --git a/block/genhd.c b/block/genhd.c
index 9f42526..b6f1dee 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1199,14 +1199,14 @@ static int diskstats_show(struct seq_file *seqf, void *v)
"%u %lu %lu %lu %u %u %u %u\n",
MAJOR(part_devt(hd)), MINOR(part_devt(hd)),
disk_name(gp, hd->partno, buf),
- part_stat_read(hd, ios[READ]),
- part_stat_read(hd, merges[READ]),
- part_stat_read(hd, sectors[READ]),
- jiffies_to_msecs(part_stat_read(hd, ticks[READ])),
- part_stat_read(hd, ios[WRITE]),
- part_stat_read(hd, merges[WRITE]),
- part_stat_read(hd, sectors[WRITE]),
- jiffies_to_msecs(part_stat_read(hd, ticks[WRITE])),
+ part_stat_read(hd, ios[STAT_READ]),
+ part_stat_read(hd, merges[STAT_READ]),
+ part_stat_read(hd, sectors[STAT_READ]),
+ jiffies_to_msecs(part_stat_read(hd, ticks[STAT_READ])),
+ part_stat_read(hd, ios[STAT_WRITE]),
+ part_stat_read(hd, merges[STAT_WRITE]),
+ part_stat_read(hd, sectors[STAT_WRITE]),
+ jiffies_to_msecs(part_stat_read(hd, ticks[STAT_WRITE])),
part_in_flight(hd),
jiffies_to_msecs(part_stat_read(hd, io_ticks)),
jiffies_to_msecs(part_stat_read(hd, time_in_queue))
diff --git a/block/partition-generic.c b/block/partition-generic.c
index d7eb77e..15bf298 100644
--- a/block/partition-generic.c
+++ b/block/partition-generic.c
@@ -123,14 +123,14 @@ ssize_t part_stat_show(struct device *dev,
"%8lu %8lu %8llu %8u "
"%8u %8u %8u"
"\n",
- part_stat_read(p, ios[READ]),
- part_stat_read(p, merges[READ]),
- (unsigned long long)part_stat_read(p, sectors[READ]),
- jiffies_to_msecs(part_stat_read(p, ticks[READ])),
- part_stat_read(p, ios[WRITE]),
- part_stat_read(p, merges[WRITE]),
- (unsigned long long)part_stat_read(p, sectors[WRITE]),
- jiffies_to_msecs(part_stat_read(p, ticks[WRITE])),
+ part_stat_read(p, ios[STAT_READ]),
+ part_stat_read(p, merges[STAT_READ]),
+ (unsigned long long)part_stat_read(p, sectors[STAT_READ]),
+ jiffies_to_msecs(part_stat_read(p, ticks[STAT_READ])),
+ part_stat_read(p, ios[STAT_WRITE]),
+ part_stat_read(p, merges[STAT_WRITE]),
+ (unsigned long long)part_stat_read(p, sectors[STAT_WRITE]),
+ jiffies_to_msecs(part_stat_read(p, ticks[STAT_WRITE])),
part_in_flight(p),
jiffies_to_msecs(part_stat_read(p, io_ticks)),
jiffies_to_msecs(part_stat_read(p, time_in_queue)));
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 3822a5a..47ef2da 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -3183,7 +3183,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
sbi->s_sb_block = sb_block;
if (sb->s_bdev->bd_part)
sbi->s_sectors_written_start =
- part_stat_read(sb->s_bdev->bd_part, sectors[1]);
+ part_stat_read(sb->s_bdev->bd_part, sectors[STAT_WRITE]);
/* Cleanup superblock name */
strreplace(sb->s_id, '/', '!');
@@ -4356,7 +4356,8 @@ static int ext4_commit_super(struct super_block *sb, int sync)
if (sb->s_bdev->bd_part)
es->s_kbytes_written =
cpu_to_le64(EXT4_SB(sb)->s_kbytes_written +
- ((part_stat_read(sb->s_bdev->bd_part, sectors[1]) -
+ ((part_stat_read(sb->s_bdev->bd_part,
+ sectors[STAT_WRITE]) -
EXT4_SB(sb)->s_sectors_written_start) >> 1));
else
es->s_kbytes_written =
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
index 1420a3c..10ad87e 100644
--- a/fs/ext4/sysfs.c
+++ b/fs/ext4/sysfs.c
@@ -55,7 +55,8 @@ static ssize_t session_write_kbytes_show(struct ext4_attr *a,
if (!sb->s_bdev->bd_part)
return snprintf(buf, PAGE_SIZE, "0\n");
return snprintf(buf, PAGE_SIZE, "%lu\n",
- (part_stat_read(sb->s_bdev->bd_part, sectors[1]) -
+ (part_stat_read(sb->s_bdev->bd_part,
+ sectors[STAT_WRITE]) -
sbi->s_sectors_written_start) >> 1);
}
@@ -68,7 +69,8 @@ static ssize_t lifetime_write_kbytes_show(struct ext4_attr *a,
return snprintf(buf, PAGE_SIZE, "0\n");
return snprintf(buf, PAGE_SIZE, "%llu\n",
(unsigned long long)(sbi->s_kbytes_written +
- ((part_stat_read(sb->s_bdev->bd_part, sectors[1]) -
+ ((part_stat_read(sb->s_bdev->bd_part,
+ sectors[STAT_WRITE]) -
EXT4_SB(sb)->s_sectors_written_start) >> 1)));
}
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 916e7c2..f313944 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -873,7 +873,7 @@ struct f2fs_sb_info {
* and the return value is in kbytes. s is of struct f2fs_sb_info.
*/
#define BD_PART_WRITTEN(s) \
-(((u64)part_stat_read(s->sb->s_bdev->bd_part, sectors[1]) - \
+(((u64)part_stat_read(s->sb->s_bdev->bd_part, sectors[STAT_WRITE]) - \
s->sectors_written_start) >> 1)
static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type)
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 74cc852..d4001a3 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1636,7 +1636,8 @@ try_onemore:
/* For write statistics */
if (sb->s_bdev->bd_part)
sbi->sectors_written_start =
- (u64)part_stat_read(sb->s_bdev->bd_part, sectors[1]);
+ (u64)part_stat_read(sb->s_bdev->bd_part,
+ sectors[STAT_WRITE]);
/* Read accumulated write IO statistics if exists */
seg_i = CURSEG_I(sbi, CURSEG_HOT_NODE);
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 6ab71b6..17f1c09 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -60,6 +60,9 @@ enum {
#define DISK_MAX_PARTS 256
#define DISK_NAME_LEN 32
+#define STAT_READ 0
+#define STAT_WRITE 1
+
#include <linux/major.h>
#include <linux/device.h>
#include <linux/smp.h>
@@ -81,7 +84,7 @@ struct partition {
} __attribute__((packed));
struct disk_stats {
- unsigned long sectors[2]; /* READs and WRITEs */
+ unsigned long sectors[2]; /* STAT_READs and STAT_WRITEs */
unsigned long ios[2];
unsigned long merges[2];
unsigned long ticks[2];
@@ -365,8 +368,8 @@ static inline void free_part_stats(struct hd_struct *part)
#endif /* CONFIG_SMP */
#define part_stat_read_accum(part, field) \
- (part_stat_read(part, field[0]) + \
- part_stat_read(part, field[1]))
+ (part_stat_read(part, field[STAT_READ]) + \
+ part_stat_read(part, field[STAT_WRITE]))
#define part_stat_add(cpu, part, field, addnd) do { \
__part_stat_add((cpu), (part), field, addnd); \
--
1.9.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/4] block: Add and use a rw_stat_group function for indexing disk_stat fields.
2016-06-08 20:50 [PATCH 0/4] block: Separating discards from writes in Linux IO statistics Michael Callahan
2016-06-08 20:50 ` [PATCH 1/4] block: Add part_stat_read_accum to read across field entries Michael Callahan
2016-06-08 20:50 ` [PATCH 2/4] block: Define and use STAT_READ and STAT_WRITE Michael Callahan
@ 2016-06-08 20:50 ` Michael Callahan
2016-06-08 20:50 ` [PATCH 4/4] block: Track DISCARD statistics and output them in stat and diskstat Michael Callahan
3 siblings, 0 replies; 5+ messages in thread
From: Michael Callahan @ 2016-06-08 20:50 UTC (permalink / raw)
To: linux-block; +Cc: Michael Callahan, axboe, coder.callahan, kernel-team
Add and use a new rw_stat_group function for indexing partition stat
fields rather than indexing them by rq_data_dir or bio_data_dir. This
function works similarly to rw_is_sync in that it takes the
request::cmd_flags or bio::bi_rw flags and determines which stats should
et updated.
In addition the first parameter to generic_start_io_acct and
generic_end_io_acct is now a stat group rather than simply a read or
write bit. STAT_READ and STAT_WRITE have been defined such that this
is backwards compatible but callers who want to track new stats should
be updated to pass the extended parameter. This has been done by this
patch for all current consumers of these two functions.
Note that the partition in_flight counts are not part of the per-cpu
statistics and as such are not indexed via this function. An additional
stat_group_to_rw helper macro has been added to more cleanly handle that
case.
Signed-off-by: Michael Callahan <michaelcallahan@fb.com>
---
block/bio.c | 14 +++++++++-----
block/blk-core.c | 9 +++++----
drivers/block/drbd/drbd_req.c | 4 ++--
drivers/block/rsxx/dev.c | 5 ++---
drivers/block/zram/zram_drv.c | 14 +++++++-------
drivers/md/bcache/request.c | 10 +++++-----
drivers/md/dm.c | 7 +++----
drivers/md/md.c | 5 +++--
drivers/nvdimm/core.c | 8 +++++---
include/linux/bio.h | 4 ++--
include/linux/genhd.h | 5 +++++
11 files changed, 48 insertions(+), 37 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index 0e4aa42..dca7b95 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1667,27 +1667,31 @@ void bio_check_pages_dirty(struct bio *bio)
}
}
-void generic_start_io_acct(int rw, unsigned long sectors,
+void generic_start_io_acct(int rw_flags, unsigned long sectors,
struct hd_struct *part)
{
+ const int sgrp = rw_stat_group(rw_flags);
+ const int rw = (rw_flags & REQ_WRITE) != 0;
int cpu = part_stat_lock();
part_round_stats(cpu, part);
- part_stat_inc(cpu, part, ios[rw]);
- part_stat_add(cpu, part, sectors[rw], sectors);
+ part_stat_inc(cpu, part, ios[sgrp]);
+ part_stat_add(cpu, part, sectors[sgrp], sectors);
part_inc_in_flight(part, rw);
part_stat_unlock();
}
EXPORT_SYMBOL(generic_start_io_acct);
-void generic_end_io_acct(int rw, struct hd_struct *part,
+void generic_end_io_acct(int rw_flags, struct hd_struct *part,
unsigned long start_time)
{
unsigned long duration = jiffies - start_time;
+ const int sgrp = rw_stat_group(rw_flags);
+ const int rw = (rw_flags & REQ_WRITE) != 0;
int cpu = part_stat_lock();
- part_stat_add(cpu, part, ticks[rw], duration);
+ part_stat_add(cpu, part, ticks[sgrp], duration);
part_round_stats(cpu, part);
part_dec_in_flight(part, rw);
diff --git a/block/blk-core.c b/block/blk-core.c
index 2475b1c7..123bf60 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -2275,13 +2275,13 @@ EXPORT_SYMBOL_GPL(blk_rq_err_bytes);
void blk_account_io_completion(struct request *req, unsigned int bytes)
{
if (blk_do_io_stat(req)) {
- const int rw = rq_data_dir(req);
+ const int sgrp = rw_stat_group(req->cmd_flags);
struct hd_struct *part;
int cpu;
cpu = part_stat_lock();
part = req->part;
- part_stat_add(cpu, part, sectors[rw], bytes >> 9);
+ part_stat_add(cpu, part, sectors[sgrp], bytes >> 9);
part_stat_unlock();
}
}
@@ -2295,6 +2295,7 @@ void blk_account_io_done(struct request *req)
*/
if (blk_do_io_stat(req) && !(req->cmd_flags & REQ_FLUSH_SEQ)) {
unsigned long duration = jiffies - req->start_time;
+ const int sgrp = rw_stat_group(req->cmd_flags);
const int rw = rq_data_dir(req);
struct hd_struct *part;
int cpu;
@@ -2302,8 +2303,8 @@ void blk_account_io_done(struct request *req)
cpu = part_stat_lock();
part = req->part;
- part_stat_inc(cpu, part, ios[rw]);
- part_stat_add(cpu, part, ticks[rw], duration);
+ part_stat_inc(cpu, part, ios[sgrp]);
+ part_stat_add(cpu, part, ticks[sgrp], duration);
part_round_stats(cpu, part);
part_dec_in_flight(part, rw);
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index 2255dcf..6f31f67 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -36,14 +36,14 @@ static bool drbd_may_do_local_read(struct drbd_device *device, sector_t sector,
/* Update disk stats at start of I/O request */
static void _drbd_start_io_acct(struct drbd_device *device, struct drbd_request *req)
{
- generic_start_io_acct(bio_data_dir(req->master_bio), req->i.size >> 9,
+ generic_start_io_acct(req->master_bio->bi_rw, req->i.size >> 9,
&device->vdisk->part0);
}
/* Update disk stats when completing request upwards */
static void _drbd_end_io_acct(struct drbd_device *device, struct drbd_request *req)
{
- generic_end_io_acct(bio_data_dir(req->master_bio),
+ generic_end_io_acct(req->master_bio->bi_rw,
&device->vdisk->part0, req->start_jif);
}
diff --git a/drivers/block/rsxx/dev.c b/drivers/block/rsxx/dev.c
index e1b8b70..17ab33f 100644
--- a/drivers/block/rsxx/dev.c
+++ b/drivers/block/rsxx/dev.c
@@ -112,7 +112,7 @@ static const struct block_device_operations rsxx_fops = {
static void disk_stats_start(struct rsxx_cardinfo *card, struct bio *bio)
{
- generic_start_io_acct(bio_data_dir(bio), bio_sectors(bio),
+ generic_start_io_acct(bio->bi_rw, bio_sectors(bio),
&card->gendisk->part0);
}
@@ -120,8 +120,7 @@ static void disk_stats_complete(struct rsxx_cardinfo *card,
struct bio *bio,
unsigned long start_time)
{
- generic_end_io_acct(bio_data_dir(bio), &card->gendisk->part0,
- start_time);
+ generic_end_io_acct(bio->bi_rw, &card->gendisk->part0, start_time);
}
static void bio_dma_done_cb(struct rsxx_cardinfo *card,
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 8fcad8b..e7dd54b 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -835,12 +835,12 @@ static void zram_bio_discard(struct zram *zram, u32 index,
}
static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index,
- int offset, int rw)
+ int offset, int rw, int rw_flags)
{
unsigned long start_time = jiffies;
int ret;
- generic_start_io_acct(rw, bvec->bv_len >> SECTOR_SHIFT,
+ generic_start_io_acct(rw_flags, bvec->bv_len >> SECTOR_SHIFT,
&zram->disk->part0);
if (rw == READ) {
@@ -851,7 +851,7 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index,
ret = zram_bvec_write(zram, bvec, index, offset);
}
- generic_end_io_acct(rw, &zram->disk->part0, start_time);
+ generic_end_io_acct(rw_flags, &zram->disk->part0, start_time);
if (unlikely(ret)) {
if (rw == READ)
@@ -895,15 +895,15 @@ static void __zram_make_request(struct zram *zram, struct bio *bio)
bv.bv_len = max_transfer_size;
bv.bv_offset = bvec.bv_offset;
- if (zram_bvec_rw(zram, &bv, index, offset, rw) < 0)
+ if (zram_bvec_rw(zram, &bv, index, offset, rw, bio->bi_rw) < 0)
goto out;
bv.bv_len = bvec.bv_len - max_transfer_size;
bv.bv_offset += max_transfer_size;
- if (zram_bvec_rw(zram, &bv, index + 1, 0, rw) < 0)
+ if (zram_bvec_rw(zram, &bv, index + 1, 0, rw, bio->bi_rw) < 0)
goto out;
} else
- if (zram_bvec_rw(zram, &bvec, index, offset, rw) < 0)
+ if (zram_bvec_rw(zram, &bvec, index, offset, rw, bio->bi_rw) < 0)
goto out;
update_position(&index, &offset, &bvec);
@@ -984,7 +984,7 @@ static int zram_rw_page(struct block_device *bdev, sector_t sector,
bv.bv_len = PAGE_SIZE;
bv.bv_offset = 0;
- err = zram_bvec_rw(zram, &bv, index, offset, rw);
+ err = zram_bvec_rw(zram, &bv, index, offset, rw, rw ? REQ_WRITE : 0);
put_zram:
zram_meta_put(zram);
out:
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index 25fa844..3319ce1 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -609,7 +609,7 @@ static void request_endio(struct bio *bio)
static void bio_complete(struct search *s)
{
if (s->orig_bio) {
- generic_end_io_acct(bio_data_dir(s->orig_bio),
+ generic_end_io_acct(s->orig_bio->bi_rw,
&s->d->disk->part0, s->start_time);
trace_bcache_request_end(s->d, s->orig_bio);
@@ -964,9 +964,9 @@ static blk_qc_t cached_dev_make_request(struct request_queue *q,
struct search *s;
struct bcache_device *d = bio->bi_bdev->bd_disk->private_data;
struct cached_dev *dc = container_of(d, struct cached_dev, disk);
- int rw = bio_data_dir(bio);
+ const int rw = bio_data_dir(bio);
- generic_start_io_acct(rw, bio_sectors(bio), &d->disk->part0);
+ generic_start_io_acct(bio->bi_rw, bio_sectors(bio), &d->disk->part0);
bio->bi_bdev = dc->bdev;
bio->bi_iter.bi_sector += dc->sb.data_offset;
@@ -1079,9 +1079,9 @@ static blk_qc_t flash_dev_make_request(struct request_queue *q,
struct search *s;
struct closure *cl;
struct bcache_device *d = bio->bi_bdev->bd_disk->private_data;
- int rw = bio_data_dir(bio);
+ const int rw = bio_data_dir(bio);
- generic_start_io_acct(rw, bio_sectors(bio), &d->disk->part0);
+ generic_start_io_acct(bio->bi_rw, bio_sectors(bio), &d->disk->part0);
s = search_alloc(bio, d);
cl = &s->cl;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 1b2f962..883544a1 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -733,9 +733,9 @@ static void end_io_acct(struct dm_io *io)
struct bio *bio = io->bio;
unsigned long duration = jiffies - io->start_time;
int pending;
- int rw = bio_data_dir(bio);
+ const int rw = bio_data_dir(bio);
- generic_end_io_acct(rw, &dm_disk(md)->part0, io->start_time);
+ generic_end_io_acct(bio->bi_rw, &dm_disk(md)->part0, io->start_time);
if (unlikely(dm_stats_used(&md->stats)))
dm_stats_account_io(&md->stats, bio->bi_rw, bio->bi_iter.bi_sector,
@@ -1818,14 +1818,13 @@ static void __split_and_process_bio(struct mapped_device *md,
*/
static blk_qc_t dm_make_request(struct request_queue *q, struct bio *bio)
{
- int rw = bio_data_dir(bio);
struct mapped_device *md = q->queuedata;
int srcu_idx;
struct dm_table *map;
map = dm_get_live_table(md, &srcu_idx);
- generic_start_io_acct(rw, bio_sectors(bio), &dm_disk(md)->part0);
+ generic_start_io_acct(bio->bi_rw, bio_sectors(bio), &dm_disk(md)->part0);
/* if we're suspended, we have to queue this io for later */
if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags))) {
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 38f819c..fcd85f5 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -245,6 +245,7 @@ static DEFINE_SPINLOCK(all_mddevs_lock);
static blk_qc_t md_make_request(struct request_queue *q, struct bio *bio)
{
const int rw = bio_data_dir(bio);
+ const int sgrp = rw_stat_group(bio->bi_rw);
struct mddev *mddev = q->queuedata;
unsigned int sectors;
int cpu;
@@ -289,8 +290,8 @@ static blk_qc_t md_make_request(struct request_queue *q, struct bio *bio)
mddev->pers->make_request(mddev, bio);
cpu = part_stat_lock();
- part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]);
- part_stat_add(cpu, &mddev->gendisk->part0, sectors[rw], sectors);
+ part_stat_inc(cpu, &mddev->gendisk->part0, ios[sgrp]);
+ part_stat_add(cpu, &mddev->gendisk->part0, sectors[sgrp], sectors);
part_stat_unlock();
if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended)
diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c
index be89764..c1435a2 100644
--- a/drivers/nvdimm/core.c
+++ b/drivers/nvdimm/core.c
@@ -219,12 +219,13 @@ void __nd_iostat_start(struct bio *bio, unsigned long *start)
{
struct gendisk *disk = bio->bi_bdev->bd_disk;
const int rw = bio_data_dir(bio);
+ const int sgrp = rw_stat_group(bio->bi_rw);
int cpu = part_stat_lock();
*start = jiffies;
part_round_stats(cpu, &disk->part0);
- part_stat_inc(cpu, &disk->part0, ios[rw]);
- part_stat_add(cpu, &disk->part0, sectors[rw], bio_sectors(bio));
+ part_stat_inc(cpu, &disk->part0, ios[sgrp]);
+ part_stat_add(cpu, &disk->part0, sectors[sgrp], bio_sectors(bio));
part_inc_in_flight(&disk->part0, rw);
part_stat_unlock();
}
@@ -234,10 +235,11 @@ void nd_iostat_end(struct bio *bio, unsigned long start)
{
struct gendisk *disk = bio->bi_bdev->bd_disk;
unsigned long duration = jiffies - start;
+ const int sgrp = rw_stat_group(bio->bi_rw);
const int rw = bio_data_dir(bio);
int cpu = part_stat_lock();
- part_stat_add(cpu, &disk->part0, ticks[rw], duration);
+ part_stat_add(cpu, &disk->part0, ticks[sgrp], duration);
part_round_stats(cpu, &disk->part0);
part_dec_in_flight(&disk->part0, rw);
part_stat_unlock();
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 9faebf7..8bb1ca4 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -494,9 +494,9 @@ extern struct bio *bio_copy_kern(struct request_queue *, void *, unsigned int,
extern void bio_set_pages_dirty(struct bio *bio);
extern void bio_check_pages_dirty(struct bio *bio);
-void generic_start_io_acct(int rw, unsigned long sectors,
+void generic_start_io_acct(int rw_flags, unsigned long sectors,
struct hd_struct *part);
-void generic_end_io_acct(int rw, struct hd_struct *part,
+void generic_end_io_acct(int rw_flags, struct hd_struct *part,
unsigned long start_time);
#ifndef ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 17f1c09..b9250a3 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -417,6 +417,11 @@ static inline void free_part_info(struct hd_struct *part)
kfree(part->info);
}
+static inline int rw_stat_group(unsigned int rw_flags)
+{
+ return (rw_flags & REQ_WRITE) != 0;
+}
+
/* block/blk-core.c */
extern void part_round_stats(int cpu, struct hd_struct *part);
--
1.9.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 4/4] block: Track DISCARD statistics and output them in stat and diskstat.
2016-06-08 20:50 [PATCH 0/4] block: Separating discards from writes in Linux IO statistics Michael Callahan
` (2 preceding siblings ...)
2016-06-08 20:50 ` [PATCH 3/4] block: Add and use a rw_stat_group function for indexing disk_stat fields Michael Callahan
@ 2016-06-08 20:50 ` Michael Callahan
3 siblings, 0 replies; 5+ messages in thread
From: Michael Callahan @ 2016-06-08 20:50 UTC (permalink / raw)
To: linux-block; +Cc: Michael Callahan, axboe, coder.callahan, kernel-team
Add tracking of REQ_DISCARD ios to the partition statistics and append
them to the various stat files in /sys as well as /proc/diskstats.
These are tracked with the same four stats as reads and writes:
Number of discard ios completed.
Number of discard ios merged
Number of discard sectors completed
Milliseconds spent on discard requests
This is done via adding a new STAT_DISCARD define to genhd.h and then
using it to index that stat field for discard requests.
Signed-off-by: Michael Callahan <michaelcallahan@fb.com>
---
Documentation/ABI/testing/procfs-diskstats | 10 ++++++++++
Documentation/block/stat.txt | 28 ++++++++++++++++------------
Documentation/iostats.txt | 15 ++++++++++++++-
block/genhd.c | 14 +++++++++++---
block/partition-generic.c | 9 +++++++--
include/linux/genhd.h | 14 +++++++++-----
6 files changed, 67 insertions(+), 23 deletions(-)
diff --git a/Documentation/ABI/testing/procfs-diskstats b/Documentation/ABI/testing/procfs-diskstats
index f91a973..59616c6 100644
--- a/Documentation/ABI/testing/procfs-diskstats
+++ b/Documentation/ABI/testing/procfs-diskstats
@@ -5,6 +5,7 @@ Description:
The /proc/diskstats file displays the I/O statistics
of block devices. Each line contains the following 14
fields:
+
1 - major number
2 - minor mumber
3 - device name
@@ -19,4 +20,13 @@ Description:
12 - I/Os currently in progress
13 - time spent doing I/Os (ms)
14 - weighted time spent doing I/Os (ms)
+
+ Kernel 4.8 appends four more fields for discard tracking
+ putting the total at 18:
+
+ 15 - discards completed successfully
+ 16 - discards merged
+ 17 - sectors discarded
+ 18 - time spent discarding
+
For more details refer to Documentation/iostats.txt
diff --git a/Documentation/block/stat.txt b/Documentation/block/stat.txt
index 0dbc946..0aace9c 100644
--- a/Documentation/block/stat.txt
+++ b/Documentation/block/stat.txt
@@ -31,28 +31,32 @@ write ticks milliseconds total wait time for write requests
in_flight requests number of I/Os currently in flight
io_ticks milliseconds total time this block device has been active
time_in_queue milliseconds total wait time for all requests
+discard I/Os requests number of discard I/Os processed
+discard merges requests number of discard I/Os merged with in-queue I/O
+discard sectors sectors number of sectors discarded
+discard ticks milliseconds total wait time for discard requests
-read I/Os, write I/Os
-=====================
+read I/Os, write I/Os, discard I/0s
+===================================
These values increment when an I/O request completes.
-read merges, write merges
-=========================
+read merges, write merges, discard merges
+=========================================
These values increment when an I/O request is merged with an
already-queued I/O request.
-read sectors, write sectors
-===========================
+read sectors, write sectors, discard_sectors
+============================================
-These values count the number of sectors read from or written to this
-block device. The "sectors" in question are the standard UNIX 512-byte
-sectors, not any device- or filesystem-specific block size. The
-counters are incremented when the I/O completes.
+These values count the number of sectors read from, written to, or
+discarded from this block device. The "sectors" in question are the
+standard UNIX 512-byte sectors, not any device- or filesystem-specific
+block size. The counters are incremented when the I/O completes.
-read ticks, write ticks
-=======================
+read ticks, write ticks, discard ticks
+======================================
These values count the number of milliseconds that I/O requests have
waited on this block device. If there are multiple I/O requests waiting,
diff --git a/Documentation/iostats.txt b/Documentation/iostats.txt
index 65f694f..e6e2795 100644
--- a/Documentation/iostats.txt
+++ b/Documentation/iostats.txt
@@ -22,7 +22,6 @@ Here are examples of these different formats:
3 0 39082680 hda 446216 784926 9550688 4382310 424847 312726 5922052 19310380 0 3376340 23705160
3 1 9221278 hda1 35486 0 35496 38030 0 0 0 0 0 38030 38030
-
2.6 sysfs:
446216 784926 9550688 4382310 424847 312726 5922052 19310380 0 3376340 23705160
35486 38030 38030 38030
@@ -31,6 +30,9 @@ Here are examples of these different formats:
3 0 hda 446216 784926 9550688 4382310 424847 312726 5922052 19310380 0 3376340 23705160
3 1 hda1 35486 38030 38030 38030
+4.8 diskstats:
+ 3 0 hda 446216 784926 9550688 4382310 424847 312726 5922052 19310380 0 3376340 23705160 0 0 0 0
+
On 2.4 you might execute "grep 'hda ' /proc/partitions". On 2.6, you have
a choice of "cat /sys/block/hda/stat" or "grep 'hda ' /proc/diskstats".
The advantage of one over the other is that the sysfs choice works well
@@ -90,6 +92,17 @@ Field 11 -- weighted # of milliseconds spent doing I/Os
last update of this field. This can provide an easy measure of both
I/O completion time and the backlog that may be accumulating.
+In 4.8 the following four fields are appended for discard tracking:
+
+Field 12 -- # of discards completed
+ This is the total number of discards completed successfully.
+Field 13 -- # of discards merged
+ See the description of field 2
+Field 14 -- # of sectors discarded
+ This is the total number of sectors discarded successfully.
+Field 15 -- # of milliseconds spent discarding
+ This is the total number of milliseconds spent by all discards (as
+ measured from __make_request() to end_that_request_last()).
To avoid introducing performance bottlenecks, no locks are held while
modifying these counters. This implies that minor inaccuracies may be
diff --git a/block/genhd.c b/block/genhd.c
index b6f1dee..1b558dc 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1195,8 +1195,12 @@ static int diskstats_show(struct seq_file *seqf, void *v)
cpu = part_stat_lock();
part_round_stats(cpu, hd);
part_stat_unlock();
- seq_printf(seqf, "%4d %7d %s %lu %lu %lu "
- "%u %lu %lu %lu %u %u %u %u\n",
+ seq_printf(seqf, "%4d %7d %s "
+ "%lu %lu %lu %u "
+ "%lu %lu %lu %u "
+ "%u %u %u "
+ "%lu %lu %lu %u"
+ "\n",
MAJOR(part_devt(hd)), MINOR(part_devt(hd)),
disk_name(gp, hd->partno, buf),
part_stat_read(hd, ios[STAT_READ]),
@@ -1209,7 +1213,11 @@ static int diskstats_show(struct seq_file *seqf, void *v)
jiffies_to_msecs(part_stat_read(hd, ticks[STAT_WRITE])),
part_in_flight(hd),
jiffies_to_msecs(part_stat_read(hd, io_ticks)),
- jiffies_to_msecs(part_stat_read(hd, time_in_queue))
+ jiffies_to_msecs(part_stat_read(hd, time_in_queue)),
+ part_stat_read(hd, ios[STAT_DISCARD]),
+ part_stat_read(hd, merges[STAT_DISCARD]),
+ part_stat_read(hd, sectors[STAT_DISCARD]),
+ jiffies_to_msecs(part_stat_read(hd, ticks[STAT_DISCARD]))
);
}
disk_part_iter_exit(&piter);
diff --git a/block/partition-generic.c b/block/partition-generic.c
index 15bf298..0f18368 100644
--- a/block/partition-generic.c
+++ b/block/partition-generic.c
@@ -121,7 +121,8 @@ ssize_t part_stat_show(struct device *dev,
return sprintf(buf,
"%8lu %8lu %8llu %8u "
"%8lu %8lu %8llu %8u "
- "%8u %8u %8u"
+ "%8u %8u %8u "
+ "%8lu %8lu %8llu %8u"
"\n",
part_stat_read(p, ios[STAT_READ]),
part_stat_read(p, merges[STAT_READ]),
@@ -133,7 +134,11 @@ ssize_t part_stat_show(struct device *dev,
jiffies_to_msecs(part_stat_read(p, ticks[STAT_WRITE])),
part_in_flight(p),
jiffies_to_msecs(part_stat_read(p, io_ticks)),
- jiffies_to_msecs(part_stat_read(p, time_in_queue)));
+ jiffies_to_msecs(part_stat_read(p, time_in_queue)),
+ part_stat_read(p, ios[STAT_DISCARD]),
+ part_stat_read(p, merges[STAT_DISCARD]),
+ (unsigned long long)part_stat_read(p, sectors[STAT_DISCARD]),
+ jiffies_to_msecs(part_stat_read(p, ticks[STAT_DISCARD])));
}
ssize_t part_inflight_show(struct device *dev,
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index b9250a3..977b0e0 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -62,6 +62,7 @@ enum {
#define STAT_READ 0
#define STAT_WRITE 1
+#define STAT_DISCARD 2
#include <linux/major.h>
#include <linux/device.h>
@@ -84,10 +85,10 @@ struct partition {
} __attribute__((packed));
struct disk_stats {
- unsigned long sectors[2]; /* STAT_READs and STAT_WRITEs */
- unsigned long ios[2];
- unsigned long merges[2];
- unsigned long ticks[2];
+ unsigned long sectors[3]; /* STAT_READ, STAT_WRITE, STAT_DISCARD */
+ unsigned long ios[3];
+ unsigned long merges[3];
+ unsigned long ticks[3];
unsigned long io_ticks;
unsigned long time_in_queue;
};
@@ -369,7 +370,8 @@ static inline void free_part_stats(struct hd_struct *part)
#define part_stat_read_accum(part, field) \
(part_stat_read(part, field[STAT_READ]) + \
- part_stat_read(part, field[STAT_WRITE]))
+ part_stat_read(part, field[STAT_WRITE]) + \
+ part_stat_read(part, field[STAT_DISCARD]))
#define part_stat_add(cpu, part, field, addnd) do { \
__part_stat_add((cpu), (part), field, addnd); \
@@ -419,6 +421,8 @@ static inline void free_part_info(struct hd_struct *part)
static inline int rw_stat_group(unsigned int rw_flags)
{
+ if (rw_flags & REQ_DISCARD)
+ return STAT_DISCARD;
return (rw_flags & REQ_WRITE) != 0;
}
--
1.9.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2016-06-08 20:51 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-06-08 20:50 [PATCH 0/4] block: Separating discards from writes in Linux IO statistics Michael Callahan
2016-06-08 20:50 ` [PATCH 1/4] block: Add part_stat_read_accum to read across field entries Michael Callahan
2016-06-08 20:50 ` [PATCH 2/4] block: Define and use STAT_READ and STAT_WRITE Michael Callahan
2016-06-08 20:50 ` [PATCH 3/4] block: Add and use a rw_stat_group function for indexing disk_stat fields Michael Callahan
2016-06-08 20:50 ` [PATCH 4/4] block: Track DISCARD statistics and output them in stat and diskstat Michael Callahan
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).