diff options
author | Tejun Heo <tj@kernel.org> | 2008-08-25 06:47:21 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2008-10-09 02:56:06 -0400 |
commit | c9959059161ddd7bf4670cf47367033d6b2f79c4 (patch) | |
tree | 6454db55f8e34361fe472358e10e0c5cfac1e366 /include/linux/genhd.h | |
parent | e71bf0d0ee89e51b92776391c5634938236977d5 (diff) |
block: fix diskstats access
There are two variants of stat functions - ones prefixed with double
underbars which don't care about preemption and ones without which
disable preemption before manipulating per-cpu counters. It's unclear
whether the underbarred ones assume that preemtion is disabled on
entry as some callers don't do that.
This patch unifies diskstats access by implementing disk_stat_lock()
and disk_stat_unlock() which take care of both RCU (for partition
access) and preemption (for per-cpu counter access). diskstats access
should always be enclosed between the two functions. As such, there's
no need for the versions which disables preemption. They're removed
and double underbars ones are renamed to drop the underbars. As an
extra argument is added, there's no danger of using the old version
unconverted.
disk_stat_lock() uses get_cpu() and returns the cpu index and all
diskstat functions which access per-cpu counters now has @cpu
argument to help RT.
This change adds RCU or preemption operations at some places but also
collapses several preemption ops into one at others. Overall, the
performance difference should be negligible as all involved ops are
very lightweight per-cpu ones.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'include/linux/genhd.h')
-rw-r--r-- | include/linux/genhd.h | 139 |
1 files changed, 57 insertions, 82 deletions
diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 7fbba19e076b..ac8a901f2002 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h | |||
@@ -209,16 +209,24 @@ extern void disk_part_iter_exit(struct disk_part_iter *piter); | |||
209 | extern struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, | 209 | extern struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, |
210 | sector_t sector); | 210 | sector_t sector); |
211 | 211 | ||
212 | /* | 212 | /* |
213 | * Macros to operate on percpu disk statistics: | 213 | * Macros to operate on percpu disk statistics: |
214 | * | 214 | * |
215 | * The __ variants should only be called in critical sections. The full | 215 | * {disk|part|all}_stat_{add|sub|inc|dec}() modify the stat counters |
216 | * variants disable/enable preemption. | 216 | * and should be called between disk_stat_lock() and |
217 | * disk_stat_unlock(). | ||
218 | * | ||
219 | * part_stat_read() can be called at any time. | ||
220 | * | ||
221 | * part_stat_{add|set_all}() and {init|free}_part_stats are for | ||
222 | * internal use only. | ||
217 | */ | 223 | */ |
218 | |||
219 | #ifdef CONFIG_SMP | 224 | #ifdef CONFIG_SMP |
220 | #define __disk_stat_add(gendiskp, field, addnd) \ | 225 | #define disk_stat_lock() ({ rcu_read_lock(); get_cpu(); }) |
221 | (per_cpu_ptr(gendiskp->dkstats, smp_processor_id())->field += addnd) | 226 | #define disk_stat_unlock() do { put_cpu(); rcu_read_unlock(); } while (0) |
227 | |||
228 | #define disk_stat_add(cpu, gendiskp, field, addnd) \ | ||
229 | (per_cpu_ptr(gendiskp->dkstats, cpu)->field += addnd) | ||
222 | 230 | ||
223 | #define disk_stat_read(gendiskp, field) \ | 231 | #define disk_stat_read(gendiskp, field) \ |
224 | ({ \ | 232 | ({ \ |
@@ -229,7 +237,8 @@ extern struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, | |||
229 | res; \ | 237 | res; \ |
230 | }) | 238 | }) |
231 | 239 | ||
232 | static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { | 240 | static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) |
241 | { | ||
233 | int i; | 242 | int i; |
234 | 243 | ||
235 | for_each_possible_cpu(i) | 244 | for_each_possible_cpu(i) |
@@ -237,14 +246,14 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) { | |||
237 | sizeof(struct disk_stats)); | 246 | sizeof(struct disk_stats)); |
238 | } | 247 | } |
239 | 248 | ||
240 | #define __part_stat_add(part, field, addnd) \ | 249 | #define part_stat_add(cpu, part, field, addnd) \ |
241 | (per_cpu_ptr(part->dkstats, smp_processor_id())->field += addnd) | 250 | (per_cpu_ptr(part->dkstats, cpu)->field += addnd) |
242 | 251 | ||
243 | #define __all_stat_add(gendiskp, part, field, addnd, sector) \ | 252 | #define all_stat_add(cpu, gendiskp, part, field, addnd, sector) \ |
244 | ({ \ | 253 | ({ \ |
245 | if (part) \ | 254 | if (part) \ |
246 | __part_stat_add(part, field, addnd); \ | 255 | part_stat_add(cpu, part, field, addnd); \ |
247 | __disk_stat_add(gendiskp, field, addnd); \ | 256 | disk_stat_add(cpu, gendiskp, field, addnd); \ |
248 | }) | 257 | }) |
249 | 258 | ||
250 | #define part_stat_read(part, field) \ | 259 | #define part_stat_read(part, field) \ |
@@ -264,10 +273,13 @@ static inline void part_stat_set_all(struct hd_struct *part, int value) | |||
264 | memset(per_cpu_ptr(part->dkstats, i), value, | 273 | memset(per_cpu_ptr(part->dkstats, i), value, |
265 | sizeof(struct disk_stats)); | 274 | sizeof(struct disk_stats)); |
266 | } | 275 | } |
267 | 276 | ||
268 | #else /* !CONFIG_SMP */ | 277 | #else /* !CONFIG_SMP */ |
269 | #define __disk_stat_add(gendiskp, field, addnd) \ | 278 | #define disk_stat_lock() ({ rcu_read_lock(); 0; }) |
270 | (gendiskp->dkstats.field += addnd) | 279 | #define disk_stat_unlock() rcu_read_unlock() |
280 | |||
281 | #define disk_stat_add(cpu, gendiskp, field, addnd) \ | ||
282 | (gendiskp->dkstats.field += addnd) | ||
271 | #define disk_stat_read(gendiskp, field) (gendiskp->dkstats.field) | 283 | #define disk_stat_read(gendiskp, field) (gendiskp->dkstats.field) |
272 | 284 | ||
273 | static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) | 285 | static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) |
@@ -275,14 +287,14 @@ static inline void disk_stat_set_all(struct gendisk *gendiskp, int value) | |||
275 | memset(&gendiskp->dkstats, value, sizeof (struct disk_stats)); | 287 | memset(&gendiskp->dkstats, value, sizeof (struct disk_stats)); |
276 | } | 288 | } |
277 | 289 | ||
278 | #define __part_stat_add(part, field, addnd) \ | 290 | #define part_stat_add(cpu, part, field, addnd) \ |
279 | (part->dkstats.field += addnd) | 291 | (part->dkstats.field += addnd) |
280 | 292 | ||
281 | #define __all_stat_add(gendiskp, part, field, addnd, sector) \ | 293 | #define all_stat_add(cpu, gendiskp, part, field, addnd, sector) \ |
282 | ({ \ | 294 | ({ \ |
283 | if (part) \ | 295 | if (part) \ |
284 | part->dkstats.field += addnd; \ | 296 | part_stat_add(cpu, part, field, addnd); \ |
285 | __disk_stat_add(gendiskp, field, addnd); \ | 297 | disk_stat_add(cpu, gendiskp, field, addnd); \ |
286 | }) | 298 | }) |
287 | 299 | ||
288 | #define part_stat_read(part, field) (part->dkstats.field) | 300 | #define part_stat_read(part, field) (part->dkstats.field) |
@@ -294,63 +306,26 @@ static inline void part_stat_set_all(struct hd_struct *part, int value) | |||
294 | 306 | ||
295 | #endif /* CONFIG_SMP */ | 307 | #endif /* CONFIG_SMP */ |
296 | 308 | ||
297 | #define disk_stat_add(gendiskp, field, addnd) \ | 309 | #define disk_stat_dec(cpu, gendiskp, field) \ |
298 | do { \ | 310 | disk_stat_add(cpu, gendiskp, field, -1) |
299 | preempt_disable(); \ | 311 | #define disk_stat_inc(cpu, gendiskp, field) \ |
300 | __disk_stat_add(gendiskp, field, addnd); \ | 312 | disk_stat_add(cpu, gendiskp, field, 1) |
301 | preempt_enable(); \ | 313 | #define disk_stat_sub(cpu, gendiskp, field, subnd) \ |
302 | } while (0) | 314 | disk_stat_add(cpu, gendiskp, field, -subnd) |
303 | 315 | ||
304 | #define __disk_stat_dec(gendiskp, field) __disk_stat_add(gendiskp, field, -1) | 316 | #define part_stat_dec(cpu, gendiskp, field) \ |
305 | #define disk_stat_dec(gendiskp, field) disk_stat_add(gendiskp, field, -1) | 317 | part_stat_add(cpu, gendiskp, field, -1) |
306 | 318 | #define part_stat_inc(cpu, gendiskp, field) \ | |
307 | #define __disk_stat_inc(gendiskp, field) __disk_stat_add(gendiskp, field, 1) | 319 | part_stat_add(cpu, gendiskp, field, 1) |
308 | #define disk_stat_inc(gendiskp, field) disk_stat_add(gendiskp, field, 1) | 320 | #define part_stat_sub(cpu, gendiskp, field, subnd) \ |
309 | 321 | part_stat_add(cpu, gendiskp, field, -subnd) | |
310 | #define __disk_stat_sub(gendiskp, field, subnd) \ | 322 | |
311 | __disk_stat_add(gendiskp, field, -subnd) | 323 | #define all_stat_dec(cpu, gendiskp, field, sector) \ |
312 | #define disk_stat_sub(gendiskp, field, subnd) \ | 324 | all_stat_add(cpu, gendiskp, field, -1, sector) |
313 | disk_stat_add(gendiskp, field, -subnd) | 325 | #define all_stat_inc(cpu, gendiskp, part, field, sector) \ |
314 | 326 | all_stat_add(cpu, gendiskp, part, field, 1, sector) | |
315 | #define part_stat_add(gendiskp, field, addnd) \ | 327 | #define all_stat_sub(cpu, gendiskp, part, field, subnd, sector) \ |
316 | do { \ | 328 | all_stat_add(cpu, gendiskp, part, field, -subnd, sector) |
317 | preempt_disable(); \ | ||
318 | __part_stat_add(gendiskp, field, addnd);\ | ||
319 | preempt_enable(); \ | ||
320 | } while (0) | ||
321 | |||
322 | #define __part_stat_dec(gendiskp, field) __part_stat_add(gendiskp, field, -1) | ||
323 | #define part_stat_dec(gendiskp, field) part_stat_add(gendiskp, field, -1) | ||
324 | |||
325 | #define __part_stat_inc(gendiskp, field) __part_stat_add(gendiskp, field, 1) | ||
326 | #define part_stat_inc(gendiskp, field) part_stat_add(gendiskp, field, 1) | ||
327 | |||
328 | #define __part_stat_sub(gendiskp, field, subnd) \ | ||
329 | __part_stat_add(gendiskp, field, -subnd) | ||
330 | #define part_stat_sub(gendiskp, field, subnd) \ | ||
331 | part_stat_add(gendiskp, field, -subnd) | ||
332 | |||
333 | #define all_stat_add(gendiskp, part, field, addnd, sector) \ | ||
334 | do { \ | ||
335 | preempt_disable(); \ | ||
336 | __all_stat_add(gendiskp, part, field, addnd, sector); \ | ||
337 | preempt_enable(); \ | ||
338 | } while (0) | ||
339 | |||
340 | #define __all_stat_dec(gendiskp, field, sector) \ | ||
341 | __all_stat_add(gendiskp, field, -1, sector) | ||
342 | #define all_stat_dec(gendiskp, field, sector) \ | ||
343 | all_stat_add(gendiskp, field, -1, sector) | ||
344 | |||
345 | #define __all_stat_inc(gendiskp, part, field, sector) \ | ||
346 | __all_stat_add(gendiskp, part, field, 1, sector) | ||
347 | #define all_stat_inc(gendiskp, part, field, sector) \ | ||
348 | all_stat_add(gendiskp, part, field, 1, sector) | ||
349 | |||
350 | #define __all_stat_sub(gendiskp, part, field, subnd, sector) \ | ||
351 | __all_stat_add(gendiskp, part, field, -subnd, sector) | ||
352 | #define all_stat_sub(gendiskp, part, field, subnd, sector) \ | ||
353 | all_stat_add(gendiskp, part, field, -subnd, sector) | ||
354 | 329 | ||
355 | /* Inlines to alloc and free disk stats in struct gendisk */ | 330 | /* Inlines to alloc and free disk stats in struct gendisk */ |
356 | #ifdef CONFIG_SMP | 331 | #ifdef CONFIG_SMP |
@@ -401,8 +376,8 @@ static inline void free_part_stats(struct hd_struct *part) | |||
401 | #endif /* CONFIG_SMP */ | 376 | #endif /* CONFIG_SMP */ |
402 | 377 | ||
403 | /* drivers/block/ll_rw_blk.c */ | 378 | /* drivers/block/ll_rw_blk.c */ |
404 | extern void disk_round_stats(struct gendisk *disk); | 379 | extern void disk_round_stats(int cpu, struct gendisk *disk); |
405 | extern void part_round_stats(struct hd_struct *part); | 380 | extern void part_round_stats(int cpu, struct hd_struct *part); |
406 | 381 | ||
407 | /* drivers/block/genhd.c */ | 382 | /* drivers/block/genhd.c */ |
408 | extern int get_blkdev_list(char *, int); | 383 | extern int get_blkdev_list(char *, int); |