mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-25 19:31:09 +07:00
556ee818c0
request_queue bypassing is used to suppress higher-level function of a request_queue so that they can be switched, reconfigured and shut down. A request_queue does the followings while bypassing. * bypasses elevator and io_cq association and queues requests directly to the FIFO dispatch queue. * bypasses block cgroup request_list lookup and always uses the root request_list. Once confirmed to be bypassing, specific elevator and block cgroup policy implementations can assume that nothing is in flight for them and perform various operations which would be dangerous otherwise. Such confirmation is acheived by short-circuiting all new requests directly to the dispatch queue and waiting for all the requests which were issued before to finish. Unfortunately, while the request allocating and draining sides were properly handled, we forgot to actually plug the request dispatch path. Even after bypassing mode is confirmed, if the attached driver tries to fetch a request and the dispatch queue is empty, __elv_next_request() would invoke the current elevator's elevator_dispatch_fn() callback. As all in-flight requests were drained, the elevator wouldn't contain any request but once bypass is confirmed we don't even know whether the elevator is even there. It might be in the process of being switched and half torn down. Frank Mayhar reports that this actually happened while switching elevators, leading to an oops. Let's fix it by making __elv_next_request() avoid invoking the elevator_dispatch_fn() callback if the queue is bypassing. It already avoids invoking the callback if the queue is dying. As a dying queue is guaranteed to be bypassing, we can simply replace blk_queue_dying() check with blk_queue_bypass(). Reported-by: Frank Mayhar <fmayhar@google.com> References: http://lkml.kernel.org/g/1390319905.20232.38.camel@bobble.lax.corp.google.com Cc: stable@vger.kernel.org Tested-by: Frank Mayhar <fmayhar@google.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
252 lines
7.8 KiB
C
252 lines
7.8 KiB
C
#ifndef BLK_INTERNAL_H
|
|
#define BLK_INTERNAL_H
|
|
|
|
#include <linux/idr.h>
|
|
|
|
/* Amount of time in which a process may batch requests */
|
|
#define BLK_BATCH_TIME (HZ/50UL)
|
|
|
|
/* Number of requests a "batching" process may submit */
|
|
#define BLK_BATCH_REQ 32
|
|
|
|
extern struct kmem_cache *blk_requestq_cachep;
|
|
extern struct kmem_cache *request_cachep;
|
|
extern struct kobj_type blk_queue_ktype;
|
|
extern struct ida blk_queue_ida;
|
|
|
|
static inline void __blk_get_queue(struct request_queue *q)
|
|
{
|
|
kobject_get(&q->kobj);
|
|
}
|
|
|
|
int blk_init_rl(struct request_list *rl, struct request_queue *q,
|
|
gfp_t gfp_mask);
|
|
void blk_exit_rl(struct request_list *rl);
|
|
void init_request_from_bio(struct request *req, struct bio *bio);
|
|
void blk_rq_bio_prep(struct request_queue *q, struct request *rq,
|
|
struct bio *bio);
|
|
int blk_rq_append_bio(struct request_queue *q, struct request *rq,
|
|
struct bio *bio);
|
|
void blk_queue_bypass_start(struct request_queue *q);
|
|
void blk_queue_bypass_end(struct request_queue *q);
|
|
void blk_dequeue_request(struct request *rq);
|
|
void __blk_queue_free_tags(struct request_queue *q);
|
|
bool __blk_end_bidi_request(struct request *rq, int error,
|
|
unsigned int nr_bytes, unsigned int bidi_bytes);
|
|
|
|
void blk_rq_timed_out_timer(unsigned long data);
|
|
void blk_rq_check_expired(struct request *rq, unsigned long *next_timeout,
|
|
unsigned int *next_set);
|
|
void __blk_add_timer(struct request *req, struct list_head *timeout_list);
|
|
void blk_delete_timer(struct request *);
|
|
void blk_add_timer(struct request *);
|
|
|
|
|
|
bool bio_attempt_front_merge(struct request_queue *q, struct request *req,
|
|
struct bio *bio);
|
|
bool bio_attempt_back_merge(struct request_queue *q, struct request *req,
|
|
struct bio *bio);
|
|
bool blk_attempt_plug_merge(struct request_queue *q, struct bio *bio,
|
|
unsigned int *request_count);
|
|
|
|
void blk_account_io_start(struct request *req, bool new_io);
|
|
void blk_account_io_completion(struct request *req, unsigned int bytes);
|
|
void blk_account_io_done(struct request *req);
|
|
|
|
/*
|
|
* Internal atomic flags for request handling
|
|
*/
|
|
enum rq_atomic_flags {
|
|
REQ_ATOM_COMPLETE = 0,
|
|
REQ_ATOM_STARTED,
|
|
};
|
|
|
|
/*
|
|
* EH timer and IO completion will both attempt to 'grab' the request, make
|
|
* sure that only one of them succeeds
|
|
*/
|
|
static inline int blk_mark_rq_complete(struct request *rq)
|
|
{
|
|
return test_and_set_bit(REQ_ATOM_COMPLETE, &rq->atomic_flags);
|
|
}
|
|
|
|
static inline void blk_clear_rq_complete(struct request *rq)
|
|
{
|
|
clear_bit(REQ_ATOM_COMPLETE, &rq->atomic_flags);
|
|
}
|
|
|
|
/*
|
|
* Internal elevator interface
|
|
*/
|
|
#define ELV_ON_HASH(rq) hash_hashed(&(rq)->hash)
|
|
|
|
void blk_insert_flush(struct request *rq);
|
|
void blk_abort_flushes(struct request_queue *q);
|
|
|
|
static inline struct request *__elv_next_request(struct request_queue *q)
|
|
{
|
|
struct request *rq;
|
|
|
|
while (1) {
|
|
if (!list_empty(&q->queue_head)) {
|
|
rq = list_entry_rq(q->queue_head.next);
|
|
return rq;
|
|
}
|
|
|
|
/*
|
|
* Flush request is running and flush request isn't queueable
|
|
* in the drive, we can hold the queue till flush request is
|
|
* finished. Even we don't do this, driver can't dispatch next
|
|
* requests and will requeue them. And this can improve
|
|
* throughput too. For example, we have request flush1, write1,
|
|
* flush 2. flush1 is dispatched, then queue is hold, write1
|
|
* isn't inserted to queue. After flush1 is finished, flush2
|
|
* will be dispatched. Since disk cache is already clean,
|
|
* flush2 will be finished very soon, so looks like flush2 is
|
|
* folded to flush1.
|
|
* Since the queue is hold, a flag is set to indicate the queue
|
|
* should be restarted later. Please see flush_end_io() for
|
|
* details.
|
|
*/
|
|
if (q->flush_pending_idx != q->flush_running_idx &&
|
|
!queue_flush_queueable(q)) {
|
|
q->flush_queue_delayed = 1;
|
|
return NULL;
|
|
}
|
|
if (unlikely(blk_queue_bypass(q)) ||
|
|
!q->elevator->type->ops.elevator_dispatch_fn(q, 0))
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static inline void elv_activate_rq(struct request_queue *q, struct request *rq)
|
|
{
|
|
struct elevator_queue *e = q->elevator;
|
|
|
|
if (e->type->ops.elevator_activate_req_fn)
|
|
e->type->ops.elevator_activate_req_fn(q, rq);
|
|
}
|
|
|
|
static inline void elv_deactivate_rq(struct request_queue *q, struct request *rq)
|
|
{
|
|
struct elevator_queue *e = q->elevator;
|
|
|
|
if (e->type->ops.elevator_deactivate_req_fn)
|
|
e->type->ops.elevator_deactivate_req_fn(q, rq);
|
|
}
|
|
|
|
#ifdef CONFIG_FAIL_IO_TIMEOUT
|
|
int blk_should_fake_timeout(struct request_queue *);
|
|
ssize_t part_timeout_show(struct device *, struct device_attribute *, char *);
|
|
ssize_t part_timeout_store(struct device *, struct device_attribute *,
|
|
const char *, size_t);
|
|
#else
|
|
static inline int blk_should_fake_timeout(struct request_queue *q)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int ll_back_merge_fn(struct request_queue *q, struct request *req,
|
|
struct bio *bio);
|
|
int ll_front_merge_fn(struct request_queue *q, struct request *req,
|
|
struct bio *bio);
|
|
int attempt_back_merge(struct request_queue *q, struct request *rq);
|
|
int attempt_front_merge(struct request_queue *q, struct request *rq);
|
|
int blk_attempt_req_merge(struct request_queue *q, struct request *rq,
|
|
struct request *next);
|
|
void blk_recalc_rq_segments(struct request *rq);
|
|
void blk_rq_set_mixed_merge(struct request *rq);
|
|
bool blk_rq_merge_ok(struct request *rq, struct bio *bio);
|
|
int blk_try_merge(struct request *rq, struct bio *bio);
|
|
|
|
void blk_queue_congestion_threshold(struct request_queue *q);
|
|
|
|
void __blk_run_queue_uncond(struct request_queue *q);
|
|
|
|
int blk_dev_init(void);
|
|
|
|
|
|
/*
|
|
* Return the threshold (number of used requests) at which the queue is
|
|
* considered to be congested. It include a little hysteresis to keep the
|
|
* context switch rate down.
|
|
*/
|
|
static inline int queue_congestion_on_threshold(struct request_queue *q)
|
|
{
|
|
return q->nr_congestion_on;
|
|
}
|
|
|
|
/*
|
|
* The threshold at which a queue is considered to be uncongested
|
|
*/
|
|
static inline int queue_congestion_off_threshold(struct request_queue *q)
|
|
{
|
|
return q->nr_congestion_off;
|
|
}
|
|
|
|
/*
|
|
* Contribute to IO statistics IFF:
|
|
*
|
|
* a) it's attached to a gendisk, and
|
|
* b) the queue had IO stats enabled when this request was started, and
|
|
* c) it's a file system request
|
|
*/
|
|
static inline int blk_do_io_stat(struct request *rq)
|
|
{
|
|
return rq->rq_disk &&
|
|
(rq->cmd_flags & REQ_IO_STAT) &&
|
|
(rq->cmd_type == REQ_TYPE_FS);
|
|
}
|
|
|
|
/*
|
|
* Internal io_context interface
|
|
*/
|
|
void get_io_context(struct io_context *ioc);
|
|
struct io_cq *ioc_lookup_icq(struct io_context *ioc, struct request_queue *q);
|
|
struct io_cq *ioc_create_icq(struct io_context *ioc, struct request_queue *q,
|
|
gfp_t gfp_mask);
|
|
void ioc_clear_queue(struct request_queue *q);
|
|
|
|
int create_task_io_context(struct task_struct *task, gfp_t gfp_mask, int node);
|
|
|
|
/**
|
|
* create_io_context - try to create task->io_context
|
|
* @gfp_mask: allocation mask
|
|
* @node: allocation node
|
|
*
|
|
* If %current->io_context is %NULL, allocate a new io_context and install
|
|
* it. Returns the current %current->io_context which may be %NULL if
|
|
* allocation failed.
|
|
*
|
|
* Note that this function can't be called with IRQ disabled because
|
|
* task_lock which protects %current->io_context is IRQ-unsafe.
|
|
*/
|
|
static inline struct io_context *create_io_context(gfp_t gfp_mask, int node)
|
|
{
|
|
WARN_ON_ONCE(irqs_disabled());
|
|
if (unlikely(!current->io_context))
|
|
create_task_io_context(current, gfp_mask, node);
|
|
return current->io_context;
|
|
}
|
|
|
|
/*
|
|
* Internal throttling interface
|
|
*/
|
|
#ifdef CONFIG_BLK_DEV_THROTTLING
|
|
extern bool blk_throtl_bio(struct request_queue *q, struct bio *bio);
|
|
extern void blk_throtl_drain(struct request_queue *q);
|
|
extern int blk_throtl_init(struct request_queue *q);
|
|
extern void blk_throtl_exit(struct request_queue *q);
|
|
#else /* CONFIG_BLK_DEV_THROTTLING */
|
|
static inline bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
|
|
{
|
|
return false;
|
|
}
|
|
static inline void blk_throtl_drain(struct request_queue *q) { }
|
|
static inline int blk_throtl_init(struct request_queue *q) { return 0; }
|
|
static inline void blk_throtl_exit(struct request_queue *q) { }
|
|
#endif /* CONFIG_BLK_DEV_THROTTLING */
|
|
|
|
#endif /* BLK_INTERNAL_H */
|