aboutsummaryrefslogtreecommitdiffstats
path: root/block
diff options
context:
space:
mode:
authorJens Axboe <jens.axboe@oracle.com>2008-10-24 06:52:42 -0400
committerJens Axboe <jens.axboe@oracle.com>2008-12-29 02:29:51 -0500
commita6f23657d3072bde6844055bbc2290e497f33fbc (patch)
treebd96916615d04228cc9492ae198ed5012d5ee86a /block
parent30e0dc28bff9dc456cdfc2aae4aca78b8b1a1cec (diff)
block: add one-hit cache for disk partition lookup
disk_map_sector_rcu() returns a partition from a sector offset, which we use for IO statistics on a per-partition basis. The lookup itself is an O(N) list lookup, where N is the number of partitions. This actually hurts performance quite a bit, even on the lower end partitions. On higher numbered partitions, it can get pretty bad. Solve this by adding a one-hit cache for partition lookup. This makes the lookup O(1) for the case where we do most IO to one partition. Even for mixed partition workloads, amortized cost is pretty close to O(1) since the natural IO batching makes the one-hit cache last for lots of IOs. Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'block')
-rw-r--r--block/genhd.c23
1 files changed, 19 insertions, 4 deletions
diff --git a/block/genhd.c b/block/genhd.c
index 2f7feda61e35..d84a7df1e2a0 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -181,6 +181,12 @@ void disk_part_iter_exit(struct disk_part_iter *piter)
181} 181}
182EXPORT_SYMBOL_GPL(disk_part_iter_exit); 182EXPORT_SYMBOL_GPL(disk_part_iter_exit);
183 183
184static inline int sector_in_part(struct hd_struct *part, sector_t sector)
185{
186 return part->start_sect <= sector &&
187 sector < part->start_sect + part->nr_sects;
188}
189
184/** 190/**
185 * disk_map_sector_rcu - map sector to partition 191 * disk_map_sector_rcu - map sector to partition
186 * @disk: gendisk of interest 192 * @disk: gendisk of interest
@@ -199,16 +205,22 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit);
199struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) 205struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector)
200{ 206{
201 struct disk_part_tbl *ptbl; 207 struct disk_part_tbl *ptbl;
208 struct hd_struct *part;
202 int i; 209 int i;
203 210
204 ptbl = rcu_dereference(disk->part_tbl); 211 ptbl = rcu_dereference(disk->part_tbl);
205 212
213 part = rcu_dereference(ptbl->last_lookup);
214 if (part && sector_in_part(part, sector))
215 return part;
216
206 for (i = 1; i < ptbl->len; i++) { 217 for (i = 1; i < ptbl->len; i++) {
207 struct hd_struct *part = rcu_dereference(ptbl->part[i]); 218 part = rcu_dereference(ptbl->part[i]);
208 219
209 if (part && part->start_sect <= sector && 220 if (part && sector_in_part(part, sector)) {
210 sector < part->start_sect + part->nr_sects) 221 rcu_assign_pointer(ptbl->last_lookup, part);
211 return part; 222 return part;
223 }
212 } 224 }
213 return &disk->part0; 225 return &disk->part0;
214} 226}
@@ -888,8 +900,11 @@ static void disk_replace_part_tbl(struct gendisk *disk,
888 struct disk_part_tbl *old_ptbl = disk->part_tbl; 900 struct disk_part_tbl *old_ptbl = disk->part_tbl;
889 901
890 rcu_assign_pointer(disk->part_tbl, new_ptbl); 902 rcu_assign_pointer(disk->part_tbl, new_ptbl);
891 if (old_ptbl) 903
904 if (old_ptbl) {
905 rcu_assign_pointer(old_ptbl->last_lookup, NULL);
892 call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb); 906 call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb);
907 }
893} 908}
894 909
895/** 910/**