Blame view

block/mq-deadline.c 20.8 KB
3dcf60bcb   Christoph Hellwig   block: add SPDX t...
1
  // SPDX-License-Identifier: GPL-2.0
945ffb60c   Jens Axboe   mq-deadline: add ...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   *  MQ Deadline i/o scheduler - adaptation of the legacy deadline scheduler,
   *  for the blk-mq scheduling framework
   *
   *  Copyright (C) 2016 Jens Axboe <axboe@kernel.dk>
   */
  #include <linux/kernel.h>
  #include <linux/fs.h>
  #include <linux/blkdev.h>
  #include <linux/blk-mq.h>
  #include <linux/elevator.h>
  #include <linux/bio.h>
  #include <linux/module.h>
  #include <linux/slab.h>
  #include <linux/init.h>
  #include <linux/compiler.h>
  #include <linux/rbtree.h>
  #include <linux/sbitmap.h>
  
  #include "blk.h"
  #include "blk-mq.h"
daaadb3e9   Omar Sandoval   mq-deadline: add ...
23
  #include "blk-mq-debugfs.h"
945ffb60c   Jens Axboe   mq-deadline: add ...
24
25
26
27
  #include "blk-mq-tag.h"
  #include "blk-mq-sched.h"
  
  /*
898bd37a9   Mauro Carvalho Chehab   docs: block: conv...
28
   * See Documentation/block/deadline-iosched.rst
945ffb60c   Jens Axboe   mq-deadline: add ...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
   */
  static const int read_expire = HZ / 2;  /* max time before a read is submitted. */
  static const int write_expire = 5 * HZ; /* ditto for writes, these limits are SOFT! */
  static const int writes_starved = 2;    /* max times reads can starve a write */
  static const int fifo_batch = 16;       /* # of sequential requests treated as one
  				     by the above parameters. For throughput. */
  
  struct deadline_data {
  	/*
  	 * run time data
  	 */
  
  	/*
  	 * requests (deadline_rq s) are present on both sort_list and fifo_list
  	 */
  	struct rb_root sort_list[2];
  	struct list_head fifo_list[2];
  
  	/*
  	 * next in sort order. read, write or both are NULL
  	 */
  	struct request *next_rq[2];
  	unsigned int batching;		/* number of sequential requests made */
  	unsigned int starved;		/* times reads have starved writes */
  
  	/*
  	 * settings that change how the i/o scheduler behaves
  	 */
  	int fifo_expire[2];
  	int fifo_batch;
  	int writes_starved;
  	int front_merges;
  
  	spinlock_t lock;
5700f6917   Damien Le Moal   mq-deadline: Intr...
63
  	spinlock_t zone_lock;
945ffb60c   Jens Axboe   mq-deadline: add ...
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  	struct list_head dispatch;
  };
  
  static inline struct rb_root *
  deadline_rb_root(struct deadline_data *dd, struct request *rq)
  {
  	return &dd->sort_list[rq_data_dir(rq)];
  }
  
  /*
   * get the request after `rq' in sector-sorted order
   */
  static inline struct request *
  deadline_latter_request(struct request *rq)
  {
  	struct rb_node *node = rb_next(&rq->rb_node);
  
  	if (node)
  		return rb_entry_rq(node);
  
  	return NULL;
  }
  
  static void
  deadline_add_rq_rb(struct deadline_data *dd, struct request *rq)
  {
  	struct rb_root *root = deadline_rb_root(dd, rq);
  
  	elv_rb_add(root, rq);
  }
  
  static inline void
  deadline_del_rq_rb(struct deadline_data *dd, struct request *rq)
  {
  	const int data_dir = rq_data_dir(rq);
  
  	if (dd->next_rq[data_dir] == rq)
  		dd->next_rq[data_dir] = deadline_latter_request(rq);
  
  	elv_rb_del(deadline_rb_root(dd, rq), rq);
  }
  
  /*
   * remove rq from rbtree and fifo.
   */
  static void deadline_remove_request(struct request_queue *q, struct request *rq)
  {
  	struct deadline_data *dd = q->elevator->elevator_data;
  
  	list_del_init(&rq->queuelist);
  
  	/*
  	 * We might not be on the rbtree, if we are doing an insert merge
  	 */
  	if (!RB_EMPTY_NODE(&rq->rb_node))
  		deadline_del_rq_rb(dd, rq);
  
  	elv_rqhash_del(q, rq);
  	if (q->last_merge == rq)
  		q->last_merge = NULL;
  }
  
  static void dd_request_merged(struct request_queue *q, struct request *req,
34fe7c054   Christoph Hellwig   block: enumify EL...
127
  			      enum elv_merge type)
945ffb60c   Jens Axboe   mq-deadline: add ...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  {
  	struct deadline_data *dd = q->elevator->elevator_data;
  
  	/*
  	 * if the merge was a front merge, we need to reposition request
  	 */
  	if (type == ELEVATOR_FRONT_MERGE) {
  		elv_rb_del(deadline_rb_root(dd, req), req);
  		deadline_add_rq_rb(dd, req);
  	}
  }
  
  static void dd_merged_requests(struct request_queue *q, struct request *req,
  			       struct request *next)
  {
  	/*
  	 * if next expires before rq, assign its expire time to rq
  	 * and move into next position (next will be deleted) in fifo
  	 */
  	if (!list_empty(&req->queuelist) && !list_empty(&next->queuelist)) {
  		if (time_before((unsigned long)next->fifo_time,
  				(unsigned long)req->fifo_time)) {
  			list_move(&req->queuelist, &next->queuelist);
  			req->fifo_time = next->fifo_time;
  		}
  	}
  
  	/*
  	 * kill knowledge of next, this one is a goner
  	 */
  	deadline_remove_request(q, next);
  }
  
  /*
   * move an entry to dispatch queue
   */
  static void
  deadline_move_request(struct deadline_data *dd, struct request *rq)
  {
  	const int data_dir = rq_data_dir(rq);
  
  	dd->next_rq[READ] = NULL;
  	dd->next_rq[WRITE] = NULL;
  	dd->next_rq[data_dir] = deadline_latter_request(rq);
  
  	/*
  	 * take it off the sort and fifo list
  	 */
  	deadline_remove_request(rq->q, rq);
  }
  
  /*
   * deadline_check_fifo returns 0 if there are no expired requests on the fifo,
   * 1 otherwise. Requires !list_empty(&dd->fifo_list[data_dir])
   */
  static inline int deadline_check_fifo(struct deadline_data *dd, int ddir)
  {
  	struct request *rq = rq_entry_fifo(dd->fifo_list[ddir].next);
  
  	/*
  	 * rq is expired!
  	 */
  	if (time_after_eq(jiffies, (unsigned long)rq->fifo_time))
  		return 1;
  
  	return 0;
  }
  
  /*
bf09ce56f   Damien Le Moal   mq-deadline: Intr...
197
198
199
200
201
202
   * For the specified data direction, return the next request to
   * dispatch using arrival ordered lists.
   */
  static struct request *
  deadline_fifo_request(struct deadline_data *dd, int data_dir)
  {
5700f6917   Damien Le Moal   mq-deadline: Intr...
203
204
  	struct request *rq;
  	unsigned long flags;
bf09ce56f   Damien Le Moal   mq-deadline: Intr...
205
206
207
208
209
  	if (WARN_ON_ONCE(data_dir != READ && data_dir != WRITE))
  		return NULL;
  
  	if (list_empty(&dd->fifo_list[data_dir]))
  		return NULL;
5700f6917   Damien Le Moal   mq-deadline: Intr...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  	rq = rq_entry_fifo(dd->fifo_list[data_dir].next);
  	if (data_dir == READ || !blk_queue_is_zoned(rq->q))
  		return rq;
  
  	/*
  	 * Look for a write request that can be dispatched, that is one with
  	 * an unlocked target zone.
  	 */
  	spin_lock_irqsave(&dd->zone_lock, flags);
  	list_for_each_entry(rq, &dd->fifo_list[WRITE], queuelist) {
  		if (blk_req_can_dispatch_to_zone(rq))
  			goto out;
  	}
  	rq = NULL;
  out:
  	spin_unlock_irqrestore(&dd->zone_lock, flags);
  
  	return rq;
bf09ce56f   Damien Le Moal   mq-deadline: Intr...
228
229
230
231
232
233
234
235
236
  }
  
  /*
   * For the specified data direction, return the next request to
   * dispatch using sector position sorted lists.
   */
  static struct request *
  deadline_next_request(struct deadline_data *dd, int data_dir)
  {
5700f6917   Damien Le Moal   mq-deadline: Intr...
237
238
  	struct request *rq;
  	unsigned long flags;
bf09ce56f   Damien Le Moal   mq-deadline: Intr...
239
240
  	if (WARN_ON_ONCE(data_dir != READ && data_dir != WRITE))
  		return NULL;
5700f6917   Damien Le Moal   mq-deadline: Intr...
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  	rq = dd->next_rq[data_dir];
  	if (!rq)
  		return NULL;
  
  	if (data_dir == READ || !blk_queue_is_zoned(rq->q))
  		return rq;
  
  	/*
  	 * Look for a write request that can be dispatched, that is one with
  	 * an unlocked target zone.
  	 */
  	spin_lock_irqsave(&dd->zone_lock, flags);
  	while (rq) {
  		if (blk_req_can_dispatch_to_zone(rq))
  			break;
  		rq = deadline_latter_request(rq);
  	}
  	spin_unlock_irqrestore(&dd->zone_lock, flags);
  
  	return rq;
bf09ce56f   Damien Le Moal   mq-deadline: Intr...
261
262
263
  }
  
  /*
945ffb60c   Jens Axboe   mq-deadline: add ...
264
265
266
   * deadline_dispatch_requests selects the best request according to
   * read/write expire, fifo_batch, etc
   */
ca11f209a   Jens Axboe   mq-deadline: make...
267
  static struct request *__dd_dispatch_request(struct deadline_data *dd)
945ffb60c   Jens Axboe   mq-deadline: add ...
268
  {
bf09ce56f   Damien Le Moal   mq-deadline: Intr...
269
  	struct request *rq, *next_rq;
945ffb60c   Jens Axboe   mq-deadline: add ...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
  	bool reads, writes;
  	int data_dir;
  
  	if (!list_empty(&dd->dispatch)) {
  		rq = list_first_entry(&dd->dispatch, struct request, queuelist);
  		list_del_init(&rq->queuelist);
  		goto done;
  	}
  
  	reads = !list_empty(&dd->fifo_list[READ]);
  	writes = !list_empty(&dd->fifo_list[WRITE]);
  
  	/*
  	 * batches are currently reads XOR writes
  	 */
bf09ce56f   Damien Le Moal   mq-deadline: Intr...
285
286
287
  	rq = deadline_next_request(dd, WRITE);
  	if (!rq)
  		rq = deadline_next_request(dd, READ);
945ffb60c   Jens Axboe   mq-deadline: add ...
288
289
290
291
292
293
294
295
296
297
298
299
  
  	if (rq && dd->batching < dd->fifo_batch)
  		/* we have a next request are still entitled to batch */
  		goto dispatch_request;
  
  	/*
  	 * at this point we are not running a batch. select the appropriate
  	 * data direction (read / write)
  	 */
  
  	if (reads) {
  		BUG_ON(RB_EMPTY_ROOT(&dd->sort_list[READ]));
5700f6917   Damien Le Moal   mq-deadline: Intr...
300
301
  		if (deadline_fifo_request(dd, WRITE) &&
  		    (dd->starved++ >= dd->writes_starved))
945ffb60c   Jens Axboe   mq-deadline: add ...
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  			goto dispatch_writes;
  
  		data_dir = READ;
  
  		goto dispatch_find_request;
  	}
  
  	/*
  	 * there are either no reads or writes have been starved
  	 */
  
  	if (writes) {
  dispatch_writes:
  		BUG_ON(RB_EMPTY_ROOT(&dd->sort_list[WRITE]));
  
  		dd->starved = 0;
  
  		data_dir = WRITE;
  
  		goto dispatch_find_request;
  	}
  
  	return NULL;
  
  dispatch_find_request:
  	/*
  	 * we are not running a batch, find best request for selected data_dir
  	 */
bf09ce56f   Damien Le Moal   mq-deadline: Intr...
330
331
  	next_rq = deadline_next_request(dd, data_dir);
  	if (deadline_check_fifo(dd, data_dir) || !next_rq) {
945ffb60c   Jens Axboe   mq-deadline: add ...
332
333
334
335
336
  		/*
  		 * A deadline has expired, the last request was in the other
  		 * direction, or we have run out of higher-sectored requests.
  		 * Start again from the request with the earliest expiry time.
  		 */
bf09ce56f   Damien Le Moal   mq-deadline: Intr...
337
  		rq = deadline_fifo_request(dd, data_dir);
945ffb60c   Jens Axboe   mq-deadline: add ...
338
339
340
341
342
  	} else {
  		/*
  		 * The last req was the same dir and we have a next request in
  		 * sort order. No expired requests so continue on from here.
  		 */
bf09ce56f   Damien Le Moal   mq-deadline: Intr...
343
  		rq = next_rq;
945ffb60c   Jens Axboe   mq-deadline: add ...
344
  	}
5700f6917   Damien Le Moal   mq-deadline: Intr...
345
346
347
348
349
350
  	/*
  	 * For a zoned block device, if we only have writes queued and none of
  	 * them can be dispatched, rq will be NULL.
  	 */
  	if (!rq)
  		return NULL;
945ffb60c   Jens Axboe   mq-deadline: add ...
351
352
353
354
355
356
357
358
359
  	dd->batching = 0;
  
  dispatch_request:
  	/*
  	 * rq is the selected appropriate request.
  	 */
  	dd->batching++;
  	deadline_move_request(dd, rq);
  done:
5700f6917   Damien Le Moal   mq-deadline: Intr...
360
361
362
363
  	/*
  	 * If the request needs its target zone locked, do it.
  	 */
  	blk_req_zone_write_lock(rq);
945ffb60c   Jens Axboe   mq-deadline: add ...
364
365
366
  	rq->rq_flags |= RQF_STARTED;
  	return rq;
  }
ca11f209a   Jens Axboe   mq-deadline: make...
367
368
  /*
   * One confusing aspect here is that we get called for a specific
7211aef86   Damien Le Moal   block: mq-deadlin...
369
   * hardware queue, but we may return a request that is for a
ca11f209a   Jens Axboe   mq-deadline: make...
370
371
372
   * different hardware queue. This is because mq-deadline has shared
   * state for all hardware queues, in terms of sorting, FIFOs, etc.
   */
c13660a08   Jens Axboe   blk-mq-sched: cha...
373
  static struct request *dd_dispatch_request(struct blk_mq_hw_ctx *hctx)
945ffb60c   Jens Axboe   mq-deadline: add ...
374
375
  {
  	struct deadline_data *dd = hctx->queue->elevator->elevator_data;
c13660a08   Jens Axboe   blk-mq-sched: cha...
376
  	struct request *rq;
945ffb60c   Jens Axboe   mq-deadline: add ...
377
378
  
  	spin_lock(&dd->lock);
ca11f209a   Jens Axboe   mq-deadline: make...
379
  	rq = __dd_dispatch_request(dd);
945ffb60c   Jens Axboe   mq-deadline: add ...
380
  	spin_unlock(&dd->lock);
b445547ec   Kashyap Desai   blk-mq, elevator:...
381
382
  	if (rq)
  		atomic_dec(&rq->mq_hctx->elevator_queued);
c13660a08   Jens Axboe   blk-mq-sched: cha...
383
384
  
  	return rq;
945ffb60c   Jens Axboe   mq-deadline: add ...
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  }
  
  static void dd_exit_queue(struct elevator_queue *e)
  {
  	struct deadline_data *dd = e->elevator_data;
  
  	BUG_ON(!list_empty(&dd->fifo_list[READ]));
  	BUG_ON(!list_empty(&dd->fifo_list[WRITE]));
  
  	kfree(dd);
  }
  
  /*
   * initialize elevator private data (deadline_data).
   */
  static int dd_init_queue(struct request_queue *q, struct elevator_type *e)
  {
  	struct deadline_data *dd;
  	struct elevator_queue *eq;
  
  	eq = elevator_alloc(q, e);
  	if (!eq)
  		return -ENOMEM;
  
  	dd = kzalloc_node(sizeof(*dd), GFP_KERNEL, q->node);
  	if (!dd) {
  		kobject_put(&eq->kobj);
  		return -ENOMEM;
  	}
  	eq->elevator_data = dd;
  
  	INIT_LIST_HEAD(&dd->fifo_list[READ]);
  	INIT_LIST_HEAD(&dd->fifo_list[WRITE]);
  	dd->sort_list[READ] = RB_ROOT;
  	dd->sort_list[WRITE] = RB_ROOT;
  	dd->fifo_expire[READ] = read_expire;
  	dd->fifo_expire[WRITE] = write_expire;
  	dd->writes_starved = writes_starved;
  	dd->front_merges = 1;
  	dd->fifo_batch = fifo_batch;
  	spin_lock_init(&dd->lock);
5700f6917   Damien Le Moal   mq-deadline: Intr...
426
  	spin_lock_init(&dd->zone_lock);
945ffb60c   Jens Axboe   mq-deadline: add ...
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
  	INIT_LIST_HEAD(&dd->dispatch);
  
  	q->elevator = eq;
  	return 0;
  }
  
  static int dd_request_merge(struct request_queue *q, struct request **rq,
  			    struct bio *bio)
  {
  	struct deadline_data *dd = q->elevator->elevator_data;
  	sector_t sector = bio_end_sector(bio);
  	struct request *__rq;
  
  	if (!dd->front_merges)
  		return ELEVATOR_NO_MERGE;
  
  	__rq = elv_rb_find(&dd->sort_list[bio_data_dir(bio)], sector);
  	if (__rq) {
  		BUG_ON(sector != blk_rq_pos(__rq));
  
  		if (elv_bio_merge_ok(__rq, bio)) {
  			*rq = __rq;
  			return ELEVATOR_FRONT_MERGE;
  		}
  	}
  
  	return ELEVATOR_NO_MERGE;
  }
14ccb66b3   Christoph Hellwig   block: remove the...
455
456
  static bool dd_bio_merge(struct blk_mq_hw_ctx *hctx, struct bio *bio,
  		unsigned int nr_segs)
945ffb60c   Jens Axboe   mq-deadline: add ...
457
458
459
  {
  	struct request_queue *q = hctx->queue;
  	struct deadline_data *dd = q->elevator->elevator_data;
e4d750c97   Jens Axboe   block: free merge...
460
461
  	struct request *free = NULL;
  	bool ret;
945ffb60c   Jens Axboe   mq-deadline: add ...
462
463
  
  	spin_lock(&dd->lock);
14ccb66b3   Christoph Hellwig   block: remove the...
464
  	ret = blk_mq_sched_try_merge(q, bio, nr_segs, &free);
945ffb60c   Jens Axboe   mq-deadline: add ...
465
  	spin_unlock(&dd->lock);
e4d750c97   Jens Axboe   block: free merge...
466
467
  	if (free)
  		blk_mq_free_request(free);
945ffb60c   Jens Axboe   mq-deadline: add ...
468
469
470
471
472
473
474
475
476
477
478
479
  	return ret;
  }
  
  /*
   * add rq to rbtree and fifo
   */
  static void dd_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,
  			      bool at_head)
  {
  	struct request_queue *q = hctx->queue;
  	struct deadline_data *dd = q->elevator->elevator_data;
  	const int data_dir = rq_data_dir(rq);
5700f6917   Damien Le Moal   mq-deadline: Intr...
480
481
482
483
484
  	/*
  	 * This may be a requeue of a write request that has locked its
  	 * target zone. If it is the case, this releases the zone lock.
  	 */
  	blk_req_zone_write_unlock(rq);
945ffb60c   Jens Axboe   mq-deadline: add ...
485
486
487
488
  	if (blk_mq_sched_try_insert_merge(q, rq))
  		return;
  
  	blk_mq_sched_request_inserted(rq);
57292b58d   Christoph Hellwig   block: introduce ...
489
  	if (at_head || blk_rq_is_passthrough(rq)) {
945ffb60c   Jens Axboe   mq-deadline: add ...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
  		if (at_head)
  			list_add(&rq->queuelist, &dd->dispatch);
  		else
  			list_add_tail(&rq->queuelist, &dd->dispatch);
  	} else {
  		deadline_add_rq_rb(dd, rq);
  
  		if (rq_mergeable(rq)) {
  			elv_rqhash_add(q, rq);
  			if (!q->last_merge)
  				q->last_merge = rq;
  		}
  
  		/*
  		 * set expire time and add to fifo list
  		 */
  		rq->fifo_time = jiffies + dd->fifo_expire[data_dir];
  		list_add_tail(&rq->queuelist, &dd->fifo_list[data_dir]);
  	}
  }
  
  static void dd_insert_requests(struct blk_mq_hw_ctx *hctx,
  			       struct list_head *list, bool at_head)
  {
  	struct request_queue *q = hctx->queue;
  	struct deadline_data *dd = q->elevator->elevator_data;
  
  	spin_lock(&dd->lock);
  	while (!list_empty(list)) {
  		struct request *rq;
  
  		rq = list_first_entry(list, struct request, queuelist);
  		list_del_init(&rq->queuelist);
  		dd_insert_request(hctx, rq, at_head);
b445547ec   Kashyap Desai   blk-mq, elevator:...
524
  		atomic_inc(&hctx->elevator_queued);
945ffb60c   Jens Axboe   mq-deadline: add ...
525
526
527
  	}
  	spin_unlock(&dd->lock);
  }
5700f6917   Damien Le Moal   mq-deadline: Intr...
528
  /*
f3bc78d2d   Damien Le Moal   mq-deadline: Make...
529
530
531
   * Nothing to do here. This is defined only to ensure that .finish_request
   * method is called upon request completion.
   */
5d9c305b8   Christoph Hellwig   blk-mq: remove th...
532
  static void dd_prepare_request(struct request *rq)
f3bc78d2d   Damien Le Moal   mq-deadline: Make...
533
534
535
536
  {
  }
  
  /*
5700f6917   Damien Le Moal   mq-deadline: Intr...
537
538
539
   * For zoned block devices, write unlock the target zone of
   * completed write requests. Do this while holding the zone lock
   * spinlock so that the zone is never unlocked while deadline_fifo_request()
f3bc78d2d   Damien Le Moal   mq-deadline: Make...
540
541
   * or deadline_next_request() are executing. This function is called for
   * all requests, whether or not these requests complete successfully.
cb8acabbe   Damien Le Moal   block: mq-deadlin...
542
543
544
545
546
547
548
   *
   * For a zoned block device, __dd_dispatch_request() may have stopped
   * dispatching requests if all the queued requests are write requests directed
   * at zones that are already locked due to on-going write requests. To ensure
   * write request dispatch progress in this case, mark the queue as needing a
   * restart to ensure that the queue is run again after completion of the
   * request and zones being unlocked.
5700f6917   Damien Le Moal   mq-deadline: Intr...
549
   */
f3bc78d2d   Damien Le Moal   mq-deadline: Make...
550
  static void dd_finish_request(struct request *rq)
5700f6917   Damien Le Moal   mq-deadline: Intr...
551
552
553
554
555
556
557
558
559
  {
  	struct request_queue *q = rq->q;
  
  	if (blk_queue_is_zoned(q)) {
  		struct deadline_data *dd = q->elevator->elevator_data;
  		unsigned long flags;
  
  		spin_lock_irqsave(&dd->zone_lock, flags);
  		blk_req_zone_write_unlock(rq);
cb8acabbe   Damien Le Moal   block: mq-deadlin...
560
561
  		if (!list_empty(&dd->fifo_list[WRITE]))
  			blk_mq_sched_mark_restart_hctx(rq->mq_hctx);
5700f6917   Damien Le Moal   mq-deadline: Intr...
562
563
564
  		spin_unlock_irqrestore(&dd->zone_lock, flags);
  	}
  }
945ffb60c   Jens Axboe   mq-deadline: add ...
565
566
567
  static bool dd_has_work(struct blk_mq_hw_ctx *hctx)
  {
  	struct deadline_data *dd = hctx->queue->elevator->elevator_data;
b445547ec   Kashyap Desai   blk-mq, elevator:...
568
569
  	if (!atomic_read(&hctx->elevator_queued))
  		return false;
945ffb60c   Jens Axboe   mq-deadline: add ...
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  	return !list_empty_careful(&dd->dispatch) ||
  		!list_empty_careful(&dd->fifo_list[0]) ||
  		!list_empty_careful(&dd->fifo_list[1]);
  }
  
  /*
   * sysfs parts below
   */
  static ssize_t
  deadline_var_show(int var, char *page)
  {
  	return sprintf(page, "%d
  ", var);
  }
235f8da11   weiping zhang   block, scheduler:...
584
585
  static void
  deadline_var_store(int *var, const char *page)
945ffb60c   Jens Axboe   mq-deadline: add ...
586
587
588
589
  {
  	char *p = (char *) page;
  
  	*var = simple_strtol(p, &p, 10);
945ffb60c   Jens Axboe   mq-deadline: add ...
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
  }
  
  #define SHOW_FUNCTION(__FUNC, __VAR, __CONV)				\
  static ssize_t __FUNC(struct elevator_queue *e, char *page)		\
  {									\
  	struct deadline_data *dd = e->elevator_data;			\
  	int __data = __VAR;						\
  	if (__CONV)							\
  		__data = jiffies_to_msecs(__data);			\
  	return deadline_var_show(__data, (page));			\
  }
  SHOW_FUNCTION(deadline_read_expire_show, dd->fifo_expire[READ], 1);
  SHOW_FUNCTION(deadline_write_expire_show, dd->fifo_expire[WRITE], 1);
  SHOW_FUNCTION(deadline_writes_starved_show, dd->writes_starved, 0);
  SHOW_FUNCTION(deadline_front_merges_show, dd->front_merges, 0);
  SHOW_FUNCTION(deadline_fifo_batch_show, dd->fifo_batch, 0);
  #undef SHOW_FUNCTION
  
  #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV)			\
  static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count)	\
  {									\
  	struct deadline_data *dd = e->elevator_data;			\
  	int __data;							\
235f8da11   weiping zhang   block, scheduler:...
613
  	deadline_var_store(&__data, (page));				\
945ffb60c   Jens Axboe   mq-deadline: add ...
614
615
616
617
618
619
620
621
  	if (__data < (MIN))						\
  		__data = (MIN);						\
  	else if (__data > (MAX))					\
  		__data = (MAX);						\
  	if (__CONV)							\
  		*(__PTR) = msecs_to_jiffies(__data);			\
  	else								\
  		*(__PTR) = __data;					\
235f8da11   weiping zhang   block, scheduler:...
622
  	return count;							\
945ffb60c   Jens Axboe   mq-deadline: add ...
623
624
625
626
627
628
629
630
631
  }
  STORE_FUNCTION(deadline_read_expire_store, &dd->fifo_expire[READ], 0, INT_MAX, 1);
  STORE_FUNCTION(deadline_write_expire_store, &dd->fifo_expire[WRITE], 0, INT_MAX, 1);
  STORE_FUNCTION(deadline_writes_starved_store, &dd->writes_starved, INT_MIN, INT_MAX, 0);
  STORE_FUNCTION(deadline_front_merges_store, &dd->front_merges, 0, 1, 0);
  STORE_FUNCTION(deadline_fifo_batch_store, &dd->fifo_batch, 0, INT_MAX, 0);
  #undef STORE_FUNCTION
  
  #define DD_ATTR(name) \
5657a819a   Joe Perches   block drivers/blo...
632
  	__ATTR(name, 0644, deadline_##name##_show, deadline_##name##_store)
945ffb60c   Jens Axboe   mq-deadline: add ...
633
634
635
636
637
638
639
640
641
  
  static struct elv_fs_entry deadline_attrs[] = {
  	DD_ATTR(read_expire),
  	DD_ATTR(write_expire),
  	DD_ATTR(writes_starved),
  	DD_ATTR(front_merges),
  	DD_ATTR(fifo_batch),
  	__ATTR_NULL
  };
daaadb3e9   Omar Sandoval   mq-deadline: add ...
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
  #ifdef CONFIG_BLK_DEBUG_FS
  #define DEADLINE_DEBUGFS_DDIR_ATTRS(ddir, name)				\
  static void *deadline_##name##_fifo_start(struct seq_file *m,		\
  					  loff_t *pos)			\
  	__acquires(&dd->lock)						\
  {									\
  	struct request_queue *q = m->private;				\
  	struct deadline_data *dd = q->elevator->elevator_data;		\
  									\
  	spin_lock(&dd->lock);						\
  	return seq_list_start(&dd->fifo_list[ddir], *pos);		\
  }									\
  									\
  static void *deadline_##name##_fifo_next(struct seq_file *m, void *v,	\
  					 loff_t *pos)			\
  {									\
  	struct request_queue *q = m->private;				\
  	struct deadline_data *dd = q->elevator->elevator_data;		\
  									\
  	return seq_list_next(v, &dd->fifo_list[ddir], pos);		\
  }									\
  									\
  static void deadline_##name##_fifo_stop(struct seq_file *m, void *v)	\
  	__releases(&dd->lock)						\
  {									\
  	struct request_queue *q = m->private;				\
  	struct deadline_data *dd = q->elevator->elevator_data;		\
  									\
  	spin_unlock(&dd->lock);						\
  }									\
  									\
  static const struct seq_operations deadline_##name##_fifo_seq_ops = {	\
  	.start	= deadline_##name##_fifo_start,				\
  	.next	= deadline_##name##_fifo_next,				\
  	.stop	= deadline_##name##_fifo_stop,				\
  	.show	= blk_mq_debugfs_rq_show,				\
  };									\
  									\
  static int deadline_##name##_next_rq_show(void *data,			\
  					  struct seq_file *m)		\
  {									\
  	struct request_queue *q = data;					\
  	struct deadline_data *dd = q->elevator->elevator_data;		\
  	struct request *rq = dd->next_rq[ddir];				\
  									\
  	if (rq)								\
  		__blk_mq_debugfs_rq_show(m, rq);			\
  	return 0;							\
  }
  DEADLINE_DEBUGFS_DDIR_ATTRS(READ, read)
  DEADLINE_DEBUGFS_DDIR_ATTRS(WRITE, write)
  #undef DEADLINE_DEBUGFS_DDIR_ATTRS
  
  static int deadline_batching_show(void *data, struct seq_file *m)
  {
  	struct request_queue *q = data;
  	struct deadline_data *dd = q->elevator->elevator_data;
  
  	seq_printf(m, "%u
  ", dd->batching);
  	return 0;
  }
  
  static int deadline_starved_show(void *data, struct seq_file *m)
  {
  	struct request_queue *q = data;
  	struct deadline_data *dd = q->elevator->elevator_data;
  
  	seq_printf(m, "%u
  ", dd->starved);
  	return 0;
  }
  
  static void *deadline_dispatch_start(struct seq_file *m, loff_t *pos)
  	__acquires(&dd->lock)
  {
  	struct request_queue *q = m->private;
  	struct deadline_data *dd = q->elevator->elevator_data;
  
  	spin_lock(&dd->lock);
  	return seq_list_start(&dd->dispatch, *pos);
  }
  
  static void *deadline_dispatch_next(struct seq_file *m, void *v, loff_t *pos)
  {
  	struct request_queue *q = m->private;
  	struct deadline_data *dd = q->elevator->elevator_data;
  
  	return seq_list_next(v, &dd->dispatch, pos);
  }
  
  static void deadline_dispatch_stop(struct seq_file *m, void *v)
  	__releases(&dd->lock)
  {
  	struct request_queue *q = m->private;
  	struct deadline_data *dd = q->elevator->elevator_data;
  
  	spin_unlock(&dd->lock);
  }
  
  static const struct seq_operations deadline_dispatch_seq_ops = {
  	.start	= deadline_dispatch_start,
  	.next	= deadline_dispatch_next,
  	.stop	= deadline_dispatch_stop,
  	.show	= blk_mq_debugfs_rq_show,
  };
  
  #define DEADLINE_QUEUE_DDIR_ATTRS(name)						\
  	{#name "_fifo_list", 0400, .seq_ops = &deadline_##name##_fifo_seq_ops},	\
  	{#name "_next_rq", 0400, deadline_##name##_next_rq_show}
  static const struct blk_mq_debugfs_attr deadline_queue_debugfs_attrs[] = {
  	DEADLINE_QUEUE_DDIR_ATTRS(read),
  	DEADLINE_QUEUE_DDIR_ATTRS(write),
  	{"batching", 0400, deadline_batching_show},
  	{"starved", 0400, deadline_starved_show},
  	{"dispatch", 0400, .seq_ops = &deadline_dispatch_seq_ops},
  	{},
  };
  #undef DEADLINE_QUEUE_DDIR_ATTRS
  #endif
945ffb60c   Jens Axboe   mq-deadline: add ...
762
  static struct elevator_type mq_deadline = {
f9cd4bfe9   Jens Axboe   block: get rid of...
763
  	.ops = {
945ffb60c   Jens Axboe   mq-deadline: add ...
764
  		.insert_requests	= dd_insert_requests,
c13660a08   Jens Axboe   blk-mq-sched: cha...
765
  		.dispatch_request	= dd_dispatch_request,
f3bc78d2d   Damien Le Moal   mq-deadline: Make...
766
767
  		.prepare_request	= dd_prepare_request,
  		.finish_request		= dd_finish_request,
945ffb60c   Jens Axboe   mq-deadline: add ...
768
769
770
771
772
773
774
775
776
777
  		.next_request		= elv_rb_latter_request,
  		.former_request		= elv_rb_former_request,
  		.bio_merge		= dd_bio_merge,
  		.request_merge		= dd_request_merge,
  		.requests_merged	= dd_merged_requests,
  		.request_merged		= dd_request_merged,
  		.has_work		= dd_has_work,
  		.init_sched		= dd_init_queue,
  		.exit_sched		= dd_exit_queue,
  	},
daaadb3e9   Omar Sandoval   mq-deadline: add ...
778
779
780
  #ifdef CONFIG_BLK_DEBUG_FS
  	.queue_debugfs_attrs = deadline_queue_debugfs_attrs,
  #endif
945ffb60c   Jens Axboe   mq-deadline: add ...
781
782
  	.elevator_attrs = deadline_attrs,
  	.elevator_name = "mq-deadline",
4d740bc9f   Jens Axboe   mq-deadline: add ...
783
  	.elevator_alias = "deadline",
68c43f133   Damien Le Moal   block: Introduce ...
784
  	.elevator_features = ELEVATOR_F_ZBD_SEQ_WRITE,
945ffb60c   Jens Axboe   mq-deadline: add ...
785
786
  	.elevator_owner = THIS_MODULE,
  };
7de967e76   Ben Hutchings   mq-deadline: Enab...
787
  MODULE_ALIAS("mq-deadline-iosched");
945ffb60c   Jens Axboe   mq-deadline: add ...
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
  
  static int __init deadline_init(void)
  {
  	return elv_register(&mq_deadline);
  }
  
  static void __exit deadline_exit(void)
  {
  	elv_unregister(&mq_deadline);
  }
  
  module_init(deadline_init);
  module_exit(deadline_exit);
  
  MODULE_AUTHOR("Jens Axboe");
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("MQ deadline IO scheduler");