mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 03:27:44 -04:00
Merge branch 'for-4.12/block' of git://git.kernel.dk/linux-block
Pull block layer updates from Jens Axboe: - Add BFQ IO scheduler under the new blk-mq scheduling framework. BFQ was initially a fork of CFQ, but subsequently changed to implement fairness based on B-WF2Q+, a modified variant of WF2Q. BFQ is meant to be used on desktop type single drives, providing good fairness. From Paolo. - Add Kyber IO scheduler. This is a full multiqueue aware scheduler, using a scalable token based algorithm that throttles IO based on live completion IO stats, similary to blk-wbt. From Omar. - A series from Jan, moving users to separately allocated backing devices. This continues the work of separating backing device life times, solving various problems with hot removal. - A series of updates for lightnvm, mostly from Javier. Includes a 'pblk' target that exposes an open channel SSD as a physical block device. - A series of fixes and improvements for nbd from Josef. - A series from Omar, removing queue sharing between devices on mostly legacy drivers. This helps us clean up other bits, if we know that a queue only has a single device backing. This has been overdue for more than a decade. - Fixes for the blk-stats, and improvements to unify the stats and user windows. This both improves blk-wbt, and enables other users to register a need to receive IO stats for a device. From Omar. - blk-throttle improvements from Shaohua. This provides a scalable framework for implementing scalable priotization - particularly for blk-mq, but applicable to any type of block device. The interface is marked experimental for now. - Bucketized IO stats for IO polling from Stephen Bates. This improves efficiency of polled workloads in the presence of mixed block size IO. - A few fixes for opal, from Scott. - A few pulls for NVMe, including a lot of fixes for NVMe-over-fabrics. From a variety of folks, mostly Sagi and James Smart. - A series from Bart, improving our exposed info and capabilities from the blk-mq debugfs support. - A series from Christoph, cleaning up how handle WRITE_ZEROES. - A series from Christoph, cleaning up the block layer handling of how we track errors in a request. On top of being a nice cleanup, it also shrinks the size of struct request a bit. - Removal of mg_disk and hd (sorry Linus) by Christoph. The former was never used by platforms, and the latter has outlived it's usefulness. - Various little bug fixes and cleanups from a wide variety of folks. * 'for-4.12/block' of git://git.kernel.dk/linux-block: (329 commits) block: hide badblocks attribute by default blk-mq: unify hctx delay_work and run_work block: add kblock_mod_delayed_work_on() blk-mq: unify hctx delayed_run_work and run_work nbd: fix use after free on module unload MAINTAINERS: bfq: Add Paolo as maintainer for the BFQ I/O scheduler blk-mq-sched: alloate reserved tags out of normal pool mtip32xx: use runtime tag to initialize command header scsi: Implement blk_mq_ops.show_rq() blk-mq: Add blk_mq_ops.show_rq() blk-mq: Show operation, cmd_flags and rq_flags names blk-mq: Make blk_flags_show() callers append a newline character blk-mq: Move the "state" debugfs attribute one level down blk-mq: Unregister debugfs attributes earlier blk-mq: Only unregister hctxs for which registration succeeded blk-mq-debugfs: Rename functions for registering and unregistering the mq directory blk-mq: Let blk_mq_debugfs_register() look up the queue name blk-mq: Register <dev>/queue/mq after having registered <dev>/queue ide-pm: always pass 0 error to ide_complete_rq in ide_do_devset ide-pm: always pass 0 error to __blk_end_request_all ..
This commit is contained in:
commit
694752922b
255 changed files with 24643 additions and 6152 deletions
|
|
@ -115,6 +115,18 @@ config BLK_DEV_THROTTLING
|
|||
|
||||
See Documentation/cgroups/blkio-controller.txt for more information.
|
||||
|
||||
config BLK_DEV_THROTTLING_LOW
|
||||
bool "Block throttling .low limit interface support (EXPERIMENTAL)"
|
||||
depends on BLK_DEV_THROTTLING
|
||||
default n
|
||||
---help---
|
||||
Add .low limit interface for block throttling. The low limit is a best
|
||||
effort limit to prioritize cgroups. Depending on the setting, the limit
|
||||
can be used to protect cgroups in terms of bandwidth/iops and better
|
||||
utilize disk resource.
|
||||
|
||||
Note, this is an experimental interface and could be changed someday.
|
||||
|
||||
config BLK_CMDLINE_PARSER
|
||||
bool "Block device command line partition parser"
|
||||
default n
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ config CFQ_GROUP_IOSCHED
|
|||
Enable group IO scheduling in CFQ.
|
||||
|
||||
choice
|
||||
|
||||
prompt "Default I/O scheduler"
|
||||
default DEFAULT_CFQ
|
||||
help
|
||||
|
|
@ -69,6 +70,35 @@ config MQ_IOSCHED_DEADLINE
|
|||
---help---
|
||||
MQ version of the deadline IO scheduler.
|
||||
|
||||
config MQ_IOSCHED_KYBER
|
||||
tristate "Kyber I/O scheduler"
|
||||
default y
|
||||
---help---
|
||||
The Kyber I/O scheduler is a low-overhead scheduler suitable for
|
||||
multiqueue and other fast devices. Given target latencies for reads and
|
||||
synchronous writes, it will self-tune queue depths to achieve that
|
||||
goal.
|
||||
|
||||
config IOSCHED_BFQ
|
||||
tristate "BFQ I/O scheduler"
|
||||
default n
|
||||
---help---
|
||||
BFQ I/O scheduler for BLK-MQ. BFQ distributes the bandwidth of
|
||||
of the device among all processes according to their weights,
|
||||
regardless of the device parameters and with any workload. It
|
||||
also guarantees a low latency to interactive and soft
|
||||
real-time applications. Details in
|
||||
Documentation/block/bfq-iosched.txt
|
||||
|
||||
config BFQ_GROUP_IOSCHED
|
||||
bool "BFQ hierarchical scheduling support"
|
||||
depends on IOSCHED_BFQ && BLK_CGROUP
|
||||
default n
|
||||
---help---
|
||||
|
||||
Enable hierarchical scheduling in BFQ, using the blkio
|
||||
(cgroups-v1) or io (cgroups-v2) controller.
|
||||
|
||||
endmenu
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
|
|||
obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o
|
||||
obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o
|
||||
obj-$(CONFIG_MQ_IOSCHED_DEADLINE) += mq-deadline.o
|
||||
obj-$(CONFIG_MQ_IOSCHED_KYBER) += kyber-iosched.o
|
||||
bfq-y := bfq-iosched.o bfq-wf2q.o bfq-cgroup.o
|
||||
obj-$(CONFIG_IOSCHED_BFQ) += bfq.o
|
||||
|
||||
obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o
|
||||
obj-$(CONFIG_BLK_CMDLINE_PARSER) += cmdline-parser.o
|
||||
|
|
|
|||
1139
block/bfq-cgroup.c
Normal file
1139
block/bfq-cgroup.c
Normal file
File diff suppressed because it is too large
Load diff
5047
block/bfq-iosched.c
Normal file
5047
block/bfq-iosched.c
Normal file
File diff suppressed because it is too large
Load diff
941
block/bfq-iosched.h
Normal file
941
block/bfq-iosched.h
Normal file
|
|
@ -0,0 +1,941 @@
|
|||
/*
|
||||
* Header file for the BFQ I/O scheduler: data structures and
|
||||
* prototypes of interface functions among BFQ components.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
#ifndef _BFQ_H
|
||||
#define _BFQ_H
|
||||
|
||||
#include <linux/blktrace_api.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/blk-cgroup.h>
|
||||
|
||||
#define BFQ_IOPRIO_CLASSES 3
|
||||
#define BFQ_CL_IDLE_TIMEOUT (HZ/5)
|
||||
|
||||
#define BFQ_MIN_WEIGHT 1
|
||||
#define BFQ_MAX_WEIGHT 1000
|
||||
#define BFQ_WEIGHT_CONVERSION_COEFF 10
|
||||
|
||||
#define BFQ_DEFAULT_QUEUE_IOPRIO 4
|
||||
|
||||
#define BFQ_WEIGHT_LEGACY_DFL 100
|
||||
#define BFQ_DEFAULT_GRP_IOPRIO 0
|
||||
#define BFQ_DEFAULT_GRP_CLASS IOPRIO_CLASS_BE
|
||||
|
||||
/*
|
||||
* Soft real-time applications are extremely more latency sensitive
|
||||
* than interactive ones. Over-raise the weight of the former to
|
||||
* privilege them against the latter.
|
||||
*/
|
||||
#define BFQ_SOFTRT_WEIGHT_FACTOR 100
|
||||
|
||||
struct bfq_entity;
|
||||
|
||||
/**
|
||||
* struct bfq_service_tree - per ioprio_class service tree.
|
||||
*
|
||||
* Each service tree represents a B-WF2Q+ scheduler on its own. Each
|
||||
* ioprio_class has its own independent scheduler, and so its own
|
||||
* bfq_service_tree. All the fields are protected by the queue lock
|
||||
* of the containing bfqd.
|
||||
*/
|
||||
struct bfq_service_tree {
|
||||
/* tree for active entities (i.e., those backlogged) */
|
||||
struct rb_root active;
|
||||
/* tree for idle entities (i.e., not backlogged, with V <= F_i)*/
|
||||
struct rb_root idle;
|
||||
|
||||
/* idle entity with minimum F_i */
|
||||
struct bfq_entity *first_idle;
|
||||
/* idle entity with maximum F_i */
|
||||
struct bfq_entity *last_idle;
|
||||
|
||||
/* scheduler virtual time */
|
||||
u64 vtime;
|
||||
/* scheduler weight sum; active and idle entities contribute to it */
|
||||
unsigned long wsum;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct bfq_sched_data - multi-class scheduler.
|
||||
*
|
||||
* bfq_sched_data is the basic scheduler queue. It supports three
|
||||
* ioprio_classes, and can be used either as a toplevel queue or as an
|
||||
* intermediate queue on a hierarchical setup. @next_in_service
|
||||
* points to the active entity of the sched_data service trees that
|
||||
* will be scheduled next. It is used to reduce the number of steps
|
||||
* needed for each hierarchical-schedule update.
|
||||
*
|
||||
* The supported ioprio_classes are the same as in CFQ, in descending
|
||||
* priority order, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE.
|
||||
* Requests from higher priority queues are served before all the
|
||||
* requests from lower priority queues; among requests of the same
|
||||
* queue requests are served according to B-WF2Q+.
|
||||
* All the fields are protected by the queue lock of the containing bfqd.
|
||||
*/
|
||||
struct bfq_sched_data {
|
||||
/* entity in service */
|
||||
struct bfq_entity *in_service_entity;
|
||||
/* head-of-line entity (see comments above) */
|
||||
struct bfq_entity *next_in_service;
|
||||
/* array of service trees, one per ioprio_class */
|
||||
struct bfq_service_tree service_tree[BFQ_IOPRIO_CLASSES];
|
||||
/* last time CLASS_IDLE was served */
|
||||
unsigned long bfq_class_idle_last_service;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* struct bfq_weight_counter - counter of the number of all active entities
|
||||
* with a given weight.
|
||||
*/
|
||||
struct bfq_weight_counter {
|
||||
unsigned int weight; /* weight of the entities this counter refers to */
|
||||
unsigned int num_active; /* nr of active entities with this weight */
|
||||
/*
|
||||
* Weights tree member (see bfq_data's @queue_weights_tree and
|
||||
* @group_weights_tree)
|
||||
*/
|
||||
struct rb_node weights_node;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct bfq_entity - schedulable entity.
|
||||
*
|
||||
* A bfq_entity is used to represent either a bfq_queue (leaf node in the
|
||||
* cgroup hierarchy) or a bfq_group into the upper level scheduler. Each
|
||||
* entity belongs to the sched_data of the parent group in the cgroup
|
||||
* hierarchy. Non-leaf entities have also their own sched_data, stored
|
||||
* in @my_sched_data.
|
||||
*
|
||||
* Each entity stores independently its priority values; this would
|
||||
* allow different weights on different devices, but this
|
||||
* functionality is not exported to userspace by now. Priorities and
|
||||
* weights are updated lazily, first storing the new values into the
|
||||
* new_* fields, then setting the @prio_changed flag. As soon as
|
||||
* there is a transition in the entity state that allows the priority
|
||||
* update to take place the effective and the requested priority
|
||||
* values are synchronized.
|
||||
*
|
||||
* Unless cgroups are used, the weight value is calculated from the
|
||||
* ioprio to export the same interface as CFQ. When dealing with
|
||||
* ``well-behaved'' queues (i.e., queues that do not spend too much
|
||||
* time to consume their budget and have true sequential behavior, and
|
||||
* when there are no external factors breaking anticipation) the
|
||||
* relative weights at each level of the cgroups hierarchy should be
|
||||
* guaranteed. All the fields are protected by the queue lock of the
|
||||
* containing bfqd.
|
||||
*/
|
||||
struct bfq_entity {
|
||||
/* service_tree member */
|
||||
struct rb_node rb_node;
|
||||
/* pointer to the weight counter associated with this entity */
|
||||
struct bfq_weight_counter *weight_counter;
|
||||
|
||||
/*
|
||||
* Flag, true if the entity is on a tree (either the active or
|
||||
* the idle one of its service_tree) or is in service.
|
||||
*/
|
||||
bool on_st;
|
||||
|
||||
/* B-WF2Q+ start and finish timestamps [sectors/weight] */
|
||||
u64 start, finish;
|
||||
|
||||
/* tree the entity is enqueued into; %NULL if not on a tree */
|
||||
struct rb_root *tree;
|
||||
|
||||
/*
|
||||
* minimum start time of the (active) subtree rooted at this
|
||||
* entity; used for O(log N) lookups into active trees
|
||||
*/
|
||||
u64 min_start;
|
||||
|
||||
/* amount of service received during the last service slot */
|
||||
int service;
|
||||
|
||||
/* budget, used also to calculate F_i: F_i = S_i + @budget / @weight */
|
||||
int budget;
|
||||
|
||||
/* weight of the queue */
|
||||
int weight;
|
||||
/* next weight if a change is in progress */
|
||||
int new_weight;
|
||||
|
||||
/* original weight, used to implement weight boosting */
|
||||
int orig_weight;
|
||||
|
||||
/* parent entity, for hierarchical scheduling */
|
||||
struct bfq_entity *parent;
|
||||
|
||||
/*
|
||||
* For non-leaf nodes in the hierarchy, the associated
|
||||
* scheduler queue, %NULL on leaf nodes.
|
||||
*/
|
||||
struct bfq_sched_data *my_sched_data;
|
||||
/* the scheduler queue this entity belongs to */
|
||||
struct bfq_sched_data *sched_data;
|
||||
|
||||
/* flag, set to request a weight, ioprio or ioprio_class change */
|
||||
int prio_changed;
|
||||
};
|
||||
|
||||
struct bfq_group;
|
||||
|
||||
/**
|
||||
* struct bfq_ttime - per process thinktime stats.
|
||||
*/
|
||||
struct bfq_ttime {
|
||||
/* completion time of the last request */
|
||||
u64 last_end_request;
|
||||
|
||||
/* total process thinktime */
|
||||
u64 ttime_total;
|
||||
/* number of thinktime samples */
|
||||
unsigned long ttime_samples;
|
||||
/* average process thinktime */
|
||||
u64 ttime_mean;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct bfq_queue - leaf schedulable entity.
|
||||
*
|
||||
* A bfq_queue is a leaf request queue; it can be associated with an
|
||||
* io_context or more, if it is async or shared between cooperating
|
||||
* processes. @cgroup holds a reference to the cgroup, to be sure that it
|
||||
* does not disappear while a bfqq still references it (mostly to avoid
|
||||
* races between request issuing and task migration followed by cgroup
|
||||
* destruction).
|
||||
* All the fields are protected by the queue lock of the containing bfqd.
|
||||
*/
|
||||
struct bfq_queue {
|
||||
/* reference counter */
|
||||
int ref;
|
||||
/* parent bfq_data */
|
||||
struct bfq_data *bfqd;
|
||||
|
||||
/* current ioprio and ioprio class */
|
||||
unsigned short ioprio, ioprio_class;
|
||||
/* next ioprio and ioprio class if a change is in progress */
|
||||
unsigned short new_ioprio, new_ioprio_class;
|
||||
|
||||
/*
|
||||
* Shared bfq_queue if queue is cooperating with one or more
|
||||
* other queues.
|
||||
*/
|
||||
struct bfq_queue *new_bfqq;
|
||||
/* request-position tree member (see bfq_group's @rq_pos_tree) */
|
||||
struct rb_node pos_node;
|
||||
/* request-position tree root (see bfq_group's @rq_pos_tree) */
|
||||
struct rb_root *pos_root;
|
||||
|
||||
/* sorted list of pending requests */
|
||||
struct rb_root sort_list;
|
||||
/* if fifo isn't expired, next request to serve */
|
||||
struct request *next_rq;
|
||||
/* number of sync and async requests queued */
|
||||
int queued[2];
|
||||
/* number of requests currently allocated */
|
||||
int allocated;
|
||||
/* number of pending metadata requests */
|
||||
int meta_pending;
|
||||
/* fifo list of requests in sort_list */
|
||||
struct list_head fifo;
|
||||
|
||||
/* entity representing this queue in the scheduler */
|
||||
struct bfq_entity entity;
|
||||
|
||||
/* maximum budget allowed from the feedback mechanism */
|
||||
int max_budget;
|
||||
/* budget expiration (in jiffies) */
|
||||
unsigned long budget_timeout;
|
||||
|
||||
/* number of requests on the dispatch list or inside driver */
|
||||
int dispatched;
|
||||
|
||||
/* status flags */
|
||||
unsigned long flags;
|
||||
|
||||
/* node for active/idle bfqq list inside parent bfqd */
|
||||
struct list_head bfqq_list;
|
||||
|
||||
/* associated @bfq_ttime struct */
|
||||
struct bfq_ttime ttime;
|
||||
|
||||
/* bit vector: a 1 for each seeky requests in history */
|
||||
u32 seek_history;
|
||||
|
||||
/* node for the device's burst list */
|
||||
struct hlist_node burst_list_node;
|
||||
|
||||
/* position of the last request enqueued */
|
||||
sector_t last_request_pos;
|
||||
|
||||
/* Number of consecutive pairs of request completion and
|
||||
* arrival, such that the queue becomes idle after the
|
||||
* completion, but the next request arrives within an idle
|
||||
* time slice; used only if the queue's IO_bound flag has been
|
||||
* cleared.
|
||||
*/
|
||||
unsigned int requests_within_timer;
|
||||
|
||||
/* pid of the process owning the queue, used for logging purposes */
|
||||
pid_t pid;
|
||||
|
||||
/*
|
||||
* Pointer to the bfq_io_cq owning the bfq_queue, set to %NULL
|
||||
* if the queue is shared.
|
||||
*/
|
||||
struct bfq_io_cq *bic;
|
||||
|
||||
/* current maximum weight-raising time for this queue */
|
||||
unsigned long wr_cur_max_time;
|
||||
/*
|
||||
* Minimum time instant such that, only if a new request is
|
||||
* enqueued after this time instant in an idle @bfq_queue with
|
||||
* no outstanding requests, then the task associated with the
|
||||
* queue it is deemed as soft real-time (see the comments on
|
||||
* the function bfq_bfqq_softrt_next_start())
|
||||
*/
|
||||
unsigned long soft_rt_next_start;
|
||||
/*
|
||||
* Start time of the current weight-raising period if
|
||||
* the @bfq-queue is being weight-raised, otherwise
|
||||
* finish time of the last weight-raising period.
|
||||
*/
|
||||
unsigned long last_wr_start_finish;
|
||||
/* factor by which the weight of this queue is multiplied */
|
||||
unsigned int wr_coeff;
|
||||
/*
|
||||
* Time of the last transition of the @bfq_queue from idle to
|
||||
* backlogged.
|
||||
*/
|
||||
unsigned long last_idle_bklogged;
|
||||
/*
|
||||
* Cumulative service received from the @bfq_queue since the
|
||||
* last transition from idle to backlogged.
|
||||
*/
|
||||
unsigned long service_from_backlogged;
|
||||
|
||||
/*
|
||||
* Value of wr start time when switching to soft rt
|
||||
*/
|
||||
unsigned long wr_start_at_switch_to_srt;
|
||||
|
||||
unsigned long split_time; /* time of last split */
|
||||
};
|
||||
|
||||
/**
|
||||
* struct bfq_io_cq - per (request_queue, io_context) structure.
|
||||
*/
|
||||
struct bfq_io_cq {
|
||||
/* associated io_cq structure */
|
||||
struct io_cq icq; /* must be the first member */
|
||||
/* array of two process queues, the sync and the async */
|
||||
struct bfq_queue *bfqq[2];
|
||||
/* per (request_queue, blkcg) ioprio */
|
||||
int ioprio;
|
||||
#ifdef CONFIG_BFQ_GROUP_IOSCHED
|
||||
uint64_t blkcg_serial_nr; /* the current blkcg serial */
|
||||
#endif
|
||||
/*
|
||||
* Snapshot of the idle window before merging; taken to
|
||||
* remember this value while the queue is merged, so as to be
|
||||
* able to restore it in case of split.
|
||||
*/
|
||||
bool saved_idle_window;
|
||||
/*
|
||||
* Same purpose as the previous two fields for the I/O bound
|
||||
* classification of a queue.
|
||||
*/
|
||||
bool saved_IO_bound;
|
||||
|
||||
/*
|
||||
* Same purpose as the previous fields for the value of the
|
||||
* field keeping the queue's belonging to a large burst
|
||||
*/
|
||||
bool saved_in_large_burst;
|
||||
/*
|
||||
* True if the queue belonged to a burst list before its merge
|
||||
* with another cooperating queue.
|
||||
*/
|
||||
bool was_in_burst_list;
|
||||
|
||||
/*
|
||||
* Similar to previous fields: save wr information.
|
||||
*/
|
||||
unsigned long saved_wr_coeff;
|
||||
unsigned long saved_last_wr_start_finish;
|
||||
unsigned long saved_wr_start_at_switch_to_srt;
|
||||
unsigned int saved_wr_cur_max_time;
|
||||
struct bfq_ttime saved_ttime;
|
||||
};
|
||||
|
||||
enum bfq_device_speed {
|
||||
BFQ_BFQD_FAST,
|
||||
BFQ_BFQD_SLOW,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct bfq_data - per-device data structure.
|
||||
*
|
||||
* All the fields are protected by @lock.
|
||||
*/
|
||||
struct bfq_data {
|
||||
/* device request queue */
|
||||
struct request_queue *queue;
|
||||
/* dispatch queue */
|
||||
struct list_head dispatch;
|
||||
|
||||
/* root bfq_group for the device */
|
||||
struct bfq_group *root_group;
|
||||
|
||||
/*
|
||||
* rbtree of weight counters of @bfq_queues, sorted by
|
||||
* weight. Used to keep track of whether all @bfq_queues have
|
||||
* the same weight. The tree contains one counter for each
|
||||
* distinct weight associated to some active and not
|
||||
* weight-raised @bfq_queue (see the comments to the functions
|
||||
* bfq_weights_tree_[add|remove] for further details).
|
||||
*/
|
||||
struct rb_root queue_weights_tree;
|
||||
/*
|
||||
* rbtree of non-queue @bfq_entity weight counters, sorted by
|
||||
* weight. Used to keep track of whether all @bfq_groups have
|
||||
* the same weight. The tree contains one counter for each
|
||||
* distinct weight associated to some active @bfq_group (see
|
||||
* the comments to the functions bfq_weights_tree_[add|remove]
|
||||
* for further details).
|
||||
*/
|
||||
struct rb_root group_weights_tree;
|
||||
|
||||
/*
|
||||
* Number of bfq_queues containing requests (including the
|
||||
* queue in service, even if it is idling).
|
||||
*/
|
||||
int busy_queues;
|
||||
/* number of weight-raised busy @bfq_queues */
|
||||
int wr_busy_queues;
|
||||
/* number of queued requests */
|
||||
int queued;
|
||||
/* number of requests dispatched and waiting for completion */
|
||||
int rq_in_driver;
|
||||
|
||||
/*
|
||||
* Maximum number of requests in driver in the last
|
||||
* @hw_tag_samples completed requests.
|
||||
*/
|
||||
int max_rq_in_driver;
|
||||
/* number of samples used to calculate hw_tag */
|
||||
int hw_tag_samples;
|
||||
/* flag set to one if the driver is showing a queueing behavior */
|
||||
int hw_tag;
|
||||
|
||||
/* number of budgets assigned */
|
||||
int budgets_assigned;
|
||||
|
||||
/*
|
||||
* Timer set when idling (waiting) for the next request from
|
||||
* the queue in service.
|
||||
*/
|
||||
struct hrtimer idle_slice_timer;
|
||||
|
||||
/* bfq_queue in service */
|
||||
struct bfq_queue *in_service_queue;
|
||||
|
||||
/* on-disk position of the last served request */
|
||||
sector_t last_position;
|
||||
|
||||
/* time of last request completion (ns) */
|
||||
u64 last_completion;
|
||||
|
||||
/* time of first rq dispatch in current observation interval (ns) */
|
||||
u64 first_dispatch;
|
||||
/* time of last rq dispatch in current observation interval (ns) */
|
||||
u64 last_dispatch;
|
||||
|
||||
/* beginning of the last budget */
|
||||
ktime_t last_budget_start;
|
||||
/* beginning of the last idle slice */
|
||||
ktime_t last_idling_start;
|
||||
|
||||
/* number of samples in current observation interval */
|
||||
int peak_rate_samples;
|
||||
/* num of samples of seq dispatches in current observation interval */
|
||||
u32 sequential_samples;
|
||||
/* total num of sectors transferred in current observation interval */
|
||||
u64 tot_sectors_dispatched;
|
||||
/* max rq size seen during current observation interval (sectors) */
|
||||
u32 last_rq_max_size;
|
||||
/* time elapsed from first dispatch in current observ. interval (us) */
|
||||
u64 delta_from_first;
|
||||
/*
|
||||
* Current estimate of the device peak rate, measured in
|
||||
* [BFQ_RATE_SHIFT * sectors/usec]. The left-shift by
|
||||
* BFQ_RATE_SHIFT is performed to increase precision in
|
||||
* fixed-point calculations.
|
||||
*/
|
||||
u32 peak_rate;
|
||||
|
||||
/* maximum budget allotted to a bfq_queue before rescheduling */
|
||||
int bfq_max_budget;
|
||||
|
||||
/* list of all the bfq_queues active on the device */
|
||||
struct list_head active_list;
|
||||
/* list of all the bfq_queues idle on the device */
|
||||
struct list_head idle_list;
|
||||
|
||||
/*
|
||||
* Timeout for async/sync requests; when it fires, requests
|
||||
* are served in fifo order.
|
||||
*/
|
||||
u64 bfq_fifo_expire[2];
|
||||
/* weight of backward seeks wrt forward ones */
|
||||
unsigned int bfq_back_penalty;
|
||||
/* maximum allowed backward seek */
|
||||
unsigned int bfq_back_max;
|
||||
/* maximum idling time */
|
||||
u32 bfq_slice_idle;
|
||||
|
||||
/* user-configured max budget value (0 for auto-tuning) */
|
||||
int bfq_user_max_budget;
|
||||
/*
|
||||
* Timeout for bfq_queues to consume their budget; used to
|
||||
* prevent seeky queues from imposing long latencies to
|
||||
* sequential or quasi-sequential ones (this also implies that
|
||||
* seeky queues cannot receive guarantees in the service
|
||||
* domain; after a timeout they are charged for the time they
|
||||
* have been in service, to preserve fairness among them, but
|
||||
* without service-domain guarantees).
|
||||
*/
|
||||
unsigned int bfq_timeout;
|
||||
|
||||
/*
|
||||
* Number of consecutive requests that must be issued within
|
||||
* the idle time slice to set again idling to a queue which
|
||||
* was marked as non-I/O-bound (see the definition of the
|
||||
* IO_bound flag for further details).
|
||||
*/
|
||||
unsigned int bfq_requests_within_timer;
|
||||
|
||||
/*
|
||||
* Force device idling whenever needed to provide accurate
|
||||
* service guarantees, without caring about throughput
|
||||
* issues. CAVEAT: this may even increase latencies, in case
|
||||
* of useless idling for processes that did stop doing I/O.
|
||||
*/
|
||||
bool strict_guarantees;
|
||||
|
||||
/*
|
||||
* Last time at which a queue entered the current burst of
|
||||
* queues being activated shortly after each other; for more
|
||||
* details about this and the following parameters related to
|
||||
* a burst of activations, see the comments on the function
|
||||
* bfq_handle_burst.
|
||||
*/
|
||||
unsigned long last_ins_in_burst;
|
||||
/*
|
||||
* Reference time interval used to decide whether a queue has
|
||||
* been activated shortly after @last_ins_in_burst.
|
||||
*/
|
||||
unsigned long bfq_burst_interval;
|
||||
/* number of queues in the current burst of queue activations */
|
||||
int burst_size;
|
||||
|
||||
/* common parent entity for the queues in the burst */
|
||||
struct bfq_entity *burst_parent_entity;
|
||||
/* Maximum burst size above which the current queue-activation
|
||||
* burst is deemed as 'large'.
|
||||
*/
|
||||
unsigned long bfq_large_burst_thresh;
|
||||
/* true if a large queue-activation burst is in progress */
|
||||
bool large_burst;
|
||||
/*
|
||||
* Head of the burst list (as for the above fields, more
|
||||
* details in the comments on the function bfq_handle_burst).
|
||||
*/
|
||||
struct hlist_head burst_list;
|
||||
|
||||
/* if set to true, low-latency heuristics are enabled */
|
||||
bool low_latency;
|
||||
/*
|
||||
* Maximum factor by which the weight of a weight-raised queue
|
||||
* is multiplied.
|
||||
*/
|
||||
unsigned int bfq_wr_coeff;
|
||||
/* maximum duration of a weight-raising period (jiffies) */
|
||||
unsigned int bfq_wr_max_time;
|
||||
|
||||
/* Maximum weight-raising duration for soft real-time processes */
|
||||
unsigned int bfq_wr_rt_max_time;
|
||||
/*
|
||||
* Minimum idle period after which weight-raising may be
|
||||
* reactivated for a queue (in jiffies).
|
||||
*/
|
||||
unsigned int bfq_wr_min_idle_time;
|
||||
/*
|
||||
* Minimum period between request arrivals after which
|
||||
* weight-raising may be reactivated for an already busy async
|
||||
* queue (in jiffies).
|
||||
*/
|
||||
unsigned long bfq_wr_min_inter_arr_async;
|
||||
|
||||
/* Max service-rate for a soft real-time queue, in sectors/sec */
|
||||
unsigned int bfq_wr_max_softrt_rate;
|
||||
/*
|
||||
* Cached value of the product R*T, used for computing the
|
||||
* maximum duration of weight raising automatically.
|
||||
*/
|
||||
u64 RT_prod;
|
||||
/* device-speed class for the low-latency heuristic */
|
||||
enum bfq_device_speed device_speed;
|
||||
|
||||
/* fallback dummy bfqq for extreme OOM conditions */
|
||||
struct bfq_queue oom_bfqq;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
/*
|
||||
* bic associated with the task issuing current bio for
|
||||
* merging. This and the next field are used as a support to
|
||||
* be able to perform the bic lookup, needed by bio-merge
|
||||
* functions, before the scheduler lock is taken, and thus
|
||||
* avoid taking the request-queue lock while the scheduler
|
||||
* lock is being held.
|
||||
*/
|
||||
struct bfq_io_cq *bio_bic;
|
||||
/* bfqq associated with the task issuing current bio for merging */
|
||||
struct bfq_queue *bio_bfqq;
|
||||
};
|
||||
|
||||
enum bfqq_state_flags {
|
||||
BFQQF_just_created = 0, /* queue just allocated */
|
||||
BFQQF_busy, /* has requests or is in service */
|
||||
BFQQF_wait_request, /* waiting for a request */
|
||||
BFQQF_non_blocking_wait_rq, /*
|
||||
* waiting for a request
|
||||
* without idling the device
|
||||
*/
|
||||
BFQQF_fifo_expire, /* FIFO checked in this slice */
|
||||
BFQQF_idle_window, /* slice idling enabled */
|
||||
BFQQF_sync, /* synchronous queue */
|
||||
BFQQF_IO_bound, /*
|
||||
* bfqq has timed-out at least once
|
||||
* having consumed at most 2/10 of
|
||||
* its budget
|
||||
*/
|
||||
BFQQF_in_large_burst, /*
|
||||
* bfqq activated in a large burst,
|
||||
* see comments to bfq_handle_burst.
|
||||
*/
|
||||
BFQQF_softrt_update, /*
|
||||
* may need softrt-next-start
|
||||
* update
|
||||
*/
|
||||
BFQQF_coop, /* bfqq is shared */
|
||||
BFQQF_split_coop /* shared bfqq will be split */
|
||||
};
|
||||
|
||||
#define BFQ_BFQQ_FNS(name) \
|
||||
void bfq_mark_bfqq_##name(struct bfq_queue *bfqq); \
|
||||
void bfq_clear_bfqq_##name(struct bfq_queue *bfqq); \
|
||||
int bfq_bfqq_##name(const struct bfq_queue *bfqq);
|
||||
|
||||
BFQ_BFQQ_FNS(just_created);
|
||||
BFQ_BFQQ_FNS(busy);
|
||||
BFQ_BFQQ_FNS(wait_request);
|
||||
BFQ_BFQQ_FNS(non_blocking_wait_rq);
|
||||
BFQ_BFQQ_FNS(fifo_expire);
|
||||
BFQ_BFQQ_FNS(idle_window);
|
||||
BFQ_BFQQ_FNS(sync);
|
||||
BFQ_BFQQ_FNS(IO_bound);
|
||||
BFQ_BFQQ_FNS(in_large_burst);
|
||||
BFQ_BFQQ_FNS(coop);
|
||||
BFQ_BFQQ_FNS(split_coop);
|
||||
BFQ_BFQQ_FNS(softrt_update);
|
||||
#undef BFQ_BFQQ_FNS
|
||||
|
||||
/* Expiration reasons. */
|
||||
enum bfqq_expiration {
|
||||
BFQQE_TOO_IDLE = 0, /*
|
||||
* queue has been idling for
|
||||
* too long
|
||||
*/
|
||||
BFQQE_BUDGET_TIMEOUT, /* budget took too long to be used */
|
||||
BFQQE_BUDGET_EXHAUSTED, /* budget consumed */
|
||||
BFQQE_NO_MORE_REQUESTS, /* the queue has no more requests */
|
||||
BFQQE_PREEMPTED /* preemption in progress */
|
||||
};
|
||||
|
||||
struct bfqg_stats {
|
||||
#ifdef CONFIG_BFQ_GROUP_IOSCHED
|
||||
/* number of ios merged */
|
||||
struct blkg_rwstat merged;
|
||||
/* total time spent on device in ns, may not be accurate w/ queueing */
|
||||
struct blkg_rwstat service_time;
|
||||
/* total time spent waiting in scheduler queue in ns */
|
||||
struct blkg_rwstat wait_time;
|
||||
/* number of IOs queued up */
|
||||
struct blkg_rwstat queued;
|
||||
/* total disk time and nr sectors dispatched by this group */
|
||||
struct blkg_stat time;
|
||||
/* sum of number of ios queued across all samples */
|
||||
struct blkg_stat avg_queue_size_sum;
|
||||
/* count of samples taken for average */
|
||||
struct blkg_stat avg_queue_size_samples;
|
||||
/* how many times this group has been removed from service tree */
|
||||
struct blkg_stat dequeue;
|
||||
/* total time spent waiting for it to be assigned a timeslice. */
|
||||
struct blkg_stat group_wait_time;
|
||||
/* time spent idling for this blkcg_gq */
|
||||
struct blkg_stat idle_time;
|
||||
/* total time with empty current active q with other requests queued */
|
||||
struct blkg_stat empty_time;
|
||||
/* fields after this shouldn't be cleared on stat reset */
|
||||
uint64_t start_group_wait_time;
|
||||
uint64_t start_idle_time;
|
||||
uint64_t start_empty_time;
|
||||
uint16_t flags;
|
||||
#endif /* CONFIG_BFQ_GROUP_IOSCHED */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BFQ_GROUP_IOSCHED
|
||||
|
||||
/*
|
||||
* struct bfq_group_data - per-blkcg storage for the blkio subsystem.
|
||||
*
|
||||
* @ps: @blkcg_policy_storage that this structure inherits
|
||||
* @weight: weight of the bfq_group
|
||||
*/
|
||||
struct bfq_group_data {
|
||||
/* must be the first member */
|
||||
struct blkcg_policy_data pd;
|
||||
|
||||
unsigned int weight;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct bfq_group - per (device, cgroup) data structure.
|
||||
* @entity: schedulable entity to insert into the parent group sched_data.
|
||||
* @sched_data: own sched_data, to contain child entities (they may be
|
||||
* both bfq_queues and bfq_groups).
|
||||
* @bfqd: the bfq_data for the device this group acts upon.
|
||||
* @async_bfqq: array of async queues for all the tasks belonging to
|
||||
* the group, one queue per ioprio value per ioprio_class,
|
||||
* except for the idle class that has only one queue.
|
||||
* @async_idle_bfqq: async queue for the idle class (ioprio is ignored).
|
||||
* @my_entity: pointer to @entity, %NULL for the toplevel group; used
|
||||
* to avoid too many special cases during group creation/
|
||||
* migration.
|
||||
* @stats: stats for this bfqg.
|
||||
* @active_entities: number of active entities belonging to the group;
|
||||
* unused for the root group. Used to know whether there
|
||||
* are groups with more than one active @bfq_entity
|
||||
* (see the comments to the function
|
||||
* bfq_bfqq_may_idle()).
|
||||
* @rq_pos_tree: rbtree sorted by next_request position, used when
|
||||
* determining if two or more queues have interleaving
|
||||
* requests (see bfq_find_close_cooperator()).
|
||||
*
|
||||
* Each (device, cgroup) pair has its own bfq_group, i.e., for each cgroup
|
||||
* there is a set of bfq_groups, each one collecting the lower-level
|
||||
* entities belonging to the group that are acting on the same device.
|
||||
*
|
||||
* Locking works as follows:
|
||||
* o @bfqd is protected by the queue lock, RCU is used to access it
|
||||
* from the readers.
|
||||
* o All the other fields are protected by the @bfqd queue lock.
|
||||
*/
|
||||
struct bfq_group {
|
||||
/* must be the first member */
|
||||
struct blkg_policy_data pd;
|
||||
|
||||
struct bfq_entity entity;
|
||||
struct bfq_sched_data sched_data;
|
||||
|
||||
void *bfqd;
|
||||
|
||||
struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR];
|
||||
struct bfq_queue *async_idle_bfqq;
|
||||
|
||||
struct bfq_entity *my_entity;
|
||||
|
||||
int active_entities;
|
||||
|
||||
struct rb_root rq_pos_tree;
|
||||
|
||||
struct bfqg_stats stats;
|
||||
};
|
||||
|
||||
#else
|
||||
struct bfq_group {
|
||||
struct bfq_sched_data sched_data;
|
||||
|
||||
struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR];
|
||||
struct bfq_queue *async_idle_bfqq;
|
||||
|
||||
struct rb_root rq_pos_tree;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct bfq_queue *bfq_entity_to_bfqq(struct bfq_entity *entity);
|
||||
|
||||
/* --------------- main algorithm interface ----------------- */
|
||||
|
||||
#define BFQ_SERVICE_TREE_INIT ((struct bfq_service_tree) \
|
||||
{ RB_ROOT, RB_ROOT, NULL, NULL, 0, 0 })
|
||||
|
||||
extern const int bfq_timeout;
|
||||
|
||||
struct bfq_queue *bic_to_bfqq(struct bfq_io_cq *bic, bool is_sync);
|
||||
void bic_set_bfqq(struct bfq_io_cq *bic, struct bfq_queue *bfqq, bool is_sync);
|
||||
struct bfq_data *bic_to_bfqd(struct bfq_io_cq *bic);
|
||||
void bfq_requeue_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq);
|
||||
void bfq_pos_tree_add_move(struct bfq_data *bfqd, struct bfq_queue *bfqq);
|
||||
void bfq_weights_tree_add(struct bfq_data *bfqd, struct bfq_entity *entity,
|
||||
struct rb_root *root);
|
||||
void bfq_weights_tree_remove(struct bfq_data *bfqd, struct bfq_entity *entity,
|
||||
struct rb_root *root);
|
||||
void bfq_bfqq_expire(struct bfq_data *bfqd, struct bfq_queue *bfqq,
|
||||
bool compensate, enum bfqq_expiration reason);
|
||||
void bfq_put_queue(struct bfq_queue *bfqq);
|
||||
void bfq_end_wr_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg);
|
||||
void bfq_schedule_dispatch(struct bfq_data *bfqd);
|
||||
void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg);
|
||||
|
||||
/* ------------ end of main algorithm interface -------------- */
|
||||
|
||||
/* ---------------- cgroups-support interface ---------------- */
|
||||
|
||||
void bfqg_stats_update_io_add(struct bfq_group *bfqg, struct bfq_queue *bfqq,
|
||||
unsigned int op);
|
||||
void bfqg_stats_update_io_remove(struct bfq_group *bfqg, unsigned int op);
|
||||
void bfqg_stats_update_io_merged(struct bfq_group *bfqg, unsigned int op);
|
||||
void bfqg_stats_update_completion(struct bfq_group *bfqg, uint64_t start_time,
|
||||
uint64_t io_start_time, unsigned int op);
|
||||
void bfqg_stats_update_dequeue(struct bfq_group *bfqg);
|
||||
void bfqg_stats_set_start_empty_time(struct bfq_group *bfqg);
|
||||
void bfqg_stats_update_idle_time(struct bfq_group *bfqg);
|
||||
void bfqg_stats_set_start_idle_time(struct bfq_group *bfqg);
|
||||
void bfqg_stats_update_avg_queue_size(struct bfq_group *bfqg);
|
||||
void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq,
|
||||
struct bfq_group *bfqg);
|
||||
|
||||
void bfq_init_entity(struct bfq_entity *entity, struct bfq_group *bfqg);
|
||||
void bfq_bic_update_cgroup(struct bfq_io_cq *bic, struct bio *bio);
|
||||
void bfq_end_wr_async(struct bfq_data *bfqd);
|
||||
struct bfq_group *bfq_find_set_group(struct bfq_data *bfqd,
|
||||
struct blkcg *blkcg);
|
||||
struct blkcg_gq *bfqg_to_blkg(struct bfq_group *bfqg);
|
||||
struct bfq_group *bfqq_group(struct bfq_queue *bfqq);
|
||||
struct bfq_group *bfq_create_group_hierarchy(struct bfq_data *bfqd, int node);
|
||||
void bfqg_put(struct bfq_group *bfqg);
|
||||
|
||||
#ifdef CONFIG_BFQ_GROUP_IOSCHED
|
||||
extern struct cftype bfq_blkcg_legacy_files[];
|
||||
extern struct cftype bfq_blkg_files[];
|
||||
extern struct blkcg_policy blkcg_policy_bfq;
|
||||
#endif
|
||||
|
||||
/* ------------- end of cgroups-support interface ------------- */
|
||||
|
||||
/* - interface of the internal hierarchical B-WF2Q+ scheduler - */
|
||||
|
||||
#ifdef CONFIG_BFQ_GROUP_IOSCHED
|
||||
/* both next loops stop at one of the child entities of the root group */
|
||||
#define for_each_entity(entity) \
|
||||
for (; entity ; entity = entity->parent)
|
||||
|
||||
/*
|
||||
* For each iteration, compute parent in advance, so as to be safe if
|
||||
* entity is deallocated during the iteration. Such a deallocation may
|
||||
* happen as a consequence of a bfq_put_queue that frees the bfq_queue
|
||||
* containing entity.
|
||||
*/
|
||||
#define for_each_entity_safe(entity, parent) \
|
||||
for (; entity && ({ parent = entity->parent; 1; }); entity = parent)
|
||||
|
||||
#else /* CONFIG_BFQ_GROUP_IOSCHED */
|
||||
/*
|
||||
* Next two macros are fake loops when cgroups support is not
|
||||
* enabled. I fact, in such a case, there is only one level to go up
|
||||
* (to reach the root group).
|
||||
*/
|
||||
#define for_each_entity(entity) \
|
||||
for (; entity ; entity = NULL)
|
||||
|
||||
#define for_each_entity_safe(entity, parent) \
|
||||
for (parent = NULL; entity ; entity = parent)
|
||||
#endif /* CONFIG_BFQ_GROUP_IOSCHED */
|
||||
|
||||
struct bfq_group *bfq_bfqq_to_bfqg(struct bfq_queue *bfqq);
|
||||
struct bfq_queue *bfq_entity_to_bfqq(struct bfq_entity *entity);
|
||||
struct bfq_service_tree *bfq_entity_service_tree(struct bfq_entity *entity);
|
||||
struct bfq_entity *bfq_entity_of(struct rb_node *node);
|
||||
unsigned short bfq_ioprio_to_weight(int ioprio);
|
||||
void bfq_put_idle_entity(struct bfq_service_tree *st,
|
||||
struct bfq_entity *entity);
|
||||
struct bfq_service_tree *
|
||||
__bfq_entity_update_weight_prio(struct bfq_service_tree *old_st,
|
||||
struct bfq_entity *entity);
|
||||
void bfq_bfqq_served(struct bfq_queue *bfqq, int served);
|
||||
void bfq_bfqq_charge_time(struct bfq_data *bfqd, struct bfq_queue *bfqq,
|
||||
unsigned long time_ms);
|
||||
bool __bfq_deactivate_entity(struct bfq_entity *entity,
|
||||
bool ins_into_idle_tree);
|
||||
bool next_queue_may_preempt(struct bfq_data *bfqd);
|
||||
struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd);
|
||||
void __bfq_bfqd_reset_in_service(struct bfq_data *bfqd);
|
||||
void bfq_deactivate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq,
|
||||
bool ins_into_idle_tree, bool expiration);
|
||||
void bfq_activate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq);
|
||||
void bfq_requeue_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq);
|
||||
void bfq_del_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq,
|
||||
bool expiration);
|
||||
void bfq_add_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq);
|
||||
|
||||
/* --------------- end of interface of B-WF2Q+ ---------------- */
|
||||
|
||||
/* Logging facilities. */
|
||||
#ifdef CONFIG_BFQ_GROUP_IOSCHED
|
||||
struct bfq_group *bfqq_group(struct bfq_queue *bfqq);
|
||||
|
||||
#define bfq_log_bfqq(bfqd, bfqq, fmt, args...) do { \
|
||||
char __pbuf[128]; \
|
||||
\
|
||||
blkg_path(bfqg_to_blkg(bfqq_group(bfqq)), __pbuf, sizeof(__pbuf)); \
|
||||
blk_add_trace_msg((bfqd)->queue, "bfq%d%c %s " fmt, (bfqq)->pid, \
|
||||
bfq_bfqq_sync((bfqq)) ? 'S' : 'A', \
|
||||
__pbuf, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define bfq_log_bfqg(bfqd, bfqg, fmt, args...) do { \
|
||||
char __pbuf[128]; \
|
||||
\
|
||||
blkg_path(bfqg_to_blkg(bfqg), __pbuf, sizeof(__pbuf)); \
|
||||
blk_add_trace_msg((bfqd)->queue, "%s " fmt, __pbuf, ##args); \
|
||||
} while (0)
|
||||
|
||||
#else /* CONFIG_BFQ_GROUP_IOSCHED */
|
||||
|
||||
#define bfq_log_bfqq(bfqd, bfqq, fmt, args...) \
|
||||
blk_add_trace_msg((bfqd)->queue, "bfq%d%c " fmt, (bfqq)->pid, \
|
||||
bfq_bfqq_sync((bfqq)) ? 'S' : 'A', \
|
||||
##args)
|
||||
#define bfq_log_bfqg(bfqd, bfqg, fmt, args...) do {} while (0)
|
||||
|
||||
#endif /* CONFIG_BFQ_GROUP_IOSCHED */
|
||||
|
||||
#define bfq_log(bfqd, fmt, args...) \
|
||||
blk_add_trace_msg((bfqd)->queue, "bfq " fmt, ##args)
|
||||
|
||||
#endif /* _BFQ_H */
|
||||
1616
block/bfq-wf2q.c
Normal file
1616
block/bfq-wf2q.c
Normal file
File diff suppressed because it is too large
Load diff
19
block/bio.c
19
block/bio.c
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/cgroup.h>
|
||||
|
||||
#include <trace/events/block.h>
|
||||
#include "blk.h"
|
||||
|
||||
/*
|
||||
* Test patch to inline a certain number of bi_io_vec's inside the bio
|
||||
|
|
@ -427,7 +428,8 @@ static void punt_bios_to_rescuer(struct bio_set *bs)
|
|||
* RETURNS:
|
||||
* Pointer to new bio on success, NULL on failure.
|
||||
*/
|
||||
struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
|
||||
struct bio *bio_alloc_bioset(gfp_t gfp_mask, unsigned int nr_iovecs,
|
||||
struct bio_set *bs)
|
||||
{
|
||||
gfp_t saved_gfp = gfp_mask;
|
||||
unsigned front_pad;
|
||||
|
|
@ -1824,6 +1826,11 @@ static inline bool bio_remaining_done(struct bio *bio)
|
|||
* bio_endio() will end I/O on the whole bio. bio_endio() is the preferred
|
||||
* way to end I/O on a bio. No one should call bi_end_io() directly on a
|
||||
* bio unless they own it and thus know that it has an end_io function.
|
||||
*
|
||||
* bio_endio() can be called several times on a bio that has been chained
|
||||
* using bio_chain(). The ->bi_end_io() function will only be called the
|
||||
* last time. At this point the BLK_TA_COMPLETE tracing event will be
|
||||
* generated if BIO_TRACE_COMPLETION is set.
|
||||
**/
|
||||
void bio_endio(struct bio *bio)
|
||||
{
|
||||
|
|
@ -1844,6 +1851,13 @@ again:
|
|||
goto again;
|
||||
}
|
||||
|
||||
if (bio->bi_bdev && bio_flagged(bio, BIO_TRACE_COMPLETION)) {
|
||||
trace_block_bio_complete(bdev_get_queue(bio->bi_bdev),
|
||||
bio, bio->bi_error);
|
||||
bio_clear_flag(bio, BIO_TRACE_COMPLETION);
|
||||
}
|
||||
|
||||
blk_throtl_bio_endio(bio);
|
||||
if (bio->bi_end_io)
|
||||
bio->bi_end_io(bio);
|
||||
}
|
||||
|
|
@ -1882,6 +1896,9 @@ struct bio *bio_split(struct bio *bio, int sectors,
|
|||
|
||||
bio_advance(bio, split->bi_iter.bi_size);
|
||||
|
||||
if (bio_flagged(bio, BIO_TRACE_COMPLETION))
|
||||
bio_set_flag(bio, BIO_TRACE_COMPLETION);
|
||||
|
||||
return split;
|
||||
}
|
||||
EXPORT_SYMBOL(bio_split);
|
||||
|
|
|
|||
|
|
@ -772,6 +772,27 @@ struct blkg_rwstat blkg_rwstat_recursive_sum(struct blkcg_gq *blkg,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(blkg_rwstat_recursive_sum);
|
||||
|
||||
/* Performs queue bypass and policy enabled checks then looks up blkg. */
|
||||
static struct blkcg_gq *blkg_lookup_check(struct blkcg *blkcg,
|
||||
const struct blkcg_policy *pol,
|
||||
struct request_queue *q)
|
||||
{
|
||||
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||
lockdep_assert_held(q->queue_lock);
|
||||
|
||||
if (!blkcg_policy_enabled(q, pol))
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
|
||||
/*
|
||||
* This could be the first entry point of blkcg implementation and
|
||||
* we shouldn't allow anything to go through for a bypassing queue.
|
||||
*/
|
||||
if (unlikely(blk_queue_bypass(q)))
|
||||
return ERR_PTR(blk_queue_dying(q) ? -ENODEV : -EBUSY);
|
||||
|
||||
return __blkg_lookup(blkcg, q, true /* update_hint */);
|
||||
}
|
||||
|
||||
/**
|
||||
* blkg_conf_prep - parse and prepare for per-blkg config update
|
||||
* @blkcg: target block cgroup
|
||||
|
|
@ -789,6 +810,7 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
|
|||
__acquires(rcu) __acquires(disk->queue->queue_lock)
|
||||
{
|
||||
struct gendisk *disk;
|
||||
struct request_queue *q;
|
||||
struct blkcg_gq *blkg;
|
||||
struct module *owner;
|
||||
unsigned int major, minor;
|
||||
|
|
@ -807,44 +829,95 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
|
|||
if (!disk)
|
||||
return -ENODEV;
|
||||
if (part) {
|
||||
owner = disk->fops->owner;
|
||||
put_disk(disk);
|
||||
module_put(owner);
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
q = disk->queue;
|
||||
|
||||
rcu_read_lock();
|
||||
spin_lock_irq(disk->queue->queue_lock);
|
||||
|
||||
if (blkcg_policy_enabled(disk->queue, pol))
|
||||
blkg = blkg_lookup_create(blkcg, disk->queue);
|
||||
else
|
||||
blkg = ERR_PTR(-EOPNOTSUPP);
|
||||
spin_lock_irq(q->queue_lock);
|
||||
|
||||
blkg = blkg_lookup_check(blkcg, pol, q);
|
||||
if (IS_ERR(blkg)) {
|
||||
ret = PTR_ERR(blkg);
|
||||
rcu_read_unlock();
|
||||
spin_unlock_irq(disk->queue->queue_lock);
|
||||
owner = disk->fops->owner;
|
||||
put_disk(disk);
|
||||
module_put(owner);
|
||||
/*
|
||||
* If queue was bypassing, we should retry. Do so after a
|
||||
* short msleep(). It isn't strictly necessary but queue
|
||||
* can be bypassing for some time and it's always nice to
|
||||
* avoid busy looping.
|
||||
*/
|
||||
if (ret == -EBUSY) {
|
||||
msleep(10);
|
||||
ret = restart_syscall();
|
||||
}
|
||||
return ret;
|
||||
goto fail_unlock;
|
||||
}
|
||||
|
||||
if (blkg)
|
||||
goto success;
|
||||
|
||||
/*
|
||||
* Create blkgs walking down from blkcg_root to @blkcg, so that all
|
||||
* non-root blkgs have access to their parents.
|
||||
*/
|
||||
while (true) {
|
||||
struct blkcg *pos = blkcg;
|
||||
struct blkcg *parent;
|
||||
struct blkcg_gq *new_blkg;
|
||||
|
||||
parent = blkcg_parent(blkcg);
|
||||
while (parent && !__blkg_lookup(parent, q, false)) {
|
||||
pos = parent;
|
||||
parent = blkcg_parent(parent);
|
||||
}
|
||||
|
||||
/* Drop locks to do new blkg allocation with GFP_KERNEL. */
|
||||
spin_unlock_irq(q->queue_lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
new_blkg = blkg_alloc(pos, q, GFP_KERNEL);
|
||||
if (unlikely(!new_blkg)) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
spin_lock_irq(q->queue_lock);
|
||||
|
||||
blkg = blkg_lookup_check(pos, pol, q);
|
||||
if (IS_ERR(blkg)) {
|
||||
ret = PTR_ERR(blkg);
|
||||
goto fail_unlock;
|
||||
}
|
||||
|
||||
if (blkg) {
|
||||
blkg_free(new_blkg);
|
||||
} else {
|
||||
blkg = blkg_create(pos, q, new_blkg);
|
||||
if (unlikely(IS_ERR(blkg))) {
|
||||
ret = PTR_ERR(blkg);
|
||||
goto fail_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos == blkcg)
|
||||
goto success;
|
||||
}
|
||||
success:
|
||||
ctx->disk = disk;
|
||||
ctx->blkg = blkg;
|
||||
ctx->body = body;
|
||||
return 0;
|
||||
|
||||
fail_unlock:
|
||||
spin_unlock_irq(q->queue_lock);
|
||||
rcu_read_unlock();
|
||||
fail:
|
||||
owner = disk->fops->owner;
|
||||
put_disk(disk);
|
||||
module_put(owner);
|
||||
/*
|
||||
* If queue was bypassing, we should retry. Do so after a
|
||||
* short msleep(). It isn't strictly necessary but queue
|
||||
* can be bypassing for some time and it's always nice to
|
||||
* avoid busy looping.
|
||||
*/
|
||||
if (ret == -EBUSY) {
|
||||
msleep(10);
|
||||
ret = restart_syscall();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blkg_conf_prep);
|
||||
|
||||
|
|
|
|||
143
block/blk-core.c
143
block/blk-core.c
|
|
@ -268,10 +268,8 @@ void blk_sync_queue(struct request_queue *q)
|
|||
struct blk_mq_hw_ctx *hctx;
|
||||
int i;
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
cancel_work_sync(&hctx->run_work);
|
||||
cancel_delayed_work_sync(&hctx->delay_work);
|
||||
}
|
||||
queue_for_each_hw_ctx(q, hctx, i)
|
||||
cancel_delayed_work_sync(&hctx->run_work);
|
||||
} else {
|
||||
cancel_delayed_work_sync(&q->delay_work);
|
||||
}
|
||||
|
|
@ -500,6 +498,13 @@ void blk_set_queue_dying(struct request_queue *q)
|
|||
queue_flag_set(QUEUE_FLAG_DYING, q);
|
||||
spin_unlock_irq(q->queue_lock);
|
||||
|
||||
/*
|
||||
* When queue DYING flag is set, we need to block new req
|
||||
* entering queue, so we call blk_freeze_queue_start() to
|
||||
* prevent I/O from crossing blk_queue_enter().
|
||||
*/
|
||||
blk_freeze_queue_start(q);
|
||||
|
||||
if (q->mq_ops)
|
||||
blk_mq_wake_waiters(q);
|
||||
else {
|
||||
|
|
@ -556,9 +561,13 @@ void blk_cleanup_queue(struct request_queue *q)
|
|||
* prevent that q->request_fn() gets invoked after draining finished.
|
||||
*/
|
||||
blk_freeze_queue(q);
|
||||
spin_lock_irq(lock);
|
||||
if (!q->mq_ops)
|
||||
if (!q->mq_ops) {
|
||||
spin_lock_irq(lock);
|
||||
__blk_drain_queue(q, true);
|
||||
} else {
|
||||
blk_mq_debugfs_unregister_mq(q);
|
||||
spin_lock_irq(lock);
|
||||
}
|
||||
queue_flag_set(QUEUE_FLAG_DEAD, q);
|
||||
spin_unlock_irq(lock);
|
||||
|
||||
|
|
@ -669,6 +678,15 @@ int blk_queue_enter(struct request_queue *q, bool nowait)
|
|||
if (nowait)
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* read pair of barrier in blk_freeze_queue_start(),
|
||||
* we need to order reading __PERCPU_REF_DEAD flag of
|
||||
* .q_usage_counter and reading .mq_freeze_depth or
|
||||
* queue dying flag, otherwise the following wait may
|
||||
* never return if the two reads are reordered.
|
||||
*/
|
||||
smp_rmb();
|
||||
|
||||
ret = wait_event_interruptible(q->mq_freeze_wq,
|
||||
!atomic_read(&q->mq_freeze_depth) ||
|
||||
blk_queue_dying(q));
|
||||
|
|
@ -720,6 +738,10 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
|
|||
if (!q->backing_dev_info)
|
||||
goto fail_split;
|
||||
|
||||
q->stats = blk_alloc_queue_stats();
|
||||
if (!q->stats)
|
||||
goto fail_stats;
|
||||
|
||||
q->backing_dev_info->ra_pages =
|
||||
(VM_MAX_READAHEAD * 1024) / PAGE_SIZE;
|
||||
q->backing_dev_info->capabilities = BDI_CAP_CGROUP_WRITEBACK;
|
||||
|
|
@ -776,6 +798,8 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
|
|||
fail_ref:
|
||||
percpu_ref_exit(&q->q_usage_counter);
|
||||
fail_bdi:
|
||||
blk_free_queue_stats(q->stats);
|
||||
fail_stats:
|
||||
bdi_put(q->backing_dev_info);
|
||||
fail_split:
|
||||
bioset_free(q->bio_split);
|
||||
|
|
@ -889,7 +913,6 @@ out_exit_flush_rq:
|
|||
q->exit_rq_fn(q, q->fq->flush_rq);
|
||||
out_free_flush_queue:
|
||||
blk_free_flush_queue(q->fq);
|
||||
wbt_exit(q);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(blk_init_allocated_queue);
|
||||
|
|
@ -1128,7 +1151,6 @@ static struct request *__get_request(struct request_list *rl, unsigned int op,
|
|||
|
||||
blk_rq_init(q, rq);
|
||||
blk_rq_set_rl(rq, rl);
|
||||
blk_rq_set_prio(rq, ioc);
|
||||
rq->cmd_flags = op;
|
||||
rq->rq_flags = rq_flags;
|
||||
|
||||
|
|
@ -1608,17 +1630,23 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
void init_request_from_bio(struct request *req, struct bio *bio)
|
||||
void blk_init_request_from_bio(struct request *req, struct bio *bio)
|
||||
{
|
||||
struct io_context *ioc = rq_ioc(bio);
|
||||
|
||||
if (bio->bi_opf & REQ_RAHEAD)
|
||||
req->cmd_flags |= REQ_FAILFAST_MASK;
|
||||
|
||||
req->errors = 0;
|
||||
req->__sector = bio->bi_iter.bi_sector;
|
||||
if (ioprio_valid(bio_prio(bio)))
|
||||
req->ioprio = bio_prio(bio);
|
||||
else if (ioc)
|
||||
req->ioprio = ioc->ioprio;
|
||||
else
|
||||
req->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, 0);
|
||||
blk_rq_bio_prep(req->q, req, bio);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_init_request_from_bio);
|
||||
|
||||
static blk_qc_t blk_queue_bio(struct request_queue *q, struct bio *bio)
|
||||
{
|
||||
|
|
@ -1709,7 +1737,7 @@ get_rq:
|
|||
* We don't worry about that case for efficiency. It won't happen
|
||||
* often, and the elevators are able to handle it.
|
||||
*/
|
||||
init_request_from_bio(req, bio);
|
||||
blk_init_request_from_bio(req, bio);
|
||||
|
||||
if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags))
|
||||
req->cpu = raw_smp_processor_id();
|
||||
|
|
@ -1936,7 +1964,13 @@ generic_make_request_checks(struct bio *bio)
|
|||
if (!blkcg_bio_issue_check(q, bio))
|
||||
return false;
|
||||
|
||||
trace_block_bio_queue(q, bio);
|
||||
if (!bio_flagged(bio, BIO_TRACE_COMPLETION)) {
|
||||
trace_block_bio_queue(q, bio);
|
||||
/* Now that enqueuing has been traced, we need to trace
|
||||
* completion as well.
|
||||
*/
|
||||
bio_set_flag(bio, BIO_TRACE_COMPLETION);
|
||||
}
|
||||
return true;
|
||||
|
||||
not_supported:
|
||||
|
|
@ -2478,7 +2512,7 @@ void blk_start_request(struct request *req)
|
|||
blk_dequeue_request(req);
|
||||
|
||||
if (test_bit(QUEUE_FLAG_STATS, &req->q->queue_flags)) {
|
||||
blk_stat_set_issue_time(&req->issue_stat);
|
||||
blk_stat_set_issue(&req->issue_stat, blk_rq_sectors(req));
|
||||
req->rq_flags |= RQF_STATS;
|
||||
wbt_issue(req->q->rq_wb, &req->issue_stat);
|
||||
}
|
||||
|
|
@ -2540,22 +2574,11 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes)
|
|||
{
|
||||
int total_bytes;
|
||||
|
||||
trace_block_rq_complete(req->q, req, nr_bytes);
|
||||
trace_block_rq_complete(req, error, nr_bytes);
|
||||
|
||||
if (!req->bio)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* For fs requests, rq is just carrier of independent bio's
|
||||
* and each partial completion should be handled separately.
|
||||
* Reset per-request error on each partial completion.
|
||||
*
|
||||
* TODO: tj: This is too subtle. It would be better to let
|
||||
* low level drivers do what they see fit.
|
||||
*/
|
||||
if (!blk_rq_is_passthrough(req))
|
||||
req->errors = 0;
|
||||
|
||||
if (error && !blk_rq_is_passthrough(req) &&
|
||||
!(req->rq_flags & RQF_QUIET)) {
|
||||
char *error_type;
|
||||
|
|
@ -2601,6 +2624,8 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes)
|
|||
if (bio_bytes == bio->bi_iter.bi_size)
|
||||
req->bio = bio->bi_next;
|
||||
|
||||
/* Completion has already been traced */
|
||||
bio_clear_flag(bio, BIO_TRACE_COMPLETION);
|
||||
req_bio_endio(req, bio, bio_bytes, error);
|
||||
|
||||
total_bytes += bio_bytes;
|
||||
|
|
@ -2699,7 +2724,7 @@ void blk_finish_request(struct request *req, int error)
|
|||
struct request_queue *q = req->q;
|
||||
|
||||
if (req->rq_flags & RQF_STATS)
|
||||
blk_stat_add(&q->rq_stats[rq_data_dir(req)], req);
|
||||
blk_stat_add(req);
|
||||
|
||||
if (req->rq_flags & RQF_QUEUED)
|
||||
blk_queue_end_tag(q, req);
|
||||
|
|
@ -2776,7 +2801,7 @@ static bool blk_end_bidi_request(struct request *rq, int error,
|
|||
* %false - we are done with this request
|
||||
* %true - still buffers pending for this request
|
||||
**/
|
||||
bool __blk_end_bidi_request(struct request *rq, int error,
|
||||
static bool __blk_end_bidi_request(struct request *rq, int error,
|
||||
unsigned int nr_bytes, unsigned int bidi_bytes)
|
||||
{
|
||||
if (blk_update_bidi_request(rq, error, nr_bytes, bidi_bytes))
|
||||
|
|
@ -2828,43 +2853,6 @@ void blk_end_request_all(struct request *rq, int error)
|
|||
}
|
||||
EXPORT_SYMBOL(blk_end_request_all);
|
||||
|
||||
/**
|
||||
* blk_end_request_cur - Helper function to finish the current request chunk.
|
||||
* @rq: the request to finish the current chunk for
|
||||
* @error: %0 for success, < %0 for error
|
||||
*
|
||||
* Description:
|
||||
* Complete the current consecutively mapped chunk from @rq.
|
||||
*
|
||||
* Return:
|
||||
* %false - we are done with this request
|
||||
* %true - still buffers pending for this request
|
||||
*/
|
||||
bool blk_end_request_cur(struct request *rq, int error)
|
||||
{
|
||||
return blk_end_request(rq, error, blk_rq_cur_bytes(rq));
|
||||
}
|
||||
EXPORT_SYMBOL(blk_end_request_cur);
|
||||
|
||||
/**
|
||||
* blk_end_request_err - Finish a request till the next failure boundary.
|
||||
* @rq: the request to finish till the next failure boundary for
|
||||
* @error: must be negative errno
|
||||
*
|
||||
* Description:
|
||||
* Complete @rq till the next failure boundary.
|
||||
*
|
||||
* Return:
|
||||
* %false - we are done with this request
|
||||
* %true - still buffers pending for this request
|
||||
*/
|
||||
bool blk_end_request_err(struct request *rq, int error)
|
||||
{
|
||||
WARN_ON(error >= 0);
|
||||
return blk_end_request(rq, error, blk_rq_err_bytes(rq));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_end_request_err);
|
||||
|
||||
/**
|
||||
* __blk_end_request - Helper function for drivers to complete the request.
|
||||
* @rq: the request being processed
|
||||
|
|
@ -2924,26 +2912,6 @@ bool __blk_end_request_cur(struct request *rq, int error)
|
|||
}
|
||||
EXPORT_SYMBOL(__blk_end_request_cur);
|
||||
|
||||
/**
|
||||
* __blk_end_request_err - Finish a request till the next failure boundary.
|
||||
* @rq: the request to finish till the next failure boundary for
|
||||
* @error: must be negative errno
|
||||
*
|
||||
* Description:
|
||||
* Complete @rq till the next failure boundary. Must be called
|
||||
* with queue lock held.
|
||||
*
|
||||
* Return:
|
||||
* %false - we are done with this request
|
||||
* %true - still buffers pending for this request
|
||||
*/
|
||||
bool __blk_end_request_err(struct request *rq, int error)
|
||||
{
|
||||
WARN_ON(error >= 0);
|
||||
return __blk_end_request(rq, error, blk_rq_err_bytes(rq));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__blk_end_request_err);
|
||||
|
||||
void blk_rq_bio_prep(struct request_queue *q, struct request *rq,
|
||||
struct bio *bio)
|
||||
{
|
||||
|
|
@ -3106,6 +3074,13 @@ int kblockd_schedule_work_on(int cpu, struct work_struct *work)
|
|||
}
|
||||
EXPORT_SYMBOL(kblockd_schedule_work_on);
|
||||
|
||||
int kblockd_mod_delayed_work_on(int cpu, struct delayed_work *dwork,
|
||||
unsigned long delay)
|
||||
{
|
||||
return mod_delayed_work_on(cpu, kblockd_workqueue, dwork, delay);
|
||||
}
|
||||
EXPORT_SYMBOL(kblockd_mod_delayed_work_on);
|
||||
|
||||
int kblockd_schedule_delayed_work(struct delayed_work *dwork,
|
||||
unsigned long delay)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -69,8 +69,7 @@ void blk_execute_rq_nowait(struct request_queue *q, struct gendisk *bd_disk,
|
|||
|
||||
if (unlikely(blk_queue_dying(q))) {
|
||||
rq->rq_flags |= RQF_QUIET;
|
||||
rq->errors = -ENXIO;
|
||||
__blk_end_request_all(rq, rq->errors);
|
||||
__blk_end_request_all(rq, -ENXIO);
|
||||
spin_unlock_irq(q->queue_lock);
|
||||
return;
|
||||
}
|
||||
|
|
@ -92,11 +91,10 @@ EXPORT_SYMBOL_GPL(blk_execute_rq_nowait);
|
|||
* Insert a fully prepared request at the back of the I/O scheduler queue
|
||||
* for execution and wait for completion.
|
||||
*/
|
||||
int blk_execute_rq(struct request_queue *q, struct gendisk *bd_disk,
|
||||
void blk_execute_rq(struct request_queue *q, struct gendisk *bd_disk,
|
||||
struct request *rq, int at_head)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(wait);
|
||||
int err = 0;
|
||||
unsigned long hang_check;
|
||||
|
||||
rq->end_io_data = &wait;
|
||||
|
|
@ -108,10 +106,5 @@ int blk_execute_rq(struct request_queue *q, struct gendisk *bd_disk,
|
|||
while (!wait_for_completion_io_timeout(&wait, hang_check * (HZ/2)));
|
||||
else
|
||||
wait_for_completion_io(&wait);
|
||||
|
||||
if (rq->errors)
|
||||
err = -EIO;
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(blk_execute_rq);
|
||||
|
|
|
|||
|
|
@ -447,7 +447,7 @@ void blk_insert_flush(struct request *rq)
|
|||
if (q->mq_ops)
|
||||
blk_mq_end_request(rq, 0);
|
||||
else
|
||||
__blk_end_bidi_request(rq, 0, 0, 0);
|
||||
__blk_end_request(rq, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -497,8 +497,7 @@ void blk_insert_flush(struct request *rq)
|
|||
* Description:
|
||||
* Issue a flush for the block device in question. Caller can supply
|
||||
* room for storing the error offset in case of a flush error, if they
|
||||
* wish to. If WAIT flag is not passed then caller may check only what
|
||||
* request was pushed in some internal queue for later handling.
|
||||
* wish to.
|
||||
*/
|
||||
int blkdev_issue_flush(struct block_device *bdev, gfp_t gfp_mask,
|
||||
sector_t *error_sector)
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ static int blk_integrity_nop_fn(struct blk_integrity_iter *iter)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct blk_integrity_profile nop_profile = {
|
||||
static const struct blk_integrity_profile nop_profile = {
|
||||
.name = "nop",
|
||||
.generate_fn = blk_integrity_nop_fn,
|
||||
.verify_fn = blk_integrity_nop_fn,
|
||||
|
|
@ -412,12 +412,13 @@ void blk_integrity_register(struct gendisk *disk, struct blk_integrity *template
|
|||
|
||||
bi->flags = BLK_INTEGRITY_VERIFY | BLK_INTEGRITY_GENERATE |
|
||||
template->flags;
|
||||
bi->interval_exp = ilog2(queue_logical_block_size(disk->queue));
|
||||
bi->interval_exp = template->interval_exp ? :
|
||||
ilog2(queue_logical_block_size(disk->queue));
|
||||
bi->profile = template->profile ? template->profile : &nop_profile;
|
||||
bi->tuple_size = template->tuple_size;
|
||||
bi->tag_size = template->tag_size;
|
||||
|
||||
blk_integrity_revalidate(disk);
|
||||
disk->queue->backing_dev_info->capabilities |= BDI_CAP_STABLE_WRITES;
|
||||
}
|
||||
EXPORT_SYMBOL(blk_integrity_register);
|
||||
|
||||
|
|
@ -430,26 +431,11 @@ EXPORT_SYMBOL(blk_integrity_register);
|
|||
*/
|
||||
void blk_integrity_unregister(struct gendisk *disk)
|
||||
{
|
||||
blk_integrity_revalidate(disk);
|
||||
disk->queue->backing_dev_info->capabilities &= ~BDI_CAP_STABLE_WRITES;
|
||||
memset(&disk->queue->integrity, 0, sizeof(struct blk_integrity));
|
||||
}
|
||||
EXPORT_SYMBOL(blk_integrity_unregister);
|
||||
|
||||
void blk_integrity_revalidate(struct gendisk *disk)
|
||||
{
|
||||
struct blk_integrity *bi = &disk->queue->integrity;
|
||||
|
||||
if (!(disk->flags & GENHD_FL_UP))
|
||||
return;
|
||||
|
||||
if (bi->profile)
|
||||
disk->queue->backing_dev_info->capabilities |=
|
||||
BDI_CAP_STABLE_WRITES;
|
||||
else
|
||||
disk->queue->backing_dev_info->capabilities &=
|
||||
~BDI_CAP_STABLE_WRITES;
|
||||
}
|
||||
|
||||
void blk_integrity_add(struct gendisk *disk)
|
||||
{
|
||||
if (kobject_init_and_add(&disk->integrity_kobj, &integrity_ktype,
|
||||
|
|
|
|||
|
|
@ -37,17 +37,12 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
|
|||
return -ENXIO;
|
||||
|
||||
if (flags & BLKDEV_DISCARD_SECURE) {
|
||||
if (flags & BLKDEV_DISCARD_ZERO)
|
||||
return -EOPNOTSUPP;
|
||||
if (!blk_queue_secure_erase(q))
|
||||
return -EOPNOTSUPP;
|
||||
op = REQ_OP_SECURE_ERASE;
|
||||
} else {
|
||||
if (!blk_queue_discard(q))
|
||||
return -EOPNOTSUPP;
|
||||
if ((flags & BLKDEV_DISCARD_ZERO) &&
|
||||
!q->limits.discard_zeroes_data)
|
||||
return -EOPNOTSUPP;
|
||||
op = REQ_OP_DISCARD;
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +104,7 @@ EXPORT_SYMBOL(__blkdev_issue_discard);
|
|||
* @sector: start sector
|
||||
* @nr_sects: number of sectors to discard
|
||||
* @gfp_mask: memory allocation flags (for bio_alloc)
|
||||
* @flags: BLKDEV_IFL_* flags to control behaviour
|
||||
* @flags: BLKDEV_DISCARD_* flags to control behaviour
|
||||
*
|
||||
* Description:
|
||||
* Issue a discard request for the sectors in question.
|
||||
|
|
@ -126,7 +121,7 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
|
|||
&bio);
|
||||
if (!ret && bio) {
|
||||
ret = submit_bio_wait(bio);
|
||||
if (ret == -EOPNOTSUPP && !(flags & BLKDEV_DISCARD_ZERO))
|
||||
if (ret == -EOPNOTSUPP)
|
||||
ret = 0;
|
||||
bio_put(bio);
|
||||
}
|
||||
|
|
@ -226,20 +221,9 @@ int blkdev_issue_write_same(struct block_device *bdev, sector_t sector,
|
|||
}
|
||||
EXPORT_SYMBOL(blkdev_issue_write_same);
|
||||
|
||||
/**
|
||||
* __blkdev_issue_write_zeroes - generate number of bios with WRITE ZEROES
|
||||
* @bdev: blockdev to issue
|
||||
* @sector: start sector
|
||||
* @nr_sects: number of sectors to write
|
||||
* @gfp_mask: memory allocation flags (for bio_alloc)
|
||||
* @biop: pointer to anchor bio
|
||||
*
|
||||
* Description:
|
||||
* Generate and issue number of bios(REQ_OP_WRITE_ZEROES) with zerofiled pages.
|
||||
*/
|
||||
static int __blkdev_issue_write_zeroes(struct block_device *bdev,
|
||||
sector_t sector, sector_t nr_sects, gfp_t gfp_mask,
|
||||
struct bio **biop)
|
||||
struct bio **biop, unsigned flags)
|
||||
{
|
||||
struct bio *bio = *biop;
|
||||
unsigned int max_write_zeroes_sectors;
|
||||
|
|
@ -258,7 +242,9 @@ static int __blkdev_issue_write_zeroes(struct block_device *bdev,
|
|||
bio = next_bio(bio, 0, gfp_mask);
|
||||
bio->bi_iter.bi_sector = sector;
|
||||
bio->bi_bdev = bdev;
|
||||
bio_set_op_attrs(bio, REQ_OP_WRITE_ZEROES, 0);
|
||||
bio->bi_opf = REQ_OP_WRITE_ZEROES;
|
||||
if (flags & BLKDEV_ZERO_NOUNMAP)
|
||||
bio->bi_opf |= REQ_NOUNMAP;
|
||||
|
||||
if (nr_sects > max_write_zeroes_sectors) {
|
||||
bio->bi_iter.bi_size = max_write_zeroes_sectors << 9;
|
||||
|
|
@ -282,14 +268,27 @@ static int __blkdev_issue_write_zeroes(struct block_device *bdev,
|
|||
* @nr_sects: number of sectors to write
|
||||
* @gfp_mask: memory allocation flags (for bio_alloc)
|
||||
* @biop: pointer to anchor bio
|
||||
* @discard: discard flag
|
||||
* @flags: controls detailed behavior
|
||||
*
|
||||
* Description:
|
||||
* Generate and issue number of bios with zerofiled pages.
|
||||
* Zero-fill a block range, either using hardware offload or by explicitly
|
||||
* writing zeroes to the device.
|
||||
*
|
||||
* Note that this function may fail with -EOPNOTSUPP if the driver signals
|
||||
* zeroing offload support, but the device fails to process the command (for
|
||||
* some devices there is no non-destructive way to verify whether this
|
||||
* operation is actually supported). In this case the caller should call
|
||||
* retry the call to blkdev_issue_zeroout() and the fallback path will be used.
|
||||
*
|
||||
* If a device is using logical block provisioning, the underlying space will
|
||||
* not be released if %flags contains BLKDEV_ZERO_NOUNMAP.
|
||||
*
|
||||
* If %flags contains BLKDEV_ZERO_NOFALLBACK, the function will return
|
||||
* -EOPNOTSUPP if no explicit hardware offload for zeroing is provided.
|
||||
*/
|
||||
int __blkdev_issue_zeroout(struct block_device *bdev, sector_t sector,
|
||||
sector_t nr_sects, gfp_t gfp_mask, struct bio **biop,
|
||||
bool discard)
|
||||
unsigned flags)
|
||||
{
|
||||
int ret;
|
||||
int bi_size = 0;
|
||||
|
|
@ -302,8 +301,8 @@ int __blkdev_issue_zeroout(struct block_device *bdev, sector_t sector,
|
|||
return -EINVAL;
|
||||
|
||||
ret = __blkdev_issue_write_zeroes(bdev, sector, nr_sects, gfp_mask,
|
||||
biop);
|
||||
if (ret == 0 || (ret && ret != -EOPNOTSUPP))
|
||||
biop, flags);
|
||||
if (ret != -EOPNOTSUPP || (flags & BLKDEV_ZERO_NOFALLBACK))
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
|
|
@ -337,40 +336,23 @@ EXPORT_SYMBOL(__blkdev_issue_zeroout);
|
|||
* @sector: start sector
|
||||
* @nr_sects: number of sectors to write
|
||||
* @gfp_mask: memory allocation flags (for bio_alloc)
|
||||
* @discard: whether to discard the block range
|
||||
* @flags: controls detailed behavior
|
||||
*
|
||||
* Description:
|
||||
* Zero-fill a block range. If the discard flag is set and the block
|
||||
* device guarantees that subsequent READ operations to the block range
|
||||
* in question will return zeroes, the blocks will be discarded. Should
|
||||
* the discard request fail, if the discard flag is not set, or if
|
||||
* discard_zeroes_data is not supported, this function will resort to
|
||||
* zeroing the blocks manually, thus provisioning (allocating,
|
||||
* anchoring) them. If the block device supports WRITE ZEROES or WRITE SAME
|
||||
* command(s), blkdev_issue_zeroout() will use it to optimize the process of
|
||||
* clearing the block range. Otherwise the zeroing will be performed
|
||||
* using regular WRITE calls.
|
||||
* Zero-fill a block range, either using hardware offload or by explicitly
|
||||
* writing zeroes to the device. See __blkdev_issue_zeroout() for the
|
||||
* valid values for %flags.
|
||||
*/
|
||||
int blkdev_issue_zeroout(struct block_device *bdev, sector_t sector,
|
||||
sector_t nr_sects, gfp_t gfp_mask, bool discard)
|
||||
sector_t nr_sects, gfp_t gfp_mask, unsigned flags)
|
||||
{
|
||||
int ret;
|
||||
struct bio *bio = NULL;
|
||||
struct blk_plug plug;
|
||||
|
||||
if (discard) {
|
||||
if (!blkdev_issue_discard(bdev, sector, nr_sects, gfp_mask,
|
||||
BLKDEV_DISCARD_ZERO))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!blkdev_issue_write_same(bdev, sector, nr_sects, gfp_mask,
|
||||
ZERO_PAGE(0)))
|
||||
return 0;
|
||||
|
||||
blk_start_plug(&plug);
|
||||
ret = __blkdev_issue_zeroout(bdev, sector, nr_sects, gfp_mask,
|
||||
&bio, discard);
|
||||
&bio, flags);
|
||||
if (ret == 0 && bio) {
|
||||
ret = submit_bio_wait(bio);
|
||||
bio_put(bio);
|
||||
|
|
|
|||
|
|
@ -54,6 +54,20 @@ static struct bio *blk_bio_discard_split(struct request_queue *q,
|
|||
return bio_split(bio, split_sectors, GFP_NOIO, bs);
|
||||
}
|
||||
|
||||
static struct bio *blk_bio_write_zeroes_split(struct request_queue *q,
|
||||
struct bio *bio, struct bio_set *bs, unsigned *nsegs)
|
||||
{
|
||||
*nsegs = 1;
|
||||
|
||||
if (!q->limits.max_write_zeroes_sectors)
|
||||
return NULL;
|
||||
|
||||
if (bio_sectors(bio) <= q->limits.max_write_zeroes_sectors)
|
||||
return NULL;
|
||||
|
||||
return bio_split(bio, q->limits.max_write_zeroes_sectors, GFP_NOIO, bs);
|
||||
}
|
||||
|
||||
static struct bio *blk_bio_write_same_split(struct request_queue *q,
|
||||
struct bio *bio,
|
||||
struct bio_set *bs,
|
||||
|
|
@ -200,8 +214,7 @@ void blk_queue_split(struct request_queue *q, struct bio **bio,
|
|||
split = blk_bio_discard_split(q, *bio, bs, &nsegs);
|
||||
break;
|
||||
case REQ_OP_WRITE_ZEROES:
|
||||
split = NULL;
|
||||
nsegs = (*bio)->bi_phys_segments;
|
||||
split = blk_bio_write_zeroes_split(q, *bio, bs, &nsegs);
|
||||
break;
|
||||
case REQ_OP_WRITE_SAME:
|
||||
split = blk_bio_write_same_split(q, *bio, bs, &nsegs);
|
||||
|
|
|
|||
|
|
@ -43,11 +43,157 @@ static int blk_mq_debugfs_seq_open(struct inode *inode, struct file *file,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int blk_flags_show(struct seq_file *m, const unsigned long flags,
|
||||
const char *const *flag_name, int flag_name_count)
|
||||
{
|
||||
bool sep = false;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(flags) * BITS_PER_BYTE; i++) {
|
||||
if (!(flags & BIT(i)))
|
||||
continue;
|
||||
if (sep)
|
||||
seq_puts(m, " ");
|
||||
sep = true;
|
||||
if (i < flag_name_count && flag_name[i])
|
||||
seq_puts(m, flag_name[i]);
|
||||
else
|
||||
seq_printf(m, "%d", i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const blk_queue_flag_name[] = {
|
||||
[QUEUE_FLAG_QUEUED] = "QUEUED",
|
||||
[QUEUE_FLAG_STOPPED] = "STOPPED",
|
||||
[QUEUE_FLAG_SYNCFULL] = "SYNCFULL",
|
||||
[QUEUE_FLAG_ASYNCFULL] = "ASYNCFULL",
|
||||
[QUEUE_FLAG_DYING] = "DYING",
|
||||
[QUEUE_FLAG_BYPASS] = "BYPASS",
|
||||
[QUEUE_FLAG_BIDI] = "BIDI",
|
||||
[QUEUE_FLAG_NOMERGES] = "NOMERGES",
|
||||
[QUEUE_FLAG_SAME_COMP] = "SAME_COMP",
|
||||
[QUEUE_FLAG_FAIL_IO] = "FAIL_IO",
|
||||
[QUEUE_FLAG_STACKABLE] = "STACKABLE",
|
||||
[QUEUE_FLAG_NONROT] = "NONROT",
|
||||
[QUEUE_FLAG_IO_STAT] = "IO_STAT",
|
||||
[QUEUE_FLAG_DISCARD] = "DISCARD",
|
||||
[QUEUE_FLAG_NOXMERGES] = "NOXMERGES",
|
||||
[QUEUE_FLAG_ADD_RANDOM] = "ADD_RANDOM",
|
||||
[QUEUE_FLAG_SECERASE] = "SECERASE",
|
||||
[QUEUE_FLAG_SAME_FORCE] = "SAME_FORCE",
|
||||
[QUEUE_FLAG_DEAD] = "DEAD",
|
||||
[QUEUE_FLAG_INIT_DONE] = "INIT_DONE",
|
||||
[QUEUE_FLAG_NO_SG_MERGE] = "NO_SG_MERGE",
|
||||
[QUEUE_FLAG_POLL] = "POLL",
|
||||
[QUEUE_FLAG_WC] = "WC",
|
||||
[QUEUE_FLAG_FUA] = "FUA",
|
||||
[QUEUE_FLAG_FLUSH_NQ] = "FLUSH_NQ",
|
||||
[QUEUE_FLAG_DAX] = "DAX",
|
||||
[QUEUE_FLAG_STATS] = "STATS",
|
||||
[QUEUE_FLAG_POLL_STATS] = "POLL_STATS",
|
||||
[QUEUE_FLAG_REGISTERED] = "REGISTERED",
|
||||
};
|
||||
|
||||
static int blk_queue_flags_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct request_queue *q = m->private;
|
||||
|
||||
blk_flags_show(m, q->queue_flags, blk_queue_flag_name,
|
||||
ARRAY_SIZE(blk_queue_flag_name));
|
||||
seq_puts(m, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t blk_queue_flags_store(struct file *file, const char __user *ubuf,
|
||||
size_t len, loff_t *offp)
|
||||
{
|
||||
struct request_queue *q = file_inode(file)->i_private;
|
||||
char op[16] = { }, *s;
|
||||
|
||||
len = min(len, sizeof(op) - 1);
|
||||
if (copy_from_user(op, ubuf, len))
|
||||
return -EFAULT;
|
||||
s = op;
|
||||
strsep(&s, " \t\n"); /* strip trailing whitespace */
|
||||
if (strcmp(op, "run") == 0) {
|
||||
blk_mq_run_hw_queues(q, true);
|
||||
} else if (strcmp(op, "start") == 0) {
|
||||
blk_mq_start_stopped_hw_queues(q, true);
|
||||
} else {
|
||||
pr_err("%s: unsupported operation %s. Use either 'run' or 'start'\n",
|
||||
__func__, op);
|
||||
return -EINVAL;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int blk_queue_flags_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, blk_queue_flags_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations blk_queue_flags_fops = {
|
||||
.open = blk_queue_flags_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.write = blk_queue_flags_store,
|
||||
};
|
||||
|
||||
static void print_stat(struct seq_file *m, struct blk_rq_stat *stat)
|
||||
{
|
||||
if (stat->nr_samples) {
|
||||
seq_printf(m, "samples=%d, mean=%lld, min=%llu, max=%llu",
|
||||
stat->nr_samples, stat->mean, stat->min, stat->max);
|
||||
} else {
|
||||
seq_puts(m, "samples=0");
|
||||
}
|
||||
}
|
||||
|
||||
static int queue_poll_stat_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct request_queue *q = m->private;
|
||||
int bucket;
|
||||
|
||||
for (bucket = 0; bucket < BLK_MQ_POLL_STATS_BKTS/2; bucket++) {
|
||||
seq_printf(m, "read (%d Bytes): ", 1 << (9+bucket));
|
||||
print_stat(m, &q->poll_stat[2*bucket]);
|
||||
seq_puts(m, "\n");
|
||||
|
||||
seq_printf(m, "write (%d Bytes): ", 1 << (9+bucket));
|
||||
print_stat(m, &q->poll_stat[2*bucket+1]);
|
||||
seq_puts(m, "\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int queue_poll_stat_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, queue_poll_stat_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations queue_poll_stat_fops = {
|
||||
.open = queue_poll_stat_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static const char *const hctx_state_name[] = {
|
||||
[BLK_MQ_S_STOPPED] = "STOPPED",
|
||||
[BLK_MQ_S_TAG_ACTIVE] = "TAG_ACTIVE",
|
||||
[BLK_MQ_S_SCHED_RESTART] = "SCHED_RESTART",
|
||||
[BLK_MQ_S_TAG_WAITING] = "TAG_WAITING",
|
||||
|
||||
};
|
||||
static int hctx_state_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx = m->private;
|
||||
|
||||
seq_printf(m, "0x%lx\n", hctx->state);
|
||||
blk_flags_show(m, hctx->state, hctx_state_name,
|
||||
ARRAY_SIZE(hctx_state_name));
|
||||
seq_puts(m, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -63,11 +209,35 @@ static const struct file_operations hctx_state_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
static const char *const alloc_policy_name[] = {
|
||||
[BLK_TAG_ALLOC_FIFO] = "fifo",
|
||||
[BLK_TAG_ALLOC_RR] = "rr",
|
||||
};
|
||||
|
||||
static const char *const hctx_flag_name[] = {
|
||||
[ilog2(BLK_MQ_F_SHOULD_MERGE)] = "SHOULD_MERGE",
|
||||
[ilog2(BLK_MQ_F_TAG_SHARED)] = "TAG_SHARED",
|
||||
[ilog2(BLK_MQ_F_SG_MERGE)] = "SG_MERGE",
|
||||
[ilog2(BLK_MQ_F_BLOCKING)] = "BLOCKING",
|
||||
[ilog2(BLK_MQ_F_NO_SCHED)] = "NO_SCHED",
|
||||
};
|
||||
|
||||
static int hctx_flags_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx = m->private;
|
||||
const int alloc_policy = BLK_MQ_FLAG_TO_ALLOC_POLICY(hctx->flags);
|
||||
|
||||
seq_printf(m, "0x%lx\n", hctx->flags);
|
||||
seq_puts(m, "alloc_policy=");
|
||||
if (alloc_policy < ARRAY_SIZE(alloc_policy_name) &&
|
||||
alloc_policy_name[alloc_policy])
|
||||
seq_puts(m, alloc_policy_name[alloc_policy]);
|
||||
else
|
||||
seq_printf(m, "%d", alloc_policy);
|
||||
seq_puts(m, " ");
|
||||
blk_flags_show(m,
|
||||
hctx->flags ^ BLK_ALLOC_POLICY_TO_MQ_FLAG(alloc_policy),
|
||||
hctx_flag_name, ARRAY_SIZE(hctx_flag_name));
|
||||
seq_puts(m, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -83,13 +253,83 @@ static const struct file_operations hctx_flags_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
static const char *const op_name[] = {
|
||||
[REQ_OP_READ] = "READ",
|
||||
[REQ_OP_WRITE] = "WRITE",
|
||||
[REQ_OP_FLUSH] = "FLUSH",
|
||||
[REQ_OP_DISCARD] = "DISCARD",
|
||||
[REQ_OP_ZONE_REPORT] = "ZONE_REPORT",
|
||||
[REQ_OP_SECURE_ERASE] = "SECURE_ERASE",
|
||||
[REQ_OP_ZONE_RESET] = "ZONE_RESET",
|
||||
[REQ_OP_WRITE_SAME] = "WRITE_SAME",
|
||||
[REQ_OP_WRITE_ZEROES] = "WRITE_ZEROES",
|
||||
[REQ_OP_SCSI_IN] = "SCSI_IN",
|
||||
[REQ_OP_SCSI_OUT] = "SCSI_OUT",
|
||||
[REQ_OP_DRV_IN] = "DRV_IN",
|
||||
[REQ_OP_DRV_OUT] = "DRV_OUT",
|
||||
};
|
||||
|
||||
static const char *const cmd_flag_name[] = {
|
||||
[__REQ_FAILFAST_DEV] = "FAILFAST_DEV",
|
||||
[__REQ_FAILFAST_TRANSPORT] = "FAILFAST_TRANSPORT",
|
||||
[__REQ_FAILFAST_DRIVER] = "FAILFAST_DRIVER",
|
||||
[__REQ_SYNC] = "SYNC",
|
||||
[__REQ_META] = "META",
|
||||
[__REQ_PRIO] = "PRIO",
|
||||
[__REQ_NOMERGE] = "NOMERGE",
|
||||
[__REQ_IDLE] = "IDLE",
|
||||
[__REQ_INTEGRITY] = "INTEGRITY",
|
||||
[__REQ_FUA] = "FUA",
|
||||
[__REQ_PREFLUSH] = "PREFLUSH",
|
||||
[__REQ_RAHEAD] = "RAHEAD",
|
||||
[__REQ_BACKGROUND] = "BACKGROUND",
|
||||
[__REQ_NR_BITS] = "NR_BITS",
|
||||
};
|
||||
|
||||
static const char *const rqf_name[] = {
|
||||
[ilog2((__force u32)RQF_SORTED)] = "SORTED",
|
||||
[ilog2((__force u32)RQF_STARTED)] = "STARTED",
|
||||
[ilog2((__force u32)RQF_QUEUED)] = "QUEUED",
|
||||
[ilog2((__force u32)RQF_SOFTBARRIER)] = "SOFTBARRIER",
|
||||
[ilog2((__force u32)RQF_FLUSH_SEQ)] = "FLUSH_SEQ",
|
||||
[ilog2((__force u32)RQF_MIXED_MERGE)] = "MIXED_MERGE",
|
||||
[ilog2((__force u32)RQF_MQ_INFLIGHT)] = "MQ_INFLIGHT",
|
||||
[ilog2((__force u32)RQF_DONTPREP)] = "DONTPREP",
|
||||
[ilog2((__force u32)RQF_PREEMPT)] = "PREEMPT",
|
||||
[ilog2((__force u32)RQF_COPY_USER)] = "COPY_USER",
|
||||
[ilog2((__force u32)RQF_FAILED)] = "FAILED",
|
||||
[ilog2((__force u32)RQF_QUIET)] = "QUIET",
|
||||
[ilog2((__force u32)RQF_ELVPRIV)] = "ELVPRIV",
|
||||
[ilog2((__force u32)RQF_IO_STAT)] = "IO_STAT",
|
||||
[ilog2((__force u32)RQF_ALLOCED)] = "ALLOCED",
|
||||
[ilog2((__force u32)RQF_PM)] = "PM",
|
||||
[ilog2((__force u32)RQF_HASHED)] = "HASHED",
|
||||
[ilog2((__force u32)RQF_STATS)] = "STATS",
|
||||
[ilog2((__force u32)RQF_SPECIAL_PAYLOAD)] = "SPECIAL_PAYLOAD",
|
||||
};
|
||||
|
||||
static int blk_mq_debugfs_rq_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct request *rq = list_entry_rq(v);
|
||||
const struct blk_mq_ops *const mq_ops = rq->q->mq_ops;
|
||||
const unsigned int op = rq->cmd_flags & REQ_OP_MASK;
|
||||
|
||||
seq_printf(m, "%p {.cmd_flags=0x%x, .rq_flags=0x%x, .tag=%d, .internal_tag=%d}\n",
|
||||
rq, rq->cmd_flags, (__force unsigned int)rq->rq_flags,
|
||||
rq->tag, rq->internal_tag);
|
||||
seq_printf(m, "%p {.op=", rq);
|
||||
if (op < ARRAY_SIZE(op_name) && op_name[op])
|
||||
seq_printf(m, "%s", op_name[op]);
|
||||
else
|
||||
seq_printf(m, "%d", op);
|
||||
seq_puts(m, ", .cmd_flags=");
|
||||
blk_flags_show(m, rq->cmd_flags & ~REQ_OP_MASK, cmd_flag_name,
|
||||
ARRAY_SIZE(cmd_flag_name));
|
||||
seq_puts(m, ", .rq_flags=");
|
||||
blk_flags_show(m, (__force unsigned int)rq->rq_flags, rqf_name,
|
||||
ARRAY_SIZE(rqf_name));
|
||||
seq_printf(m, ", .tag=%d, .internal_tag=%d", rq->tag,
|
||||
rq->internal_tag);
|
||||
if (mq_ops->show_rq)
|
||||
mq_ops->show_rq(m, rq);
|
||||
seq_puts(m, "}\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -322,60 +562,6 @@ static const struct file_operations hctx_io_poll_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
static void print_stat(struct seq_file *m, struct blk_rq_stat *stat)
|
||||
{
|
||||
seq_printf(m, "samples=%d, mean=%lld, min=%llu, max=%llu",
|
||||
stat->nr_samples, stat->mean, stat->min, stat->max);
|
||||
}
|
||||
|
||||
static int hctx_stats_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx = m->private;
|
||||
struct blk_rq_stat stat[2];
|
||||
|
||||
blk_stat_init(&stat[BLK_STAT_READ]);
|
||||
blk_stat_init(&stat[BLK_STAT_WRITE]);
|
||||
|
||||
blk_hctx_stat_get(hctx, stat);
|
||||
|
||||
seq_puts(m, "read: ");
|
||||
print_stat(m, &stat[BLK_STAT_READ]);
|
||||
seq_puts(m, "\n");
|
||||
|
||||
seq_puts(m, "write: ");
|
||||
print_stat(m, &stat[BLK_STAT_WRITE]);
|
||||
seq_puts(m, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hctx_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, hctx_stats_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t hctx_stats_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *m = file->private_data;
|
||||
struct blk_mq_hw_ctx *hctx = m->private;
|
||||
struct blk_mq_ctx *ctx;
|
||||
int i;
|
||||
|
||||
hctx_for_each_ctx(hctx, ctx, i) {
|
||||
blk_stat_init(&ctx->stat[BLK_STAT_READ]);
|
||||
blk_stat_init(&ctx->stat[BLK_STAT_WRITE]);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations hctx_stats_fops = {
|
||||
.open = hctx_stats_open,
|
||||
.read = seq_read,
|
||||
.write = hctx_stats_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int hctx_dispatched_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx = m->private;
|
||||
|
|
@ -636,6 +822,12 @@ static const struct file_operations ctx_completed_fops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
static const struct blk_mq_debugfs_attr blk_mq_debugfs_queue_attrs[] = {
|
||||
{"poll_stat", 0400, &queue_poll_stat_fops},
|
||||
{"state", 0600, &blk_queue_flags_fops},
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct blk_mq_debugfs_attr blk_mq_debugfs_hctx_attrs[] = {
|
||||
{"state", 0400, &hctx_state_fops},
|
||||
{"flags", 0400, &hctx_flags_fops},
|
||||
|
|
@ -646,7 +838,6 @@ static const struct blk_mq_debugfs_attr blk_mq_debugfs_hctx_attrs[] = {
|
|||
{"sched_tags", 0400, &hctx_sched_tags_fops},
|
||||
{"sched_tags_bitmap", 0400, &hctx_sched_tags_bitmap_fops},
|
||||
{"io_poll", 0600, &hctx_io_poll_fops},
|
||||
{"stats", 0600, &hctx_stats_fops},
|
||||
{"dispatched", 0600, &hctx_dispatched_fops},
|
||||
{"queued", 0600, &hctx_queued_fops},
|
||||
{"run", 0600, &hctx_run_fops},
|
||||
|
|
@ -662,16 +853,17 @@ static const struct blk_mq_debugfs_attr blk_mq_debugfs_ctx_attrs[] = {
|
|||
{},
|
||||
};
|
||||
|
||||
int blk_mq_debugfs_register(struct request_queue *q, const char *name)
|
||||
int blk_mq_debugfs_register(struct request_queue *q)
|
||||
{
|
||||
if (!blk_debugfs_root)
|
||||
return -ENOENT;
|
||||
|
||||
q->debugfs_dir = debugfs_create_dir(name, blk_debugfs_root);
|
||||
q->debugfs_dir = debugfs_create_dir(kobject_name(q->kobj.parent),
|
||||
blk_debugfs_root);
|
||||
if (!q->debugfs_dir)
|
||||
goto err;
|
||||
|
||||
if (blk_mq_debugfs_register_hctxs(q))
|
||||
if (blk_mq_debugfs_register_mq(q))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
|
@ -741,7 +933,7 @@ static int blk_mq_debugfs_register_hctx(struct request_queue *q,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int blk_mq_debugfs_register_hctxs(struct request_queue *q)
|
||||
int blk_mq_debugfs_register_mq(struct request_queue *q)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
int i;
|
||||
|
|
@ -753,6 +945,9 @@ int blk_mq_debugfs_register_hctxs(struct request_queue *q)
|
|||
if (!q->mq_debugfs_dir)
|
||||
goto err;
|
||||
|
||||
if (!debugfs_create_files(q->mq_debugfs_dir, q, blk_mq_debugfs_queue_attrs))
|
||||
goto err;
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
if (blk_mq_debugfs_register_hctx(q, hctx))
|
||||
goto err;
|
||||
|
|
@ -761,11 +956,11 @@ int blk_mq_debugfs_register_hctxs(struct request_queue *q)
|
|||
return 0;
|
||||
|
||||
err:
|
||||
blk_mq_debugfs_unregister_hctxs(q);
|
||||
blk_mq_debugfs_unregister_mq(q);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void blk_mq_debugfs_unregister_hctxs(struct request_queue *q)
|
||||
void blk_mq_debugfs_unregister_mq(struct request_queue *q)
|
||||
{
|
||||
debugfs_remove_recursive(q->mq_debugfs_dir);
|
||||
q->mq_debugfs_dir = NULL;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
* @pdev: PCI device associated with @set.
|
||||
*
|
||||
* This function assumes the PCI device @pdev has at least as many available
|
||||
* interrupt vetors as @set has queues. It will then queuery the vector
|
||||
* interrupt vectors as @set has queues. It will then query the vector
|
||||
* corresponding to each queue for it's affinity mask and built queue mapping
|
||||
* that maps a queue to the CPUs that have irq affinity for the corresponding
|
||||
* vector.
|
||||
|
|
|
|||
|
|
@ -30,43 +30,6 @@ void blk_mq_sched_free_hctx_data(struct request_queue *q,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(blk_mq_sched_free_hctx_data);
|
||||
|
||||
int blk_mq_sched_init_hctx_data(struct request_queue *q, size_t size,
|
||||
int (*init)(struct blk_mq_hw_ctx *),
|
||||
void (*exit)(struct blk_mq_hw_ctx *))
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
hctx->sched_data = kmalloc_node(size, GFP_KERNEL, hctx->numa_node);
|
||||
if (!hctx->sched_data) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (init) {
|
||||
ret = init(hctx);
|
||||
if (ret) {
|
||||
/*
|
||||
* We don't want to give exit() a partially
|
||||
* initialized sched_data. init() must clean up
|
||||
* if it fails.
|
||||
*/
|
||||
kfree(hctx->sched_data);
|
||||
hctx->sched_data = NULL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
blk_mq_sched_free_hctx_data(q, exit);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_mq_sched_init_hctx_data);
|
||||
|
||||
static void __blk_mq_sched_assign_ioc(struct request_queue *q,
|
||||
struct request *rq,
|
||||
struct bio *bio,
|
||||
|
|
@ -119,7 +82,11 @@ struct request *blk_mq_sched_get_request(struct request_queue *q,
|
|||
if (likely(!data->hctx))
|
||||
data->hctx = blk_mq_map_queue(q, data->ctx->cpu);
|
||||
|
||||
if (e) {
|
||||
/*
|
||||
* For a reserved tag, allocate a normal request since we might
|
||||
* have driver dependencies on the value of the internal tag.
|
||||
*/
|
||||
if (e && !(data->flags & BLK_MQ_REQ_RESERVED)) {
|
||||
data->flags |= BLK_MQ_REQ_INTERNAL;
|
||||
|
||||
/*
|
||||
|
|
@ -227,22 +194,6 @@ void blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx)
|
|||
}
|
||||
}
|
||||
|
||||
void blk_mq_sched_move_to_dispatch(struct blk_mq_hw_ctx *hctx,
|
||||
struct list_head *rq_list,
|
||||
struct request *(*get_rq)(struct blk_mq_hw_ctx *))
|
||||
{
|
||||
do {
|
||||
struct request *rq;
|
||||
|
||||
rq = get_rq(hctx);
|
||||
if (!rq)
|
||||
break;
|
||||
|
||||
list_add_tail(&rq->queuelist, rq_list);
|
||||
} while (1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_mq_sched_move_to_dispatch);
|
||||
|
||||
bool blk_mq_sched_try_merge(struct request_queue *q, struct bio *bio,
|
||||
struct request **merged_request)
|
||||
{
|
||||
|
|
@ -508,11 +459,24 @@ int blk_mq_sched_init_hctx(struct request_queue *q, struct blk_mq_hw_ctx *hctx,
|
|||
unsigned int hctx_idx)
|
||||
{
|
||||
struct elevator_queue *e = q->elevator;
|
||||
int ret;
|
||||
|
||||
if (!e)
|
||||
return 0;
|
||||
|
||||
return blk_mq_sched_alloc_tags(q, hctx, hctx_idx);
|
||||
ret = blk_mq_sched_alloc_tags(q, hctx, hctx_idx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (e->type->ops.mq.init_hctx) {
|
||||
ret = e->type->ops.mq.init_hctx(hctx, hctx_idx);
|
||||
if (ret) {
|
||||
blk_mq_sched_free_tags(q->tag_set, hctx, hctx_idx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blk_mq_sched_exit_hctx(struct request_queue *q, struct blk_mq_hw_ctx *hctx,
|
||||
|
|
@ -523,12 +487,18 @@ void blk_mq_sched_exit_hctx(struct request_queue *q, struct blk_mq_hw_ctx *hctx,
|
|||
if (!e)
|
||||
return;
|
||||
|
||||
if (e->type->ops.mq.exit_hctx && hctx->sched_data) {
|
||||
e->type->ops.mq.exit_hctx(hctx, hctx_idx);
|
||||
hctx->sched_data = NULL;
|
||||
}
|
||||
|
||||
blk_mq_sched_free_tags(q->tag_set, hctx, hctx_idx);
|
||||
}
|
||||
|
||||
int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
struct elevator_queue *eq;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
|
|
@ -553,6 +523,18 @@ int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e)
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (e->ops.mq.init_hctx) {
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
ret = e->ops.mq.init_hctx(hctx, i);
|
||||
if (ret) {
|
||||
eq = q->elevator;
|
||||
blk_mq_exit_sched(q, eq);
|
||||
kobject_put(&eq->kobj);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
|
@ -563,6 +545,17 @@ err:
|
|||
|
||||
void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
unsigned int i;
|
||||
|
||||
if (e->type->ops.mq.exit_hctx) {
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
if (hctx->sched_data) {
|
||||
e->type->ops.mq.exit_hctx(hctx, i);
|
||||
hctx->sched_data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e->type->ops.mq.exit_sched)
|
||||
e->type->ops.mq.exit_sched(e);
|
||||
blk_mq_sched_tags_teardown(q);
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@
|
|||
#include "blk-mq.h"
|
||||
#include "blk-mq-tag.h"
|
||||
|
||||
int blk_mq_sched_init_hctx_data(struct request_queue *q, size_t size,
|
||||
int (*init)(struct blk_mq_hw_ctx *),
|
||||
void (*exit)(struct blk_mq_hw_ctx *));
|
||||
|
||||
void blk_mq_sched_free_hctx_data(struct request_queue *q,
|
||||
void (*exit)(struct blk_mq_hw_ctx *));
|
||||
|
||||
|
|
@ -28,9 +24,6 @@ void blk_mq_sched_insert_requests(struct request_queue *q,
|
|||
struct list_head *list, bool run_queue_async);
|
||||
|
||||
void blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx);
|
||||
void blk_mq_sched_move_to_dispatch(struct blk_mq_hw_ctx *hctx,
|
||||
struct list_head *rq_list,
|
||||
struct request *(*get_rq)(struct blk_mq_hw_ctx *));
|
||||
|
||||
int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e);
|
||||
void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e);
|
||||
|
|
@ -86,17 +79,12 @@ blk_mq_sched_allow_merge(struct request_queue *q, struct request *rq,
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
blk_mq_sched_completed_request(struct blk_mq_hw_ctx *hctx, struct request *rq)
|
||||
static inline void blk_mq_sched_completed_request(struct request *rq)
|
||||
{
|
||||
struct elevator_queue *e = hctx->queue->elevator;
|
||||
struct elevator_queue *e = rq->q->elevator;
|
||||
|
||||
if (e && e->type->ops.mq.completed_request)
|
||||
e->type->ops.mq.completed_request(hctx, rq);
|
||||
|
||||
BUG_ON(rq->internal_tag == -1);
|
||||
|
||||
blk_mq_put_tag(hctx, hctx->sched_tags, rq->mq_ctx, rq->internal_tag);
|
||||
e->type->ops.mq.completed_request(rq);
|
||||
}
|
||||
|
||||
static inline void blk_mq_sched_started_request(struct request *rq)
|
||||
|
|
|
|||
|
|
@ -253,10 +253,12 @@ static void __blk_mq_unregister_dev(struct device *dev, struct request_queue *q)
|
|||
struct blk_mq_hw_ctx *hctx;
|
||||
int i;
|
||||
|
||||
lockdep_assert_held(&q->sysfs_lock);
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i)
|
||||
blk_mq_unregister_hctx(hctx);
|
||||
|
||||
blk_mq_debugfs_unregister_hctxs(q);
|
||||
blk_mq_debugfs_unregister_mq(q);
|
||||
|
||||
kobject_uevent(&q->mq_kobj, KOBJ_REMOVE);
|
||||
kobject_del(&q->mq_kobj);
|
||||
|
|
@ -267,9 +269,9 @@ static void __blk_mq_unregister_dev(struct device *dev, struct request_queue *q)
|
|||
|
||||
void blk_mq_unregister_dev(struct device *dev, struct request_queue *q)
|
||||
{
|
||||
blk_mq_disable_hotplug();
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
__blk_mq_unregister_dev(dev, q);
|
||||
blk_mq_enable_hotplug();
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
}
|
||||
|
||||
void blk_mq_hctx_kobj_init(struct blk_mq_hw_ctx *hctx)
|
||||
|
|
@ -302,12 +304,13 @@ void blk_mq_sysfs_init(struct request_queue *q)
|
|||
}
|
||||
}
|
||||
|
||||
int blk_mq_register_dev(struct device *dev, struct request_queue *q)
|
||||
int __blk_mq_register_dev(struct device *dev, struct request_queue *q)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
int ret, i;
|
||||
|
||||
blk_mq_disable_hotplug();
|
||||
WARN_ON_ONCE(!q->kobj.parent);
|
||||
lockdep_assert_held(&q->sysfs_lock);
|
||||
|
||||
ret = kobject_add(&q->mq_kobj, kobject_get(&dev->kobj), "%s", "mq");
|
||||
if (ret < 0)
|
||||
|
|
@ -315,20 +318,38 @@ int blk_mq_register_dev(struct device *dev, struct request_queue *q)
|
|||
|
||||
kobject_uevent(&q->mq_kobj, KOBJ_ADD);
|
||||
|
||||
blk_mq_debugfs_register(q, kobject_name(&dev->kobj));
|
||||
blk_mq_debugfs_register(q);
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
ret = blk_mq_register_hctx(hctx);
|
||||
if (ret)
|
||||
break;
|
||||
goto unreg;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
__blk_mq_unregister_dev(dev, q);
|
||||
else
|
||||
q->mq_sysfs_init_done = true;
|
||||
q->mq_sysfs_init_done = true;
|
||||
|
||||
out:
|
||||
blk_mq_enable_hotplug();
|
||||
return ret;
|
||||
|
||||
unreg:
|
||||
while (--i >= 0)
|
||||
blk_mq_unregister_hctx(q->queue_hw_ctx[i]);
|
||||
|
||||
blk_mq_debugfs_unregister_mq(q);
|
||||
|
||||
kobject_uevent(&q->mq_kobj, KOBJ_REMOVE);
|
||||
kobject_del(&q->mq_kobj);
|
||||
kobject_put(&dev->kobj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int blk_mq_register_dev(struct device *dev, struct request_queue *q)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
ret = __blk_mq_register_dev(dev, q);
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -339,13 +360,17 @@ void blk_mq_sysfs_unregister(struct request_queue *q)
|
|||
struct blk_mq_hw_ctx *hctx;
|
||||
int i;
|
||||
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
if (!q->mq_sysfs_init_done)
|
||||
return;
|
||||
goto unlock;
|
||||
|
||||
blk_mq_debugfs_unregister_hctxs(q);
|
||||
blk_mq_debugfs_unregister_mq(q);
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i)
|
||||
blk_mq_unregister_hctx(hctx);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
}
|
||||
|
||||
int blk_mq_sysfs_register(struct request_queue *q)
|
||||
|
|
@ -353,10 +378,11 @@ int blk_mq_sysfs_register(struct request_queue *q)
|
|||
struct blk_mq_hw_ctx *hctx;
|
||||
int i, ret = 0;
|
||||
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
if (!q->mq_sysfs_init_done)
|
||||
return ret;
|
||||
goto unlock;
|
||||
|
||||
blk_mq_debugfs_register_hctxs(q);
|
||||
blk_mq_debugfs_register_mq(q);
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
ret = blk_mq_register_hctx(hctx);
|
||||
|
|
@ -364,5 +390,8 @@ int blk_mq_sysfs_register(struct request_queue *q)
|
|||
break;
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&q->sysfs_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,10 @@ static int __blk_mq_get_tag(struct blk_mq_alloc_data *data,
|
|||
if (!(data->flags & BLK_MQ_REQ_INTERNAL) &&
|
||||
!hctx_may_queue(data->hctx, bt))
|
||||
return -1;
|
||||
return __sbitmap_queue_get(bt);
|
||||
if (data->shallow_depth)
|
||||
return __sbitmap_queue_get_shallow(bt, data->shallow_depth);
|
||||
else
|
||||
return __sbitmap_queue_get(bt);
|
||||
}
|
||||
|
||||
unsigned int blk_mq_get_tag(struct blk_mq_alloc_data *data)
|
||||
|
|
|
|||
569
block/blk-mq.c
569
block/blk-mq.c
|
|
@ -39,6 +39,26 @@
|
|||
static DEFINE_MUTEX(all_q_mutex);
|
||||
static LIST_HEAD(all_q_list);
|
||||
|
||||
static void blk_mq_poll_stats_start(struct request_queue *q);
|
||||
static void blk_mq_poll_stats_fn(struct blk_stat_callback *cb);
|
||||
|
||||
static int blk_mq_poll_stats_bkt(const struct request *rq)
|
||||
{
|
||||
int ddir, bytes, bucket;
|
||||
|
||||
ddir = rq_data_dir(rq);
|
||||
bytes = blk_rq_bytes(rq);
|
||||
|
||||
bucket = ddir + 2*(ilog2(bytes) - 9);
|
||||
|
||||
if (bucket < 0)
|
||||
return -1;
|
||||
else if (bucket >= BLK_MQ_POLL_STATS_BKTS)
|
||||
return ddir + BLK_MQ_POLL_STATS_BKTS - 2;
|
||||
|
||||
return bucket;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if any of the ctx's have pending work in this hardware queue
|
||||
*/
|
||||
|
|
@ -65,7 +85,7 @@ static void blk_mq_hctx_clear_pending(struct blk_mq_hw_ctx *hctx,
|
|||
sbitmap_clear_bit(&hctx->ctx_map, ctx->index_hw);
|
||||
}
|
||||
|
||||
void blk_mq_freeze_queue_start(struct request_queue *q)
|
||||
void blk_freeze_queue_start(struct request_queue *q)
|
||||
{
|
||||
int freeze_depth;
|
||||
|
||||
|
|
@ -75,7 +95,7 @@ void blk_mq_freeze_queue_start(struct request_queue *q)
|
|||
blk_mq_run_hw_queues(q, false);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_mq_freeze_queue_start);
|
||||
EXPORT_SYMBOL_GPL(blk_freeze_queue_start);
|
||||
|
||||
void blk_mq_freeze_queue_wait(struct request_queue *q)
|
||||
{
|
||||
|
|
@ -105,7 +125,7 @@ void blk_freeze_queue(struct request_queue *q)
|
|||
* no blk_unfreeze_queue(), and blk_freeze_queue() is not
|
||||
* exported to drivers as the only user for unfreeze is blk_mq.
|
||||
*/
|
||||
blk_mq_freeze_queue_start(q);
|
||||
blk_freeze_queue_start(q);
|
||||
blk_mq_freeze_queue_wait(q);
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +230,6 @@ void blk_mq_rq_ctx_init(struct request_queue *q, struct blk_mq_ctx *ctx,
|
|||
#endif
|
||||
rq->special = NULL;
|
||||
/* tag was already set */
|
||||
rq->errors = 0;
|
||||
rq->extra_len = 0;
|
||||
|
||||
INIT_LIST_HEAD(&rq->timeout_list);
|
||||
|
|
@ -347,7 +366,7 @@ void __blk_mq_finish_request(struct blk_mq_hw_ctx *hctx, struct blk_mq_ctx *ctx,
|
|||
if (rq->tag != -1)
|
||||
blk_mq_put_tag(hctx, hctx->tags, ctx, rq->tag);
|
||||
if (sched_tag != -1)
|
||||
blk_mq_sched_completed_request(hctx, rq);
|
||||
blk_mq_put_tag(hctx, hctx->sched_tags, ctx, sched_tag);
|
||||
blk_mq_sched_restart(hctx);
|
||||
blk_queue_exit(q);
|
||||
}
|
||||
|
|
@ -365,6 +384,7 @@ void blk_mq_finish_request(struct request *rq)
|
|||
{
|
||||
blk_mq_finish_hctx_request(blk_mq_map_queue(rq->q, rq->mq_ctx->cpu), rq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_mq_finish_request);
|
||||
|
||||
void blk_mq_free_request(struct request *rq)
|
||||
{
|
||||
|
|
@ -402,12 +422,19 @@ static void __blk_mq_complete_request_remote(void *data)
|
|||
rq->q->softirq_done_fn(rq);
|
||||
}
|
||||
|
||||
static void blk_mq_ipi_complete_request(struct request *rq)
|
||||
static void __blk_mq_complete_request(struct request *rq)
|
||||
{
|
||||
struct blk_mq_ctx *ctx = rq->mq_ctx;
|
||||
bool shared = false;
|
||||
int cpu;
|
||||
|
||||
if (rq->internal_tag != -1)
|
||||
blk_mq_sched_completed_request(rq);
|
||||
if (rq->rq_flags & RQF_STATS) {
|
||||
blk_mq_poll_stats_start(rq->q);
|
||||
blk_stat_add(rq);
|
||||
}
|
||||
|
||||
if (!test_bit(QUEUE_FLAG_SAME_COMP, &rq->q->queue_flags)) {
|
||||
rq->q->softirq_done_fn(rq);
|
||||
return;
|
||||
|
|
@ -428,33 +455,6 @@ static void blk_mq_ipi_complete_request(struct request *rq)
|
|||
put_cpu();
|
||||
}
|
||||
|
||||
static void blk_mq_stat_add(struct request *rq)
|
||||
{
|
||||
if (rq->rq_flags & RQF_STATS) {
|
||||
/*
|
||||
* We could rq->mq_ctx here, but there's less of a risk
|
||||
* of races if we have the completion event add the stats
|
||||
* to the local software queue.
|
||||
*/
|
||||
struct blk_mq_ctx *ctx;
|
||||
|
||||
ctx = __blk_mq_get_ctx(rq->q, raw_smp_processor_id());
|
||||
blk_stat_add(&ctx->stat[rq_data_dir(rq)], rq);
|
||||
}
|
||||
}
|
||||
|
||||
static void __blk_mq_complete_request(struct request *rq)
|
||||
{
|
||||
struct request_queue *q = rq->q;
|
||||
|
||||
blk_mq_stat_add(rq);
|
||||
|
||||
if (!q->softirq_done_fn)
|
||||
blk_mq_end_request(rq, rq->errors);
|
||||
else
|
||||
blk_mq_ipi_complete_request(rq);
|
||||
}
|
||||
|
||||
/**
|
||||
* blk_mq_complete_request - end I/O on a request
|
||||
* @rq: the request being processed
|
||||
|
|
@ -463,16 +463,14 @@ static void __blk_mq_complete_request(struct request *rq)
|
|||
* Ends all I/O on a request. It does not handle partial completions.
|
||||
* The actual completion happens out-of-order, through a IPI handler.
|
||||
**/
|
||||
void blk_mq_complete_request(struct request *rq, int error)
|
||||
void blk_mq_complete_request(struct request *rq)
|
||||
{
|
||||
struct request_queue *q = rq->q;
|
||||
|
||||
if (unlikely(blk_should_fake_timeout(q)))
|
||||
return;
|
||||
if (!blk_mark_rq_complete(rq)) {
|
||||
rq->errors = error;
|
||||
if (!blk_mark_rq_complete(rq))
|
||||
__blk_mq_complete_request(rq);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(blk_mq_complete_request);
|
||||
|
||||
|
|
@ -491,7 +489,7 @@ void blk_mq_start_request(struct request *rq)
|
|||
trace_block_rq_issue(q, rq);
|
||||
|
||||
if (test_bit(QUEUE_FLAG_STATS, &q->queue_flags)) {
|
||||
blk_stat_set_issue_time(&rq->issue_stat);
|
||||
blk_stat_set_issue(&rq->issue_stat, blk_rq_sectors(rq));
|
||||
rq->rq_flags |= RQF_STATS;
|
||||
wbt_issue(q->rq_wb, &rq->issue_stat);
|
||||
}
|
||||
|
|
@ -526,6 +524,15 @@ void blk_mq_start_request(struct request *rq)
|
|||
}
|
||||
EXPORT_SYMBOL(blk_mq_start_request);
|
||||
|
||||
/*
|
||||
* When we reach here because queue is busy, REQ_ATOM_COMPLETE
|
||||
* flag isn't set yet, so there may be race with timeout handler,
|
||||
* but given rq->deadline is just set in .queue_rq() under
|
||||
* this situation, the race won't be possible in reality because
|
||||
* rq->timeout should be set as big enough to cover the window
|
||||
* between blk_mq_start_request() called from .queue_rq() and
|
||||
* clearing REQ_ATOM_STARTED here.
|
||||
*/
|
||||
static void __blk_mq_requeue_request(struct request *rq)
|
||||
{
|
||||
struct request_queue *q = rq->q;
|
||||
|
|
@ -633,8 +640,7 @@ void blk_mq_abort_requeue_list(struct request_queue *q)
|
|||
|
||||
rq = list_first_entry(&rq_list, struct request, queuelist);
|
||||
list_del_init(&rq->queuelist);
|
||||
rq->errors = -EIO;
|
||||
blk_mq_end_request(rq, rq->errors);
|
||||
blk_mq_end_request(rq, -EIO);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(blk_mq_abort_requeue_list);
|
||||
|
|
@ -666,7 +672,7 @@ void blk_mq_rq_timed_out(struct request *req, bool reserved)
|
|||
* just be ignored. This can happen due to the bitflag ordering.
|
||||
* Timeout first checks if STARTED is set, and if it is, assumes
|
||||
* the request is active. But if we race with completion, then
|
||||
* we both flags will get cleared. So check here again, and ignore
|
||||
* both flags will get cleared. So check here again, and ignore
|
||||
* a timeout event with a request that isn't active.
|
||||
*/
|
||||
if (!test_bit(REQ_ATOM_STARTED, &req->atomic_flags))
|
||||
|
|
@ -699,6 +705,19 @@ static void blk_mq_check_expired(struct blk_mq_hw_ctx *hctx,
|
|||
if (!test_bit(REQ_ATOM_STARTED, &rq->atomic_flags))
|
||||
return;
|
||||
|
||||
/*
|
||||
* The rq being checked may have been freed and reallocated
|
||||
* out already here, we avoid this race by checking rq->deadline
|
||||
* and REQ_ATOM_COMPLETE flag together:
|
||||
*
|
||||
* - if rq->deadline is observed as new value because of
|
||||
* reusing, the rq won't be timed out because of timing.
|
||||
* - if rq->deadline is observed as previous value,
|
||||
* REQ_ATOM_COMPLETE flag won't be cleared in reuse path
|
||||
* because we put a barrier between setting rq->deadline
|
||||
* and clearing the flag in blk_mq_start_request(), so
|
||||
* this rq won't be timed out too.
|
||||
*/
|
||||
if (time_after_eq(jiffies, rq->deadline)) {
|
||||
if (!blk_mark_rq_complete(rq))
|
||||
blk_mq_rq_timed_out(rq, reserved);
|
||||
|
|
@ -727,7 +746,7 @@ static void blk_mq_timeout_work(struct work_struct *work)
|
|||
* percpu_ref_tryget directly, because we need to be able to
|
||||
* obtain a reference even in the short window between the queue
|
||||
* starting to freeze, by dropping the first reference in
|
||||
* blk_mq_freeze_queue_start, and the moment the last request is
|
||||
* blk_freeze_queue_start, and the moment the last request is
|
||||
* consumed, marked by the instant q_usage_counter reaches
|
||||
* zero.
|
||||
*/
|
||||
|
|
@ -845,6 +864,8 @@ bool blk_mq_get_driver_tag(struct request *rq, struct blk_mq_hw_ctx **hctx,
|
|||
.flags = wait ? 0 : BLK_MQ_REQ_NOWAIT,
|
||||
};
|
||||
|
||||
might_sleep_if(wait);
|
||||
|
||||
if (rq->tag != -1)
|
||||
goto done;
|
||||
|
||||
|
|
@ -964,19 +985,11 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list)
|
|||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
struct request *rq;
|
||||
LIST_HEAD(driver_list);
|
||||
struct list_head *dptr;
|
||||
int errors, queued, ret = BLK_MQ_RQ_QUEUE_OK;
|
||||
|
||||
if (list_empty(list))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Start off with dptr being NULL, so we start the first request
|
||||
* immediately, even if we have more pending.
|
||||
*/
|
||||
dptr = NULL;
|
||||
|
||||
/*
|
||||
* Now process all the entries, sending them to the driver.
|
||||
*/
|
||||
|
|
@ -993,23 +1006,21 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list)
|
|||
* The initial allocation attempt failed, so we need to
|
||||
* rerun the hardware queue when a tag is freed.
|
||||
*/
|
||||
if (blk_mq_dispatch_wait_add(hctx)) {
|
||||
/*
|
||||
* It's possible that a tag was freed in the
|
||||
* window between the allocation failure and
|
||||
* adding the hardware queue to the wait queue.
|
||||
*/
|
||||
if (!blk_mq_get_driver_tag(rq, &hctx, false))
|
||||
break;
|
||||
} else {
|
||||
if (!blk_mq_dispatch_wait_add(hctx))
|
||||
break;
|
||||
|
||||
/*
|
||||
* It's possible that a tag was freed in the window
|
||||
* between the allocation failure and adding the
|
||||
* hardware queue to the wait queue.
|
||||
*/
|
||||
if (!blk_mq_get_driver_tag(rq, &hctx, false))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list_del_init(&rq->queuelist);
|
||||
|
||||
bd.rq = rq;
|
||||
bd.list = dptr;
|
||||
|
||||
/*
|
||||
* Flag last if we have no more requests, or if we have more
|
||||
|
|
@ -1038,20 +1049,12 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list)
|
|||
pr_err("blk-mq: bad return on queue: %d\n", ret);
|
||||
case BLK_MQ_RQ_QUEUE_ERROR:
|
||||
errors++;
|
||||
rq->errors = -EIO;
|
||||
blk_mq_end_request(rq, rq->errors);
|
||||
blk_mq_end_request(rq, -EIO);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret == BLK_MQ_RQ_QUEUE_BUSY)
|
||||
break;
|
||||
|
||||
/*
|
||||
* We've done the first request. If we have more than 1
|
||||
* left in the list, set dptr to defer issue.
|
||||
*/
|
||||
if (!dptr && list->next != list->prev)
|
||||
dptr = &driver_list;
|
||||
} while (!list_empty(list));
|
||||
|
||||
hctx->dispatched[queued_to_index(queued)]++;
|
||||
|
|
@ -1062,8 +1065,8 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list)
|
|||
*/
|
||||
if (!list_empty(list)) {
|
||||
/*
|
||||
* If we got a driver tag for the next request already,
|
||||
* free it again.
|
||||
* If an I/O scheduler has been configured and we got a driver
|
||||
* tag for the next request already, free it again.
|
||||
*/
|
||||
rq = list_first_entry(list, struct request, queuelist);
|
||||
blk_mq_put_driver_tag(rq);
|
||||
|
|
@ -1073,16 +1076,24 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list)
|
|||
spin_unlock(&hctx->lock);
|
||||
|
||||
/*
|
||||
* the queue is expected stopped with BLK_MQ_RQ_QUEUE_BUSY, but
|
||||
* it's possible the queue is stopped and restarted again
|
||||
* before this. Queue restart will dispatch requests. And since
|
||||
* requests in rq_list aren't added into hctx->dispatch yet,
|
||||
* the requests in rq_list might get lost.
|
||||
* If SCHED_RESTART was set by the caller of this function and
|
||||
* it is no longer set that means that it was cleared by another
|
||||
* thread and hence that a queue rerun is needed.
|
||||
*
|
||||
* blk_mq_run_hw_queue() already checks the STOPPED bit
|
||||
* If TAG_WAITING is set that means that an I/O scheduler has
|
||||
* been configured and another thread is waiting for a driver
|
||||
* tag. To guarantee fairness, do not rerun this hardware queue
|
||||
* but let the other thread grab the driver tag.
|
||||
*
|
||||
* If RESTART or TAG_WAITING is set, then let completion restart
|
||||
* the queue instead of potentially looping here.
|
||||
* If no I/O scheduler has been configured it is possible that
|
||||
* the hardware queue got stopped and restarted before requests
|
||||
* were pushed back onto the dispatch list. Rerun the queue to
|
||||
* avoid starvation. Notes:
|
||||
* - blk_mq_run_hw_queue() checks whether or not a queue has
|
||||
* been stopped before rerunning a queue.
|
||||
* - Some but not all block drivers stop a queue before
|
||||
* returning BLK_MQ_RQ_QUEUE_BUSY. Two exceptions are scsi-mq
|
||||
* and dm-rq.
|
||||
*/
|
||||
if (!blk_mq_sched_needs_restart(hctx) &&
|
||||
!test_bit(BLK_MQ_S_TAG_WAITING, &hctx->state))
|
||||
|
|
@ -1104,6 +1115,8 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx)
|
|||
blk_mq_sched_dispatch_requests(hctx);
|
||||
rcu_read_unlock();
|
||||
} else {
|
||||
might_sleep();
|
||||
|
||||
srcu_idx = srcu_read_lock(&hctx->queue_rq_srcu);
|
||||
blk_mq_sched_dispatch_requests(hctx);
|
||||
srcu_read_unlock(&hctx->queue_rq_srcu, srcu_idx);
|
||||
|
|
@ -1153,13 +1166,9 @@ static void __blk_mq_delay_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async,
|
|||
put_cpu();
|
||||
}
|
||||
|
||||
if (msecs == 0)
|
||||
kblockd_schedule_work_on(blk_mq_hctx_next_cpu(hctx),
|
||||
&hctx->run_work);
|
||||
else
|
||||
kblockd_schedule_delayed_work_on(blk_mq_hctx_next_cpu(hctx),
|
||||
&hctx->delayed_run_work,
|
||||
msecs_to_jiffies(msecs));
|
||||
kblockd_schedule_delayed_work_on(blk_mq_hctx_next_cpu(hctx),
|
||||
&hctx->run_work,
|
||||
msecs_to_jiffies(msecs));
|
||||
}
|
||||
|
||||
void blk_mq_delay_run_hw_queue(struct blk_mq_hw_ctx *hctx, unsigned long msecs)
|
||||
|
|
@ -1172,6 +1181,7 @@ void blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async)
|
|||
{
|
||||
__blk_mq_delay_run_hw_queue(hctx, async, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(blk_mq_run_hw_queue);
|
||||
|
||||
void blk_mq_run_hw_queues(struct request_queue *q, bool async)
|
||||
{
|
||||
|
|
@ -1210,8 +1220,7 @@ EXPORT_SYMBOL(blk_mq_queue_stopped);
|
|||
|
||||
void blk_mq_stop_hw_queue(struct blk_mq_hw_ctx *hctx)
|
||||
{
|
||||
cancel_work(&hctx->run_work);
|
||||
cancel_delayed_work(&hctx->delay_work);
|
||||
cancel_delayed_work_sync(&hctx->run_work);
|
||||
set_bit(BLK_MQ_S_STOPPED, &hctx->state);
|
||||
}
|
||||
EXPORT_SYMBOL(blk_mq_stop_hw_queue);
|
||||
|
|
@ -1268,38 +1277,40 @@ static void blk_mq_run_work_fn(struct work_struct *work)
|
|||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
|
||||
hctx = container_of(work, struct blk_mq_hw_ctx, run_work);
|
||||
hctx = container_of(work, struct blk_mq_hw_ctx, run_work.work);
|
||||
|
||||
/*
|
||||
* If we are stopped, don't run the queue. The exception is if
|
||||
* BLK_MQ_S_START_ON_RUN is set. For that case, we auto-clear
|
||||
* the STOPPED bit and run it.
|
||||
*/
|
||||
if (test_bit(BLK_MQ_S_STOPPED, &hctx->state)) {
|
||||
if (!test_bit(BLK_MQ_S_START_ON_RUN, &hctx->state))
|
||||
return;
|
||||
|
||||
clear_bit(BLK_MQ_S_START_ON_RUN, &hctx->state);
|
||||
clear_bit(BLK_MQ_S_STOPPED, &hctx->state);
|
||||
}
|
||||
|
||||
__blk_mq_run_hw_queue(hctx);
|
||||
}
|
||||
|
||||
static void blk_mq_delayed_run_work_fn(struct work_struct *work)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
|
||||
hctx = container_of(work, struct blk_mq_hw_ctx, delayed_run_work.work);
|
||||
|
||||
__blk_mq_run_hw_queue(hctx);
|
||||
}
|
||||
|
||||
static void blk_mq_delay_work_fn(struct work_struct *work)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
|
||||
hctx = container_of(work, struct blk_mq_hw_ctx, delay_work.work);
|
||||
|
||||
if (test_and_clear_bit(BLK_MQ_S_STOPPED, &hctx->state))
|
||||
__blk_mq_run_hw_queue(hctx);
|
||||
}
|
||||
|
||||
void blk_mq_delay_queue(struct blk_mq_hw_ctx *hctx, unsigned long msecs)
|
||||
{
|
||||
if (unlikely(!blk_mq_hw_queue_mapped(hctx)))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Stop the hw queue, then modify currently delayed work.
|
||||
* This should prevent us from running the queue prematurely.
|
||||
* Mark the queue as auto-clearing STOPPED when it runs.
|
||||
*/
|
||||
blk_mq_stop_hw_queue(hctx);
|
||||
kblockd_schedule_delayed_work_on(blk_mq_hctx_next_cpu(hctx),
|
||||
&hctx->delay_work, msecs_to_jiffies(msecs));
|
||||
set_bit(BLK_MQ_S_START_ON_RUN, &hctx->state);
|
||||
kblockd_mod_delayed_work_on(blk_mq_hctx_next_cpu(hctx),
|
||||
&hctx->run_work,
|
||||
msecs_to_jiffies(msecs));
|
||||
}
|
||||
EXPORT_SYMBOL(blk_mq_delay_queue);
|
||||
|
||||
|
|
@ -1408,7 +1419,7 @@ void blk_mq_flush_plug_list(struct blk_plug *plug, bool from_schedule)
|
|||
|
||||
static void blk_mq_bio_to_request(struct request *rq, struct bio *bio)
|
||||
{
|
||||
init_request_from_bio(rq, bio);
|
||||
blk_init_request_from_bio(rq, bio);
|
||||
|
||||
blk_account_io_start(rq, true);
|
||||
}
|
||||
|
|
@ -1453,14 +1464,13 @@ static blk_qc_t request_to_qc_t(struct blk_mq_hw_ctx *hctx, struct request *rq)
|
|||
return blk_tag_to_qc_t(rq->internal_tag, hctx->queue_num, true);
|
||||
}
|
||||
|
||||
static void blk_mq_try_issue_directly(struct request *rq, blk_qc_t *cookie,
|
||||
static void __blk_mq_try_issue_directly(struct request *rq, blk_qc_t *cookie,
|
||||
bool may_sleep)
|
||||
{
|
||||
struct request_queue *q = rq->q;
|
||||
struct blk_mq_queue_data bd = {
|
||||
.rq = rq,
|
||||
.list = NULL,
|
||||
.last = 1
|
||||
.last = true,
|
||||
};
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
blk_qc_t new_cookie;
|
||||
|
|
@ -1485,31 +1495,42 @@ static void blk_mq_try_issue_directly(struct request *rq, blk_qc_t *cookie,
|
|||
return;
|
||||
}
|
||||
|
||||
__blk_mq_requeue_request(rq);
|
||||
|
||||
if (ret == BLK_MQ_RQ_QUEUE_ERROR) {
|
||||
*cookie = BLK_QC_T_NONE;
|
||||
rq->errors = -EIO;
|
||||
blk_mq_end_request(rq, rq->errors);
|
||||
blk_mq_end_request(rq, -EIO);
|
||||
return;
|
||||
}
|
||||
|
||||
__blk_mq_requeue_request(rq);
|
||||
insert:
|
||||
blk_mq_sched_insert_request(rq, false, true, false, may_sleep);
|
||||
}
|
||||
|
||||
/*
|
||||
* Multiple hardware queue variant. This will not use per-process plugs,
|
||||
* but will attempt to bypass the hctx queueing if we can go straight to
|
||||
* hardware for SYNC IO.
|
||||
*/
|
||||
static void blk_mq_try_issue_directly(struct blk_mq_hw_ctx *hctx,
|
||||
struct request *rq, blk_qc_t *cookie)
|
||||
{
|
||||
if (!(hctx->flags & BLK_MQ_F_BLOCKING)) {
|
||||
rcu_read_lock();
|
||||
__blk_mq_try_issue_directly(rq, cookie, false);
|
||||
rcu_read_unlock();
|
||||
} else {
|
||||
unsigned int srcu_idx;
|
||||
|
||||
might_sleep();
|
||||
|
||||
srcu_idx = srcu_read_lock(&hctx->queue_rq_srcu);
|
||||
__blk_mq_try_issue_directly(rq, cookie, true);
|
||||
srcu_read_unlock(&hctx->queue_rq_srcu, srcu_idx);
|
||||
}
|
||||
}
|
||||
|
||||
static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio)
|
||||
{
|
||||
const int is_sync = op_is_sync(bio->bi_opf);
|
||||
const int is_flush_fua = op_is_flush(bio->bi_opf);
|
||||
struct blk_mq_alloc_data data = { .flags = 0 };
|
||||
struct request *rq;
|
||||
unsigned int request_count = 0, srcu_idx;
|
||||
unsigned int request_count = 0;
|
||||
struct blk_plug *plug;
|
||||
struct request *same_queue_rq = NULL;
|
||||
blk_qc_t cookie;
|
||||
|
|
@ -1545,147 +1566,21 @@ static blk_qc_t blk_mq_make_request(struct request_queue *q, struct bio *bio)
|
|||
|
||||
cookie = request_to_qc_t(data.hctx, rq);
|
||||
|
||||
if (unlikely(is_flush_fua)) {
|
||||
if (q->elevator)
|
||||
goto elv_insert;
|
||||
blk_mq_bio_to_request(rq, bio);
|
||||
blk_insert_flush(rq);
|
||||
goto run_queue;
|
||||
}
|
||||
|
||||
plug = current->plug;
|
||||
/*
|
||||
* If the driver supports defer issued based on 'last', then
|
||||
* queue it up like normal since we can potentially save some
|
||||
* CPU this way.
|
||||
*/
|
||||
if (((plug && !blk_queue_nomerges(q)) || is_sync) &&
|
||||
!(data.hctx->flags & BLK_MQ_F_DEFER_ISSUE)) {
|
||||
struct request *old_rq = NULL;
|
||||
|
||||
blk_mq_bio_to_request(rq, bio);
|
||||
|
||||
/*
|
||||
* We do limited plugging. If the bio can be merged, do that.
|
||||
* Otherwise the existing request in the plug list will be
|
||||
* issued. So the plug list will have one request at most
|
||||
*/
|
||||
if (plug) {
|
||||
/*
|
||||
* The plug list might get flushed before this. If that
|
||||
* happens, same_queue_rq is invalid and plug list is
|
||||
* empty
|
||||
*/
|
||||
if (same_queue_rq && !list_empty(&plug->mq_list)) {
|
||||
old_rq = same_queue_rq;
|
||||
list_del_init(&old_rq->queuelist);
|
||||
}
|
||||
list_add_tail(&rq->queuelist, &plug->mq_list);
|
||||
} else /* is_sync */
|
||||
old_rq = rq;
|
||||
if (unlikely(is_flush_fua)) {
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
if (!old_rq)
|
||||
goto done;
|
||||
|
||||
if (!(data.hctx->flags & BLK_MQ_F_BLOCKING)) {
|
||||
rcu_read_lock();
|
||||
blk_mq_try_issue_directly(old_rq, &cookie, false);
|
||||
rcu_read_unlock();
|
||||
blk_mq_bio_to_request(rq, bio);
|
||||
if (q->elevator) {
|
||||
blk_mq_sched_insert_request(rq, false, true, true,
|
||||
true);
|
||||
} else {
|
||||
srcu_idx = srcu_read_lock(&data.hctx->queue_rq_srcu);
|
||||
blk_mq_try_issue_directly(old_rq, &cookie, true);
|
||||
srcu_read_unlock(&data.hctx->queue_rq_srcu, srcu_idx);
|
||||
blk_insert_flush(rq);
|
||||
blk_mq_run_hw_queue(data.hctx, true);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (q->elevator) {
|
||||
elv_insert:
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
blk_mq_bio_to_request(rq, bio);
|
||||
blk_mq_sched_insert_request(rq, false, true,
|
||||
!is_sync || is_flush_fua, true);
|
||||
goto done;
|
||||
}
|
||||
if (!blk_mq_merge_queue_io(data.hctx, data.ctx, rq, bio)) {
|
||||
/*
|
||||
* For a SYNC request, send it to the hardware immediately. For
|
||||
* an ASYNC request, just ensure that we run it later on. The
|
||||
* latter allows for merging opportunities and more efficient
|
||||
* dispatching.
|
||||
*/
|
||||
run_queue:
|
||||
blk_mq_run_hw_queue(data.hctx, !is_sync || is_flush_fua);
|
||||
}
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
done:
|
||||
return cookie;
|
||||
}
|
||||
|
||||
/*
|
||||
* Single hardware queue variant. This will attempt to use any per-process
|
||||
* plug for merging and IO deferral.
|
||||
*/
|
||||
static blk_qc_t blk_sq_make_request(struct request_queue *q, struct bio *bio)
|
||||
{
|
||||
const int is_sync = op_is_sync(bio->bi_opf);
|
||||
const int is_flush_fua = op_is_flush(bio->bi_opf);
|
||||
struct blk_plug *plug;
|
||||
unsigned int request_count = 0;
|
||||
struct blk_mq_alloc_data data = { .flags = 0 };
|
||||
struct request *rq;
|
||||
blk_qc_t cookie;
|
||||
unsigned int wb_acct;
|
||||
|
||||
blk_queue_bounce(q, &bio);
|
||||
|
||||
if (bio_integrity_enabled(bio) && bio_integrity_prep(bio)) {
|
||||
bio_io_error(bio);
|
||||
return BLK_QC_T_NONE;
|
||||
}
|
||||
|
||||
blk_queue_split(q, &bio, q->bio_split);
|
||||
|
||||
if (!is_flush_fua && !blk_queue_nomerges(q)) {
|
||||
if (blk_attempt_plug_merge(q, bio, &request_count, NULL))
|
||||
return BLK_QC_T_NONE;
|
||||
} else
|
||||
request_count = blk_plug_queued_count(q);
|
||||
|
||||
if (blk_mq_sched_bio_merge(q, bio))
|
||||
return BLK_QC_T_NONE;
|
||||
|
||||
wb_acct = wbt_wait(q->rq_wb, bio, NULL);
|
||||
|
||||
trace_block_getrq(q, bio, bio->bi_opf);
|
||||
|
||||
rq = blk_mq_sched_get_request(q, bio, bio->bi_opf, &data);
|
||||
if (unlikely(!rq)) {
|
||||
__wbt_done(q->rq_wb, wb_acct);
|
||||
return BLK_QC_T_NONE;
|
||||
}
|
||||
|
||||
wbt_track(&rq->issue_stat, wb_acct);
|
||||
|
||||
cookie = request_to_qc_t(data.hctx, rq);
|
||||
|
||||
if (unlikely(is_flush_fua)) {
|
||||
if (q->elevator)
|
||||
goto elv_insert;
|
||||
blk_mq_bio_to_request(rq, bio);
|
||||
blk_insert_flush(rq);
|
||||
goto run_queue;
|
||||
}
|
||||
|
||||
/*
|
||||
* A task plug currently exists. Since this is completely lockless,
|
||||
* utilize that to temporarily store requests until the task is
|
||||
* either done or scheduled away.
|
||||
*/
|
||||
plug = current->plug;
|
||||
if (plug) {
|
||||
} else if (plug && q->nr_hw_queues == 1) {
|
||||
struct request *last = NULL;
|
||||
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
blk_mq_bio_to_request(rq, bio);
|
||||
|
||||
/*
|
||||
|
|
@ -1694,13 +1589,14 @@ static blk_qc_t blk_sq_make_request(struct request_queue *q, struct bio *bio)
|
|||
*/
|
||||
if (list_empty(&plug->mq_list))
|
||||
request_count = 0;
|
||||
else if (blk_queue_nomerges(q))
|
||||
request_count = blk_plug_queued_count(q);
|
||||
|
||||
if (!request_count)
|
||||
trace_block_plug(q);
|
||||
else
|
||||
last = list_entry_rq(plug->mq_list.prev);
|
||||
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
|
||||
if (request_count >= BLK_MAX_REQUEST_COUNT || (last &&
|
||||
blk_rq_bytes(last) >= BLK_PLUG_FLUSH_SIZE)) {
|
||||
blk_flush_plug_list(plug, false);
|
||||
|
|
@ -1708,30 +1604,41 @@ static blk_qc_t blk_sq_make_request(struct request_queue *q, struct bio *bio)
|
|||
}
|
||||
|
||||
list_add_tail(&rq->queuelist, &plug->mq_list);
|
||||
return cookie;
|
||||
}
|
||||
} else if (plug && !blk_queue_nomerges(q)) {
|
||||
blk_mq_bio_to_request(rq, bio);
|
||||
|
||||
if (q->elevator) {
|
||||
elv_insert:
|
||||
/*
|
||||
* We do limited plugging. If the bio can be merged, do that.
|
||||
* Otherwise the existing request in the plug list will be
|
||||
* issued. So the plug list will have one request at most
|
||||
* The plug list might get flushed before this. If that happens,
|
||||
* the plug list is empty, and same_queue_rq is invalid.
|
||||
*/
|
||||
if (list_empty(&plug->mq_list))
|
||||
same_queue_rq = NULL;
|
||||
if (same_queue_rq)
|
||||
list_del_init(&same_queue_rq->queuelist);
|
||||
list_add_tail(&rq->queuelist, &plug->mq_list);
|
||||
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
|
||||
if (same_queue_rq)
|
||||
blk_mq_try_issue_directly(data.hctx, same_queue_rq,
|
||||
&cookie);
|
||||
} else if (q->nr_hw_queues > 1 && is_sync) {
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
blk_mq_bio_to_request(rq, bio);
|
||||
blk_mq_sched_insert_request(rq, false, true,
|
||||
!is_sync || is_flush_fua, true);
|
||||
goto done;
|
||||
}
|
||||
if (!blk_mq_merge_queue_io(data.hctx, data.ctx, rq, bio)) {
|
||||
/*
|
||||
* For a SYNC request, send it to the hardware immediately. For
|
||||
* an ASYNC request, just ensure that we run it later on. The
|
||||
* latter allows for merging opportunities and more efficient
|
||||
* dispatching.
|
||||
*/
|
||||
run_queue:
|
||||
blk_mq_run_hw_queue(data.hctx, !is_sync || is_flush_fua);
|
||||
}
|
||||
blk_mq_try_issue_directly(data.hctx, rq, &cookie);
|
||||
} else if (q->elevator) {
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
blk_mq_bio_to_request(rq, bio);
|
||||
blk_mq_sched_insert_request(rq, false, true, true, true);
|
||||
} else if (!blk_mq_merge_queue_io(data.hctx, data.ctx, rq, bio)) {
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
blk_mq_run_hw_queue(data.hctx, true);
|
||||
} else
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
|
||||
blk_mq_put_ctx(data.ctx);
|
||||
done:
|
||||
return cookie;
|
||||
}
|
||||
|
||||
|
|
@ -1988,9 +1895,7 @@ static int blk_mq_init_hctx(struct request_queue *q,
|
|||
if (node == NUMA_NO_NODE)
|
||||
node = hctx->numa_node = set->numa_node;
|
||||
|
||||
INIT_WORK(&hctx->run_work, blk_mq_run_work_fn);
|
||||
INIT_DELAYED_WORK(&hctx->delayed_run_work, blk_mq_delayed_run_work_fn);
|
||||
INIT_DELAYED_WORK(&hctx->delay_work, blk_mq_delay_work_fn);
|
||||
INIT_DELAYED_WORK(&hctx->run_work, blk_mq_run_work_fn);
|
||||
spin_lock_init(&hctx->lock);
|
||||
INIT_LIST_HEAD(&hctx->dispatch);
|
||||
hctx->queue = q;
|
||||
|
|
@ -2067,8 +1972,6 @@ static void blk_mq_init_cpu_queues(struct request_queue *q,
|
|||
spin_lock_init(&__ctx->lock);
|
||||
INIT_LIST_HEAD(&__ctx->rq_list);
|
||||
__ctx->queue = q;
|
||||
blk_stat_init(&__ctx->stat[BLK_STAT_READ]);
|
||||
blk_stat_init(&__ctx->stat[BLK_STAT_WRITE]);
|
||||
|
||||
/* If the cpu isn't online, the cpu is mapped to first hctx */
|
||||
if (!cpu_online(i))
|
||||
|
|
@ -2215,6 +2118,8 @@ static void blk_mq_update_tag_set_depth(struct blk_mq_tag_set *set, bool shared)
|
|||
{
|
||||
struct request_queue *q;
|
||||
|
||||
lockdep_assert_held(&set->tag_list_lock);
|
||||
|
||||
list_for_each_entry(q, &set->tag_list, tag_set_list) {
|
||||
blk_mq_freeze_queue(q);
|
||||
queue_set_hctx_shared(q, shared);
|
||||
|
|
@ -2227,7 +2132,8 @@ static void blk_mq_del_queue_tag_set(struct request_queue *q)
|
|||
struct blk_mq_tag_set *set = q->tag_set;
|
||||
|
||||
mutex_lock(&set->tag_list_lock);
|
||||
list_del_init(&q->tag_set_list);
|
||||
list_del_rcu(&q->tag_set_list);
|
||||
INIT_LIST_HEAD(&q->tag_set_list);
|
||||
if (list_is_singular(&set->tag_list)) {
|
||||
/* just transitioned to unshared */
|
||||
set->flags &= ~BLK_MQ_F_TAG_SHARED;
|
||||
|
|
@ -2235,6 +2141,8 @@ static void blk_mq_del_queue_tag_set(struct request_queue *q)
|
|||
blk_mq_update_tag_set_depth(set, false);
|
||||
}
|
||||
mutex_unlock(&set->tag_list_lock);
|
||||
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set,
|
||||
|
|
@ -2252,7 +2160,7 @@ static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set,
|
|||
}
|
||||
if (set->flags & BLK_MQ_F_TAG_SHARED)
|
||||
queue_set_hctx_shared(q, true);
|
||||
list_add_tail(&q->tag_set_list, &set->tag_list);
|
||||
list_add_tail_rcu(&q->tag_set_list, &set->tag_list);
|
||||
|
||||
mutex_unlock(&set->tag_list_lock);
|
||||
}
|
||||
|
|
@ -2364,6 +2272,12 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
|
|||
/* mark the queue as mq asap */
|
||||
q->mq_ops = set->ops;
|
||||
|
||||
q->poll_cb = blk_stat_alloc_callback(blk_mq_poll_stats_fn,
|
||||
blk_mq_poll_stats_bkt,
|
||||
BLK_MQ_POLL_STATS_BKTS, q);
|
||||
if (!q->poll_cb)
|
||||
goto err_exit;
|
||||
|
||||
q->queue_ctx = alloc_percpu(struct blk_mq_ctx);
|
||||
if (!q->queue_ctx)
|
||||
goto err_exit;
|
||||
|
|
@ -2398,10 +2312,7 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
|
|||
INIT_LIST_HEAD(&q->requeue_list);
|
||||
spin_lock_init(&q->requeue_lock);
|
||||
|
||||
if (q->nr_hw_queues > 1)
|
||||
blk_queue_make_request(q, blk_mq_make_request);
|
||||
else
|
||||
blk_queue_make_request(q, blk_sq_make_request);
|
||||
blk_queue_make_request(q, blk_mq_make_request);
|
||||
|
||||
/*
|
||||
* Do this after blk_queue_make_request() overrides it...
|
||||
|
|
@ -2456,8 +2367,6 @@ void blk_mq_free_queue(struct request_queue *q)
|
|||
list_del_init(&q->all_q_node);
|
||||
mutex_unlock(&all_q_mutex);
|
||||
|
||||
wbt_exit(q);
|
||||
|
||||
blk_mq_del_queue_tag_set(q);
|
||||
|
||||
blk_mq_exit_hw_queues(q, set, set->nr_hw_queues);
|
||||
|
|
@ -2502,7 +2411,7 @@ static void blk_mq_queue_reinit_work(void)
|
|||
* take place in parallel.
|
||||
*/
|
||||
list_for_each_entry(q, &all_q_list, all_q_node)
|
||||
blk_mq_freeze_queue_start(q);
|
||||
blk_freeze_queue_start(q);
|
||||
list_for_each_entry(q, &all_q_list, all_q_node)
|
||||
blk_mq_freeze_queue_wait(q);
|
||||
|
||||
|
|
@ -2743,6 +2652,8 @@ void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues)
|
|||
{
|
||||
struct request_queue *q;
|
||||
|
||||
lockdep_assert_held(&set->tag_list_lock);
|
||||
|
||||
if (nr_hw_queues > nr_cpu_ids)
|
||||
nr_hw_queues = nr_cpu_ids;
|
||||
if (nr_hw_queues < 1 || nr_hw_queues == set->nr_hw_queues)
|
||||
|
|
@ -2755,16 +2666,6 @@ void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues)
|
|||
blk_mq_update_queue_map(set);
|
||||
list_for_each_entry(q, &set->tag_list, tag_set_list) {
|
||||
blk_mq_realloc_hw_ctxs(set, q);
|
||||
|
||||
/*
|
||||
* Manually set the make_request_fn as blk_queue_make_request
|
||||
* resets a lot of the queue settings.
|
||||
*/
|
||||
if (q->nr_hw_queues > 1)
|
||||
q->make_request_fn = blk_mq_make_request;
|
||||
else
|
||||
q->make_request_fn = blk_sq_make_request;
|
||||
|
||||
blk_mq_queue_reinit(q, cpu_online_mask);
|
||||
}
|
||||
|
||||
|
|
@ -2773,39 +2674,69 @@ void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(blk_mq_update_nr_hw_queues);
|
||||
|
||||
/* Enable polling stats and return whether they were already enabled. */
|
||||
static bool blk_poll_stats_enable(struct request_queue *q)
|
||||
{
|
||||
if (test_bit(QUEUE_FLAG_POLL_STATS, &q->queue_flags) ||
|
||||
test_and_set_bit(QUEUE_FLAG_POLL_STATS, &q->queue_flags))
|
||||
return true;
|
||||
blk_stat_add_callback(q, q->poll_cb);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void blk_mq_poll_stats_start(struct request_queue *q)
|
||||
{
|
||||
/*
|
||||
* We don't arm the callback if polling stats are not enabled or the
|
||||
* callback is already active.
|
||||
*/
|
||||
if (!test_bit(QUEUE_FLAG_POLL_STATS, &q->queue_flags) ||
|
||||
blk_stat_is_active(q->poll_cb))
|
||||
return;
|
||||
|
||||
blk_stat_activate_msecs(q->poll_cb, 100);
|
||||
}
|
||||
|
||||
static void blk_mq_poll_stats_fn(struct blk_stat_callback *cb)
|
||||
{
|
||||
struct request_queue *q = cb->data;
|
||||
int bucket;
|
||||
|
||||
for (bucket = 0; bucket < BLK_MQ_POLL_STATS_BKTS; bucket++) {
|
||||
if (cb->stat[bucket].nr_samples)
|
||||
q->poll_stat[bucket] = cb->stat[bucket];
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long blk_mq_poll_nsecs(struct request_queue *q,
|
||||
struct blk_mq_hw_ctx *hctx,
|
||||
struct request *rq)
|
||||
{
|
||||
struct blk_rq_stat stat[2];
|
||||
unsigned long ret = 0;
|
||||
int bucket;
|
||||
|
||||
/*
|
||||
* If stats collection isn't on, don't sleep but turn it on for
|
||||
* future users
|
||||
*/
|
||||
if (!blk_stat_enable(q))
|
||||
if (!blk_poll_stats_enable(q))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We don't have to do this once per IO, should optimize this
|
||||
* to just use the current window of stats until it changes
|
||||
*/
|
||||
memset(&stat, 0, sizeof(stat));
|
||||
blk_hctx_stat_get(hctx, stat);
|
||||
|
||||
/*
|
||||
* As an optimistic guess, use half of the mean service time
|
||||
* for this type of request. We can (and should) make this smarter.
|
||||
* For instance, if the completion latencies are tight, we can
|
||||
* get closer than just half the mean. This is especially
|
||||
* important on devices where the completion latencies are longer
|
||||
* than ~10 usec.
|
||||
* than ~10 usec. We do use the stats for the relevant IO size
|
||||
* if available which does lead to better estimates.
|
||||
*/
|
||||
if (req_op(rq) == REQ_OP_READ && stat[BLK_STAT_READ].nr_samples)
|
||||
ret = (stat[BLK_STAT_READ].mean + 1) / 2;
|
||||
else if (req_op(rq) == REQ_OP_WRITE && stat[BLK_STAT_WRITE].nr_samples)
|
||||
ret = (stat[BLK_STAT_WRITE].mean + 1) / 2;
|
||||
bucket = blk_mq_poll_stats_bkt(rq);
|
||||
if (bucket < 0)
|
||||
return ret;
|
||||
|
||||
if (q->poll_stat[bucket].nr_samples)
|
||||
ret = (q->poll_stat[bucket].mean + 1) / 2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ struct blk_mq_ctx {
|
|||
|
||||
/* incremented at completion time */
|
||||
unsigned long ____cacheline_aligned_in_smp rq_completed[2];
|
||||
struct blk_rq_stat stat[2];
|
||||
|
||||
struct request_queue *queue;
|
||||
struct kobject kobj;
|
||||
|
|
@ -79,6 +78,7 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *q,
|
|||
*/
|
||||
extern void blk_mq_sysfs_init(struct request_queue *q);
|
||||
extern void blk_mq_sysfs_deinit(struct request_queue *q);
|
||||
extern int __blk_mq_register_dev(struct device *dev, struct request_queue *q);
|
||||
extern int blk_mq_sysfs_register(struct request_queue *q);
|
||||
extern void blk_mq_sysfs_unregister(struct request_queue *q);
|
||||
extern void blk_mq_hctx_kobj_init(struct blk_mq_hw_ctx *hctx);
|
||||
|
|
@ -87,13 +87,12 @@ extern void blk_mq_hctx_kobj_init(struct blk_mq_hw_ctx *hctx);
|
|||
* debugfs helpers
|
||||
*/
|
||||
#ifdef CONFIG_BLK_DEBUG_FS
|
||||
int blk_mq_debugfs_register(struct request_queue *q, const char *name);
|
||||
int blk_mq_debugfs_register(struct request_queue *q);
|
||||
void blk_mq_debugfs_unregister(struct request_queue *q);
|
||||
int blk_mq_debugfs_register_hctxs(struct request_queue *q);
|
||||
void blk_mq_debugfs_unregister_hctxs(struct request_queue *q);
|
||||
int blk_mq_debugfs_register_mq(struct request_queue *q);
|
||||
void blk_mq_debugfs_unregister_mq(struct request_queue *q);
|
||||
#else
|
||||
static inline int blk_mq_debugfs_register(struct request_queue *q,
|
||||
const char *name)
|
||||
static inline int blk_mq_debugfs_register(struct request_queue *q)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -102,12 +101,12 @@ static inline void blk_mq_debugfs_unregister(struct request_queue *q)
|
|||
{
|
||||
}
|
||||
|
||||
static inline int blk_mq_debugfs_register_hctxs(struct request_queue *q)
|
||||
static inline int blk_mq_debugfs_register_mq(struct request_queue *q)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void blk_mq_debugfs_unregister_hctxs(struct request_queue *q)
|
||||
static inline void blk_mq_debugfs_unregister_mq(struct request_queue *q)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
|
@ -142,6 +141,7 @@ struct blk_mq_alloc_data {
|
|||
/* input parameter */
|
||||
struct request_queue *q;
|
||||
unsigned int flags;
|
||||
unsigned int shallow_depth;
|
||||
|
||||
/* input & output parameter */
|
||||
struct blk_mq_ctx *ctx;
|
||||
|
|
|
|||
|
|
@ -103,7 +103,6 @@ void blk_set_default_limits(struct queue_limits *lim)
|
|||
lim->discard_granularity = 0;
|
||||
lim->discard_alignment = 0;
|
||||
lim->discard_misaligned = 0;
|
||||
lim->discard_zeroes_data = 0;
|
||||
lim->logical_block_size = lim->physical_block_size = lim->io_min = 512;
|
||||
lim->bounce_pfn = (unsigned long)(BLK_BOUNCE_ANY >> PAGE_SHIFT);
|
||||
lim->alignment_offset = 0;
|
||||
|
|
@ -127,7 +126,6 @@ void blk_set_stacking_limits(struct queue_limits *lim)
|
|||
blk_set_default_limits(lim);
|
||||
|
||||
/* Inherit limits from component devices */
|
||||
lim->discard_zeroes_data = 1;
|
||||
lim->max_segments = USHRT_MAX;
|
||||
lim->max_discard_segments = 1;
|
||||
lim->max_hw_sectors = UINT_MAX;
|
||||
|
|
@ -609,7 +607,6 @@ int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
|
|||
t->io_opt = lcm_not_zero(t->io_opt, b->io_opt);
|
||||
|
||||
t->cluster &= b->cluster;
|
||||
t->discard_zeroes_data &= b->discard_zeroes_data;
|
||||
|
||||
/* Physical block size a multiple of the logical block size? */
|
||||
if (t->physical_block_size & (t->logical_block_size - 1)) {
|
||||
|
|
|
|||
363
block/blk-stat.c
363
block/blk-stat.c
|
|
@ -4,10 +4,27 @@
|
|||
* Copyright (C) 2016 Jens Axboe
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/blk-mq.h>
|
||||
|
||||
#include "blk-stat.h"
|
||||
#include "blk-mq.h"
|
||||
#include "blk.h"
|
||||
|
||||
#define BLK_RQ_STAT_BATCH 64
|
||||
|
||||
struct blk_queue_stats {
|
||||
struct list_head callbacks;
|
||||
spinlock_t lock;
|
||||
bool enable_accounting;
|
||||
};
|
||||
|
||||
static void blk_stat_init(struct blk_rq_stat *stat)
|
||||
{
|
||||
stat->min = -1ULL;
|
||||
stat->max = stat->nr_samples = stat->mean = 0;
|
||||
stat->batch = stat->nr_batch = 0;
|
||||
}
|
||||
|
||||
static void blk_stat_flush_batch(struct blk_rq_stat *stat)
|
||||
{
|
||||
|
|
@ -48,166 +65,10 @@ static void blk_stat_sum(struct blk_rq_stat *dst, struct blk_rq_stat *src)
|
|||
dst->nr_samples += src->nr_samples;
|
||||
}
|
||||
|
||||
static void blk_mq_stat_get(struct request_queue *q, struct blk_rq_stat *dst)
|
||||
static void __blk_stat_add(struct blk_rq_stat *stat, u64 value)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
struct blk_mq_ctx *ctx;
|
||||
uint64_t latest = 0;
|
||||
int i, j, nr;
|
||||
|
||||
blk_stat_init(&dst[BLK_STAT_READ]);
|
||||
blk_stat_init(&dst[BLK_STAT_WRITE]);
|
||||
|
||||
nr = 0;
|
||||
do {
|
||||
uint64_t newest = 0;
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
hctx_for_each_ctx(hctx, ctx, j) {
|
||||
blk_stat_flush_batch(&ctx->stat[BLK_STAT_READ]);
|
||||
blk_stat_flush_batch(&ctx->stat[BLK_STAT_WRITE]);
|
||||
|
||||
if (!ctx->stat[BLK_STAT_READ].nr_samples &&
|
||||
!ctx->stat[BLK_STAT_WRITE].nr_samples)
|
||||
continue;
|
||||
if (ctx->stat[BLK_STAT_READ].time > newest)
|
||||
newest = ctx->stat[BLK_STAT_READ].time;
|
||||
if (ctx->stat[BLK_STAT_WRITE].time > newest)
|
||||
newest = ctx->stat[BLK_STAT_WRITE].time;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* No samples
|
||||
*/
|
||||
if (!newest)
|
||||
break;
|
||||
|
||||
if (newest > latest)
|
||||
latest = newest;
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
hctx_for_each_ctx(hctx, ctx, j) {
|
||||
if (ctx->stat[BLK_STAT_READ].time == newest) {
|
||||
blk_stat_sum(&dst[BLK_STAT_READ],
|
||||
&ctx->stat[BLK_STAT_READ]);
|
||||
nr++;
|
||||
}
|
||||
if (ctx->stat[BLK_STAT_WRITE].time == newest) {
|
||||
blk_stat_sum(&dst[BLK_STAT_WRITE],
|
||||
&ctx->stat[BLK_STAT_WRITE]);
|
||||
nr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we race on finding an entry, just loop back again.
|
||||
* Should be very rare.
|
||||
*/
|
||||
} while (!nr);
|
||||
|
||||
dst[BLK_STAT_READ].time = dst[BLK_STAT_WRITE].time = latest;
|
||||
}
|
||||
|
||||
void blk_queue_stat_get(struct request_queue *q, struct blk_rq_stat *dst)
|
||||
{
|
||||
if (q->mq_ops)
|
||||
blk_mq_stat_get(q, dst);
|
||||
else {
|
||||
blk_stat_flush_batch(&q->rq_stats[BLK_STAT_READ]);
|
||||
blk_stat_flush_batch(&q->rq_stats[BLK_STAT_WRITE]);
|
||||
memcpy(&dst[BLK_STAT_READ], &q->rq_stats[BLK_STAT_READ],
|
||||
sizeof(struct blk_rq_stat));
|
||||
memcpy(&dst[BLK_STAT_WRITE], &q->rq_stats[BLK_STAT_WRITE],
|
||||
sizeof(struct blk_rq_stat));
|
||||
}
|
||||
}
|
||||
|
||||
void blk_hctx_stat_get(struct blk_mq_hw_ctx *hctx, struct blk_rq_stat *dst)
|
||||
{
|
||||
struct blk_mq_ctx *ctx;
|
||||
unsigned int i, nr;
|
||||
|
||||
nr = 0;
|
||||
do {
|
||||
uint64_t newest = 0;
|
||||
|
||||
hctx_for_each_ctx(hctx, ctx, i) {
|
||||
blk_stat_flush_batch(&ctx->stat[BLK_STAT_READ]);
|
||||
blk_stat_flush_batch(&ctx->stat[BLK_STAT_WRITE]);
|
||||
|
||||
if (!ctx->stat[BLK_STAT_READ].nr_samples &&
|
||||
!ctx->stat[BLK_STAT_WRITE].nr_samples)
|
||||
continue;
|
||||
|
||||
if (ctx->stat[BLK_STAT_READ].time > newest)
|
||||
newest = ctx->stat[BLK_STAT_READ].time;
|
||||
if (ctx->stat[BLK_STAT_WRITE].time > newest)
|
||||
newest = ctx->stat[BLK_STAT_WRITE].time;
|
||||
}
|
||||
|
||||
if (!newest)
|
||||
break;
|
||||
|
||||
hctx_for_each_ctx(hctx, ctx, i) {
|
||||
if (ctx->stat[BLK_STAT_READ].time == newest) {
|
||||
blk_stat_sum(&dst[BLK_STAT_READ],
|
||||
&ctx->stat[BLK_STAT_READ]);
|
||||
nr++;
|
||||
}
|
||||
if (ctx->stat[BLK_STAT_WRITE].time == newest) {
|
||||
blk_stat_sum(&dst[BLK_STAT_WRITE],
|
||||
&ctx->stat[BLK_STAT_WRITE]);
|
||||
nr++;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we race on finding an entry, just loop back again.
|
||||
* Should be very rare, as the window is only updated
|
||||
* occasionally
|
||||
*/
|
||||
} while (!nr);
|
||||
}
|
||||
|
||||
static void __blk_stat_init(struct blk_rq_stat *stat, s64 time_now)
|
||||
{
|
||||
stat->min = -1ULL;
|
||||
stat->max = stat->nr_samples = stat->mean = 0;
|
||||
stat->batch = stat->nr_batch = 0;
|
||||
stat->time = time_now & BLK_STAT_NSEC_MASK;
|
||||
}
|
||||
|
||||
void blk_stat_init(struct blk_rq_stat *stat)
|
||||
{
|
||||
__blk_stat_init(stat, ktime_to_ns(ktime_get()));
|
||||
}
|
||||
|
||||
static bool __blk_stat_is_current(struct blk_rq_stat *stat, s64 now)
|
||||
{
|
||||
return (now & BLK_STAT_NSEC_MASK) == (stat->time & BLK_STAT_NSEC_MASK);
|
||||
}
|
||||
|
||||
bool blk_stat_is_current(struct blk_rq_stat *stat)
|
||||
{
|
||||
return __blk_stat_is_current(stat, ktime_to_ns(ktime_get()));
|
||||
}
|
||||
|
||||
void blk_stat_add(struct blk_rq_stat *stat, struct request *rq)
|
||||
{
|
||||
s64 now, value;
|
||||
|
||||
now = __blk_stat_time(ktime_to_ns(ktime_get()));
|
||||
if (now < blk_stat_time(&rq->issue_stat))
|
||||
return;
|
||||
|
||||
if (!__blk_stat_is_current(stat, now))
|
||||
__blk_stat_init(stat, now);
|
||||
|
||||
value = now - blk_stat_time(&rq->issue_stat);
|
||||
if (value > stat->max)
|
||||
stat->max = value;
|
||||
if (value < stat->min)
|
||||
stat->min = value;
|
||||
stat->min = min(stat->min, value);
|
||||
stat->max = max(stat->max, value);
|
||||
|
||||
if (stat->batch + value < stat->batch ||
|
||||
stat->nr_batch + 1 == BLK_RQ_STAT_BATCH)
|
||||
|
|
@ -217,40 +78,172 @@ void blk_stat_add(struct blk_rq_stat *stat, struct request *rq)
|
|||
stat->nr_batch++;
|
||||
}
|
||||
|
||||
void blk_stat_clear(struct request_queue *q)
|
||||
void blk_stat_add(struct request *rq)
|
||||
{
|
||||
if (q->mq_ops) {
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
struct blk_mq_ctx *ctx;
|
||||
int i, j;
|
||||
struct request_queue *q = rq->q;
|
||||
struct blk_stat_callback *cb;
|
||||
struct blk_rq_stat *stat;
|
||||
int bucket;
|
||||
s64 now, value;
|
||||
|
||||
queue_for_each_hw_ctx(q, hctx, i) {
|
||||
hctx_for_each_ctx(hctx, ctx, j) {
|
||||
blk_stat_init(&ctx->stat[BLK_STAT_READ]);
|
||||
blk_stat_init(&ctx->stat[BLK_STAT_WRITE]);
|
||||
}
|
||||
now = __blk_stat_time(ktime_to_ns(ktime_get()));
|
||||
if (now < blk_stat_time(&rq->issue_stat))
|
||||
return;
|
||||
|
||||
value = now - blk_stat_time(&rq->issue_stat);
|
||||
|
||||
blk_throtl_stat_add(rq, value);
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(cb, &q->stats->callbacks, list) {
|
||||
if (blk_stat_is_active(cb)) {
|
||||
bucket = cb->bucket_fn(rq);
|
||||
if (bucket < 0)
|
||||
continue;
|
||||
stat = &this_cpu_ptr(cb->cpu_stat)[bucket];
|
||||
__blk_stat_add(stat, value);
|
||||
}
|
||||
} else {
|
||||
blk_stat_init(&q->rq_stats[BLK_STAT_READ]);
|
||||
blk_stat_init(&q->rq_stats[BLK_STAT_WRITE]);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void blk_stat_set_issue_time(struct blk_issue_stat *stat)
|
||||
static void blk_stat_timer_fn(unsigned long data)
|
||||
{
|
||||
stat->time = (stat->time & BLK_STAT_MASK) |
|
||||
(ktime_to_ns(ktime_get()) & BLK_STAT_TIME_MASK);
|
||||
}
|
||||
struct blk_stat_callback *cb = (void *)data;
|
||||
unsigned int bucket;
|
||||
int cpu;
|
||||
|
||||
/*
|
||||
* Enable stat tracking, return whether it was enabled
|
||||
*/
|
||||
bool blk_stat_enable(struct request_queue *q)
|
||||
{
|
||||
if (!test_bit(QUEUE_FLAG_STATS, &q->queue_flags)) {
|
||||
set_bit(QUEUE_FLAG_STATS, &q->queue_flags);
|
||||
return false;
|
||||
for (bucket = 0; bucket < cb->buckets; bucket++)
|
||||
blk_stat_init(&cb->stat[bucket]);
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
struct blk_rq_stat *cpu_stat;
|
||||
|
||||
cpu_stat = per_cpu_ptr(cb->cpu_stat, cpu);
|
||||
for (bucket = 0; bucket < cb->buckets; bucket++) {
|
||||
blk_stat_sum(&cb->stat[bucket], &cpu_stat[bucket]);
|
||||
blk_stat_init(&cpu_stat[bucket]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
cb->timer_fn(cb);
|
||||
}
|
||||
|
||||
struct blk_stat_callback *
|
||||
blk_stat_alloc_callback(void (*timer_fn)(struct blk_stat_callback *),
|
||||
int (*bucket_fn)(const struct request *),
|
||||
unsigned int buckets, void *data)
|
||||
{
|
||||
struct blk_stat_callback *cb;
|
||||
|
||||
cb = kmalloc(sizeof(*cb), GFP_KERNEL);
|
||||
if (!cb)
|
||||
return NULL;
|
||||
|
||||
cb->stat = kmalloc_array(buckets, sizeof(struct blk_rq_stat),
|
||||
GFP_KERNEL);
|
||||
if (!cb->stat) {
|
||||
kfree(cb);
|
||||
return NULL;
|
||||
}
|
||||
cb->cpu_stat = __alloc_percpu(buckets * sizeof(struct blk_rq_stat),
|
||||
__alignof__(struct blk_rq_stat));
|
||||
if (!cb->cpu_stat) {
|
||||
kfree(cb->stat);
|
||||
kfree(cb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cb->timer_fn = timer_fn;
|
||||
cb->bucket_fn = bucket_fn;
|
||||
cb->data = data;
|
||||
cb->buckets = buckets;
|
||||
setup_timer(&cb->timer, blk_stat_timer_fn, (unsigned long)cb);
|
||||
|
||||
return cb;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_stat_alloc_callback);
|
||||
|
||||
void blk_stat_add_callback(struct request_queue *q,
|
||||
struct blk_stat_callback *cb)
|
||||
{
|
||||
unsigned int bucket;
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct blk_rq_stat *cpu_stat;
|
||||
|
||||
cpu_stat = per_cpu_ptr(cb->cpu_stat, cpu);
|
||||
for (bucket = 0; bucket < cb->buckets; bucket++)
|
||||
blk_stat_init(&cpu_stat[bucket]);
|
||||
}
|
||||
|
||||
spin_lock(&q->stats->lock);
|
||||
list_add_tail_rcu(&cb->list, &q->stats->callbacks);
|
||||
set_bit(QUEUE_FLAG_STATS, &q->queue_flags);
|
||||
spin_unlock(&q->stats->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_stat_add_callback);
|
||||
|
||||
void blk_stat_remove_callback(struct request_queue *q,
|
||||
struct blk_stat_callback *cb)
|
||||
{
|
||||
spin_lock(&q->stats->lock);
|
||||
list_del_rcu(&cb->list);
|
||||
if (list_empty(&q->stats->callbacks) && !q->stats->enable_accounting)
|
||||
clear_bit(QUEUE_FLAG_STATS, &q->queue_flags);
|
||||
spin_unlock(&q->stats->lock);
|
||||
|
||||
del_timer_sync(&cb->timer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_stat_remove_callback);
|
||||
|
||||
static void blk_stat_free_callback_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct blk_stat_callback *cb;
|
||||
|
||||
cb = container_of(head, struct blk_stat_callback, rcu);
|
||||
free_percpu(cb->cpu_stat);
|
||||
kfree(cb->stat);
|
||||
kfree(cb);
|
||||
}
|
||||
|
||||
void blk_stat_free_callback(struct blk_stat_callback *cb)
|
||||
{
|
||||
if (cb)
|
||||
call_rcu(&cb->rcu, blk_stat_free_callback_rcu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_stat_free_callback);
|
||||
|
||||
void blk_stat_enable_accounting(struct request_queue *q)
|
||||
{
|
||||
spin_lock(&q->stats->lock);
|
||||
q->stats->enable_accounting = true;
|
||||
set_bit(QUEUE_FLAG_STATS, &q->queue_flags);
|
||||
spin_unlock(&q->stats->lock);
|
||||
}
|
||||
|
||||
struct blk_queue_stats *blk_alloc_queue_stats(void)
|
||||
{
|
||||
struct blk_queue_stats *stats;
|
||||
|
||||
stats = kmalloc(sizeof(*stats), GFP_KERNEL);
|
||||
if (!stats)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&stats->callbacks);
|
||||
spin_lock_init(&stats->lock);
|
||||
stats->enable_accounting = false;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
void blk_free_queue_stats(struct blk_queue_stats *stats)
|
||||
{
|
||||
if (!stats)
|
||||
return;
|
||||
|
||||
WARN_ON(!list_empty(&stats->callbacks));
|
||||
|
||||
kfree(stats);
|
||||
}
|
||||
|
|
|
|||
204
block/blk-stat.h
204
block/blk-stat.h
|
|
@ -1,33 +1,85 @@
|
|||
#ifndef BLK_STAT_H
|
||||
#define BLK_STAT_H
|
||||
|
||||
/*
|
||||
* ~0.13s window as a power-of-2 (2^27 nsecs)
|
||||
*/
|
||||
#define BLK_STAT_NSEC 134217728ULL
|
||||
#define BLK_STAT_NSEC_MASK ~(BLK_STAT_NSEC - 1)
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
/*
|
||||
* Upper 3 bits can be used elsewhere
|
||||
* from upper:
|
||||
* 3 bits: reserved for other usage
|
||||
* 12 bits: size
|
||||
* 49 bits: time
|
||||
*/
|
||||
#define BLK_STAT_RES_BITS 3
|
||||
#define BLK_STAT_SHIFT (64 - BLK_STAT_RES_BITS)
|
||||
#define BLK_STAT_TIME_MASK ((1ULL << BLK_STAT_SHIFT) - 1)
|
||||
#define BLK_STAT_MASK ~BLK_STAT_TIME_MASK
|
||||
#define BLK_STAT_SIZE_BITS 12
|
||||
#define BLK_STAT_RES_SHIFT (64 - BLK_STAT_RES_BITS)
|
||||
#define BLK_STAT_SIZE_SHIFT (BLK_STAT_RES_SHIFT - BLK_STAT_SIZE_BITS)
|
||||
#define BLK_STAT_TIME_MASK ((1ULL << BLK_STAT_SIZE_SHIFT) - 1)
|
||||
#define BLK_STAT_SIZE_MASK \
|
||||
(((1ULL << BLK_STAT_SIZE_BITS) - 1) << BLK_STAT_SIZE_SHIFT)
|
||||
#define BLK_STAT_RES_MASK (~((1ULL << BLK_STAT_RES_SHIFT) - 1))
|
||||
|
||||
enum {
|
||||
BLK_STAT_READ = 0,
|
||||
BLK_STAT_WRITE,
|
||||
/**
|
||||
* struct blk_stat_callback - Block statistics callback.
|
||||
*
|
||||
* A &struct blk_stat_callback is associated with a &struct request_queue. While
|
||||
* @timer is active, that queue's request completion latencies are sorted into
|
||||
* buckets by @bucket_fn and added to a per-cpu buffer, @cpu_stat. When the
|
||||
* timer fires, @cpu_stat is flushed to @stat and @timer_fn is invoked.
|
||||
*/
|
||||
struct blk_stat_callback {
|
||||
/*
|
||||
* @list: RCU list of callbacks for a &struct request_queue.
|
||||
*/
|
||||
struct list_head list;
|
||||
|
||||
/**
|
||||
* @timer: Timer for the next callback invocation.
|
||||
*/
|
||||
struct timer_list timer;
|
||||
|
||||
/**
|
||||
* @cpu_stat: Per-cpu statistics buckets.
|
||||
*/
|
||||
struct blk_rq_stat __percpu *cpu_stat;
|
||||
|
||||
/**
|
||||
* @bucket_fn: Given a request, returns which statistics bucket it
|
||||
* should be accounted under. Return -1 for no bucket for this
|
||||
* request.
|
||||
*/
|
||||
int (*bucket_fn)(const struct request *);
|
||||
|
||||
/**
|
||||
* @buckets: Number of statistics buckets.
|
||||
*/
|
||||
unsigned int buckets;
|
||||
|
||||
/**
|
||||
* @stat: Array of statistics buckets.
|
||||
*/
|
||||
struct blk_rq_stat *stat;
|
||||
|
||||
/**
|
||||
* @fn: Callback function.
|
||||
*/
|
||||
void (*timer_fn)(struct blk_stat_callback *);
|
||||
|
||||
/**
|
||||
* @data: Private pointer for the user.
|
||||
*/
|
||||
void *data;
|
||||
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
void blk_stat_add(struct blk_rq_stat *, struct request *);
|
||||
void blk_hctx_stat_get(struct blk_mq_hw_ctx *, struct blk_rq_stat *);
|
||||
void blk_queue_stat_get(struct request_queue *, struct blk_rq_stat *);
|
||||
void blk_stat_clear(struct request_queue *);
|
||||
void blk_stat_init(struct blk_rq_stat *);
|
||||
bool blk_stat_is_current(struct blk_rq_stat *);
|
||||
void blk_stat_set_issue_time(struct blk_issue_stat *);
|
||||
bool blk_stat_enable(struct request_queue *);
|
||||
struct blk_queue_stats *blk_alloc_queue_stats(void);
|
||||
void blk_free_queue_stats(struct blk_queue_stats *);
|
||||
|
||||
void blk_stat_add(struct request *);
|
||||
|
||||
static inline u64 __blk_stat_time(u64 time)
|
||||
{
|
||||
|
|
@ -36,7 +88,117 @@ static inline u64 __blk_stat_time(u64 time)
|
|||
|
||||
static inline u64 blk_stat_time(struct blk_issue_stat *stat)
|
||||
{
|
||||
return __blk_stat_time(stat->time);
|
||||
return __blk_stat_time(stat->stat);
|
||||
}
|
||||
|
||||
static inline sector_t blk_capped_size(sector_t size)
|
||||
{
|
||||
return size & ((1ULL << BLK_STAT_SIZE_BITS) - 1);
|
||||
}
|
||||
|
||||
static inline sector_t blk_stat_size(struct blk_issue_stat *stat)
|
||||
{
|
||||
return (stat->stat & BLK_STAT_SIZE_MASK) >> BLK_STAT_SIZE_SHIFT;
|
||||
}
|
||||
|
||||
static inline void blk_stat_set_issue(struct blk_issue_stat *stat,
|
||||
sector_t size)
|
||||
{
|
||||
stat->stat = (stat->stat & BLK_STAT_RES_MASK) |
|
||||
(ktime_to_ns(ktime_get()) & BLK_STAT_TIME_MASK) |
|
||||
(((u64)blk_capped_size(size)) << BLK_STAT_SIZE_SHIFT);
|
||||
}
|
||||
|
||||
/* record time/size info in request but not add a callback */
|
||||
void blk_stat_enable_accounting(struct request_queue *q);
|
||||
|
||||
/**
|
||||
* blk_stat_alloc_callback() - Allocate a block statistics callback.
|
||||
* @timer_fn: Timer callback function.
|
||||
* @bucket_fn: Bucket callback function.
|
||||
* @buckets: Number of statistics buckets.
|
||||
* @data: Value for the @data field of the &struct blk_stat_callback.
|
||||
*
|
||||
* See &struct blk_stat_callback for details on the callback functions.
|
||||
*
|
||||
* Return: &struct blk_stat_callback on success or NULL on ENOMEM.
|
||||
*/
|
||||
struct blk_stat_callback *
|
||||
blk_stat_alloc_callback(void (*timer_fn)(struct blk_stat_callback *),
|
||||
int (*bucket_fn)(const struct request *),
|
||||
unsigned int buckets, void *data);
|
||||
|
||||
/**
|
||||
* blk_stat_add_callback() - Add a block statistics callback to be run on a
|
||||
* request queue.
|
||||
* @q: The request queue.
|
||||
* @cb: The callback.
|
||||
*
|
||||
* Note that a single &struct blk_stat_callback can only be added to a single
|
||||
* &struct request_queue.
|
||||
*/
|
||||
void blk_stat_add_callback(struct request_queue *q,
|
||||
struct blk_stat_callback *cb);
|
||||
|
||||
/**
|
||||
* blk_stat_remove_callback() - Remove a block statistics callback from a
|
||||
* request queue.
|
||||
* @q: The request queue.
|
||||
* @cb: The callback.
|
||||
*
|
||||
* When this returns, the callback is not running on any CPUs and will not be
|
||||
* called again unless readded.
|
||||
*/
|
||||
void blk_stat_remove_callback(struct request_queue *q,
|
||||
struct blk_stat_callback *cb);
|
||||
|
||||
/**
|
||||
* blk_stat_free_callback() - Free a block statistics callback.
|
||||
* @cb: The callback.
|
||||
*
|
||||
* @cb may be NULL, in which case this does nothing. If it is not NULL, @cb must
|
||||
* not be associated with a request queue. I.e., if it was previously added with
|
||||
* blk_stat_add_callback(), it must also have been removed since then with
|
||||
* blk_stat_remove_callback().
|
||||
*/
|
||||
void blk_stat_free_callback(struct blk_stat_callback *cb);
|
||||
|
||||
/**
|
||||
* blk_stat_is_active() - Check if a block statistics callback is currently
|
||||
* gathering statistics.
|
||||
* @cb: The callback.
|
||||
*/
|
||||
static inline bool blk_stat_is_active(struct blk_stat_callback *cb)
|
||||
{
|
||||
return timer_pending(&cb->timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* blk_stat_activate_nsecs() - Gather block statistics during a time window in
|
||||
* nanoseconds.
|
||||
* @cb: The callback.
|
||||
* @nsecs: Number of nanoseconds to gather statistics for.
|
||||
*
|
||||
* The timer callback will be called when the window expires.
|
||||
*/
|
||||
static inline void blk_stat_activate_nsecs(struct blk_stat_callback *cb,
|
||||
u64 nsecs)
|
||||
{
|
||||
mod_timer(&cb->timer, jiffies + nsecs_to_jiffies(nsecs));
|
||||
}
|
||||
|
||||
/**
|
||||
* blk_stat_activate_msecs() - Gather block statistics during a time window in
|
||||
* milliseconds.
|
||||
* @cb: The callback.
|
||||
* @msecs: Number of milliseconds to gather statistics for.
|
||||
*
|
||||
* The timer callback will be called when the window expires.
|
||||
*/
|
||||
static inline void blk_stat_activate_msecs(struct blk_stat_callback *cb,
|
||||
unsigned int msecs)
|
||||
{
|
||||
mod_timer(&cb->timer, jiffies + msecs_to_jiffies(msecs));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ static ssize_t queue_discard_max_store(struct request_queue *q,
|
|||
|
||||
static ssize_t queue_discard_zeroes_data_show(struct request_queue *q, char *page)
|
||||
{
|
||||
return queue_var_show(queue_discard_zeroes_data(q), page);
|
||||
return queue_var_show(0, page);
|
||||
}
|
||||
|
||||
static ssize_t queue_write_same_max_show(struct request_queue *q, char *page)
|
||||
|
|
@ -503,26 +503,6 @@ static ssize_t queue_dax_show(struct request_queue *q, char *page)
|
|||
return queue_var_show(blk_queue_dax(q), page);
|
||||
}
|
||||
|
||||
static ssize_t print_stat(char *page, struct blk_rq_stat *stat, const char *pre)
|
||||
{
|
||||
return sprintf(page, "%s samples=%llu, mean=%lld, min=%lld, max=%lld\n",
|
||||
pre, (long long) stat->nr_samples,
|
||||
(long long) stat->mean, (long long) stat->min,
|
||||
(long long) stat->max);
|
||||
}
|
||||
|
||||
static ssize_t queue_stats_show(struct request_queue *q, char *page)
|
||||
{
|
||||
struct blk_rq_stat stat[2];
|
||||
ssize_t ret;
|
||||
|
||||
blk_queue_stat_get(q, stat);
|
||||
|
||||
ret = print_stat(page, &stat[BLK_STAT_READ], "read :");
|
||||
ret += print_stat(page + ret, &stat[BLK_STAT_WRITE], "write:");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct queue_sysfs_entry queue_requests_entry = {
|
||||
.attr = {.name = "nr_requests", .mode = S_IRUGO | S_IWUSR },
|
||||
.show = queue_requests_show,
|
||||
|
|
@ -691,17 +671,20 @@ static struct queue_sysfs_entry queue_dax_entry = {
|
|||
.show = queue_dax_show,
|
||||
};
|
||||
|
||||
static struct queue_sysfs_entry queue_stats_entry = {
|
||||
.attr = {.name = "stats", .mode = S_IRUGO },
|
||||
.show = queue_stats_show,
|
||||
};
|
||||
|
||||
static struct queue_sysfs_entry queue_wb_lat_entry = {
|
||||
.attr = {.name = "wbt_lat_usec", .mode = S_IRUGO | S_IWUSR },
|
||||
.show = queue_wb_lat_show,
|
||||
.store = queue_wb_lat_store,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_THROTTLING_LOW
|
||||
static struct queue_sysfs_entry throtl_sample_time_entry = {
|
||||
.attr = {.name = "throttle_sample_time", .mode = S_IRUGO | S_IWUSR },
|
||||
.show = blk_throtl_sample_time_show,
|
||||
.store = blk_throtl_sample_time_store,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct attribute *default_attrs[] = {
|
||||
&queue_requests_entry.attr,
|
||||
&queue_ra_entry.attr,
|
||||
|
|
@ -733,9 +716,11 @@ static struct attribute *default_attrs[] = {
|
|||
&queue_poll_entry.attr,
|
||||
&queue_wc_entry.attr,
|
||||
&queue_dax_entry.attr,
|
||||
&queue_stats_entry.attr,
|
||||
&queue_wb_lat_entry.attr,
|
||||
&queue_poll_delay_entry.attr,
|
||||
#ifdef CONFIG_BLK_DEV_THROTTLING_LOW
|
||||
&throtl_sample_time_entry.attr,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -810,7 +795,9 @@ static void blk_release_queue(struct kobject *kobj)
|
|||
struct request_queue *q =
|
||||
container_of(kobj, struct request_queue, kobj);
|
||||
|
||||
wbt_exit(q);
|
||||
if (test_bit(QUEUE_FLAG_POLL_STATS, &q->queue_flags))
|
||||
blk_stat_remove_callback(q, q->poll_cb);
|
||||
blk_stat_free_callback(q->poll_cb);
|
||||
bdi_put(q->backing_dev_info);
|
||||
blkcg_exit_queue(q);
|
||||
|
||||
|
|
@ -819,6 +806,8 @@ static void blk_release_queue(struct kobject *kobj)
|
|||
elevator_exit(q, q->elevator);
|
||||
}
|
||||
|
||||
blk_free_queue_stats(q->stats);
|
||||
|
||||
blk_exit_rl(&q->root_rl);
|
||||
|
||||
if (q->queue_tags)
|
||||
|
|
@ -855,23 +844,6 @@ struct kobj_type blk_queue_ktype = {
|
|||
.release = blk_release_queue,
|
||||
};
|
||||
|
||||
static void blk_wb_init(struct request_queue *q)
|
||||
{
|
||||
#ifndef CONFIG_BLK_WBT_MQ
|
||||
if (q->mq_ops)
|
||||
return;
|
||||
#endif
|
||||
#ifndef CONFIG_BLK_WBT_SQ
|
||||
if (q->request_fn)
|
||||
return;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If this fails, we don't get throttling
|
||||
*/
|
||||
wbt_init(q);
|
||||
}
|
||||
|
||||
int blk_register_queue(struct gendisk *disk)
|
||||
{
|
||||
int ret;
|
||||
|
|
@ -881,6 +853,11 @@ int blk_register_queue(struct gendisk *disk)
|
|||
if (WARN_ON(!q))
|
||||
return -ENXIO;
|
||||
|
||||
WARN_ONCE(test_bit(QUEUE_FLAG_REGISTERED, &q->queue_flags),
|
||||
"%s is registering an already registered queue\n",
|
||||
kobject_name(&dev->kobj));
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_REGISTERED, q);
|
||||
|
||||
/*
|
||||
* SCSI probing may synchronously create and destroy a lot of
|
||||
* request_queues for non-existent devices. Shutting down a fully
|
||||
|
|
@ -900,9 +877,6 @@ int blk_register_queue(struct gendisk *disk)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (q->mq_ops)
|
||||
blk_mq_register_dev(dev, q);
|
||||
|
||||
/* Prevent changes through sysfs until registration is completed. */
|
||||
mutex_lock(&q->sysfs_lock);
|
||||
|
||||
|
|
@ -912,9 +886,14 @@ int blk_register_queue(struct gendisk *disk)
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
if (q->mq_ops)
|
||||
__blk_mq_register_dev(dev, q);
|
||||
|
||||
kobject_uevent(&q->kobj, KOBJ_ADD);
|
||||
|
||||
blk_wb_init(q);
|
||||
wbt_enable_default(q);
|
||||
|
||||
blk_throtl_register_queue(q);
|
||||
|
||||
if (q->request_fn || (q->mq_ops && q->elevator)) {
|
||||
ret = elv_register_queue(q);
|
||||
|
|
@ -939,6 +918,11 @@ void blk_unregister_queue(struct gendisk *disk)
|
|||
if (WARN_ON(!q))
|
||||
return;
|
||||
|
||||
queue_flag_clear_unlocked(QUEUE_FLAG_REGISTERED, q);
|
||||
|
||||
wbt_exit(q);
|
||||
|
||||
|
||||
if (q->mq_ops)
|
||||
blk_mq_unregister_dev(disk_to_dev(disk), q);
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -89,7 +89,6 @@ static void blk_rq_timed_out(struct request *req)
|
|||
ret = q->rq_timed_out_fn(req);
|
||||
switch (ret) {
|
||||
case BLK_EH_HANDLED:
|
||||
/* Can we use req->errors here? */
|
||||
__blk_complete_request(req);
|
||||
break;
|
||||
case BLK_EH_RESET_TIMER:
|
||||
|
|
|
|||
|
|
@ -255,8 +255,8 @@ static inline bool stat_sample_valid(struct blk_rq_stat *stat)
|
|||
* that it's writes impacting us, and not just some sole read on
|
||||
* a device that is in a lower power state.
|
||||
*/
|
||||
return stat[BLK_STAT_READ].nr_samples >= 1 &&
|
||||
stat[BLK_STAT_WRITE].nr_samples >= RWB_MIN_WRITE_SAMPLES;
|
||||
return (stat[READ].nr_samples >= 1 &&
|
||||
stat[WRITE].nr_samples >= RWB_MIN_WRITE_SAMPLES);
|
||||
}
|
||||
|
||||
static u64 rwb_sync_issue_lat(struct rq_wb *rwb)
|
||||
|
|
@ -277,7 +277,7 @@ enum {
|
|||
LAT_EXCEEDED,
|
||||
};
|
||||
|
||||
static int __latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat)
|
||||
static int latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat)
|
||||
{
|
||||
struct backing_dev_info *bdi = rwb->queue->backing_dev_info;
|
||||
u64 thislat;
|
||||
|
|
@ -293,7 +293,7 @@ static int __latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat)
|
|||
*/
|
||||
thislat = rwb_sync_issue_lat(rwb);
|
||||
if (thislat > rwb->cur_win_nsec ||
|
||||
(thislat > rwb->min_lat_nsec && !stat[BLK_STAT_READ].nr_samples)) {
|
||||
(thislat > rwb->min_lat_nsec && !stat[READ].nr_samples)) {
|
||||
trace_wbt_lat(bdi, thislat);
|
||||
return LAT_EXCEEDED;
|
||||
}
|
||||
|
|
@ -308,8 +308,8 @@ static int __latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat)
|
|||
* waited or still has writes in flights, consider us doing
|
||||
* just writes as well.
|
||||
*/
|
||||
if ((stat[BLK_STAT_WRITE].nr_samples && blk_stat_is_current(stat)) ||
|
||||
wb_recent_wait(rwb) || wbt_inflight(rwb))
|
||||
if (stat[WRITE].nr_samples || wb_recent_wait(rwb) ||
|
||||
wbt_inflight(rwb))
|
||||
return LAT_UNKNOWN_WRITES;
|
||||
return LAT_UNKNOWN;
|
||||
}
|
||||
|
|
@ -317,8 +317,8 @@ static int __latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat)
|
|||
/*
|
||||
* If the 'min' latency exceeds our target, step down.
|
||||
*/
|
||||
if (stat[BLK_STAT_READ].min > rwb->min_lat_nsec) {
|
||||
trace_wbt_lat(bdi, stat[BLK_STAT_READ].min);
|
||||
if (stat[READ].min > rwb->min_lat_nsec) {
|
||||
trace_wbt_lat(bdi, stat[READ].min);
|
||||
trace_wbt_stat(bdi, stat);
|
||||
return LAT_EXCEEDED;
|
||||
}
|
||||
|
|
@ -329,14 +329,6 @@ static int __latency_exceeded(struct rq_wb *rwb, struct blk_rq_stat *stat)
|
|||
return LAT_OK;
|
||||
}
|
||||
|
||||
static int latency_exceeded(struct rq_wb *rwb)
|
||||
{
|
||||
struct blk_rq_stat stat[2];
|
||||
|
||||
blk_queue_stat_get(rwb->queue, stat);
|
||||
return __latency_exceeded(rwb, stat);
|
||||
}
|
||||
|
||||
static void rwb_trace_step(struct rq_wb *rwb, const char *msg)
|
||||
{
|
||||
struct backing_dev_info *bdi = rwb->queue->backing_dev_info;
|
||||
|
|
@ -355,7 +347,6 @@ static void scale_up(struct rq_wb *rwb)
|
|||
|
||||
rwb->scale_step--;
|
||||
rwb->unknown_cnt = 0;
|
||||
blk_stat_clear(rwb->queue);
|
||||
|
||||
rwb->scaled_max = calc_wb_limits(rwb);
|
||||
|
||||
|
|
@ -385,15 +376,12 @@ static void scale_down(struct rq_wb *rwb, bool hard_throttle)
|
|||
|
||||
rwb->scaled_max = false;
|
||||
rwb->unknown_cnt = 0;
|
||||
blk_stat_clear(rwb->queue);
|
||||
calc_wb_limits(rwb);
|
||||
rwb_trace_step(rwb, "step down");
|
||||
}
|
||||
|
||||
static void rwb_arm_timer(struct rq_wb *rwb)
|
||||
{
|
||||
unsigned long expires;
|
||||
|
||||
if (rwb->scale_step > 0) {
|
||||
/*
|
||||
* We should speed this up, using some variant of a fast
|
||||
|
|
@ -411,17 +399,16 @@ static void rwb_arm_timer(struct rq_wb *rwb)
|
|||
rwb->cur_win_nsec = rwb->win_nsec;
|
||||
}
|
||||
|
||||
expires = jiffies + nsecs_to_jiffies(rwb->cur_win_nsec);
|
||||
mod_timer(&rwb->window_timer, expires);
|
||||
blk_stat_activate_nsecs(rwb->cb, rwb->cur_win_nsec);
|
||||
}
|
||||
|
||||
static void wb_timer_fn(unsigned long data)
|
||||
static void wb_timer_fn(struct blk_stat_callback *cb)
|
||||
{
|
||||
struct rq_wb *rwb = (struct rq_wb *) data;
|
||||
struct rq_wb *rwb = cb->data;
|
||||
unsigned int inflight = wbt_inflight(rwb);
|
||||
int status;
|
||||
|
||||
status = latency_exceeded(rwb);
|
||||
status = latency_exceeded(rwb, cb->stat);
|
||||
|
||||
trace_wbt_timer(rwb->queue->backing_dev_info, status, rwb->scale_step,
|
||||
inflight);
|
||||
|
|
@ -614,7 +601,7 @@ enum wbt_flags wbt_wait(struct rq_wb *rwb, struct bio *bio, spinlock_t *lock)
|
|||
|
||||
__wbt_wait(rwb, bio->bi_opf, lock);
|
||||
|
||||
if (!timer_pending(&rwb->window_timer))
|
||||
if (!blk_stat_is_active(rwb->cb))
|
||||
rwb_arm_timer(rwb);
|
||||
|
||||
if (current_is_kswapd())
|
||||
|
|
@ -666,22 +653,37 @@ void wbt_set_write_cache(struct rq_wb *rwb, bool write_cache_on)
|
|||
rwb->wc = write_cache_on;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable wbt, if enabled by default. Only called from CFQ, if we have
|
||||
* cgroups enabled
|
||||
/*
|
||||
* Disable wbt, if enabled by default. Only called from CFQ.
|
||||
*/
|
||||
void wbt_disable_default(struct request_queue *q)
|
||||
{
|
||||
struct rq_wb *rwb = q->rq_wb;
|
||||
|
||||
if (rwb && rwb->enable_state == WBT_STATE_ON_DEFAULT) {
|
||||
del_timer_sync(&rwb->window_timer);
|
||||
rwb->win_nsec = rwb->min_lat_nsec = 0;
|
||||
wbt_update_limits(rwb);
|
||||
}
|
||||
if (rwb && rwb->enable_state == WBT_STATE_ON_DEFAULT)
|
||||
wbt_exit(q);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wbt_disable_default);
|
||||
|
||||
/*
|
||||
* Enable wbt if defaults are configured that way
|
||||
*/
|
||||
void wbt_enable_default(struct request_queue *q)
|
||||
{
|
||||
/* Throttling already enabled? */
|
||||
if (q->rq_wb)
|
||||
return;
|
||||
|
||||
/* Queue not registered? Maybe shutting down... */
|
||||
if (!test_bit(QUEUE_FLAG_REGISTERED, &q->queue_flags))
|
||||
return;
|
||||
|
||||
if ((q->mq_ops && IS_ENABLED(CONFIG_BLK_WBT_MQ)) ||
|
||||
(q->request_fn && IS_ENABLED(CONFIG_BLK_WBT_SQ)))
|
||||
wbt_init(q);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wbt_enable_default);
|
||||
|
||||
u64 wbt_default_latency_nsec(struct request_queue *q)
|
||||
{
|
||||
/*
|
||||
|
|
@ -694,29 +696,33 @@ u64 wbt_default_latency_nsec(struct request_queue *q)
|
|||
return 75000000ULL;
|
||||
}
|
||||
|
||||
static int wbt_data_dir(const struct request *rq)
|
||||
{
|
||||
return rq_data_dir(rq);
|
||||
}
|
||||
|
||||
int wbt_init(struct request_queue *q)
|
||||
{
|
||||
struct rq_wb *rwb;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* For now, we depend on the stats window being larger than
|
||||
* our monitoring window. Ensure that this isn't inadvertently
|
||||
* violated.
|
||||
*/
|
||||
BUILD_BUG_ON(RWB_WINDOW_NSEC > BLK_STAT_NSEC);
|
||||
BUILD_BUG_ON(WBT_NR_BITS > BLK_STAT_RES_BITS);
|
||||
|
||||
rwb = kzalloc(sizeof(*rwb), GFP_KERNEL);
|
||||
if (!rwb)
|
||||
return -ENOMEM;
|
||||
|
||||
rwb->cb = blk_stat_alloc_callback(wb_timer_fn, wbt_data_dir, 2, rwb);
|
||||
if (!rwb->cb) {
|
||||
kfree(rwb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < WBT_NUM_RWQ; i++) {
|
||||
atomic_set(&rwb->rq_wait[i].inflight, 0);
|
||||
init_waitqueue_head(&rwb->rq_wait[i].wait);
|
||||
}
|
||||
|
||||
setup_timer(&rwb->window_timer, wb_timer_fn, (unsigned long) rwb);
|
||||
rwb->wc = 1;
|
||||
rwb->queue_depth = RWB_DEF_DEPTH;
|
||||
rwb->last_comp = rwb->last_issue = jiffies;
|
||||
|
|
@ -726,10 +732,10 @@ int wbt_init(struct request_queue *q)
|
|||
wbt_update_limits(rwb);
|
||||
|
||||
/*
|
||||
* Assign rwb, and turn on stats tracking for this queue
|
||||
* Assign rwb and add the stats callback.
|
||||
*/
|
||||
q->rq_wb = rwb;
|
||||
blk_stat_enable(q);
|
||||
blk_stat_add_callback(q, rwb->cb);
|
||||
|
||||
rwb->min_lat_nsec = wbt_default_latency_nsec(q);
|
||||
|
||||
|
|
@ -744,7 +750,8 @@ void wbt_exit(struct request_queue *q)
|
|||
struct rq_wb *rwb = q->rq_wb;
|
||||
|
||||
if (rwb) {
|
||||
del_timer_sync(&rwb->window_timer);
|
||||
blk_stat_remove_callback(q, rwb->cb);
|
||||
blk_stat_free_callback(rwb->cb);
|
||||
q->rq_wb = NULL;
|
||||
kfree(rwb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,27 +32,27 @@ enum {
|
|||
|
||||
static inline void wbt_clear_state(struct blk_issue_stat *stat)
|
||||
{
|
||||
stat->time &= BLK_STAT_TIME_MASK;
|
||||
stat->stat &= ~BLK_STAT_RES_MASK;
|
||||
}
|
||||
|
||||
static inline enum wbt_flags wbt_stat_to_mask(struct blk_issue_stat *stat)
|
||||
{
|
||||
return (stat->time & BLK_STAT_MASK) >> BLK_STAT_SHIFT;
|
||||
return (stat->stat & BLK_STAT_RES_MASK) >> BLK_STAT_RES_SHIFT;
|
||||
}
|
||||
|
||||
static inline void wbt_track(struct blk_issue_stat *stat, enum wbt_flags wb_acct)
|
||||
{
|
||||
stat->time |= ((u64) wb_acct) << BLK_STAT_SHIFT;
|
||||
stat->stat |= ((u64) wb_acct) << BLK_STAT_RES_SHIFT;
|
||||
}
|
||||
|
||||
static inline bool wbt_is_tracked(struct blk_issue_stat *stat)
|
||||
{
|
||||
return (stat->time >> BLK_STAT_SHIFT) & WBT_TRACKED;
|
||||
return (stat->stat >> BLK_STAT_RES_SHIFT) & WBT_TRACKED;
|
||||
}
|
||||
|
||||
static inline bool wbt_is_read(struct blk_issue_stat *stat)
|
||||
{
|
||||
return (stat->time >> BLK_STAT_SHIFT) & WBT_READ;
|
||||
return (stat->stat >> BLK_STAT_RES_SHIFT) & WBT_READ;
|
||||
}
|
||||
|
||||
struct rq_wait {
|
||||
|
|
@ -81,7 +81,7 @@ struct rq_wb {
|
|||
u64 win_nsec; /* default window size */
|
||||
u64 cur_win_nsec; /* current window size */
|
||||
|
||||
struct timer_list window_timer;
|
||||
struct blk_stat_callback *cb;
|
||||
|
||||
s64 sync_issue;
|
||||
void *sync_cookie;
|
||||
|
|
@ -117,6 +117,7 @@ void wbt_update_limits(struct rq_wb *);
|
|||
void wbt_requeue(struct rq_wb *, struct blk_issue_stat *);
|
||||
void wbt_issue(struct rq_wb *, struct blk_issue_stat *);
|
||||
void wbt_disable_default(struct request_queue *);
|
||||
void wbt_enable_default(struct request_queue *);
|
||||
|
||||
void wbt_set_queue_depth(struct rq_wb *, unsigned int);
|
||||
void wbt_set_write_cache(struct rq_wb *, bool);
|
||||
|
|
@ -155,6 +156,9 @@ static inline void wbt_issue(struct rq_wb *rwb, struct blk_issue_stat *stat)
|
|||
static inline void wbt_disable_default(struct request_queue *q)
|
||||
{
|
||||
}
|
||||
static inline void wbt_enable_default(struct request_queue *q)
|
||||
{
|
||||
}
|
||||
static inline void wbt_set_queue_depth(struct rq_wb *rwb, unsigned int depth)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
15
block/blk.h
15
block/blk.h
|
|
@ -60,15 +60,12 @@ void blk_free_flush_queue(struct blk_flush_queue *q);
|
|||
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);
|
||||
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_freeze_queue(struct request_queue *q);
|
||||
|
||||
static inline void blk_queue_enter_live(struct request_queue *q)
|
||||
|
|
@ -319,10 +316,22 @@ static inline struct io_context *create_io_context(gfp_t gfp_mask, int node)
|
|||
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);
|
||||
extern void blk_throtl_register_queue(struct request_queue *q);
|
||||
#else /* CONFIG_BLK_DEV_THROTTLING */
|
||||
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) { }
|
||||
static inline void blk_throtl_register_queue(struct request_queue *q) { }
|
||||
#endif /* CONFIG_BLK_DEV_THROTTLING */
|
||||
#ifdef CONFIG_BLK_DEV_THROTTLING_LOW
|
||||
extern ssize_t blk_throtl_sample_time_show(struct request_queue *q, char *page);
|
||||
extern ssize_t blk_throtl_sample_time_store(struct request_queue *q,
|
||||
const char *page, size_t count);
|
||||
extern void blk_throtl_bio_endio(struct bio *bio);
|
||||
extern void blk_throtl_stat_add(struct request *rq, u64 time);
|
||||
#else
|
||||
static inline void blk_throtl_bio_endio(struct bio *bio) { }
|
||||
static inline void blk_throtl_stat_add(struct request *rq, u64 time) { }
|
||||
#endif
|
||||
|
||||
#endif /* BLK_INTERNAL_H */
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ static void bsg_destroy_job(struct kref *kref)
|
|||
struct bsg_job *job = container_of(kref, struct bsg_job, kref);
|
||||
struct request *rq = job->req;
|
||||
|
||||
blk_end_request_all(rq, rq->errors);
|
||||
blk_end_request_all(rq, scsi_req(rq)->result);
|
||||
|
||||
put_device(job->dev); /* release reference for the request */
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ void bsg_job_done(struct bsg_job *job, int result,
|
|||
struct scsi_request *rq = scsi_req(req);
|
||||
int err;
|
||||
|
||||
err = job->req->errors = result;
|
||||
err = scsi_req(job->req)->result = result;
|
||||
if (err < 0)
|
||||
/* we're only returning the result field in the reply */
|
||||
rq->sense_len = sizeof(u32);
|
||||
|
|
@ -177,7 +177,7 @@ failjob_rls_job:
|
|||
* @q: request queue to manage
|
||||
*
|
||||
* On error the create_bsg_job function should return a -Exyz error value
|
||||
* that will be set to the req->errors.
|
||||
* that will be set to ->result.
|
||||
*
|
||||
* Drivers/subsys should pass this to the queue init function.
|
||||
*/
|
||||
|
|
@ -201,7 +201,7 @@ static void bsg_request_fn(struct request_queue *q)
|
|||
|
||||
ret = bsg_create_job(dev, req);
|
||||
if (ret) {
|
||||
req->errors = ret;
|
||||
scsi_req(req)->result = ret;
|
||||
blk_end_request_all(req, ret);
|
||||
spin_lock_irq(q->queue_lock);
|
||||
continue;
|
||||
|
|
|
|||
12
block/bsg.c
12
block/bsg.c
|
|
@ -391,13 +391,13 @@ static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr,
|
|||
struct scsi_request *req = scsi_req(rq);
|
||||
int ret = 0;
|
||||
|
||||
dprintk("rq %p bio %p 0x%x\n", rq, bio, rq->errors);
|
||||
dprintk("rq %p bio %p 0x%x\n", rq, bio, req->result);
|
||||
/*
|
||||
* fill in all the output members
|
||||
*/
|
||||
hdr->device_status = rq->errors & 0xff;
|
||||
hdr->transport_status = host_byte(rq->errors);
|
||||
hdr->driver_status = driver_byte(rq->errors);
|
||||
hdr->device_status = req->result & 0xff;
|
||||
hdr->transport_status = host_byte(req->result);
|
||||
hdr->driver_status = driver_byte(req->result);
|
||||
hdr->info = 0;
|
||||
if (hdr->device_status || hdr->transport_status || hdr->driver_status)
|
||||
hdr->info |= SG_INFO_CHECK;
|
||||
|
|
@ -431,8 +431,8 @@ static int blk_complete_sgv4_hdr_rq(struct request *rq, struct sg_io_v4 *hdr,
|
|||
* just a protocol response (i.e. non negative), that gets
|
||||
* processed above.
|
||||
*/
|
||||
if (!ret && rq->errors < 0)
|
||||
ret = rq->errors;
|
||||
if (!ret && req->result < 0)
|
||||
ret = req->result;
|
||||
|
||||
blk_rq_unmap_user(bio);
|
||||
scsi_req_free_cmd(req);
|
||||
|
|
|
|||
|
|
@ -3761,16 +3761,14 @@ static void cfq_init_cfqq(struct cfq_data *cfqd, struct cfq_queue *cfqq,
|
|||
}
|
||||
|
||||
#ifdef CONFIG_CFQ_GROUP_IOSCHED
|
||||
static bool check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio)
|
||||
static void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio)
|
||||
{
|
||||
struct cfq_data *cfqd = cic_to_cfqd(cic);
|
||||
struct cfq_queue *cfqq;
|
||||
uint64_t serial_nr;
|
||||
bool nonroot_cg;
|
||||
|
||||
rcu_read_lock();
|
||||
serial_nr = bio_blkcg(bio)->css.serial_nr;
|
||||
nonroot_cg = bio_blkcg(bio) != &blkcg_root;
|
||||
rcu_read_unlock();
|
||||
|
||||
/*
|
||||
|
|
@ -3778,7 +3776,7 @@ static bool check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio)
|
|||
* spuriously on a newly created cic but there's no harm.
|
||||
*/
|
||||
if (unlikely(!cfqd) || likely(cic->blkcg_serial_nr == serial_nr))
|
||||
return nonroot_cg;
|
||||
return;
|
||||
|
||||
/*
|
||||
* Drop reference to queues. New queues will be assigned in new
|
||||
|
|
@ -3799,12 +3797,10 @@ static bool check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio)
|
|||
}
|
||||
|
||||
cic->blkcg_serial_nr = serial_nr;
|
||||
return nonroot_cg;
|
||||
}
|
||||
#else
|
||||
static inline bool check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio)
|
||||
static inline void check_blkcg_changed(struct cfq_io_cq *cic, struct bio *bio)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_CFQ_GROUP_IOSCHED */
|
||||
|
||||
|
|
@ -4449,12 +4445,11 @@ cfq_set_request(struct request_queue *q, struct request *rq, struct bio *bio,
|
|||
const int rw = rq_data_dir(rq);
|
||||
const bool is_sync = rq_is_sync(rq);
|
||||
struct cfq_queue *cfqq;
|
||||
bool disable_wbt;
|
||||
|
||||
spin_lock_irq(q->queue_lock);
|
||||
|
||||
check_ioprio_changed(cic, bio);
|
||||
disable_wbt = check_blkcg_changed(cic, bio);
|
||||
check_blkcg_changed(cic, bio);
|
||||
new_queue:
|
||||
cfqq = cic_to_cfqq(cic, is_sync);
|
||||
if (!cfqq || cfqq == &cfqd->oom_cfqq) {
|
||||
|
|
@ -4491,9 +4486,6 @@ new_queue:
|
|||
rq->elv.priv[1] = cfqq->cfqg;
|
||||
spin_unlock_irq(q->queue_lock);
|
||||
|
||||
if (disable_wbt)
|
||||
wbt_disable_default(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -4706,6 +4698,7 @@ static void cfq_registered_queue(struct request_queue *q)
|
|||
*/
|
||||
if (blk_queue_nonrot(q))
|
||||
cfqd->cfq_slice_idle = 0;
|
||||
wbt_disable_default(q);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -685,7 +685,7 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
|
|||
case BLKALIGNOFF:
|
||||
return compat_put_int(arg, bdev_alignment_offset(bdev));
|
||||
case BLKDISCARDZEROES:
|
||||
return compat_put_uint(arg, bdev_discard_zeroes_data(bdev));
|
||||
return compat_put_uint(arg, 0);
|
||||
case BLKFLSBUF:
|
||||
case BLKROSET:
|
||||
case BLKDISCARD:
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include "blk.h"
|
||||
#include "blk-mq-sched.h"
|
||||
#include "blk-wbt.h"
|
||||
|
||||
static DEFINE_SPINLOCK(elv_list_lock);
|
||||
static LIST_HEAD(elv_list);
|
||||
|
|
@ -877,6 +878,8 @@ void elv_unregister_queue(struct request_queue *q)
|
|||
kobject_uevent(&e->kobj, KOBJ_REMOVE);
|
||||
kobject_del(&e->kobj);
|
||||
e->registered = 0;
|
||||
/* Re-enable throttling in case elevator disabled it */
|
||||
wbt_enable_default(q);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(elv_unregister_queue);
|
||||
|
|
|
|||
|
|
@ -1060,8 +1060,19 @@ static struct attribute *disk_attrs[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static umode_t disk_visible(struct kobject *kobj, struct attribute *a, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, typeof(*dev), kobj);
|
||||
struct gendisk *disk = dev_to_disk(dev);
|
||||
|
||||
if (a == &dev_attr_badblocks.attr && !disk->bb)
|
||||
return 0;
|
||||
return a->mode;
|
||||
}
|
||||
|
||||
static struct attribute_group disk_attr_group = {
|
||||
.attrs = disk_attrs,
|
||||
.is_visible = disk_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *disk_attr_groups[] = {
|
||||
|
|
@ -1352,7 +1363,7 @@ struct kobject *get_disk(struct gendisk *disk)
|
|||
owner = disk->fops->owner;
|
||||
if (owner && !try_module_get(owner))
|
||||
return NULL;
|
||||
kobj = kobject_get(&disk_to_dev(disk)->kobj);
|
||||
kobj = kobject_get_unless_zero(&disk_to_dev(disk)->kobj);
|
||||
if (kobj == NULL) {
|
||||
module_put(owner);
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode,
|
|||
truncate_inode_pages_range(mapping, start, end);
|
||||
|
||||
return blkdev_issue_zeroout(bdev, start >> 9, len >> 9, GFP_KERNEL,
|
||||
false);
|
||||
BLKDEV_ZERO_NOUNMAP);
|
||||
}
|
||||
|
||||
static int put_ushort(unsigned long arg, unsigned short val)
|
||||
|
|
@ -547,7 +547,7 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
|
|||
case BLKALIGNOFF:
|
||||
return put_int(arg, bdev_alignment_offset(bdev));
|
||||
case BLKDISCARDZEROES:
|
||||
return put_uint(arg, bdev_discard_zeroes_data(bdev));
|
||||
return put_uint(arg, 0);
|
||||
case BLKSECTGET:
|
||||
max_sectors = min_t(unsigned int, USHRT_MAX,
|
||||
queue_max_sectors(bdev_get_queue(bdev)));
|
||||
|
|
|
|||
|
|
@ -163,22 +163,12 @@ out:
|
|||
|
||||
int ioprio_best(unsigned short aprio, unsigned short bprio)
|
||||
{
|
||||
unsigned short aclass;
|
||||
unsigned short bclass;
|
||||
|
||||
if (!ioprio_valid(aprio))
|
||||
aprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);
|
||||
if (!ioprio_valid(bprio))
|
||||
bprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);
|
||||
|
||||
aclass = IOPRIO_PRIO_CLASS(aprio);
|
||||
bclass = IOPRIO_PRIO_CLASS(bprio);
|
||||
if (aclass == bclass)
|
||||
return min(aprio, bprio);
|
||||
if (aclass > bclass)
|
||||
return bprio;
|
||||
else
|
||||
return aprio;
|
||||
return min(aprio, bprio);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(ioprio_get, int, which, int, who)
|
||||
|
|
|
|||
719
block/kyber-iosched.c
Normal file
719
block/kyber-iosched.c
Normal file
|
|
@ -0,0 +1,719 @@
|
|||
/*
|
||||
* The Kyber I/O scheduler. Controls latency by throttling queue depths using
|
||||
* scalable techniques.
|
||||
*
|
||||
* Copyright (C) 2017 Facebook
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License v2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/blk-mq.h>
|
||||
#include <linux/elevator.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sbitmap.h>
|
||||
|
||||
#include "blk.h"
|
||||
#include "blk-mq.h"
|
||||
#include "blk-mq-sched.h"
|
||||
#include "blk-mq-tag.h"
|
||||
#include "blk-stat.h"
|
||||
|
||||
/* Scheduling domains. */
|
||||
enum {
|
||||
KYBER_READ,
|
||||
KYBER_SYNC_WRITE,
|
||||
KYBER_OTHER, /* Async writes, discard, etc. */
|
||||
KYBER_NUM_DOMAINS,
|
||||
};
|
||||
|
||||
enum {
|
||||
KYBER_MIN_DEPTH = 256,
|
||||
|
||||
/*
|
||||
* In order to prevent starvation of synchronous requests by a flood of
|
||||
* asynchronous requests, we reserve 25% of requests for synchronous
|
||||
* operations.
|
||||
*/
|
||||
KYBER_ASYNC_PERCENT = 75,
|
||||
};
|
||||
|
||||
/*
|
||||
* Initial device-wide depths for each scheduling domain.
|
||||
*
|
||||
* Even for fast devices with lots of tags like NVMe, you can saturate
|
||||
* the device with only a fraction of the maximum possible queue depth.
|
||||
* So, we cap these to a reasonable value.
|
||||
*/
|
||||
static const unsigned int kyber_depth[] = {
|
||||
[KYBER_READ] = 256,
|
||||
[KYBER_SYNC_WRITE] = 128,
|
||||
[KYBER_OTHER] = 64,
|
||||
};
|
||||
|
||||
/*
|
||||
* Scheduling domain batch sizes. We favor reads.
|
||||
*/
|
||||
static const unsigned int kyber_batch_size[] = {
|
||||
[KYBER_READ] = 16,
|
||||
[KYBER_SYNC_WRITE] = 8,
|
||||
[KYBER_OTHER] = 8,
|
||||
};
|
||||
|
||||
struct kyber_queue_data {
|
||||
struct request_queue *q;
|
||||
|
||||
struct blk_stat_callback *cb;
|
||||
|
||||
/*
|
||||
* The device is divided into multiple scheduling domains based on the
|
||||
* request type. Each domain has a fixed number of in-flight requests of
|
||||
* that type device-wide, limited by these tokens.
|
||||
*/
|
||||
struct sbitmap_queue domain_tokens[KYBER_NUM_DOMAINS];
|
||||
|
||||
/*
|
||||
* Async request percentage, converted to per-word depth for
|
||||
* sbitmap_get_shallow().
|
||||
*/
|
||||
unsigned int async_depth;
|
||||
|
||||
/* Target latencies in nanoseconds. */
|
||||
u64 read_lat_nsec, write_lat_nsec;
|
||||
};
|
||||
|
||||
struct kyber_hctx_data {
|
||||
spinlock_t lock;
|
||||
struct list_head rqs[KYBER_NUM_DOMAINS];
|
||||
unsigned int cur_domain;
|
||||
unsigned int batching;
|
||||
wait_queue_t domain_wait[KYBER_NUM_DOMAINS];
|
||||
atomic_t wait_index[KYBER_NUM_DOMAINS];
|
||||
};
|
||||
|
||||
static int rq_sched_domain(const struct request *rq)
|
||||
{
|
||||
unsigned int op = rq->cmd_flags;
|
||||
|
||||
if ((op & REQ_OP_MASK) == REQ_OP_READ)
|
||||
return KYBER_READ;
|
||||
else if ((op & REQ_OP_MASK) == REQ_OP_WRITE && op_is_sync(op))
|
||||
return KYBER_SYNC_WRITE;
|
||||
else
|
||||
return KYBER_OTHER;
|
||||
}
|
||||
|
||||
enum {
|
||||
NONE = 0,
|
||||
GOOD = 1,
|
||||
GREAT = 2,
|
||||
BAD = -1,
|
||||
AWFUL = -2,
|
||||
};
|
||||
|
||||
#define IS_GOOD(status) ((status) > 0)
|
||||
#define IS_BAD(status) ((status) < 0)
|
||||
|
||||
static int kyber_lat_status(struct blk_stat_callback *cb,
|
||||
unsigned int sched_domain, u64 target)
|
||||
{
|
||||
u64 latency;
|
||||
|
||||
if (!cb->stat[sched_domain].nr_samples)
|
||||
return NONE;
|
||||
|
||||
latency = cb->stat[sched_domain].mean;
|
||||
if (latency >= 2 * target)
|
||||
return AWFUL;
|
||||
else if (latency > target)
|
||||
return BAD;
|
||||
else if (latency <= target / 2)
|
||||
return GREAT;
|
||||
else /* (latency <= target) */
|
||||
return GOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the read or synchronous write depth given the status of reads and
|
||||
* writes. The goal is that the latencies of the two domains are fair (i.e., if
|
||||
* one is good, then the other is good).
|
||||
*/
|
||||
static void kyber_adjust_rw_depth(struct kyber_queue_data *kqd,
|
||||
unsigned int sched_domain, int this_status,
|
||||
int other_status)
|
||||
{
|
||||
unsigned int orig_depth, depth;
|
||||
|
||||
/*
|
||||
* If this domain had no samples, or reads and writes are both good or
|
||||
* both bad, don't adjust the depth.
|
||||
*/
|
||||
if (this_status == NONE ||
|
||||
(IS_GOOD(this_status) && IS_GOOD(other_status)) ||
|
||||
(IS_BAD(this_status) && IS_BAD(other_status)))
|
||||
return;
|
||||
|
||||
orig_depth = depth = kqd->domain_tokens[sched_domain].sb.depth;
|
||||
|
||||
if (other_status == NONE) {
|
||||
depth++;
|
||||
} else {
|
||||
switch (this_status) {
|
||||
case GOOD:
|
||||
if (other_status == AWFUL)
|
||||
depth -= max(depth / 4, 1U);
|
||||
else
|
||||
depth -= max(depth / 8, 1U);
|
||||
break;
|
||||
case GREAT:
|
||||
if (other_status == AWFUL)
|
||||
depth /= 2;
|
||||
else
|
||||
depth -= max(depth / 4, 1U);
|
||||
break;
|
||||
case BAD:
|
||||
depth++;
|
||||
break;
|
||||
case AWFUL:
|
||||
if (other_status == GREAT)
|
||||
depth += 2;
|
||||
else
|
||||
depth++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
depth = clamp(depth, 1U, kyber_depth[sched_domain]);
|
||||
if (depth != orig_depth)
|
||||
sbitmap_queue_resize(&kqd->domain_tokens[sched_domain], depth);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the depth of other requests given the status of reads and synchronous
|
||||
* writes. As long as either domain is doing fine, we don't throttle, but if
|
||||
* both domains are doing badly, we throttle heavily.
|
||||
*/
|
||||
static void kyber_adjust_other_depth(struct kyber_queue_data *kqd,
|
||||
int read_status, int write_status,
|
||||
bool have_samples)
|
||||
{
|
||||
unsigned int orig_depth, depth;
|
||||
int status;
|
||||
|
||||
orig_depth = depth = kqd->domain_tokens[KYBER_OTHER].sb.depth;
|
||||
|
||||
if (read_status == NONE && write_status == NONE) {
|
||||
depth += 2;
|
||||
} else if (have_samples) {
|
||||
if (read_status == NONE)
|
||||
status = write_status;
|
||||
else if (write_status == NONE)
|
||||
status = read_status;
|
||||
else
|
||||
status = max(read_status, write_status);
|
||||
switch (status) {
|
||||
case GREAT:
|
||||
depth += 2;
|
||||
break;
|
||||
case GOOD:
|
||||
depth++;
|
||||
break;
|
||||
case BAD:
|
||||
depth -= max(depth / 4, 1U);
|
||||
break;
|
||||
case AWFUL:
|
||||
depth /= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
depth = clamp(depth, 1U, kyber_depth[KYBER_OTHER]);
|
||||
if (depth != orig_depth)
|
||||
sbitmap_queue_resize(&kqd->domain_tokens[KYBER_OTHER], depth);
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply heuristics for limiting queue depths based on gathered latency
|
||||
* statistics.
|
||||
*/
|
||||
static void kyber_stat_timer_fn(struct blk_stat_callback *cb)
|
||||
{
|
||||
struct kyber_queue_data *kqd = cb->data;
|
||||
int read_status, write_status;
|
||||
|
||||
read_status = kyber_lat_status(cb, KYBER_READ, kqd->read_lat_nsec);
|
||||
write_status = kyber_lat_status(cb, KYBER_SYNC_WRITE, kqd->write_lat_nsec);
|
||||
|
||||
kyber_adjust_rw_depth(kqd, KYBER_READ, read_status, write_status);
|
||||
kyber_adjust_rw_depth(kqd, KYBER_SYNC_WRITE, write_status, read_status);
|
||||
kyber_adjust_other_depth(kqd, read_status, write_status,
|
||||
cb->stat[KYBER_OTHER].nr_samples != 0);
|
||||
|
||||
/*
|
||||
* Continue monitoring latencies if we aren't hitting the targets or
|
||||
* we're still throttling other requests.
|
||||
*/
|
||||
if (!blk_stat_is_active(kqd->cb) &&
|
||||
((IS_BAD(read_status) || IS_BAD(write_status) ||
|
||||
kqd->domain_tokens[KYBER_OTHER].sb.depth < kyber_depth[KYBER_OTHER])))
|
||||
blk_stat_activate_msecs(kqd->cb, 100);
|
||||
}
|
||||
|
||||
static unsigned int kyber_sched_tags_shift(struct kyber_queue_data *kqd)
|
||||
{
|
||||
/*
|
||||
* All of the hardware queues have the same depth, so we can just grab
|
||||
* the shift of the first one.
|
||||
*/
|
||||
return kqd->q->queue_hw_ctx[0]->sched_tags->bitmap_tags.sb.shift;
|
||||
}
|
||||
|
||||
static struct kyber_queue_data *kyber_queue_data_alloc(struct request_queue *q)
|
||||
{
|
||||
struct kyber_queue_data *kqd;
|
||||
unsigned int max_tokens;
|
||||
unsigned int shift;
|
||||
int ret = -ENOMEM;
|
||||
int i;
|
||||
|
||||
kqd = kmalloc_node(sizeof(*kqd), GFP_KERNEL, q->node);
|
||||
if (!kqd)
|
||||
goto err;
|
||||
kqd->q = q;
|
||||
|
||||
kqd->cb = blk_stat_alloc_callback(kyber_stat_timer_fn, rq_sched_domain,
|
||||
KYBER_NUM_DOMAINS, kqd);
|
||||
if (!kqd->cb)
|
||||
goto err_kqd;
|
||||
|
||||
/*
|
||||
* The maximum number of tokens for any scheduling domain is at least
|
||||
* the queue depth of a single hardware queue. If the hardware doesn't
|
||||
* have many tags, still provide a reasonable number.
|
||||
*/
|
||||
max_tokens = max_t(unsigned int, q->tag_set->queue_depth,
|
||||
KYBER_MIN_DEPTH);
|
||||
for (i = 0; i < KYBER_NUM_DOMAINS; i++) {
|
||||
WARN_ON(!kyber_depth[i]);
|
||||
WARN_ON(!kyber_batch_size[i]);
|
||||
ret = sbitmap_queue_init_node(&kqd->domain_tokens[i],
|
||||
max_tokens, -1, false, GFP_KERNEL,
|
||||
q->node);
|
||||
if (ret) {
|
||||
while (--i >= 0)
|
||||
sbitmap_queue_free(&kqd->domain_tokens[i]);
|
||||
goto err_cb;
|
||||
}
|
||||
sbitmap_queue_resize(&kqd->domain_tokens[i], kyber_depth[i]);
|
||||
}
|
||||
|
||||
shift = kyber_sched_tags_shift(kqd);
|
||||
kqd->async_depth = (1U << shift) * KYBER_ASYNC_PERCENT / 100U;
|
||||
|
||||
kqd->read_lat_nsec = 2000000ULL;
|
||||
kqd->write_lat_nsec = 10000000ULL;
|
||||
|
||||
return kqd;
|
||||
|
||||
err_cb:
|
||||
blk_stat_free_callback(kqd->cb);
|
||||
err_kqd:
|
||||
kfree(kqd);
|
||||
err:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int kyber_init_sched(struct request_queue *q, struct elevator_type *e)
|
||||
{
|
||||
struct kyber_queue_data *kqd;
|
||||
struct elevator_queue *eq;
|
||||
|
||||
eq = elevator_alloc(q, e);
|
||||
if (!eq)
|
||||
return -ENOMEM;
|
||||
|
||||
kqd = kyber_queue_data_alloc(q);
|
||||
if (IS_ERR(kqd)) {
|
||||
kobject_put(&eq->kobj);
|
||||
return PTR_ERR(kqd);
|
||||
}
|
||||
|
||||
eq->elevator_data = kqd;
|
||||
q->elevator = eq;
|
||||
|
||||
blk_stat_add_callback(q, kqd->cb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kyber_exit_sched(struct elevator_queue *e)
|
||||
{
|
||||
struct kyber_queue_data *kqd = e->elevator_data;
|
||||
struct request_queue *q = kqd->q;
|
||||
int i;
|
||||
|
||||
blk_stat_remove_callback(q, kqd->cb);
|
||||
|
||||
for (i = 0; i < KYBER_NUM_DOMAINS; i++)
|
||||
sbitmap_queue_free(&kqd->domain_tokens[i]);
|
||||
blk_stat_free_callback(kqd->cb);
|
||||
kfree(kqd);
|
||||
}
|
||||
|
||||
static int kyber_init_hctx(struct blk_mq_hw_ctx *hctx, unsigned int hctx_idx)
|
||||
{
|
||||
struct kyber_hctx_data *khd;
|
||||
int i;
|
||||
|
||||
khd = kmalloc_node(sizeof(*khd), GFP_KERNEL, hctx->numa_node);
|
||||
if (!khd)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&khd->lock);
|
||||
|
||||
for (i = 0; i < KYBER_NUM_DOMAINS; i++) {
|
||||
INIT_LIST_HEAD(&khd->rqs[i]);
|
||||
INIT_LIST_HEAD(&khd->domain_wait[i].task_list);
|
||||
atomic_set(&khd->wait_index[i], 0);
|
||||
}
|
||||
|
||||
khd->cur_domain = 0;
|
||||
khd->batching = 0;
|
||||
|
||||
hctx->sched_data = khd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kyber_exit_hctx(struct blk_mq_hw_ctx *hctx, unsigned int hctx_idx)
|
||||
{
|
||||
kfree(hctx->sched_data);
|
||||
}
|
||||
|
||||
static int rq_get_domain_token(struct request *rq)
|
||||
{
|
||||
return (long)rq->elv.priv[0];
|
||||
}
|
||||
|
||||
static void rq_set_domain_token(struct request *rq, int token)
|
||||
{
|
||||
rq->elv.priv[0] = (void *)(long)token;
|
||||
}
|
||||
|
||||
static void rq_clear_domain_token(struct kyber_queue_data *kqd,
|
||||
struct request *rq)
|
||||
{
|
||||
unsigned int sched_domain;
|
||||
int nr;
|
||||
|
||||
nr = rq_get_domain_token(rq);
|
||||
if (nr != -1) {
|
||||
sched_domain = rq_sched_domain(rq);
|
||||
sbitmap_queue_clear(&kqd->domain_tokens[sched_domain], nr,
|
||||
rq->mq_ctx->cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static struct request *kyber_get_request(struct request_queue *q,
|
||||
unsigned int op,
|
||||
struct blk_mq_alloc_data *data)
|
||||
{
|
||||
struct kyber_queue_data *kqd = q->elevator->elevator_data;
|
||||
struct request *rq;
|
||||
|
||||
/*
|
||||
* We use the scheduler tags as per-hardware queue queueing tokens.
|
||||
* Async requests can be limited at this stage.
|
||||
*/
|
||||
if (!op_is_sync(op))
|
||||
data->shallow_depth = kqd->async_depth;
|
||||
|
||||
rq = __blk_mq_alloc_request(data, op);
|
||||
if (rq)
|
||||
rq_set_domain_token(rq, -1);
|
||||
return rq;
|
||||
}
|
||||
|
||||
static void kyber_put_request(struct request *rq)
|
||||
{
|
||||
struct request_queue *q = rq->q;
|
||||
struct kyber_queue_data *kqd = q->elevator->elevator_data;
|
||||
|
||||
rq_clear_domain_token(kqd, rq);
|
||||
blk_mq_finish_request(rq);
|
||||
}
|
||||
|
||||
static void kyber_completed_request(struct request *rq)
|
||||
{
|
||||
struct request_queue *q = rq->q;
|
||||
struct kyber_queue_data *kqd = q->elevator->elevator_data;
|
||||
unsigned int sched_domain;
|
||||
u64 now, latency, target;
|
||||
|
||||
/*
|
||||
* Check if this request met our latency goal. If not, quickly gather
|
||||
* some statistics and start throttling.
|
||||
*/
|
||||
sched_domain = rq_sched_domain(rq);
|
||||
switch (sched_domain) {
|
||||
case KYBER_READ:
|
||||
target = kqd->read_lat_nsec;
|
||||
break;
|
||||
case KYBER_SYNC_WRITE:
|
||||
target = kqd->write_lat_nsec;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* If we are already monitoring latencies, don't check again. */
|
||||
if (blk_stat_is_active(kqd->cb))
|
||||
return;
|
||||
|
||||
now = __blk_stat_time(ktime_to_ns(ktime_get()));
|
||||
if (now < blk_stat_time(&rq->issue_stat))
|
||||
return;
|
||||
|
||||
latency = now - blk_stat_time(&rq->issue_stat);
|
||||
|
||||
if (latency > target)
|
||||
blk_stat_activate_msecs(kqd->cb, 10);
|
||||
}
|
||||
|
||||
static void kyber_flush_busy_ctxs(struct kyber_hctx_data *khd,
|
||||
struct blk_mq_hw_ctx *hctx)
|
||||
{
|
||||
LIST_HEAD(rq_list);
|
||||
struct request *rq, *next;
|
||||
|
||||
blk_mq_flush_busy_ctxs(hctx, &rq_list);
|
||||
list_for_each_entry_safe(rq, next, &rq_list, queuelist) {
|
||||
unsigned int sched_domain;
|
||||
|
||||
sched_domain = rq_sched_domain(rq);
|
||||
list_move_tail(&rq->queuelist, &khd->rqs[sched_domain]);
|
||||
}
|
||||
}
|
||||
|
||||
static int kyber_domain_wake(wait_queue_t *wait, unsigned mode, int flags,
|
||||
void *key)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx = READ_ONCE(wait->private);
|
||||
|
||||
list_del_init(&wait->task_list);
|
||||
blk_mq_run_hw_queue(hctx, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int kyber_get_domain_token(struct kyber_queue_data *kqd,
|
||||
struct kyber_hctx_data *khd,
|
||||
struct blk_mq_hw_ctx *hctx)
|
||||
{
|
||||
unsigned int sched_domain = khd->cur_domain;
|
||||
struct sbitmap_queue *domain_tokens = &kqd->domain_tokens[sched_domain];
|
||||
wait_queue_t *wait = &khd->domain_wait[sched_domain];
|
||||
struct sbq_wait_state *ws;
|
||||
int nr;
|
||||
|
||||
nr = __sbitmap_queue_get(domain_tokens);
|
||||
if (nr >= 0)
|
||||
return nr;
|
||||
|
||||
/*
|
||||
* If we failed to get a domain token, make sure the hardware queue is
|
||||
* run when one becomes available. Note that this is serialized on
|
||||
* khd->lock, but we still need to be careful about the waker.
|
||||
*/
|
||||
if (list_empty_careful(&wait->task_list)) {
|
||||
init_waitqueue_func_entry(wait, kyber_domain_wake);
|
||||
wait->private = hctx;
|
||||
ws = sbq_wait_ptr(domain_tokens,
|
||||
&khd->wait_index[sched_domain]);
|
||||
add_wait_queue(&ws->wait, wait);
|
||||
|
||||
/*
|
||||
* Try again in case a token was freed before we got on the wait
|
||||
* queue.
|
||||
*/
|
||||
nr = __sbitmap_queue_get(domain_tokens);
|
||||
}
|
||||
return nr;
|
||||
}
|
||||
|
||||
static struct request *
|
||||
kyber_dispatch_cur_domain(struct kyber_queue_data *kqd,
|
||||
struct kyber_hctx_data *khd,
|
||||
struct blk_mq_hw_ctx *hctx,
|
||||
bool *flushed)
|
||||
{
|
||||
struct list_head *rqs;
|
||||
struct request *rq;
|
||||
int nr;
|
||||
|
||||
rqs = &khd->rqs[khd->cur_domain];
|
||||
rq = list_first_entry_or_null(rqs, struct request, queuelist);
|
||||
|
||||
/*
|
||||
* If there wasn't already a pending request and we haven't flushed the
|
||||
* software queues yet, flush the software queues and check again.
|
||||
*/
|
||||
if (!rq && !*flushed) {
|
||||
kyber_flush_busy_ctxs(khd, hctx);
|
||||
*flushed = true;
|
||||
rq = list_first_entry_or_null(rqs, struct request, queuelist);
|
||||
}
|
||||
|
||||
if (rq) {
|
||||
nr = kyber_get_domain_token(kqd, khd, hctx);
|
||||
if (nr >= 0) {
|
||||
khd->batching++;
|
||||
rq_set_domain_token(rq, nr);
|
||||
list_del_init(&rq->queuelist);
|
||||
return rq;
|
||||
}
|
||||
}
|
||||
|
||||
/* There were either no pending requests or no tokens. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct request *kyber_dispatch_request(struct blk_mq_hw_ctx *hctx)
|
||||
{
|
||||
struct kyber_queue_data *kqd = hctx->queue->elevator->elevator_data;
|
||||
struct kyber_hctx_data *khd = hctx->sched_data;
|
||||
bool flushed = false;
|
||||
struct request *rq;
|
||||
int i;
|
||||
|
||||
spin_lock(&khd->lock);
|
||||
|
||||
/*
|
||||
* First, if we are still entitled to batch, try to dispatch a request
|
||||
* from the batch.
|
||||
*/
|
||||
if (khd->batching < kyber_batch_size[khd->cur_domain]) {
|
||||
rq = kyber_dispatch_cur_domain(kqd, khd, hctx, &flushed);
|
||||
if (rq)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Either,
|
||||
* 1. We were no longer entitled to a batch.
|
||||
* 2. The domain we were batching didn't have any requests.
|
||||
* 3. The domain we were batching was out of tokens.
|
||||
*
|
||||
* Start another batch. Note that this wraps back around to the original
|
||||
* domain if no other domains have requests or tokens.
|
||||
*/
|
||||
khd->batching = 0;
|
||||
for (i = 0; i < KYBER_NUM_DOMAINS; i++) {
|
||||
if (khd->cur_domain == KYBER_NUM_DOMAINS - 1)
|
||||
khd->cur_domain = 0;
|
||||
else
|
||||
khd->cur_domain++;
|
||||
|
||||
rq = kyber_dispatch_cur_domain(kqd, khd, hctx, &flushed);
|
||||
if (rq)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rq = NULL;
|
||||
out:
|
||||
spin_unlock(&khd->lock);
|
||||
return rq;
|
||||
}
|
||||
|
||||
static bool kyber_has_work(struct blk_mq_hw_ctx *hctx)
|
||||
{
|
||||
struct kyber_hctx_data *khd = hctx->sched_data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < KYBER_NUM_DOMAINS; i++) {
|
||||
if (!list_empty_careful(&khd->rqs[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#define KYBER_LAT_SHOW_STORE(op) \
|
||||
static ssize_t kyber_##op##_lat_show(struct elevator_queue *e, \
|
||||
char *page) \
|
||||
{ \
|
||||
struct kyber_queue_data *kqd = e->elevator_data; \
|
||||
\
|
||||
return sprintf(page, "%llu\n", kqd->op##_lat_nsec); \
|
||||
} \
|
||||
\
|
||||
static ssize_t kyber_##op##_lat_store(struct elevator_queue *e, \
|
||||
const char *page, size_t count) \
|
||||
{ \
|
||||
struct kyber_queue_data *kqd = e->elevator_data; \
|
||||
unsigned long long nsec; \
|
||||
int ret; \
|
||||
\
|
||||
ret = kstrtoull(page, 10, &nsec); \
|
||||
if (ret) \
|
||||
return ret; \
|
||||
\
|
||||
kqd->op##_lat_nsec = nsec; \
|
||||
\
|
||||
return count; \
|
||||
}
|
||||
KYBER_LAT_SHOW_STORE(read);
|
||||
KYBER_LAT_SHOW_STORE(write);
|
||||
#undef KYBER_LAT_SHOW_STORE
|
||||
|
||||
#define KYBER_LAT_ATTR(op) __ATTR(op##_lat_nsec, 0644, kyber_##op##_lat_show, kyber_##op##_lat_store)
|
||||
static struct elv_fs_entry kyber_sched_attrs[] = {
|
||||
KYBER_LAT_ATTR(read),
|
||||
KYBER_LAT_ATTR(write),
|
||||
__ATTR_NULL
|
||||
};
|
||||
#undef KYBER_LAT_ATTR
|
||||
|
||||
static struct elevator_type kyber_sched = {
|
||||
.ops.mq = {
|
||||
.init_sched = kyber_init_sched,
|
||||
.exit_sched = kyber_exit_sched,
|
||||
.init_hctx = kyber_init_hctx,
|
||||
.exit_hctx = kyber_exit_hctx,
|
||||
.get_request = kyber_get_request,
|
||||
.put_request = kyber_put_request,
|
||||
.completed_request = kyber_completed_request,
|
||||
.dispatch_request = kyber_dispatch_request,
|
||||
.has_work = kyber_has_work,
|
||||
},
|
||||
.uses_mq = true,
|
||||
.elevator_attrs = kyber_sched_attrs,
|
||||
.elevator_name = "kyber",
|
||||
.elevator_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init kyber_init(void)
|
||||
{
|
||||
return elv_register(&kyber_sched);
|
||||
}
|
||||
|
||||
static void __exit kyber_exit(void)
|
||||
{
|
||||
elv_unregister(&kyber_sched);
|
||||
}
|
||||
|
||||
module_init(kyber_init);
|
||||
module_exit(kyber_exit);
|
||||
|
||||
MODULE_AUTHOR("Omar Sandoval");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Kyber I/O scheduler");
|
||||
|
|
@ -497,7 +497,6 @@ rescan:
|
|||
|
||||
if (disk->fops->revalidate_disk)
|
||||
disk->fops->revalidate_disk(disk);
|
||||
blk_integrity_revalidate(disk);
|
||||
check_disk_size_change(disk, bdev);
|
||||
bdev->bd_invalidated = 0;
|
||||
if (!get_capacity(disk) || !(state = check_partition(disk, bdev)))
|
||||
|
|
|
|||
|
|
@ -262,11 +262,11 @@ static int blk_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr,
|
|||
/*
|
||||
* fill in all the output members
|
||||
*/
|
||||
hdr->status = rq->errors & 0xff;
|
||||
hdr->masked_status = status_byte(rq->errors);
|
||||
hdr->msg_status = msg_byte(rq->errors);
|
||||
hdr->host_status = host_byte(rq->errors);
|
||||
hdr->driver_status = driver_byte(rq->errors);
|
||||
hdr->status = req->result & 0xff;
|
||||
hdr->masked_status = status_byte(req->result);
|
||||
hdr->msg_status = msg_byte(req->result);
|
||||
hdr->host_status = host_byte(req->result);
|
||||
hdr->driver_status = driver_byte(req->result);
|
||||
hdr->info = 0;
|
||||
if (hdr->masked_status || hdr->host_status || hdr->driver_status)
|
||||
hdr->info |= SG_INFO_CHECK;
|
||||
|
|
@ -362,7 +362,7 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk,
|
|||
goto out_free_cdb;
|
||||
|
||||
bio = rq->bio;
|
||||
rq->retries = 0;
|
||||
req->retries = 0;
|
||||
|
||||
start_time = jiffies;
|
||||
|
||||
|
|
@ -476,13 +476,13 @@ int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode,
|
|||
goto error;
|
||||
|
||||
/* default. possible overriden later */
|
||||
rq->retries = 5;
|
||||
req->retries = 5;
|
||||
|
||||
switch (opcode) {
|
||||
case SEND_DIAGNOSTIC:
|
||||
case FORMAT_UNIT:
|
||||
rq->timeout = FORMAT_UNIT_TIMEOUT;
|
||||
rq->retries = 1;
|
||||
req->retries = 1;
|
||||
break;
|
||||
case START_STOP:
|
||||
rq->timeout = START_STOP_TIMEOUT;
|
||||
|
|
@ -495,7 +495,7 @@ int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode,
|
|||
break;
|
||||
case READ_DEFECT_DATA:
|
||||
rq->timeout = READ_DEFECT_DATA_TIMEOUT;
|
||||
rq->retries = 1;
|
||||
req->retries = 1;
|
||||
break;
|
||||
default:
|
||||
rq->timeout = BLK_DEFAULT_SG_TIMEOUT;
|
||||
|
|
@ -509,7 +509,7 @@ int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode,
|
|||
|
||||
blk_execute_rq(q, disk, rq, 0);
|
||||
|
||||
err = rq->errors & 0xff; /* only 8 bit SCSI status */
|
||||
err = req->result & 0xff; /* only 8 bit SCSI status */
|
||||
if (err) {
|
||||
if (req->sense_len && req->sense) {
|
||||
bytes = (OMAX_SB_LEN > req->sense_len) ?
|
||||
|
|
@ -547,7 +547,8 @@ static int __blk_send_generic(struct request_queue *q, struct gendisk *bd_disk,
|
|||
scsi_req(rq)->cmd[0] = cmd;
|
||||
scsi_req(rq)->cmd[4] = data;
|
||||
scsi_req(rq)->cmd_len = 6;
|
||||
err = blk_execute_rq(q, bd_disk, rq, 0);
|
||||
blk_execute_rq(q, bd_disk, rq, 0);
|
||||
err = scsi_req(rq)->result ? -EIO : 0;
|
||||
blk_put_request(rq);
|
||||
|
||||
return err;
|
||||
|
|
|
|||
153
block/sed-opal.c
153
block/sed-opal.c
|
|
@ -275,8 +275,8 @@ static bool check_tper(const void *data)
|
|||
u8 flags = tper->supported_features;
|
||||
|
||||
if (!(flags & TPER_SYNC_SUPPORTED)) {
|
||||
pr_err("TPer sync not supported. flags = %d\n",
|
||||
tper->supported_features);
|
||||
pr_debug("TPer sync not supported. flags = %d\n",
|
||||
tper->supported_features);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -289,7 +289,7 @@ static bool check_sum(const void *data)
|
|||
u32 nlo = be32_to_cpu(sum->num_locking_objects);
|
||||
|
||||
if (nlo == 0) {
|
||||
pr_err("Need at least one locking object.\n");
|
||||
pr_debug("Need at least one locking object.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -385,9 +385,9 @@ static int next(struct opal_dev *dev)
|
|||
|
||||
error = step->fn(dev, step->data);
|
||||
if (error) {
|
||||
pr_err("Error on step function: %d with error %d: %s\n",
|
||||
state, error,
|
||||
opal_error_to_human(error));
|
||||
pr_debug("Error on step function: %d with error %d: %s\n",
|
||||
state, error,
|
||||
opal_error_to_human(error));
|
||||
|
||||
/* For each OPAL command we do a discovery0 then we
|
||||
* start some sort of session.
|
||||
|
|
@ -419,8 +419,8 @@ static int opal_discovery0_end(struct opal_dev *dev)
|
|||
print_buffer(dev->resp, hlen);
|
||||
|
||||
if (hlen > IO_BUFFER_LENGTH - sizeof(*hdr)) {
|
||||
pr_warn("Discovery length overflows buffer (%zu+%u)/%u\n",
|
||||
sizeof(*hdr), hlen, IO_BUFFER_LENGTH);
|
||||
pr_debug("Discovery length overflows buffer (%zu+%u)/%u\n",
|
||||
sizeof(*hdr), hlen, IO_BUFFER_LENGTH);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
|
|
@ -503,7 +503,7 @@ static void add_token_u8(int *err, struct opal_dev *cmd, u8 tok)
|
|||
if (*err)
|
||||
return;
|
||||
if (cmd->pos >= IO_BUFFER_LENGTH - 1) {
|
||||
pr_err("Error adding u8: end of buffer.\n");
|
||||
pr_debug("Error adding u8: end of buffer.\n");
|
||||
*err = -ERANGE;
|
||||
return;
|
||||
}
|
||||
|
|
@ -553,7 +553,7 @@ static void add_token_u64(int *err, struct opal_dev *cmd, u64 number)
|
|||
len = DIV_ROUND_UP(msb, 4);
|
||||
|
||||
if (cmd->pos >= IO_BUFFER_LENGTH - len - 1) {
|
||||
pr_err("Error adding u64: end of buffer.\n");
|
||||
pr_debug("Error adding u64: end of buffer.\n");
|
||||
*err = -ERANGE;
|
||||
return;
|
||||
}
|
||||
|
|
@ -579,7 +579,7 @@ static void add_token_bytestring(int *err, struct opal_dev *cmd,
|
|||
}
|
||||
|
||||
if (len >= IO_BUFFER_LENGTH - cmd->pos - header_len) {
|
||||
pr_err("Error adding bytestring: end of buffer.\n");
|
||||
pr_debug("Error adding bytestring: end of buffer.\n");
|
||||
*err = -ERANGE;
|
||||
return;
|
||||
}
|
||||
|
|
@ -597,7 +597,7 @@ static void add_token_bytestring(int *err, struct opal_dev *cmd,
|
|||
static int build_locking_range(u8 *buffer, size_t length, u8 lr)
|
||||
{
|
||||
if (length > OPAL_UID_LENGTH) {
|
||||
pr_err("Can't build locking range. Length OOB\n");
|
||||
pr_debug("Can't build locking range. Length OOB\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
|
|
@ -614,7 +614,7 @@ static int build_locking_range(u8 *buffer, size_t length, u8 lr)
|
|||
static int build_locking_user(u8 *buffer, size_t length, u8 lr)
|
||||
{
|
||||
if (length > OPAL_UID_LENGTH) {
|
||||
pr_err("Can't build locking range user, Length OOB\n");
|
||||
pr_debug("Can't build locking range user, Length OOB\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
|
|
@ -648,7 +648,7 @@ static int cmd_finalize(struct opal_dev *cmd, u32 hsn, u32 tsn)
|
|||
add_token_u8(&err, cmd, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error finalizing command.\n");
|
||||
pr_debug("Error finalizing command.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
|
|
@ -660,7 +660,7 @@ static int cmd_finalize(struct opal_dev *cmd, u32 hsn, u32 tsn)
|
|||
hdr->subpkt.length = cpu_to_be32(cmd->pos - sizeof(*hdr));
|
||||
while (cmd->pos % 4) {
|
||||
if (cmd->pos >= IO_BUFFER_LENGTH) {
|
||||
pr_err("Error: Buffer overrun\n");
|
||||
pr_debug("Error: Buffer overrun\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
cmd->cmd[cmd->pos++] = 0;
|
||||
|
|
@ -679,14 +679,14 @@ static const struct opal_resp_tok *response_get_token(
|
|||
const struct opal_resp_tok *tok;
|
||||
|
||||
if (n >= resp->num) {
|
||||
pr_err("Token number doesn't exist: %d, resp: %d\n",
|
||||
n, resp->num);
|
||||
pr_debug("Token number doesn't exist: %d, resp: %d\n",
|
||||
n, resp->num);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
tok = &resp->toks[n];
|
||||
if (tok->len == 0) {
|
||||
pr_err("Token length must be non-zero\n");
|
||||
pr_debug("Token length must be non-zero\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
|
|
@ -727,7 +727,7 @@ static ssize_t response_parse_short(struct opal_resp_tok *tok,
|
|||
|
||||
tok->type = OPAL_DTA_TOKENID_UINT;
|
||||
if (tok->len > 9) {
|
||||
pr_warn("uint64 with more than 8 bytes\n");
|
||||
pr_debug("uint64 with more than 8 bytes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = tok->len - 1; i > 0; i--) {
|
||||
|
|
@ -814,8 +814,8 @@ static int response_parse(const u8 *buf, size_t length,
|
|||
|
||||
if (clen == 0 || plen == 0 || slen == 0 ||
|
||||
slen > IO_BUFFER_LENGTH - sizeof(*hdr)) {
|
||||
pr_err("Bad header length. cp: %u, pkt: %u, subpkt: %u\n",
|
||||
clen, plen, slen);
|
||||
pr_debug("Bad header length. cp: %u, pkt: %u, subpkt: %u\n",
|
||||
clen, plen, slen);
|
||||
print_buffer(pos, sizeof(*hdr));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -848,7 +848,7 @@ static int response_parse(const u8 *buf, size_t length,
|
|||
}
|
||||
|
||||
if (num_entries == 0) {
|
||||
pr_err("Couldn't parse response.\n");
|
||||
pr_debug("Couldn't parse response.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
resp->num = num_entries;
|
||||
|
|
@ -861,18 +861,18 @@ static size_t response_get_string(const struct parsed_resp *resp, int n,
|
|||
{
|
||||
*store = NULL;
|
||||
if (!resp) {
|
||||
pr_err("Response is NULL\n");
|
||||
pr_debug("Response is NULL\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n > resp->num) {
|
||||
pr_err("Response has %d tokens. Can't access %d\n",
|
||||
resp->num, n);
|
||||
pr_debug("Response has %d tokens. Can't access %d\n",
|
||||
resp->num, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (resp->toks[n].type != OPAL_DTA_TOKENID_BYTESTRING) {
|
||||
pr_err("Token is not a byte string!\n");
|
||||
pr_debug("Token is not a byte string!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -883,26 +883,26 @@ static size_t response_get_string(const struct parsed_resp *resp, int n,
|
|||
static u64 response_get_u64(const struct parsed_resp *resp, int n)
|
||||
{
|
||||
if (!resp) {
|
||||
pr_err("Response is NULL\n");
|
||||
pr_debug("Response is NULL\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n > resp->num) {
|
||||
pr_err("Response has %d tokens. Can't access %d\n",
|
||||
resp->num, n);
|
||||
pr_debug("Response has %d tokens. Can't access %d\n",
|
||||
resp->num, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (resp->toks[n].type != OPAL_DTA_TOKENID_UINT) {
|
||||
pr_err("Token is not unsigned it: %d\n",
|
||||
resp->toks[n].type);
|
||||
pr_debug("Token is not unsigned it: %d\n",
|
||||
resp->toks[n].type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(resp->toks[n].width == OPAL_WIDTH_TINY ||
|
||||
resp->toks[n].width == OPAL_WIDTH_SHORT)) {
|
||||
pr_err("Atom is not short or tiny: %d\n",
|
||||
resp->toks[n].width);
|
||||
pr_debug("Atom is not short or tiny: %d\n",
|
||||
resp->toks[n].width);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -949,7 +949,7 @@ static int parse_and_check_status(struct opal_dev *dev)
|
|||
|
||||
error = response_parse(dev->resp, IO_BUFFER_LENGTH, &dev->parsed);
|
||||
if (error) {
|
||||
pr_err("Couldn't parse response.\n");
|
||||
pr_debug("Couldn't parse response.\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
@ -975,7 +975,7 @@ static int start_opal_session_cont(struct opal_dev *dev)
|
|||
tsn = response_get_u64(&dev->parsed, 5);
|
||||
|
||||
if (hsn == 0 && tsn == 0) {
|
||||
pr_err("Couldn't authenticate session\n");
|
||||
pr_debug("Couldn't authenticate session\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
|
|
@ -1012,7 +1012,7 @@ static int finalize_and_send(struct opal_dev *dev, cont_fn cont)
|
|||
|
||||
ret = cmd_finalize(dev, dev->hsn, dev->tsn);
|
||||
if (ret) {
|
||||
pr_err("Error finalizing command buffer: %d\n", ret);
|
||||
pr_debug("Error finalizing command buffer: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1041,7 +1041,7 @@ static int gen_key(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error building gen key command\n");
|
||||
pr_debug("Error building gen key command\n");
|
||||
return err;
|
||||
|
||||
}
|
||||
|
|
@ -1059,8 +1059,8 @@ static int get_active_key_cont(struct opal_dev *dev)
|
|||
return error;
|
||||
keylen = response_get_string(&dev->parsed, 4, &activekey);
|
||||
if (!activekey) {
|
||||
pr_err("%s: Couldn't extract the Activekey from the response\n",
|
||||
__func__);
|
||||
pr_debug("%s: Couldn't extract the Activekey from the response\n",
|
||||
__func__);
|
||||
return OPAL_INVAL_PARAM;
|
||||
}
|
||||
dev->prev_data = kmemdup(activekey, keylen, GFP_KERNEL);
|
||||
|
|
@ -1103,7 +1103,7 @@ static int get_active_key(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
if (err) {
|
||||
pr_err("Error building get active key command\n");
|
||||
pr_debug("Error building get active key command\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1159,7 +1159,7 @@ static inline int enable_global_lr(struct opal_dev *dev, u8 *uid,
|
|||
err = generic_lr_enable_disable(dev, uid, !!setup->RLE, !!setup->WLE,
|
||||
0, 0);
|
||||
if (err)
|
||||
pr_err("Failed to create enable global lr command\n");
|
||||
pr_debug("Failed to create enable global lr command\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1217,7 +1217,7 @@ static int setup_locking_range(struct opal_dev *dev, void *data)
|
|||
|
||||
}
|
||||
if (err) {
|
||||
pr_err("Error building Setup Locking range command.\n");
|
||||
pr_debug("Error building Setup Locking range command.\n");
|
||||
return err;
|
||||
|
||||
}
|
||||
|
|
@ -1234,11 +1234,8 @@ static int start_generic_opal_session(struct opal_dev *dev,
|
|||
u32 hsn;
|
||||
int err = 0;
|
||||
|
||||
if (key == NULL && auth != OPAL_ANYBODY_UID) {
|
||||
pr_err("%s: Attempted to open ADMIN_SP Session without a Host" \
|
||||
"Challenge, and not as the Anybody UID\n", __func__);
|
||||
if (key == NULL && auth != OPAL_ANYBODY_UID)
|
||||
return OPAL_INVAL_PARAM;
|
||||
}
|
||||
|
||||
clear_opal_cmd(dev);
|
||||
|
||||
|
|
@ -1273,12 +1270,12 @@ static int start_generic_opal_session(struct opal_dev *dev,
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
break;
|
||||
default:
|
||||
pr_err("Cannot start Admin SP session with auth %d\n", auth);
|
||||
pr_debug("Cannot start Admin SP session with auth %d\n", auth);
|
||||
return OPAL_INVAL_PARAM;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
pr_err("Error building start adminsp session command.\n");
|
||||
pr_debug("Error building start adminsp session command.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1369,7 +1366,7 @@ static int start_auth_opal_session(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error building STARTSESSION command.\n");
|
||||
pr_debug("Error building STARTSESSION command.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1391,7 +1388,7 @@ static int revert_tper(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_STARTLIST);
|
||||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
if (err) {
|
||||
pr_err("Error building REVERT TPER command.\n");
|
||||
pr_debug("Error building REVERT TPER command.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1426,7 +1423,7 @@ static int internal_activate_user(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error building Activate UserN command.\n");
|
||||
pr_debug("Error building Activate UserN command.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1453,7 +1450,7 @@ static int erase_locking_range(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error building Erase Locking Range Command.\n");
|
||||
pr_debug("Error building Erase Locking Range Command.\n");
|
||||
return err;
|
||||
}
|
||||
return finalize_and_send(dev, parse_and_check_status);
|
||||
|
|
@ -1484,7 +1481,7 @@ static int set_mbr_done(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error Building set MBR Done command\n");
|
||||
pr_debug("Error Building set MBR Done command\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1516,7 +1513,7 @@ static int set_mbr_enable_disable(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error Building set MBR done command\n");
|
||||
pr_debug("Error Building set MBR done command\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1567,7 +1564,7 @@ static int set_new_pw(struct opal_dev *dev, void *data)
|
|||
|
||||
if (generic_pw_cmd(usr->opal_key.key, usr->opal_key.key_len,
|
||||
cpin_uid, dev)) {
|
||||
pr_err("Error building set password command.\n");
|
||||
pr_debug("Error building set password command.\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
|
|
@ -1582,7 +1579,7 @@ static int set_sid_cpin_pin(struct opal_dev *dev, void *data)
|
|||
memcpy(cpin_uid, opaluid[OPAL_C_PIN_SID], OPAL_UID_LENGTH);
|
||||
|
||||
if (generic_pw_cmd(key->key, key->key_len, cpin_uid, dev)) {
|
||||
pr_err("Error building Set SID cpin\n");
|
||||
pr_debug("Error building Set SID cpin\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
return finalize_and_send(dev, parse_and_check_status);
|
||||
|
|
@ -1657,7 +1654,7 @@ static int add_user_to_lr(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error building add user to locking range command.\n");
|
||||
pr_debug("Error building add user to locking range command.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1691,7 +1688,7 @@ static int lock_unlock_locking_range(struct opal_dev *dev, void *data)
|
|||
/* vars are initalized to locked */
|
||||
break;
|
||||
default:
|
||||
pr_err("Tried to set an invalid locking state... returning to uland\n");
|
||||
pr_debug("Tried to set an invalid locking state... returning to uland\n");
|
||||
return OPAL_INVAL_PARAM;
|
||||
}
|
||||
|
||||
|
|
@ -1718,7 +1715,7 @@ static int lock_unlock_locking_range(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error building SET command.\n");
|
||||
pr_debug("Error building SET command.\n");
|
||||
return err;
|
||||
}
|
||||
return finalize_and_send(dev, parse_and_check_status);
|
||||
|
|
@ -1752,14 +1749,14 @@ static int lock_unlock_locking_range_sum(struct opal_dev *dev, void *data)
|
|||
/* vars are initalized to locked */
|
||||
break;
|
||||
default:
|
||||
pr_err("Tried to set an invalid locking state.\n");
|
||||
pr_debug("Tried to set an invalid locking state.\n");
|
||||
return OPAL_INVAL_PARAM;
|
||||
}
|
||||
ret = generic_lr_enable_disable(dev, lr_buffer, 1, 1,
|
||||
read_locked, write_locked);
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("Error building SET command.\n");
|
||||
pr_debug("Error building SET command.\n");
|
||||
return ret;
|
||||
}
|
||||
return finalize_and_send(dev, parse_and_check_status);
|
||||
|
|
@ -1811,7 +1808,7 @@ static int activate_lsp(struct opal_dev *dev, void *data)
|
|||
}
|
||||
|
||||
if (err) {
|
||||
pr_err("Error building Activate LockingSP command.\n");
|
||||
pr_debug("Error building Activate LockingSP command.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1831,7 +1828,7 @@ static int get_lsp_lifecycle_cont(struct opal_dev *dev)
|
|||
/* 0x08 is Manufacured Inactive */
|
||||
/* 0x09 is Manufactured */
|
||||
if (lc_status != OPAL_MANUFACTURED_INACTIVE) {
|
||||
pr_err("Couldn't determine the status of the Lifcycle state\n");
|
||||
pr_debug("Couldn't determine the status of the Lifecycle state\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
@ -1868,7 +1865,7 @@ static int get_lsp_lifecycle(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error Building GET Lifecycle Status command\n");
|
||||
pr_debug("Error Building GET Lifecycle Status command\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -1887,7 +1884,7 @@ static int get_msid_cpin_pin_cont(struct opal_dev *dev)
|
|||
|
||||
strlen = response_get_string(&dev->parsed, 4, &msid_pin);
|
||||
if (!msid_pin) {
|
||||
pr_err("%s: Couldn't extract PIN from response\n", __func__);
|
||||
pr_debug("%s: Couldn't extract PIN from response\n", __func__);
|
||||
return OPAL_INVAL_PARAM;
|
||||
}
|
||||
|
||||
|
|
@ -1929,7 +1926,7 @@ static int get_msid_cpin_pin(struct opal_dev *dev, void *data)
|
|||
add_token_u8(&err, dev, OPAL_ENDLIST);
|
||||
|
||||
if (err) {
|
||||
pr_err("Error building Get MSID CPIN PIN command.\n");
|
||||
pr_debug("Error building Get MSID CPIN PIN command.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -2124,18 +2121,18 @@ static int opal_add_user_to_lr(struct opal_dev *dev,
|
|||
|
||||
if (lk_unlk->l_state != OPAL_RO &&
|
||||
lk_unlk->l_state != OPAL_RW) {
|
||||
pr_err("Locking state was not RO or RW\n");
|
||||
pr_debug("Locking state was not RO or RW\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (lk_unlk->session.who < OPAL_USER1 ||
|
||||
lk_unlk->session.who > OPAL_USER9) {
|
||||
pr_err("Authority was not within the range of users: %d\n",
|
||||
lk_unlk->session.who);
|
||||
pr_debug("Authority was not within the range of users: %d\n",
|
||||
lk_unlk->session.who);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (lk_unlk->session.sum) {
|
||||
pr_err("%s not supported in sum. Use setup locking range\n",
|
||||
__func__);
|
||||
pr_debug("%s not supported in sum. Use setup locking range\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -2312,7 +2309,7 @@ static int opal_activate_user(struct opal_dev *dev,
|
|||
/* We can't activate Admin1 it's active as manufactured */
|
||||
if (opal_session->who < OPAL_USER1 ||
|
||||
opal_session->who > OPAL_USER9) {
|
||||
pr_err("Who was not a valid user: %d\n", opal_session->who);
|
||||
pr_debug("Who was not a valid user: %d\n", opal_session->who);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -2343,9 +2340,9 @@ bool opal_unlock_from_suspend(struct opal_dev *dev)
|
|||
|
||||
ret = __opal_lock_unlock(dev, &suspend->unlk);
|
||||
if (ret) {
|
||||
pr_warn("Failed to unlock LR %hhu with sum %d\n",
|
||||
suspend->unlk.session.opal_key.lr,
|
||||
suspend->unlk.session.sum);
|
||||
pr_debug("Failed to unlock LR %hhu with sum %d\n",
|
||||
suspend->unlk.session.opal_key.lr,
|
||||
suspend->unlk.session.sum);
|
||||
was_failure = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2363,10 +2360,8 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg)
|
|||
return -EACCES;
|
||||
if (!dev)
|
||||
return -ENOTSUPP;
|
||||
if (!dev->supported) {
|
||||
pr_err("Not supported\n");
|
||||
if (!dev->supported)
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
p = memdup_user(arg, _IOC_SIZE(cmd));
|
||||
if (IS_ERR(p))
|
||||
|
|
@ -2410,7 +2405,7 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg)
|
|||
ret = opal_secure_erase_locking_range(dev, p);
|
||||
break;
|
||||
default:
|
||||
pr_warn("No such Opal Ioctl %u\n", cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
kfree(p);
|
||||
|
|
|
|||
|
|
@ -160,28 +160,28 @@ static int t10_pi_type3_verify_ip(struct blk_integrity_iter *iter)
|
|||
return t10_pi_verify(iter, t10_pi_ip_fn, 3);
|
||||
}
|
||||
|
||||
struct blk_integrity_profile t10_pi_type1_crc = {
|
||||
const struct blk_integrity_profile t10_pi_type1_crc = {
|
||||
.name = "T10-DIF-TYPE1-CRC",
|
||||
.generate_fn = t10_pi_type1_generate_crc,
|
||||
.verify_fn = t10_pi_type1_verify_crc,
|
||||
};
|
||||
EXPORT_SYMBOL(t10_pi_type1_crc);
|
||||
|
||||
struct blk_integrity_profile t10_pi_type1_ip = {
|
||||
const struct blk_integrity_profile t10_pi_type1_ip = {
|
||||
.name = "T10-DIF-TYPE1-IP",
|
||||
.generate_fn = t10_pi_type1_generate_ip,
|
||||
.verify_fn = t10_pi_type1_verify_ip,
|
||||
};
|
||||
EXPORT_SYMBOL(t10_pi_type1_ip);
|
||||
|
||||
struct blk_integrity_profile t10_pi_type3_crc = {
|
||||
const struct blk_integrity_profile t10_pi_type3_crc = {
|
||||
.name = "T10-DIF-TYPE3-CRC",
|
||||
.generate_fn = t10_pi_type3_generate_crc,
|
||||
.verify_fn = t10_pi_type3_verify_crc,
|
||||
};
|
||||
EXPORT_SYMBOL(t10_pi_type3_crc);
|
||||
|
||||
struct blk_integrity_profile t10_pi_type3_ip = {
|
||||
const struct blk_integrity_profile t10_pi_type3_ip = {
|
||||
.name = "T10-DIF-TYPE3-IP",
|
||||
.generate_fn = t10_pi_type3_generate_ip,
|
||||
.verify_fn = t10_pi_type3_verify_ip,
|
||||
|
|
|
|||
Loading…
Reference in a new issue