mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'for-5.11/block-2020-12-14' of git://git.kernel.dk/linux-block
Pull block updates from Jens Axboe:
"Another series of killing more code than what is being added, again
thanks to Christoph's relentless cleanups and tech debt tackling.
This contains:
- blk-iocost improvements (Baolin Wang)
- part0 iostat fix (Jeffle Xu)
- Disable iopoll for split bios (Jeffle Xu)
- block tracepoint cleanups (Christoph Hellwig)
- Merging of struct block_device and hd_struct (Christoph Hellwig)
- Rework/cleanup of how block device sizes are updated (Christoph
Hellwig)
- Simplification of gendisk lookup and removal of block device
aliasing (Christoph Hellwig)
- Block device ioctl cleanups (Christoph Hellwig)
- Removal of bdget()/blkdev_get() as exported API (Christoph Hellwig)
- Disk change rework, avoid ->revalidate_disk() (Christoph Hellwig)
- sbitmap improvements (Pavel Begunkov)
- Hybrid polling fix (Pavel Begunkov)
- bvec iteration improvements (Pavel Begunkov)
- Zone revalidation fixes (Damien Le Moal)
- blk-throttle limit fix (Yu Kuai)
- Various little fixes"
* tag 'for-5.11/block-2020-12-14' of git://git.kernel.dk/linux-block: (126 commits)
blk-mq: fix msec comment from micro to milli seconds
blk-mq: update arg in comment of blk_mq_map_queue
blk-mq: add helper allocating tagset->tags
Revert "block: Fix a lockdep complaint triggered by request queue flushing"
nvme-loop: use blk_mq_hctx_set_fq_lock_class to set loop's lock class
blk-mq: add new API of blk_mq_hctx_set_fq_lock_class
block: disable iopoll for split bio
block: Improve blk_revalidate_disk_zones() checks
sbitmap: simplify wrap check
sbitmap: replace CAS with atomic and
sbitmap: remove swap_lock
sbitmap: optimise sbitmap_deferred_clear()
blk-mq: skip hybrid polling if iopoll doesn't spin
blk-iocost: Factor out the base vrate change into a separate function
blk-iocost: Factor out the active iocgs' state check into a separate function
blk-iocost: Move the usage ratio calculation to the correct place
blk-iocost: Remove unnecessary advance declaration
blk-iocost: Fix some typos in comments
blktrace: fix up a kerneldoc comment
block: remove the request_queue to argument request based tracepoints
...
This commit is contained in:
commit
ac7ac4618c
110 files changed
+2145
-3368
No files matched your search
+5
-5
@@ -608,13 +608,13 @@ void bio_truncate(struct bio *bio, unsigned new_size)
|
||||
void guard_bio_eod(struct bio *bio)
|
||||
{
|
||||
sector_t maxsector;
|
||||
struct hd_struct *part;
|
||||
struct block_device *part;
|
||||
|
||||
rcu_read_lock();
|
||||
part = __disk_get_part(bio->bi_disk, bio->bi_partno);
|
||||
if (part)
|
||||
maxsector = part_nr_sects_read(part);
|
||||
else
|
||||
maxsector = bdev_nr_sectors(part);
|
||||
else
|
||||
maxsector = get_capacity(bio->bi_disk);
|
||||
rcu_read_unlock();
|
||||
|
||||
@@ -1212,8 +1212,8 @@ void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,
|
||||
|
||||
flush_dcache_page(dst_bv.bv_page);
|
||||
|
||||
bio_advance_iter(src, src_iter, bytes);
|
||||
bio_advance_iter(dst, dst_iter, bytes);
|
||||
bio_advance_iter_single(src, src_iter, bytes);
|
||||
bio_advance_iter_single(dst, dst_iter, bytes);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(bio_copy_data_iter);
|
||||
|
||||
+25
-26
@@ -556,22 +556,22 @@ static struct blkcg_gq *blkg_lookup_check(struct blkcg *blkcg,
|
||||
}
|
||||
|
||||
/**
|
||||
* blkg_conf_prep - parse and prepare for per-blkg config update
|
||||
* blkcg_conf_open_bdev - parse and open bdev for per-blkg config update
|
||||
* @inputp: input string pointer
|
||||
*
|
||||
* Parse the device node prefix part, MAJ:MIN, of per-blkg config update
|
||||
* from @input and get and return the matching gendisk. *@inputp is
|
||||
* from @input and get and return the matching bdev. *@inputp is
|
||||
* updated to point past the device node prefix. Returns an ERR_PTR()
|
||||
* value on error.
|
||||
*
|
||||
* Use this function iff blkg_conf_prep() can't be used for some reason.
|
||||
*/
|
||||
struct gendisk *blkcg_conf_get_disk(char **inputp)
|
||||
struct block_device *blkcg_conf_open_bdev(char **inputp)
|
||||
{
|
||||
char *input = *inputp;
|
||||
unsigned int major, minor;
|
||||
struct gendisk *disk;
|
||||
int key_len, part;
|
||||
struct block_device *bdev;
|
||||
int key_len;
|
||||
|
||||
if (sscanf(input, "%u:%u%n", &major, &minor, &key_len) != 2)
|
||||
return ERR_PTR(-EINVAL);
|
||||
@@ -581,16 +581,16 @@ struct gendisk *blkcg_conf_get_disk(char **inputp)
|
||||
return ERR_PTR(-EINVAL);
|
||||
input = skip_spaces(input);
|
||||
|
||||
disk = get_gendisk(MKDEV(major, minor), &part);
|
||||
if (!disk)
|
||||
bdev = blkdev_get_no_open(MKDEV(major, minor));
|
||||
if (!bdev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
if (part) {
|
||||
put_disk_and_module(disk);
|
||||
if (bdev_is_partition(bdev)) {
|
||||
blkdev_put_no_open(bdev);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
*inputp = input;
|
||||
return disk;
|
||||
return bdev;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -607,18 +607,18 @@ struct gendisk *blkcg_conf_get_disk(char **inputp)
|
||||
*/
|
||||
int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
|
||||
char *input, struct blkg_conf_ctx *ctx)
|
||||
__acquires(rcu) __acquires(&disk->queue->queue_lock)
|
||||
__acquires(rcu) __acquires(&bdev->bd_disk->queue->queue_lock)
|
||||
{
|
||||
struct gendisk *disk;
|
||||
struct block_device *bdev;
|
||||
struct request_queue *q;
|
||||
struct blkcg_gq *blkg;
|
||||
int ret;
|
||||
|
||||
disk = blkcg_conf_get_disk(&input);
|
||||
if (IS_ERR(disk))
|
||||
return PTR_ERR(disk);
|
||||
bdev = blkcg_conf_open_bdev(&input);
|
||||
if (IS_ERR(bdev))
|
||||
return PTR_ERR(bdev);
|
||||
|
||||
q = disk->queue;
|
||||
q = bdev->bd_disk->queue;
|
||||
|
||||
rcu_read_lock();
|
||||
spin_lock_irq(&q->queue_lock);
|
||||
@@ -689,7 +689,7 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
|
||||
goto success;
|
||||
}
|
||||
success:
|
||||
ctx->disk = disk;
|
||||
ctx->bdev = bdev;
|
||||
ctx->blkg = blkg;
|
||||
ctx->body = input;
|
||||
return 0;
|
||||
@@ -700,7 +700,7 @@ fail_unlock:
|
||||
spin_unlock_irq(&q->queue_lock);
|
||||
rcu_read_unlock();
|
||||
fail:
|
||||
put_disk_and_module(disk);
|
||||
blkdev_put_no_open(bdev);
|
||||
/*
|
||||
* If queue was bypassing, we should retry. Do so after a
|
||||
* short msleep(). It isn't strictly necessary but queue
|
||||
@@ -723,11 +723,11 @@ EXPORT_SYMBOL_GPL(blkg_conf_prep);
|
||||
* with blkg_conf_prep().
|
||||
*/
|
||||
void blkg_conf_finish(struct blkg_conf_ctx *ctx)
|
||||
__releases(&ctx->disk->queue->queue_lock) __releases(rcu)
|
||||
__releases(&ctx->bdev->bd_disk->queue->queue_lock) __releases(rcu)
|
||||
{
|
||||
spin_unlock_irq(&ctx->disk->queue->queue_lock);
|
||||
spin_unlock_irq(&ctx->bdev->bd_disk->queue->queue_lock);
|
||||
rcu_read_unlock();
|
||||
put_disk_and_module(ctx->disk);
|
||||
blkdev_put_no_open(ctx->bdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blkg_conf_finish);
|
||||
|
||||
@@ -820,9 +820,9 @@ static void blkcg_fill_root_iostats(void)
|
||||
|
||||
class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
|
||||
while ((dev = class_dev_iter_next(&iter))) {
|
||||
struct gendisk *disk = dev_to_disk(dev);
|
||||
struct hd_struct *part = disk_get_part(disk, 0);
|
||||
struct blkcg_gq *blkg = blk_queue_root_blkg(disk->queue);
|
||||
struct block_device *bdev = dev_to_bdev(dev);
|
||||
struct blkcg_gq *blkg =
|
||||
blk_queue_root_blkg(bdev->bd_disk->queue);
|
||||
struct blkg_iostat tmp;
|
||||
int cpu;
|
||||
|
||||
@@ -830,7 +830,7 @@ static void blkcg_fill_root_iostats(void)
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct disk_stats *cpu_dkstats;
|
||||
|
||||
cpu_dkstats = per_cpu_ptr(part->dkstats, cpu);
|
||||
cpu_dkstats = per_cpu_ptr(bdev->bd_stats, cpu);
|
||||
tmp.ios[BLKG_IOSTAT_READ] +=
|
||||
cpu_dkstats->ios[STAT_READ];
|
||||
tmp.ios[BLKG_IOSTAT_WRITE] +=
|
||||
@@ -849,7 +849,6 @@ static void blkcg_fill_root_iostats(void)
|
||||
blkg_iostat_set(&blkg->iostat.cur, &tmp);
|
||||
u64_stats_update_end(&blkg->iostat.sync);
|
||||
}
|
||||
disk_put_part(part);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-38
@@ -666,9 +666,9 @@ static int __init setup_fail_make_request(char *str)
|
||||
}
|
||||
__setup("fail_make_request=", setup_fail_make_request);
|
||||
|
||||
static bool should_fail_request(struct hd_struct *part, unsigned int bytes)
|
||||
static bool should_fail_request(struct block_device *part, unsigned int bytes)
|
||||
{
|
||||
return part->make_it_fail && should_fail(&fail_make_request, bytes);
|
||||
return part->bd_make_it_fail && should_fail(&fail_make_request, bytes);
|
||||
}
|
||||
|
||||
static int __init fail_make_request_debugfs(void)
|
||||
@@ -683,7 +683,7 @@ late_initcall(fail_make_request_debugfs);
|
||||
|
||||
#else /* CONFIG_FAIL_MAKE_REQUEST */
|
||||
|
||||
static inline bool should_fail_request(struct hd_struct *part,
|
||||
static inline bool should_fail_request(struct block_device *part,
|
||||
unsigned int bytes)
|
||||
{
|
||||
return false;
|
||||
@@ -691,11 +691,11 @@ static inline bool should_fail_request(struct hd_struct *part,
|
||||
|
||||
#endif /* CONFIG_FAIL_MAKE_REQUEST */
|
||||
|
||||
static inline bool bio_check_ro(struct bio *bio, struct hd_struct *part)
|
||||
static inline bool bio_check_ro(struct bio *bio, struct block_device *part)
|
||||
{
|
||||
const int op = bio_op(bio);
|
||||
|
||||
if (part->policy && op_is_write(op)) {
|
||||
if (part->bd_read_only && op_is_write(op)) {
|
||||
char b[BDEVNAME_SIZE];
|
||||
|
||||
if (op_is_flush(bio->bi_opf) && !bio_sectors(bio))
|
||||
@@ -703,7 +703,7 @@ static inline bool bio_check_ro(struct bio *bio, struct hd_struct *part)
|
||||
|
||||
WARN_ONCE(1,
|
||||
"Trying to write to read-only block-device %s (partno %d)\n",
|
||||
bio_devname(bio, b), part->partno);
|
||||
bio_devname(bio, b), part->bd_partno);
|
||||
/* Older lvm-tools actually trigger this */
|
||||
return false;
|
||||
}
|
||||
@@ -713,7 +713,7 @@ static inline bool bio_check_ro(struct bio *bio, struct hd_struct *part)
|
||||
|
||||
static noinline int should_fail_bio(struct bio *bio)
|
||||
{
|
||||
if (should_fail_request(&bio->bi_disk->part0, bio->bi_iter.bi_size))
|
||||
if (should_fail_request(bio->bi_disk->part0, bio->bi_iter.bi_size))
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
@@ -742,7 +742,7 @@ static inline int bio_check_eod(struct bio *bio, sector_t maxsector)
|
||||
*/
|
||||
static inline int blk_partition_remap(struct bio *bio)
|
||||
{
|
||||
struct hd_struct *p;
|
||||
struct block_device *p;
|
||||
int ret = -EIO;
|
||||
|
||||
rcu_read_lock();
|
||||
@@ -755,11 +755,12 @@ static inline int blk_partition_remap(struct bio *bio)
|
||||
goto out;
|
||||
|
||||
if (bio_sectors(bio)) {
|
||||
if (bio_check_eod(bio, part_nr_sects_read(p)))
|
||||
if (bio_check_eod(bio, bdev_nr_sectors(p)))
|
||||
goto out;
|
||||
bio->bi_iter.bi_sector += p->start_sect;
|
||||
trace_block_bio_remap(bio->bi_disk->queue, bio, part_devt(p),
|
||||
bio->bi_iter.bi_sector - p->start_sect);
|
||||
bio->bi_iter.bi_sector += p->bd_start_sect;
|
||||
trace_block_bio_remap(bio, p->bd_dev,
|
||||
bio->bi_iter.bi_sector -
|
||||
p->bd_start_sect);
|
||||
}
|
||||
bio->bi_partno = 0;
|
||||
ret = 0;
|
||||
@@ -829,7 +830,7 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio)
|
||||
if (unlikely(blk_partition_remap(bio)))
|
||||
goto end_io;
|
||||
} else {
|
||||
if (unlikely(bio_check_ro(bio, &bio->bi_disk->part0)))
|
||||
if (unlikely(bio_check_ro(bio, bio->bi_disk->part0)))
|
||||
goto end_io;
|
||||
if (unlikely(bio_check_eod(bio, get_capacity(bio->bi_disk))))
|
||||
goto end_io;
|
||||
@@ -906,7 +907,7 @@ static noinline_for_stack bool submit_bio_checks(struct bio *bio)
|
||||
blkcg_bio_issue_init(bio);
|
||||
|
||||
if (!bio_flagged(bio, BIO_TRACE_COMPLETION)) {
|
||||
trace_block_bio_queue(q, bio);
|
||||
trace_block_bio_queue(bio);
|
||||
/* Now that enqueuing has been traced, we need to trace
|
||||
* completion as well.
|
||||
*/
|
||||
@@ -1201,7 +1202,7 @@ blk_status_t blk_insert_cloned_request(struct request_queue *q, struct request *
|
||||
return ret;
|
||||
|
||||
if (rq->rq_disk &&
|
||||
should_fail_request(&rq->rq_disk->part0, blk_rq_bytes(rq)))
|
||||
should_fail_request(rq->rq_disk->part0, blk_rq_bytes(rq)))
|
||||
return BLK_STS_IOERR;
|
||||
|
||||
if (blk_crypto_insert_cloned_request(rq))
|
||||
@@ -1260,17 +1261,18 @@ unsigned int blk_rq_err_bytes(const struct request *rq)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_rq_err_bytes);
|
||||
|
||||
static void update_io_ticks(struct hd_struct *part, unsigned long now, bool end)
|
||||
static void update_io_ticks(struct block_device *part, unsigned long now,
|
||||
bool end)
|
||||
{
|
||||
unsigned long stamp;
|
||||
again:
|
||||
stamp = READ_ONCE(part->stamp);
|
||||
stamp = READ_ONCE(part->bd_stamp);
|
||||
if (unlikely(stamp != now)) {
|
||||
if (likely(cmpxchg(&part->stamp, stamp, now) == stamp))
|
||||
if (likely(cmpxchg(&part->bd_stamp, stamp, now) == stamp))
|
||||
__part_stat_add(part, io_ticks, end ? now - stamp : 1);
|
||||
}
|
||||
if (part->partno) {
|
||||
part = &part_to_disk(part)->part0;
|
||||
if (part->bd_partno) {
|
||||
part = bdev_whole(part);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
@@ -1279,11 +1281,9 @@ static void blk_account_io_completion(struct request *req, unsigned int bytes)
|
||||
{
|
||||
if (req->part && blk_do_io_stat(req)) {
|
||||
const int sgrp = op_stat_group(req_op(req));
|
||||
struct hd_struct *part;
|
||||
|
||||
part_stat_lock();
|
||||
part = req->part;
|
||||
part_stat_add(part, sectors[sgrp], bytes >> 9);
|
||||
part_stat_add(req->part, sectors[sgrp], bytes >> 9);
|
||||
part_stat_unlock();
|
||||
}
|
||||
}
|
||||
@@ -1298,17 +1298,12 @@ void blk_account_io_done(struct request *req, u64 now)
|
||||
if (req->part && blk_do_io_stat(req) &&
|
||||
!(req->rq_flags & RQF_FLUSH_SEQ)) {
|
||||
const int sgrp = op_stat_group(req_op(req));
|
||||
struct hd_struct *part;
|
||||
|
||||
part_stat_lock();
|
||||
part = req->part;
|
||||
|
||||
update_io_ticks(part, jiffies, true);
|
||||
part_stat_inc(part, ios[sgrp]);
|
||||
part_stat_add(part, nsecs[sgrp], now - req->start_time_ns);
|
||||
update_io_ticks(req->part, jiffies, true);
|
||||
part_stat_inc(req->part, ios[sgrp]);
|
||||
part_stat_add(req->part, nsecs[sgrp], now - req->start_time_ns);
|
||||
part_stat_unlock();
|
||||
|
||||
hd_struct_put(part);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1324,7 +1319,7 @@ void blk_account_io_start(struct request *rq)
|
||||
part_stat_unlock();
|
||||
}
|
||||
|
||||
static unsigned long __part_start_io_acct(struct hd_struct *part,
|
||||
static unsigned long __part_start_io_acct(struct block_device *part,
|
||||
unsigned int sectors, unsigned int op)
|
||||
{
|
||||
const int sgrp = op_stat_group(op);
|
||||
@@ -1340,7 +1335,7 @@ static unsigned long __part_start_io_acct(struct hd_struct *part,
|
||||
return now;
|
||||
}
|
||||
|
||||
unsigned long part_start_io_acct(struct gendisk *disk, struct hd_struct **part,
|
||||
unsigned long part_start_io_acct(struct gendisk *disk, struct block_device **part,
|
||||
struct bio *bio)
|
||||
{
|
||||
*part = disk_map_sector_rcu(disk, bio->bi_iter.bi_sector);
|
||||
@@ -1352,11 +1347,11 @@ EXPORT_SYMBOL_GPL(part_start_io_acct);
|
||||
unsigned long disk_start_io_acct(struct gendisk *disk, unsigned int sectors,
|
||||
unsigned int op)
|
||||
{
|
||||
return __part_start_io_acct(&disk->part0, sectors, op);
|
||||
return __part_start_io_acct(disk->part0, sectors, op);
|
||||
}
|
||||
EXPORT_SYMBOL(disk_start_io_acct);
|
||||
|
||||
static void __part_end_io_acct(struct hd_struct *part, unsigned int op,
|
||||
static void __part_end_io_acct(struct block_device *part, unsigned int op,
|
||||
unsigned long start_time)
|
||||
{
|
||||
const int sgrp = op_stat_group(op);
|
||||
@@ -1370,18 +1365,17 @@ static void __part_end_io_acct(struct hd_struct *part, unsigned int op,
|
||||
part_stat_unlock();
|
||||
}
|
||||
|
||||
void part_end_io_acct(struct hd_struct *part, struct bio *bio,
|
||||
void part_end_io_acct(struct block_device *part, struct bio *bio,
|
||||
unsigned long start_time)
|
||||
{
|
||||
__part_end_io_acct(part, bio_op(bio), start_time);
|
||||
hd_struct_put(part);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(part_end_io_acct);
|
||||
|
||||
void disk_end_io_acct(struct gendisk *disk, unsigned int op,
|
||||
unsigned long start_time)
|
||||
{
|
||||
__part_end_io_acct(&disk->part0, op, start_time);
|
||||
__part_end_io_acct(disk->part0, op, start_time);
|
||||
}
|
||||
EXPORT_SYMBOL(disk_end_io_acct);
|
||||
|
||||
|
||||
+26
-6
@@ -69,7 +69,6 @@
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/blk-mq.h>
|
||||
#include <linux/lockdep.h>
|
||||
|
||||
#include "blk.h"
|
||||
#include "blk-mq.h"
|
||||
@@ -139,7 +138,7 @@ static void blk_flush_queue_rq(struct request *rq, bool add_front)
|
||||
|
||||
static void blk_account_io_flush(struct request *rq)
|
||||
{
|
||||
struct hd_struct *part = &rq->rq_disk->part0;
|
||||
struct block_device *part = rq->rq_disk->part0;
|
||||
|
||||
part_stat_lock();
|
||||
part_stat_inc(part, ios[STAT_FLUSH]);
|
||||
@@ -474,9 +473,6 @@ struct blk_flush_queue *blk_alloc_flush_queue(int node, int cmd_size,
|
||||
INIT_LIST_HEAD(&fq->flush_queue[1]);
|
||||
INIT_LIST_HEAD(&fq->flush_data_in_flight);
|
||||
|
||||
lockdep_register_key(&fq->key);
|
||||
lockdep_set_class(&fq->mq_flush_lock, &fq->key);
|
||||
|
||||
return fq;
|
||||
|
||||
fail_rq:
|
||||
@@ -491,7 +487,31 @@ void blk_free_flush_queue(struct blk_flush_queue *fq)
|
||||
if (!fq)
|
||||
return;
|
||||
|
||||
lockdep_unregister_key(&fq->key);
|
||||
kfree(fq->flush_rq);
|
||||
kfree(fq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow driver to set its own lock class to fq->mq_flush_lock for
|
||||
* avoiding lockdep complaint.
|
||||
*
|
||||
* flush_end_io() may be called recursively from some driver, such as
|
||||
* nvme-loop, so lockdep may complain 'possible recursive locking' because
|
||||
* all 'struct blk_flush_queue' instance share same mq_flush_lock lock class
|
||||
* key. We need to assign different lock class for these driver's
|
||||
* fq->mq_flush_lock for avoiding the lockdep warning.
|
||||
*
|
||||
* Use dynamically allocated lock class key for each 'blk_flush_queue'
|
||||
* instance is over-kill, and more worse it introduces horrible boot delay
|
||||
* issue because synchronize_rcu() is implied in lockdep_unregister_key which
|
||||
* is called for each hctx release. SCSI probing may synchronously create and
|
||||
* destroy lots of MQ request_queues for non-existent devices, and some robot
|
||||
* test kernel always enable lockdep option. It is observed that more than half
|
||||
* an hour is taken during SCSI MQ probe with per-fq lock class.
|
||||
*/
|
||||
void blk_mq_hctx_set_fq_lock_class(struct blk_mq_hw_ctx *hctx,
|
||||
struct lock_class_key *key)
|
||||
{
|
||||
lockdep_set_class(&hctx->fq->mq_flush_lock, key);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_mq_hctx_set_fq_lock_class);
|
||||
+178
-155
@@ -39,7 +39,7 @@
|
||||
* On top of that, a size cost proportional to the length of the IO is
|
||||
* added. While simple, this model captures the operational
|
||||
* characteristics of a wide varienty of devices well enough. Default
|
||||
* paramters for several different classes of devices are provided and the
|
||||
* parameters for several different classes of devices are provided and the
|
||||
* parameters can be configured from userspace via
|
||||
* /sys/fs/cgroup/io.cost.model.
|
||||
*
|
||||
@@ -77,7 +77,7 @@
|
||||
*
|
||||
* This constitutes the basis of IO capacity distribution. Each cgroup's
|
||||
* vtime is running at a rate determined by its hweight. A cgroup tracks
|
||||
* the vtime consumed by past IOs and can issue a new IO iff doing so
|
||||
* the vtime consumed by past IOs and can issue a new IO if doing so
|
||||
* wouldn't outrun the current device vtime. Otherwise, the IO is
|
||||
* suspended until the vtime has progressed enough to cover it.
|
||||
*
|
||||
@@ -155,7 +155,7 @@
|
||||
* Instead of debugfs or other clumsy monitoring mechanisms, this
|
||||
* controller uses a drgn based monitoring script -
|
||||
* tools/cgroup/iocost_monitor.py. For details on drgn, please see
|
||||
* https://github.com/osandov/drgn. The ouput looks like the following.
|
||||
* https://github.com/osandov/drgn. The output looks like the following.
|
||||
*
|
||||
* sdb RUN per=300ms cur_per=234.218:v203.695 busy= +1 vrate= 62.12%
|
||||
* active weight hweight% inflt% dbt delay usages%
|
||||
@@ -370,8 +370,6 @@ enum {
|
||||
AUTOP_SSD_FAST,
|
||||
};
|
||||
|
||||
struct ioc_gq;
|
||||
|
||||
struct ioc_params {
|
||||
u32 qos[NR_QOS_PARAMS];
|
||||
u64 i_lcoefs[NR_I_LCOEFS];
|
||||
@@ -492,7 +490,7 @@ struct ioc_gq {
|
||||
/*
|
||||
* `vtime` is this iocg's vtime cursor which progresses as IOs are
|
||||
* issued. If lagging behind device vtime, the delta represents
|
||||
* the currently available IO budget. If runnning ahead, the
|
||||
* the currently available IO budget. If running ahead, the
|
||||
* overage.
|
||||
*
|
||||
* `vtime_done` is the same but progressed on completion rather
|
||||
@@ -973,6 +971,58 @@ done:
|
||||
ioc->vtime_err = clamp(ioc->vtime_err, -vperiod, vperiod);
|
||||
}
|
||||
|
||||
static void ioc_adjust_base_vrate(struct ioc *ioc, u32 rq_wait_pct,
|
||||
int nr_lagging, int nr_shortages,
|
||||
int prev_busy_level, u32 *missed_ppm)
|
||||
{
|
||||
u64 vrate = ioc->vtime_base_rate;
|
||||
u64 vrate_min = ioc->vrate_min, vrate_max = ioc->vrate_max;
|
||||
|
||||
if (!ioc->busy_level || (ioc->busy_level < 0 && nr_lagging)) {
|
||||
if (ioc->busy_level != prev_busy_level || nr_lagging)
|
||||
trace_iocost_ioc_vrate_adj(ioc, atomic64_read(&ioc->vtime_rate),
|
||||
missed_ppm, rq_wait_pct,
|
||||
nr_lagging, nr_shortages);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* rq_wait signal is always reliable, ignore user vrate_min */
|
||||
if (rq_wait_pct > RQ_WAIT_BUSY_PCT)
|
||||
vrate_min = VRATE_MIN;
|
||||
|
||||
/*
|
||||
* If vrate is out of bounds, apply clamp gradually as the
|
||||
* bounds can change abruptly. Otherwise, apply busy_level
|
||||
* based adjustment.
|
||||
*/
|
||||
if (vrate < vrate_min) {
|
||||
vrate = div64_u64(vrate * (100 + VRATE_CLAMP_ADJ_PCT), 100);
|
||||
vrate = min(vrate, vrate_min);
|
||||
} else if (vrate > vrate_max) {
|
||||
vrate = div64_u64(vrate * (100 - VRATE_CLAMP_ADJ_PCT), 100);
|
||||
vrate = max(vrate, vrate_max);
|
||||
} else {
|
||||
int idx = min_t(int, abs(ioc->busy_level),
|
||||
ARRAY_SIZE(vrate_adj_pct) - 1);
|
||||
u32 adj_pct = vrate_adj_pct[idx];
|
||||
|
||||
if (ioc->busy_level > 0)
|
||||
adj_pct = 100 - adj_pct;
|
||||
else
|
||||
adj_pct = 100 + adj_pct;
|
||||
|
||||
vrate = clamp(DIV64_U64_ROUND_UP(vrate * adj_pct, 100),
|
||||
vrate_min, vrate_max);
|
||||
}
|
||||
|
||||
trace_iocost_ioc_vrate_adj(ioc, vrate, missed_ppm, rq_wait_pct,
|
||||
nr_lagging, nr_shortages);
|
||||
|
||||
ioc->vtime_base_rate = vrate;
|
||||
ioc_refresh_margins(ioc);
|
||||
}
|
||||
|
||||
/* take a snapshot of the current [v]time and vrate */
|
||||
static void ioc_now(struct ioc *ioc, struct ioc_now *now)
|
||||
{
|
||||
@@ -1046,7 +1096,7 @@ static void __propagate_weights(struct ioc_gq *iocg, u32 active, u32 inuse,
|
||||
|
||||
/*
|
||||
* The delta between inuse and active sums indicates that
|
||||
* that much of weight is being given away. Parent's inuse
|
||||
* much of weight is being given away. Parent's inuse
|
||||
* and active should reflect the ratio.
|
||||
*/
|
||||
if (parent->child_active_sum) {
|
||||
@@ -2071,13 +2121,88 @@ static void ioc_forgive_debts(struct ioc *ioc, u64 usage_us_sum, int nr_debtors,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the active iocgs' state to avoid oversleeping and deactive
|
||||
* idle iocgs.
|
||||
*
|
||||
* Since waiters determine the sleep durations based on the vrate
|
||||
* they saw at the time of sleep, if vrate has increased, some
|
||||
* waiters could be sleeping for too long. Wake up tardy waiters
|
||||
* which should have woken up in the last period and expire idle
|
||||
* iocgs.
|
||||
*/
|
||||
static int ioc_check_iocgs(struct ioc *ioc, struct ioc_now *now)
|
||||
{
|
||||
int nr_debtors = 0;
|
||||
struct ioc_gq *iocg, *tiocg;
|
||||
|
||||
list_for_each_entry_safe(iocg, tiocg, &ioc->active_iocgs, active_list) {
|
||||
if (!waitqueue_active(&iocg->waitq) && !iocg->abs_vdebt &&
|
||||
!iocg->delay && !iocg_is_idle(iocg))
|
||||
continue;
|
||||
|
||||
spin_lock(&iocg->waitq.lock);
|
||||
|
||||
/* flush wait and indebt stat deltas */
|
||||
if (iocg->wait_since) {
|
||||
iocg->local_stat.wait_us += now->now - iocg->wait_since;
|
||||
iocg->wait_since = now->now;
|
||||
}
|
||||
if (iocg->indebt_since) {
|
||||
iocg->local_stat.indebt_us +=
|
||||
now->now - iocg->indebt_since;
|
||||
iocg->indebt_since = now->now;
|
||||
}
|
||||
if (iocg->indelay_since) {
|
||||
iocg->local_stat.indelay_us +=
|
||||
now->now - iocg->indelay_since;
|
||||
iocg->indelay_since = now->now;
|
||||
}
|
||||
|
||||
if (waitqueue_active(&iocg->waitq) || iocg->abs_vdebt ||
|
||||
iocg->delay) {
|
||||
/* might be oversleeping vtime / hweight changes, kick */
|
||||
iocg_kick_waitq(iocg, true, now);
|
||||
if (iocg->abs_vdebt || iocg->delay)
|
||||
nr_debtors++;
|
||||
} else if (iocg_is_idle(iocg)) {
|
||||
/* no waiter and idle, deactivate */
|
||||
u64 vtime = atomic64_read(&iocg->vtime);
|
||||
s64 excess;
|
||||
|
||||
/*
|
||||
* @iocg has been inactive for a full duration and will
|
||||
* have a high budget. Account anything above target as
|
||||
* error and throw away. On reactivation, it'll start
|
||||
* with the target budget.
|
||||
*/
|
||||
excess = now->vnow - vtime - ioc->margins.target;
|
||||
if (excess > 0) {
|
||||
u32 old_hwi;
|
||||
|
||||
current_hweight(iocg, NULL, &old_hwi);
|
||||
ioc->vtime_err -= div64_u64(excess * old_hwi,
|
||||
WEIGHT_ONE);
|
||||
}
|
||||
|
||||
__propagate_weights(iocg, 0, 0, false, now);
|
||||
list_del_init(&iocg->active_list);
|
||||
}
|
||||
|
||||
spin_unlock(&iocg->waitq.lock);
|
||||
}
|
||||
|
||||
commit_weights(ioc);
|
||||
return nr_debtors;
|
||||
}
|
||||
|
||||
static void ioc_timer_fn(struct timer_list *timer)
|
||||
{
|
||||
struct ioc *ioc = container_of(timer, struct ioc, timer);
|
||||
struct ioc_gq *iocg, *tiocg;
|
||||
struct ioc_now now;
|
||||
LIST_HEAD(surpluses);
|
||||
int nr_debtors = 0, nr_shortages = 0, nr_lagging = 0;
|
||||
int nr_debtors, nr_shortages = 0, nr_lagging = 0;
|
||||
u64 usage_us_sum = 0;
|
||||
u32 ppm_rthr = MILLION - ioc->params.qos[QOS_RPPM];
|
||||
u32 ppm_wthr = MILLION - ioc->params.qos[QOS_WPPM];
|
||||
@@ -2099,68 +2224,7 @@ static void ioc_timer_fn(struct timer_list *timer)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Waiters determine the sleep durations based on the vrate they
|
||||
* saw at the time of sleep. If vrate has increased, some waiters
|
||||
* could be sleeping for too long. Wake up tardy waiters which
|
||||
* should have woken up in the last period and expire idle iocgs.
|
||||
*/
|
||||
list_for_each_entry_safe(iocg, tiocg, &ioc->active_iocgs, active_list) {
|
||||
if (!waitqueue_active(&iocg->waitq) && !iocg->abs_vdebt &&
|
||||
!iocg->delay && !iocg_is_idle(iocg))
|
||||
continue;
|
||||
|
||||
spin_lock(&iocg->waitq.lock);
|
||||
|
||||
/* flush wait and indebt stat deltas */
|
||||
if (iocg->wait_since) {
|
||||
iocg->local_stat.wait_us += now.now - iocg->wait_since;
|
||||
iocg->wait_since = now.now;
|
||||
}
|
||||
if (iocg->indebt_since) {
|
||||
iocg->local_stat.indebt_us +=
|
||||
now.now - iocg->indebt_since;
|
||||
iocg->indebt_since = now.now;
|
||||
}
|
||||
if (iocg->indelay_since) {
|
||||
iocg->local_stat.indelay_us +=
|
||||
now.now - iocg->indelay_since;
|
||||
iocg->indelay_since = now.now;
|
||||
}
|
||||
|
||||
if (waitqueue_active(&iocg->waitq) || iocg->abs_vdebt ||
|
||||
iocg->delay) {
|
||||
/* might be oversleeping vtime / hweight changes, kick */
|
||||
iocg_kick_waitq(iocg, true, &now);
|
||||
if (iocg->abs_vdebt || iocg->delay)
|
||||
nr_debtors++;
|
||||
} else if (iocg_is_idle(iocg)) {
|
||||
/* no waiter and idle, deactivate */
|
||||
u64 vtime = atomic64_read(&iocg->vtime);
|
||||
s64 excess;
|
||||
|
||||
/*
|
||||
* @iocg has been inactive for a full duration and will
|
||||
* have a high budget. Account anything above target as
|
||||
* error and throw away. On reactivation, it'll start
|
||||
* with the target budget.
|
||||
*/
|
||||
excess = now.vnow - vtime - ioc->margins.target;
|
||||
if (excess > 0) {
|
||||
u32 old_hwi;
|
||||
|
||||
current_hweight(iocg, NULL, &old_hwi);
|
||||
ioc->vtime_err -= div64_u64(excess * old_hwi,
|
||||
WEIGHT_ONE);
|
||||
}
|
||||
|
||||
__propagate_weights(iocg, 0, 0, false, &now);
|
||||
list_del_init(&iocg->active_list);
|
||||
}
|
||||
|
||||
spin_unlock(&iocg->waitq.lock);
|
||||
}
|
||||
commit_weights(ioc);
|
||||
nr_debtors = ioc_check_iocgs(ioc, &now);
|
||||
|
||||
/*
|
||||
* Wait and indebt stat are flushed above and the donation calculation
|
||||
@@ -2170,8 +2234,8 @@ static void ioc_timer_fn(struct timer_list *timer)
|
||||
|
||||
/* calc usage and see whether some weights need to be moved around */
|
||||
list_for_each_entry(iocg, &ioc->active_iocgs, active_list) {
|
||||
u64 vdone, vtime, usage_us, usage_dur;
|
||||
u32 usage, hw_active, hw_inuse;
|
||||
u64 vdone, vtime, usage_us;
|
||||
u32 hw_active, hw_inuse;
|
||||
|
||||
/*
|
||||
* Collect unused and wind vtime closer to vnow to prevent
|
||||
@@ -2202,30 +2266,32 @@ static void ioc_timer_fn(struct timer_list *timer)
|
||||
usage_us = iocg->usage_delta_us;
|
||||
usage_us_sum += usage_us;
|
||||
|
||||
if (vdone != vtime) {
|
||||
u64 inflight_us = DIV64_U64_ROUND_UP(
|
||||
cost_to_abs_cost(vtime - vdone, hw_inuse),
|
||||
ioc->vtime_base_rate);
|
||||
usage_us = max(usage_us, inflight_us);
|
||||
}
|
||||
|
||||
/* convert to hweight based usage ratio */
|
||||
if (time_after64(iocg->activated_at, ioc->period_at))
|
||||
usage_dur = max_t(u64, now.now - iocg->activated_at, 1);
|
||||
else
|
||||
usage_dur = max_t(u64, now.now - ioc->period_at, 1);
|
||||
|
||||
usage = clamp_t(u32,
|
||||
DIV64_U64_ROUND_UP(usage_us * WEIGHT_ONE,
|
||||
usage_dur),
|
||||
1, WEIGHT_ONE);
|
||||
|
||||
/* see whether there's surplus vtime */
|
||||
WARN_ON_ONCE(!list_empty(&iocg->surplus_list));
|
||||
if (hw_inuse < hw_active ||
|
||||
(!waitqueue_active(&iocg->waitq) &&
|
||||
time_before64(vtime, now.vnow - ioc->margins.low))) {
|
||||
u32 hwa, old_hwi, hwm, new_hwi;
|
||||
u32 hwa, old_hwi, hwm, new_hwi, usage;
|
||||
u64 usage_dur;
|
||||
|
||||
if (vdone != vtime) {
|
||||
u64 inflight_us = DIV64_U64_ROUND_UP(
|
||||
cost_to_abs_cost(vtime - vdone, hw_inuse),
|
||||
ioc->vtime_base_rate);
|
||||
|
||||
usage_us = max(usage_us, inflight_us);
|
||||
}
|
||||
|
||||
/* convert to hweight based usage ratio */
|
||||
if (time_after64(iocg->activated_at, ioc->period_at))
|
||||
usage_dur = max_t(u64, now.now - iocg->activated_at, 1);
|
||||
else
|
||||
usage_dur = max_t(u64, now.now - ioc->period_at, 1);
|
||||
|
||||
usage = clamp_t(u32,
|
||||
DIV64_U64_ROUND_UP(usage_us * WEIGHT_ONE,
|
||||
usage_dur),
|
||||
1, WEIGHT_ONE);
|
||||
|
||||
/*
|
||||
* Already donating or accumulated enough to start.
|
||||
@@ -2309,51 +2375,8 @@ static void ioc_timer_fn(struct timer_list *timer)
|
||||
|
||||
ioc->busy_level = clamp(ioc->busy_level, -1000, 1000);
|
||||
|
||||
if (ioc->busy_level > 0 || (ioc->busy_level < 0 && !nr_lagging)) {
|
||||
u64 vrate = ioc->vtime_base_rate;
|
||||
u64 vrate_min = ioc->vrate_min, vrate_max = ioc->vrate_max;
|
||||
|
||||
/* rq_wait signal is always reliable, ignore user vrate_min */
|
||||
if (rq_wait_pct > RQ_WAIT_BUSY_PCT)
|
||||
vrate_min = VRATE_MIN;
|
||||
|
||||
/*
|
||||
* If vrate is out of bounds, apply clamp gradually as the
|
||||
* bounds can change abruptly. Otherwise, apply busy_level
|
||||
* based adjustment.
|
||||
*/
|
||||
if (vrate < vrate_min) {
|
||||
vrate = div64_u64(vrate * (100 + VRATE_CLAMP_ADJ_PCT),
|
||||
100);
|
||||
vrate = min(vrate, vrate_min);
|
||||
} else if (vrate > vrate_max) {
|
||||
vrate = div64_u64(vrate * (100 - VRATE_CLAMP_ADJ_PCT),
|
||||
100);
|
||||
vrate = max(vrate, vrate_max);
|
||||
} else {
|
||||
int idx = min_t(int, abs(ioc->busy_level),
|
||||
ARRAY_SIZE(vrate_adj_pct) - 1);
|
||||
u32 adj_pct = vrate_adj_pct[idx];
|
||||
|
||||
if (ioc->busy_level > 0)
|
||||
adj_pct = 100 - adj_pct;
|
||||
else
|
||||
adj_pct = 100 + adj_pct;
|
||||
|
||||
vrate = clamp(DIV64_U64_ROUND_UP(vrate * adj_pct, 100),
|
||||
vrate_min, vrate_max);
|
||||
}
|
||||
|
||||
trace_iocost_ioc_vrate_adj(ioc, vrate, missed_ppm, rq_wait_pct,
|
||||
nr_lagging, nr_shortages);
|
||||
|
||||
ioc->vtime_base_rate = vrate;
|
||||
ioc_refresh_margins(ioc);
|
||||
} else if (ioc->busy_level != prev_busy_level || nr_lagging) {
|
||||
trace_iocost_ioc_vrate_adj(ioc, atomic64_read(&ioc->vtime_rate),
|
||||
missed_ppm, rq_wait_pct, nr_lagging,
|
||||
nr_shortages);
|
||||
}
|
||||
ioc_adjust_base_vrate(ioc, rq_wait_pct, nr_lagging, nr_shortages,
|
||||
prev_busy_level, missed_ppm);
|
||||
|
||||
ioc_refresh_params(ioc, false);
|
||||
|
||||
@@ -2400,7 +2423,7 @@ static u64 adjust_inuse_and_calc_cost(struct ioc_gq *iocg, u64 vtime,
|
||||
return cost;
|
||||
|
||||
/*
|
||||
* We only increase inuse during period and do so iff the margin has
|
||||
* We only increase inuse during period and do so if the margin has
|
||||
* deteriorated since the previous adjustment.
|
||||
*/
|
||||
if (margin >= iocg->saved_margin || margin >= margins->low ||
|
||||
@@ -3120,23 +3143,23 @@ static const match_table_t qos_tokens = {
|
||||
static ssize_t ioc_qos_write(struct kernfs_open_file *of, char *input,
|
||||
size_t nbytes, loff_t off)
|
||||
{
|
||||
struct gendisk *disk;
|
||||
struct block_device *bdev;
|
||||
struct ioc *ioc;
|
||||
u32 qos[NR_QOS_PARAMS];
|
||||
bool enable, user;
|
||||
char *p;
|
||||
int ret;
|
||||
|
||||
disk = blkcg_conf_get_disk(&input);
|
||||
if (IS_ERR(disk))
|
||||
return PTR_ERR(disk);
|
||||
bdev = blkcg_conf_open_bdev(&input);
|
||||
if (IS_ERR(bdev))
|
||||
return PTR_ERR(bdev);
|
||||
|
||||
ioc = q_to_ioc(disk->queue);
|
||||
ioc = q_to_ioc(bdev->bd_disk->queue);
|
||||
if (!ioc) {
|
||||
ret = blk_iocost_init(disk->queue);
|
||||
ret = blk_iocost_init(bdev->bd_disk->queue);
|
||||
if (ret)
|
||||
goto err;
|
||||
ioc = q_to_ioc(disk->queue);
|
||||
ioc = q_to_ioc(bdev->bd_disk->queue);
|
||||
}
|
||||
|
||||
spin_lock_irq(&ioc->lock);
|
||||
@@ -3231,12 +3254,12 @@ static ssize_t ioc_qos_write(struct kernfs_open_file *of, char *input,
|
||||
ioc_refresh_params(ioc, true);
|
||||
spin_unlock_irq(&ioc->lock);
|
||||
|
||||
put_disk_and_module(disk);
|
||||
blkdev_put_no_open(bdev);
|
||||
return nbytes;
|
||||
einval:
|
||||
ret = -EINVAL;
|
||||
err:
|
||||
put_disk_and_module(disk);
|
||||
blkdev_put_no_open(bdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3287,23 +3310,23 @@ static const match_table_t i_lcoef_tokens = {
|
||||
static ssize_t ioc_cost_model_write(struct kernfs_open_file *of, char *input,
|
||||
size_t nbytes, loff_t off)
|
||||
{
|
||||
struct gendisk *disk;
|
||||
struct block_device *bdev;
|
||||
struct ioc *ioc;
|
||||
u64 u[NR_I_LCOEFS];
|
||||
bool user;
|
||||
char *p;
|
||||
int ret;
|
||||
|
||||
disk = blkcg_conf_get_disk(&input);
|
||||
if (IS_ERR(disk))
|
||||
return PTR_ERR(disk);
|
||||
bdev = blkcg_conf_open_bdev(&input);
|
||||
if (IS_ERR(bdev))
|
||||
return PTR_ERR(bdev);
|
||||
|
||||
ioc = q_to_ioc(disk->queue);
|
||||
ioc = q_to_ioc(bdev->bd_disk->queue);
|
||||
if (!ioc) {
|
||||
ret = blk_iocost_init(disk->queue);
|
||||
ret = blk_iocost_init(bdev->bd_disk->queue);
|
||||
if (ret)
|
||||
goto err;
|
||||
ioc = q_to_ioc(disk->queue);
|
||||
ioc = q_to_ioc(bdev->bd_disk->queue);
|
||||
}
|
||||
|
||||
spin_lock_irq(&ioc->lock);
|
||||
@@ -3356,13 +3379,13 @@ static ssize_t ioc_cost_model_write(struct kernfs_open_file *of, char *input,
|
||||
ioc_refresh_params(ioc, true);
|
||||
spin_unlock_irq(&ioc->lock);
|
||||
|
||||
put_disk_and_module(disk);
|
||||
blkdev_put_no_open(bdev);
|
||||
return nbytes;
|
||||
|
||||
einval:
|
||||
ret = -EINVAL;
|
||||
err:
|
||||
put_disk_and_module(disk);
|
||||
blkdev_put_no_open(bdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
|
||||
|
||||
/* In case the discard request is in a partition */
|
||||
if (bdev_is_partition(bdev))
|
||||
part_offset = bdev->bd_part->start_sect;
|
||||
part_offset = bdev->bd_start_sect;
|
||||
|
||||
while (nr_sects) {
|
||||
sector_t granularity_aligned_lba, req_sects;
|
||||
|
||||
+12
-6
@@ -279,6 +279,14 @@ static struct bio *blk_bio_segment_split(struct request_queue *q,
|
||||
return NULL;
|
||||
split:
|
||||
*segs = nsegs;
|
||||
|
||||
/*
|
||||
* Bio splitting may cause subtle trouble such as hang when doing sync
|
||||
* iopoll in direct IO routine. Given performance gain of iopoll for
|
||||
* big IO can be trival, disable iopoll when split needed.
|
||||
*/
|
||||
bio->bi_opf &= ~REQ_HIPRI;
|
||||
|
||||
return bio_split(bio, sectors, GFP_NOIO, bs);
|
||||
}
|
||||
|
||||
@@ -338,7 +346,7 @@ void __blk_queue_split(struct bio **bio, unsigned int *nr_segs)
|
||||
split->bi_opf |= REQ_NOMERGE;
|
||||
|
||||
bio_chain(split, *bio);
|
||||
trace_block_split(q, split, (*bio)->bi_iter.bi_sector);
|
||||
trace_block_split(split, (*bio)->bi_iter.bi_sector);
|
||||
submit_bio_noacct(*bio);
|
||||
*bio = split;
|
||||
}
|
||||
@@ -683,8 +691,6 @@ static void blk_account_io_merge_request(struct request *req)
|
||||
part_stat_lock();
|
||||
part_stat_inc(req->part, merges[op_stat_group(req_op(req))]);
|
||||
part_stat_unlock();
|
||||
|
||||
hd_struct_put(req->part);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,7 +807,7 @@ static struct request *attempt_merge(struct request_queue *q,
|
||||
*/
|
||||
blk_account_io_merge_request(next);
|
||||
|
||||
trace_block_rq_merge(q, next);
|
||||
trace_block_rq_merge(next);
|
||||
|
||||
/*
|
||||
* ownership of bio passed from next to req, return 'next' for
|
||||
@@ -924,7 +930,7 @@ static enum bio_merge_status bio_attempt_back_merge(struct request *req,
|
||||
if (!ll_back_merge_fn(req, bio, nr_segs))
|
||||
return BIO_MERGE_FAILED;
|
||||
|
||||
trace_block_bio_backmerge(req->q, req, bio);
|
||||
trace_block_bio_backmerge(bio);
|
||||
rq_qos_merge(req->q, req, bio);
|
||||
|
||||
if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff)
|
||||
@@ -948,7 +954,7 @@ static enum bio_merge_status bio_attempt_front_merge(struct request *req,
|
||||
if (!ll_front_merge_fn(req, bio, nr_segs))
|
||||
return BIO_MERGE_FAILED;
|
||||
|
||||
trace_block_bio_frontmerge(req->q, req, bio);
|
||||
trace_block_bio_frontmerge(bio);
|
||||
rq_qos_merge(req->q, req, bio);
|
||||
|
||||
if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff)
|
||||
|
||||
@@ -386,7 +386,7 @@ EXPORT_SYMBOL_GPL(blk_mq_sched_try_insert_merge);
|
||||
|
||||
void blk_mq_sched_request_inserted(struct request *rq)
|
||||
{
|
||||
trace_block_rq_insert(rq->q, rq);
|
||||
trace_block_rq_insert(rq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blk_mq_sched_request_inserted);
|
||||
|
||||
|
||||
+30
-16
@@ -95,7 +95,7 @@ static void blk_mq_hctx_clear_pending(struct blk_mq_hw_ctx *hctx,
|
||||
}
|
||||
|
||||
struct mq_inflight {
|
||||
struct hd_struct *part;
|
||||
struct block_device *part;
|
||||
unsigned int inflight[2];
|
||||
};
|
||||
|
||||
@@ -105,13 +105,15 @@ static bool blk_mq_check_inflight(struct blk_mq_hw_ctx *hctx,
|
||||
{
|
||||
struct mq_inflight *mi = priv;
|
||||
|
||||
if (rq->part == mi->part && blk_mq_rq_state(rq) == MQ_RQ_IN_FLIGHT)
|
||||
if ((!mi->part->bd_partno || rq->part == mi->part) &&
|
||||
blk_mq_rq_state(rq) == MQ_RQ_IN_FLIGHT)
|
||||
mi->inflight[rq_data_dir(rq)]++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int blk_mq_in_flight(struct request_queue *q, struct hd_struct *part)
|
||||
unsigned int blk_mq_in_flight(struct request_queue *q,
|
||||
struct block_device *part)
|
||||
{
|
||||
struct mq_inflight mi = { .part = part };
|
||||
|
||||
@@ -120,8 +122,8 @@ unsigned int blk_mq_in_flight(struct request_queue *q, struct hd_struct *part)
|
||||
return mi.inflight[0] + mi.inflight[1];
|
||||
}
|
||||
|
||||
void blk_mq_in_flight_rw(struct request_queue *q, struct hd_struct *part,
|
||||
unsigned int inflight[2])
|
||||
void blk_mq_in_flight_rw(struct request_queue *q, struct block_device *part,
|
||||
unsigned int inflight[2])
|
||||
{
|
||||
struct mq_inflight mi = { .part = part };
|
||||
|
||||
@@ -729,7 +731,7 @@ void blk_mq_start_request(struct request *rq)
|
||||
{
|
||||
struct request_queue *q = rq->q;
|
||||
|
||||
trace_block_rq_issue(q, rq);
|
||||
trace_block_rq_issue(rq);
|
||||
|
||||
if (test_bit(QUEUE_FLAG_STATS, &q->queue_flags)) {
|
||||
rq->io_start_time_ns = ktime_get_ns();
|
||||
@@ -756,7 +758,7 @@ static void __blk_mq_requeue_request(struct request *rq)
|
||||
|
||||
blk_mq_put_driver_tag(rq);
|
||||
|
||||
trace_block_rq_requeue(q, rq);
|
||||
trace_block_rq_requeue(rq);
|
||||
rq_qos_requeue(q, rq);
|
||||
|
||||
if (blk_mq_request_started(rq)) {
|
||||
@@ -1590,7 +1592,7 @@ select_cpu:
|
||||
* __blk_mq_delay_run_hw_queue - Run (or schedule to run) a hardware queue.
|
||||
* @hctx: Pointer to the hardware queue to run.
|
||||
* @async: If we want to run the queue asynchronously.
|
||||
* @msecs: Microseconds of delay to wait before running the queue.
|
||||
* @msecs: Milliseconds of delay to wait before running the queue.
|
||||
*
|
||||
* If !@async, try to run the queue now. Else, run the queue asynchronously and
|
||||
* with a delay of @msecs.
|
||||
@@ -1619,7 +1621,7 @@ static void __blk_mq_delay_run_hw_queue(struct blk_mq_hw_ctx *hctx, bool async,
|
||||
/**
|
||||
* blk_mq_delay_run_hw_queue - Run a hardware queue asynchronously.
|
||||
* @hctx: Pointer to the hardware queue to run.
|
||||
* @msecs: Microseconds of delay to wait before running the queue.
|
||||
* @msecs: Milliseconds of delay to wait before running the queue.
|
||||
*
|
||||
* Run a hardware queue asynchronously with a delay of @msecs.
|
||||
*/
|
||||
@@ -1683,7 +1685,7 @@ EXPORT_SYMBOL(blk_mq_run_hw_queues);
|
||||
/**
|
||||
* blk_mq_delay_run_hw_queues - Run all hardware queues asynchronously.
|
||||
* @q: Pointer to the request queue to run.
|
||||
* @msecs: Microseconds of delay to wait before running the queues.
|
||||
* @msecs: Milliseconds of delay to wait before running the queues.
|
||||
*/
|
||||
void blk_mq_delay_run_hw_queues(struct request_queue *q, unsigned long msecs)
|
||||
{
|
||||
@@ -1817,7 +1819,7 @@ static inline void __blk_mq_insert_req_list(struct blk_mq_hw_ctx *hctx,
|
||||
|
||||
lockdep_assert_held(&ctx->lock);
|
||||
|
||||
trace_block_rq_insert(hctx->queue, rq);
|
||||
trace_block_rq_insert(rq);
|
||||
|
||||
if (at_head)
|
||||
list_add(&rq->queuelist, &ctx->rq_lists[type]);
|
||||
@@ -1874,7 +1876,7 @@ void blk_mq_insert_requests(struct blk_mq_hw_ctx *hctx, struct blk_mq_ctx *ctx,
|
||||
*/
|
||||
list_for_each_entry(rq, list, queuelist) {
|
||||
BUG_ON(rq->mq_ctx != ctx);
|
||||
trace_block_rq_insert(hctx->queue, rq);
|
||||
trace_block_rq_insert(rq);
|
||||
}
|
||||
|
||||
spin_lock(&ctx->lock);
|
||||
@@ -2155,6 +2157,7 @@ blk_qc_t blk_mq_submit_bio(struct bio *bio)
|
||||
unsigned int nr_segs;
|
||||
blk_qc_t cookie;
|
||||
blk_status_t ret;
|
||||
bool hipri;
|
||||
|
||||
blk_queue_bounce(q, &bio);
|
||||
__blk_queue_split(&bio, &nr_segs);
|
||||
@@ -2171,6 +2174,8 @@ blk_qc_t blk_mq_submit_bio(struct bio *bio)
|
||||
|
||||
rq_qos_throttle(q, bio);
|
||||
|
||||
hipri = bio->bi_opf & REQ_HIPRI;
|
||||
|
||||
data.cmd_flags = bio->bi_opf;
|
||||
rq = __blk_mq_alloc_request(&data);
|
||||
if (unlikely(!rq)) {
|
||||
@@ -2180,7 +2185,7 @@ blk_qc_t blk_mq_submit_bio(struct bio *bio)
|
||||
goto queue_exit;
|
||||
}
|
||||
|
||||
trace_block_getrq(q, bio, bio->bi_opf);
|
||||
trace_block_getrq(bio);
|
||||
|
||||
rq_qos_track(q, rq, bio);
|
||||
|
||||
@@ -2263,6 +2268,8 @@ blk_qc_t blk_mq_submit_bio(struct bio *bio)
|
||||
blk_mq_sched_insert_request(rq, false, true, true);
|
||||
}
|
||||
|
||||
if (!hipri)
|
||||
return BLK_QC_T_NONE;
|
||||
return cookie;
|
||||
queue_exit:
|
||||
blk_queue_exit(q);
|
||||
@@ -3373,6 +3380,12 @@ static int blk_mq_realloc_tag_set_tags(struct blk_mq_tag_set *set,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blk_mq_alloc_tag_set_tags(struct blk_mq_tag_set *set,
|
||||
int new_nr_hw_queues)
|
||||
{
|
||||
return blk_mq_realloc_tag_set_tags(set, 0, new_nr_hw_queues);
|
||||
}
|
||||
|
||||
/*
|
||||
* Alloc a tag set to be associated with one or more request queues.
|
||||
* May fail with EINVAL for various error conditions. May adjust the
|
||||
@@ -3426,7 +3439,7 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set)
|
||||
if (set->nr_maps == 1 && set->nr_hw_queues > nr_cpu_ids)
|
||||
set->nr_hw_queues = nr_cpu_ids;
|
||||
|
||||
if (blk_mq_realloc_tag_set_tags(set, 0, set->nr_hw_queues) < 0)
|
||||
if (blk_mq_alloc_tag_set_tags(set, set->nr_hw_queues) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = -ENOMEM;
|
||||
@@ -3861,9 +3874,10 @@ int blk_poll(struct request_queue *q, blk_qc_t cookie, bool spin)
|
||||
* the state. Like for the other success return cases, the
|
||||
* caller is responsible for checking if the IO completed. If
|
||||
* the IO isn't complete, we'll get called again and will go
|
||||
* straight to the busy poll loop.
|
||||
* straight to the busy poll loop. If specified not to spin,
|
||||
* we also should not sleep.
|
||||
*/
|
||||
if (blk_mq_poll_hybrid(q, hctx, cookie))
|
||||
if (spin && blk_mq_poll_hybrid(q, hctx, cookie))
|
||||
return 1;
|
||||
|
||||
hctx->poll_considered++;
|
||||
|
||||
+5
-4
@@ -99,7 +99,7 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type(struct request_queue *
|
||||
* blk_mq_map_queue() - map (cmd_flags,type) to hardware queue
|
||||
* @q: request queue
|
||||
* @flags: request command flags
|
||||
* @cpu: cpu ctx
|
||||
* @ctx: software queue cpu ctx
|
||||
*/
|
||||
static inline struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *q,
|
||||
unsigned int flags,
|
||||
@@ -182,9 +182,10 @@ static inline bool blk_mq_hw_queue_mapped(struct blk_mq_hw_ctx *hctx)
|
||||
return hctx->nr_ctx && hctx->tags;
|
||||
}
|
||||
|
||||
unsigned int blk_mq_in_flight(struct request_queue *q, struct hd_struct *part);
|
||||
void blk_mq_in_flight_rw(struct request_queue *q, struct hd_struct *part,
|
||||
unsigned int inflight[2]);
|
||||
unsigned int blk_mq_in_flight(struct request_queue *q,
|
||||
struct block_device *part);
|
||||
void blk_mq_in_flight_rw(struct request_queue *q, struct block_device *part,
|
||||
unsigned int inflight[2]);
|
||||
|
||||
static inline void blk_mq_put_dispatch_budget(struct request_queue *q)
|
||||
{
|
||||
|
||||
@@ -587,6 +587,7 @@ static void throtl_pd_online(struct blkg_policy_data *pd)
|
||||
tg_update_has_rules(tg);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_THROTTLING_LOW
|
||||
static void blk_throtl_update_limit_valid(struct throtl_data *td)
|
||||
{
|
||||
struct cgroup_subsys_state *pos_css;
|
||||
@@ -607,6 +608,11 @@ static void blk_throtl_update_limit_valid(struct throtl_data *td)
|
||||
|
||||
td->limit_valid[LIMIT_LOW] = low_valid;
|
||||
}
|
||||
#else
|
||||
static inline void blk_throtl_update_limit_valid(struct throtl_data *td)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void throtl_upgrade_state(struct throtl_data *td);
|
||||
static void throtl_pd_offline(struct blkg_policy_data *pd)
|
||||
|
||||
@@ -835,7 +835,6 @@ int wbt_init(struct request_queue *q)
|
||||
rwb->enable_state = WBT_STATE_ON_DEFAULT;
|
||||
rwb->wc = 1;
|
||||
rwb->rq_depth.default_depth = RWB_DEF_DEPTH;
|
||||
wbt_update_limits(rwb);
|
||||
|
||||
/*
|
||||
* Assign rwb and add the stats callback.
|
||||
|
||||
+15
-1
@@ -508,15 +508,29 @@ int blk_revalidate_disk_zones(struct gendisk *disk,
|
||||
noio_flag = memalloc_noio_save();
|
||||
ret = disk->fops->report_zones(disk, 0, UINT_MAX,
|
||||
blk_revalidate_zone_cb, &args);
|
||||
if (!ret) {
|
||||
pr_warn("%s: No zones reported\n", disk->disk_name);
|
||||
ret = -ENODEV;
|
||||
}
|
||||
memalloc_noio_restore(noio_flag);
|
||||
|
||||
/*
|
||||
* If zones where reported, make sure that the entire disk capacity
|
||||
* has been checked.
|
||||
*/
|
||||
if (ret > 0 && args.sector != get_capacity(disk)) {
|
||||
pr_warn("%s: Missing zones from sector %llu\n",
|
||||
disk->disk_name, args.sector);
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Install the new bitmaps and update nr_zones only once the queue is
|
||||
* stopped and all I/Os are completed (i.e. a scheduler is not
|
||||
* referencing the bitmaps).
|
||||
*/
|
||||
blk_mq_freeze_queue(q);
|
||||
if (ret >= 0) {
|
||||
if (ret > 0) {
|
||||
blk_queue_chunk_sectors(q, args.zone_sectors);
|
||||
q->nr_zones = args.nr_zones;
|
||||
swap(q->seq_zones_wlock, args.seq_zones_wlock);
|
||||
|
||||
+4
-81
@@ -25,7 +25,6 @@ struct blk_flush_queue {
|
||||
struct list_head flush_data_in_flight;
|
||||
struct request *flush_rq;
|
||||
|
||||
struct lock_class_key key;
|
||||
spinlock_t mq_flush_lock;
|
||||
};
|
||||
|
||||
@@ -215,7 +214,7 @@ static inline void elevator_exit(struct request_queue *q,
|
||||
__elevator_exit(q, e);
|
||||
}
|
||||
|
||||
struct hd_struct *__disk_get_part(struct gendisk *disk, int partno);
|
||||
struct block_device *__disk_get_part(struct gendisk *disk, int partno);
|
||||
|
||||
ssize_t part_size_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf);
|
||||
@@ -348,97 +347,21 @@ void blk_queue_free_zone_bitmaps(struct request_queue *q);
|
||||
static inline void blk_queue_free_zone_bitmaps(struct request_queue *q) {}
|
||||
#endif
|
||||
|
||||
struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector);
|
||||
struct block_device *disk_map_sector_rcu(struct gendisk *disk, sector_t sector);
|
||||
|
||||
int blk_alloc_devt(struct hd_struct *part, dev_t *devt);
|
||||
int blk_alloc_devt(struct block_device *part, dev_t *devt);
|
||||
void blk_free_devt(dev_t devt);
|
||||
void blk_invalidate_devt(dev_t devt);
|
||||
char *disk_name(struct gendisk *hd, int partno, char *buf);
|
||||
#define ADDPART_FLAG_NONE 0
|
||||
#define ADDPART_FLAG_RAID 1
|
||||
#define ADDPART_FLAG_WHOLEDISK 2
|
||||
void delete_partition(struct hd_struct *part);
|
||||
void delete_partition(struct block_device *part);
|
||||
int bdev_add_partition(struct block_device *bdev, int partno,
|
||||
sector_t start, sector_t length);
|
||||
int bdev_del_partition(struct block_device *bdev, int partno);
|
||||
int bdev_resize_partition(struct block_device *bdev, int partno,
|
||||
sector_t start, sector_t length);
|
||||
int disk_expand_part_tbl(struct gendisk *disk, int target);
|
||||
int hd_ref_init(struct hd_struct *part);
|
||||
|
||||
/* no need to get/put refcount of part0 */
|
||||
static inline int hd_struct_try_get(struct hd_struct *part)
|
||||
{
|
||||
if (part->partno)
|
||||
return percpu_ref_tryget_live(&part->ref);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void hd_struct_put(struct hd_struct *part)
|
||||
{
|
||||
if (part->partno)
|
||||
percpu_ref_put(&part->ref);
|
||||
}
|
||||
|
||||
static inline void hd_free_part(struct hd_struct *part)
|
||||
{
|
||||
free_percpu(part->dkstats);
|
||||
kfree(part->info);
|
||||
percpu_ref_exit(&part->ref);
|
||||
}
|
||||
|
||||
/*
|
||||
* Any access of part->nr_sects which is not protected by partition
|
||||
* bd_mutex or gendisk bdev bd_mutex, should be done using this
|
||||
* accessor function.
|
||||
*
|
||||
* Code written along the lines of i_size_read() and i_size_write().
|
||||
* CONFIG_PREEMPTION case optimizes the case of UP kernel with preemption
|
||||
* on.
|
||||
*/
|
||||
static inline sector_t part_nr_sects_read(struct hd_struct *part)
|
||||
{
|
||||
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
|
||||
sector_t nr_sects;
|
||||
unsigned seq;
|
||||
do {
|
||||
seq = read_seqcount_begin(&part->nr_sects_seq);
|
||||
nr_sects = part->nr_sects;
|
||||
} while (read_seqcount_retry(&part->nr_sects_seq, seq));
|
||||
return nr_sects;
|
||||
#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION)
|
||||
sector_t nr_sects;
|
||||
|
||||
preempt_disable();
|
||||
nr_sects = part->nr_sects;
|
||||
preempt_enable();
|
||||
return nr_sects;
|
||||
#else
|
||||
return part->nr_sects;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Should be called with mutex lock held (typically bd_mutex) of partition
|
||||
* to provide mutual exlusion among writers otherwise seqcount might be
|
||||
* left in wrong state leaving the readers spinning infinitely.
|
||||
*/
|
||||
static inline void part_nr_sects_write(struct hd_struct *part, sector_t size)
|
||||
{
|
||||
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
|
||||
preempt_disable();
|
||||
write_seqcount_begin(&part->nr_sects_seq);
|
||||
part->nr_sects = size;
|
||||
write_seqcount_end(&part->nr_sects_seq);
|
||||
preempt_enable();
|
||||
#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPTION)
|
||||
preempt_disable();
|
||||
part->nr_sects = size;
|
||||
preempt_enable();
|
||||
#else
|
||||
part->nr_sects = size;
|
||||
#endif
|
||||
}
|
||||
|
||||
int bio_add_hw_page(struct request_queue *q, struct bio *bio,
|
||||
struct page *page, unsigned int len, unsigned int offset,
|
||||
|
||||
+1
-1
@@ -340,7 +340,7 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig,
|
||||
}
|
||||
}
|
||||
|
||||
trace_block_bio_bounce(q, *bio_orig);
|
||||
trace_block_bio_bounce(*bio_orig);
|
||||
|
||||
bio->bi_flags |= (1 << BIO_BOUNCED);
|
||||
|
||||
|
||||
+172
-393
File diff suppressed because it is too large.
Load diff
+12
-62
@@ -35,15 +35,6 @@ static int blkpg_do_ioctl(struct block_device *bdev,
|
||||
start = p.start >> SECTOR_SHIFT;
|
||||
length = p.length >> SECTOR_SHIFT;
|
||||
|
||||
/* check for fit in a hd_struct */
|
||||
if (sizeof(sector_t) < sizeof(long long)) {
|
||||
long pstart = start, plength = length;
|
||||
|
||||
if (pstart != start || plength != length || pstart < 0 ||
|
||||
plength < 0 || p.pno > 65535)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case BLKPG_ADD_PARTITION:
|
||||
/* check if partition is aligned to blocksize */
|
||||
@@ -219,23 +210,6 @@ static int compat_put_ulong(compat_ulong_t __user *argp, compat_ulong_t val)
|
||||
}
|
||||
#endif
|
||||
|
||||
int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
|
||||
unsigned cmd, unsigned long arg)
|
||||
{
|
||||
struct gendisk *disk = bdev->bd_disk;
|
||||
|
||||
if (disk->fops->ioctl)
|
||||
return disk->fops->ioctl(bdev, mode, cmd, arg);
|
||||
|
||||
return -ENOTTY;
|
||||
}
|
||||
/*
|
||||
* For the record: _GPL here is only because somebody decided to slap it
|
||||
* on the previous export. Sheer idiocy, since it wasn't copyrightable
|
||||
* at all and could be open-coded without any exports by anybody who cares.
|
||||
*/
|
||||
EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl);
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
/*
|
||||
* This is the equivalent of compat_ptr_ioctl(), to be used by block
|
||||
@@ -346,38 +320,11 @@ static int blkdev_pr_clear(struct block_device *bdev,
|
||||
return ops->pr_clear(bdev, c.key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is it an unrecognized ioctl? The correct returns are either
|
||||
* ENOTTY (final) or ENOIOCTLCMD ("I don't know this one, try a
|
||||
* fallback"). ENOIOCTLCMD gets turned into ENOTTY by the ioctl
|
||||
* code before returning.
|
||||
*
|
||||
* Confused drivers sometimes return EINVAL, which is wrong. It
|
||||
* means "I understood the ioctl command, but the parameters to
|
||||
* it were wrong".
|
||||
*
|
||||
* We should aim to just fix the broken drivers, the EINVAL case
|
||||
* should go away.
|
||||
*/
|
||||
static inline int is_unrecognized_ioctl(int ret)
|
||||
{
|
||||
return ret == -EINVAL ||
|
||||
ret == -ENOTTY ||
|
||||
ret == -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static int blkdev_flushbuf(struct block_device *bdev, fmode_t mode,
|
||||
unsigned cmd, unsigned long arg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
|
||||
if (!is_unrecognized_ioctl(ret))
|
||||
return ret;
|
||||
|
||||
fsync_bdev(bdev);
|
||||
invalidate_bdev(bdev);
|
||||
return 0;
|
||||
@@ -391,12 +338,14 @@ static int blkdev_roset(struct block_device *bdev, fmode_t mode,
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
|
||||
if (!is_unrecognized_ioctl(ret))
|
||||
return ret;
|
||||
if (get_user(n, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
set_device_ro(bdev, n);
|
||||
if (bdev->bd_disk->fops->set_read_only) {
|
||||
ret = bdev->bd_disk->fops->set_read_only(bdev, n);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
bdev->bd_read_only = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -619,10 +568,12 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
|
||||
}
|
||||
|
||||
ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp);
|
||||
if (ret == -ENOIOCTLCMD)
|
||||
return __blkdev_driver_ioctl(bdev, mode, cmd, arg);
|
||||
if (ret != -ENOIOCTLCMD)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
if (!bdev->bd_disk->fops->ioctl)
|
||||
return -ENOTTY;
|
||||
return bdev->bd_disk->fops->ioctl(bdev, mode, cmd, arg);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(blkdev_ioctl); /* for /dev/raw */
|
||||
|
||||
@@ -639,8 +590,7 @@ long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
|
||||
{
|
||||
int ret;
|
||||
void __user *argp = compat_ptr(arg);
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
struct block_device *bdev = inode->i_bdev;
|
||||
struct block_device *bdev = I_BDEV(file->f_mapping->host);
|
||||
struct gendisk *disk = bdev->bd_disk;
|
||||
fmode_t mode = file->f_mode;
|
||||
loff_t size;
|
||||
|
||||
+80
-168
@@ -85,6 +85,13 @@ static int (*check_part[])(struct parsed_partitions *) = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static void bdev_set_nr_sectors(struct block_device *bdev, sector_t sectors)
|
||||
{
|
||||
spin_lock(&bdev->bd_size_lock);
|
||||
i_size_write(bdev->bd_inode, (loff_t)sectors << SECTOR_SHIFT);
|
||||
spin_unlock(&bdev->bd_size_lock);
|
||||
}
|
||||
|
||||
static struct parsed_partitions *allocate_partitions(struct gendisk *hd)
|
||||
{
|
||||
struct parsed_partitions *state;
|
||||
@@ -175,44 +182,39 @@ static struct parsed_partitions *check_partition(struct gendisk *hd,
|
||||
static ssize_t part_partition_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hd_struct *p = dev_to_part(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", p->partno);
|
||||
return sprintf(buf, "%d\n", dev_to_bdev(dev)->bd_partno);
|
||||
}
|
||||
|
||||
static ssize_t part_start_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hd_struct *p = dev_to_part(dev);
|
||||
|
||||
return sprintf(buf, "%llu\n",(unsigned long long)p->start_sect);
|
||||
return sprintf(buf, "%llu\n", dev_to_bdev(dev)->bd_start_sect);
|
||||
}
|
||||
|
||||
static ssize_t part_ro_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hd_struct *p = dev_to_part(dev);
|
||||
return sprintf(buf, "%d\n", p->policy ? 1 : 0);
|
||||
return sprintf(buf, "%d\n", dev_to_bdev(dev)->bd_read_only);
|
||||
}
|
||||
|
||||
static ssize_t part_alignment_offset_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hd_struct *p = dev_to_part(dev);
|
||||
struct block_device *bdev = dev_to_bdev(dev);
|
||||
|
||||
return sprintf(buf, "%u\n",
|
||||
queue_limit_alignment_offset(&part_to_disk(p)->queue->limits,
|
||||
p->start_sect));
|
||||
queue_limit_alignment_offset(&bdev->bd_disk->queue->limits,
|
||||
bdev->bd_start_sect));
|
||||
}
|
||||
|
||||
static ssize_t part_discard_alignment_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct hd_struct *p = dev_to_part(dev);
|
||||
struct block_device *bdev = dev_to_bdev(dev);
|
||||
|
||||
return sprintf(buf, "%u\n",
|
||||
queue_limit_discard_alignment(&part_to_disk(p)->queue->limits,
|
||||
p->start_sect));
|
||||
queue_limit_discard_alignment(&bdev->bd_disk->queue->limits,
|
||||
bdev->bd_start_sect));
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(partition, 0444, part_partition_show, NULL);
|
||||
@@ -257,19 +259,17 @@ static const struct attribute_group *part_attr_groups[] = {
|
||||
|
||||
static void part_release(struct device *dev)
|
||||
{
|
||||
struct hd_struct *p = dev_to_part(dev);
|
||||
blk_free_devt(dev->devt);
|
||||
hd_free_part(p);
|
||||
kfree(p);
|
||||
bdput(dev_to_bdev(dev));
|
||||
}
|
||||
|
||||
static int part_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct hd_struct *part = dev_to_part(dev);
|
||||
struct block_device *part = dev_to_bdev(dev);
|
||||
|
||||
add_uevent_var(env, "PARTN=%u", part->partno);
|
||||
if (part->info && part->info->volname[0])
|
||||
add_uevent_var(env, "PARTNAME=%s", part->info->volname);
|
||||
add_uevent_var(env, "PARTN=%u", part->bd_partno);
|
||||
if (part->bd_meta_info && part->bd_meta_info->volname[0])
|
||||
add_uevent_var(env, "PARTNAME=%s", part->bd_meta_info->volname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -280,73 +280,29 @@ struct device_type part_type = {
|
||||
.uevent = part_uevent,
|
||||
};
|
||||
|
||||
static void hd_struct_free_work(struct work_struct *work)
|
||||
{
|
||||
struct hd_struct *part =
|
||||
container_of(to_rcu_work(work), struct hd_struct, rcu_work);
|
||||
struct gendisk *disk = part_to_disk(part);
|
||||
|
||||
/*
|
||||
* Release the disk reference acquired in delete_partition here.
|
||||
* We can't release it in hd_struct_free because the final put_device
|
||||
* needs process context and thus can't be run directly from a
|
||||
* percpu_ref ->release handler.
|
||||
*/
|
||||
put_device(disk_to_dev(disk));
|
||||
|
||||
part->start_sect = 0;
|
||||
part->nr_sects = 0;
|
||||
part_stat_set_all(part, 0);
|
||||
put_device(part_to_dev(part));
|
||||
}
|
||||
|
||||
static void hd_struct_free(struct percpu_ref *ref)
|
||||
{
|
||||
struct hd_struct *part = container_of(ref, struct hd_struct, ref);
|
||||
struct gendisk *disk = part_to_disk(part);
|
||||
struct disk_part_tbl *ptbl =
|
||||
rcu_dereference_protected(disk->part_tbl, 1);
|
||||
|
||||
rcu_assign_pointer(ptbl->last_lookup, NULL);
|
||||
|
||||
INIT_RCU_WORK(&part->rcu_work, hd_struct_free_work);
|
||||
queue_rcu_work(system_wq, &part->rcu_work);
|
||||
}
|
||||
|
||||
int hd_ref_init(struct hd_struct *part)
|
||||
{
|
||||
if (percpu_ref_init(&part->ref, hd_struct_free, 0, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be called either with bd_mutex held, before a disk can be opened or
|
||||
* after all disk users are gone.
|
||||
*/
|
||||
void delete_partition(struct hd_struct *part)
|
||||
void delete_partition(struct block_device *part)
|
||||
{
|
||||
struct gendisk *disk = part_to_disk(part);
|
||||
struct gendisk *disk = part->bd_disk;
|
||||
struct disk_part_tbl *ptbl =
|
||||
rcu_dereference_protected(disk->part_tbl, 1);
|
||||
|
||||
/*
|
||||
* ->part_tbl is referenced in this part's release handler, so
|
||||
* we have to hold the disk device
|
||||
*/
|
||||
get_device(disk_to_dev(disk));
|
||||
rcu_assign_pointer(ptbl->part[part->partno], NULL);
|
||||
kobject_put(part->holder_dir);
|
||||
device_del(part_to_dev(part));
|
||||
rcu_assign_pointer(ptbl->part[part->bd_partno], NULL);
|
||||
rcu_assign_pointer(ptbl->last_lookup, NULL);
|
||||
|
||||
kobject_put(part->bd_holder_dir);
|
||||
device_del(&part->bd_device);
|
||||
|
||||
/*
|
||||
* Remove gendisk pointer from idr so that it cannot be looked up
|
||||
* while RCU period before freeing gendisk is running to prevent
|
||||
* use-after-free issues. Note that the device number stays
|
||||
* "in-use" until we really free the gendisk.
|
||||
* Remove the block device from the inode hash, so that it cannot be
|
||||
* looked up any more even when openers still hold references.
|
||||
*/
|
||||
blk_invalidate_devt(part_devt(part));
|
||||
percpu_ref_kill(&part->ref);
|
||||
remove_inode_hash(part->bd_inode);
|
||||
|
||||
put_device(&part->bd_device);
|
||||
}
|
||||
|
||||
static ssize_t whole_disk_show(struct device *dev,
|
||||
@@ -360,14 +316,14 @@ static DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL);
|
||||
* Must be called either with bd_mutex held, before a disk can be opened or
|
||||
* after all disk users are gone.
|
||||
*/
|
||||
static struct hd_struct *add_partition(struct gendisk *disk, int partno,
|
||||
static struct block_device *add_partition(struct gendisk *disk, int partno,
|
||||
sector_t start, sector_t len, int flags,
|
||||
struct partition_meta_info *info)
|
||||
{
|
||||
struct hd_struct *p;
|
||||
dev_t devt = MKDEV(0, 0);
|
||||
struct device *ddev = disk_to_dev(disk);
|
||||
struct device *pdev;
|
||||
struct block_device *bdev;
|
||||
struct disk_part_tbl *ptbl;
|
||||
const char *dname;
|
||||
int err;
|
||||
@@ -398,36 +354,22 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
|
||||
if (ptbl->part[partno])
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (!p)
|
||||
return ERR_PTR(-EBUSY);
|
||||
bdev = bdev_alloc(disk, partno);
|
||||
if (!bdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
p->dkstats = alloc_percpu(struct disk_stats);
|
||||
if (!p->dkstats) {
|
||||
err = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
hd_sects_seq_init(p);
|
||||
pdev = part_to_dev(p);
|
||||
|
||||
p->start_sect = start;
|
||||
p->nr_sects = len;
|
||||
p->partno = partno;
|
||||
p->policy = get_disk_ro(disk);
|
||||
bdev->bd_start_sect = start;
|
||||
bdev_set_nr_sectors(bdev, len);
|
||||
bdev->bd_read_only = get_disk_ro(disk);
|
||||
|
||||
if (info) {
|
||||
struct partition_meta_info *pinfo;
|
||||
|
||||
pinfo = kzalloc_node(sizeof(*pinfo), GFP_KERNEL, disk->node_id);
|
||||
if (!pinfo) {
|
||||
err = -ENOMEM;
|
||||
goto out_free_stats;
|
||||
}
|
||||
memcpy(pinfo, info, sizeof(*info));
|
||||
p->info = pinfo;
|
||||
err = -ENOMEM;
|
||||
bdev->bd_meta_info = kmemdup(info, sizeof(*info), GFP_KERNEL);
|
||||
if (!bdev->bd_meta_info)
|
||||
goto out_bdput;
|
||||
}
|
||||
|
||||
pdev = &bdev->bd_device;
|
||||
dname = dev_name(ddev);
|
||||
if (isdigit(dname[strlen(dname) - 1]))
|
||||
dev_set_name(pdev, "%sp%d", dname, partno);
|
||||
@@ -439,9 +381,9 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
|
||||
pdev->type = &part_type;
|
||||
pdev->parent = ddev;
|
||||
|
||||
err = blk_alloc_devt(p, &devt);
|
||||
err = blk_alloc_devt(bdev, &devt);
|
||||
if (err)
|
||||
goto out_free_info;
|
||||
goto out_bdput;
|
||||
pdev->devt = devt;
|
||||
|
||||
/* delay uevent until 'holders' subdir is created */
|
||||
@@ -451,8 +393,8 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
|
||||
goto out_put;
|
||||
|
||||
err = -ENOMEM;
|
||||
p->holder_dir = kobject_create_and_add("holders", &pdev->kobj);
|
||||
if (!p->holder_dir)
|
||||
bdev->bd_holder_dir = kobject_create_and_add("holders", &pdev->kobj);
|
||||
if (!bdev->bd_holder_dir)
|
||||
goto out_del;
|
||||
|
||||
dev_set_uevent_suppress(pdev, 0);
|
||||
@@ -462,32 +404,20 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
|
||||
goto out_del;
|
||||
}
|
||||
|
||||
err = hd_ref_init(p);
|
||||
if (err) {
|
||||
if (flags & ADDPART_FLAG_WHOLEDISK)
|
||||
goto out_remove_file;
|
||||
goto out_del;
|
||||
}
|
||||
|
||||
/* everything is up and running, commence */
|
||||
rcu_assign_pointer(ptbl->part[partno], p);
|
||||
bdev_add(bdev, devt);
|
||||
rcu_assign_pointer(ptbl->part[partno], bdev);
|
||||
|
||||
/* suppress uevent if the disk suppresses it */
|
||||
if (!dev_get_uevent_suppress(ddev))
|
||||
kobject_uevent(&pdev->kobj, KOBJ_ADD);
|
||||
return p;
|
||||
return bdev;
|
||||
|
||||
out_free_info:
|
||||
kfree(p->info);
|
||||
out_free_stats:
|
||||
free_percpu(p->dkstats);
|
||||
out_free:
|
||||
kfree(p);
|
||||
out_bdput:
|
||||
bdput(bdev);
|
||||
return ERR_PTR(err);
|
||||
out_remove_file:
|
||||
device_remove_file(pdev, &dev_attr_whole_disk);
|
||||
out_del:
|
||||
kobject_put(p->holder_dir);
|
||||
kobject_put(bdev->bd_holder_dir);
|
||||
device_del(pdev);
|
||||
out_put:
|
||||
put_device(pdev);
|
||||
@@ -498,14 +428,14 @@ static bool partition_overlaps(struct gendisk *disk, sector_t start,
|
||||
sector_t length, int skip_partno)
|
||||
{
|
||||
struct disk_part_iter piter;
|
||||
struct hd_struct *part;
|
||||
struct block_device *part;
|
||||
bool overlap = false;
|
||||
|
||||
disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
|
||||
while ((part = disk_part_iter_next(&piter))) {
|
||||
if (part->partno == skip_partno ||
|
||||
start >= part->start_sect + part->nr_sects ||
|
||||
start + length <= part->start_sect)
|
||||
if (part->bd_partno == skip_partno ||
|
||||
start >= part->bd_start_sect + bdev_nr_sectors(part) ||
|
||||
start + length <= part->bd_start_sect)
|
||||
continue;
|
||||
overlap = true;
|
||||
break;
|
||||
@@ -518,7 +448,7 @@ static bool partition_overlaps(struct gendisk *disk, sector_t start,
|
||||
int bdev_add_partition(struct block_device *bdev, int partno,
|
||||
sector_t start, sector_t length)
|
||||
{
|
||||
struct hd_struct *part;
|
||||
struct block_device *part;
|
||||
|
||||
mutex_lock(&bdev->bd_mutex);
|
||||
if (partition_overlaps(bdev->bd_disk, start, length, -1)) {
|
||||
@@ -534,77 +464,59 @@ int bdev_add_partition(struct block_device *bdev, int partno,
|
||||
|
||||
int bdev_del_partition(struct block_device *bdev, int partno)
|
||||
{
|
||||
struct block_device *bdevp;
|
||||
struct hd_struct *part = NULL;
|
||||
struct block_device *part;
|
||||
int ret;
|
||||
|
||||
bdevp = bdget_disk(bdev->bd_disk, partno);
|
||||
if (!bdevp)
|
||||
part = bdget_disk(bdev->bd_disk, partno);
|
||||
if (!part)
|
||||
return -ENXIO;
|
||||
|
||||
mutex_lock(&bdevp->bd_mutex);
|
||||
mutex_lock(&part->bd_mutex);
|
||||
mutex_lock_nested(&bdev->bd_mutex, 1);
|
||||
|
||||
ret = -ENXIO;
|
||||
part = disk_get_part(bdev->bd_disk, partno);
|
||||
if (!part)
|
||||
goto out_unlock;
|
||||
|
||||
ret = -EBUSY;
|
||||
if (bdevp->bd_openers)
|
||||
if (part->bd_openers)
|
||||
goto out_unlock;
|
||||
|
||||
sync_blockdev(bdevp);
|
||||
invalidate_bdev(bdevp);
|
||||
sync_blockdev(part);
|
||||
invalidate_bdev(part);
|
||||
|
||||
delete_partition(part);
|
||||
ret = 0;
|
||||
out_unlock:
|
||||
mutex_unlock(&bdev->bd_mutex);
|
||||
mutex_unlock(&bdevp->bd_mutex);
|
||||
bdput(bdevp);
|
||||
if (part)
|
||||
disk_put_part(part);
|
||||
mutex_unlock(&part->bd_mutex);
|
||||
bdput(part);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bdev_resize_partition(struct block_device *bdev, int partno,
|
||||
sector_t start, sector_t length)
|
||||
{
|
||||
struct block_device *bdevp;
|
||||
struct hd_struct *part;
|
||||
struct block_device *part;
|
||||
int ret = 0;
|
||||
|
||||
part = disk_get_part(bdev->bd_disk, partno);
|
||||
part = bdget_disk(bdev->bd_disk, partno);
|
||||
if (!part)
|
||||
return -ENXIO;
|
||||
|
||||
ret = -ENOMEM;
|
||||
bdevp = bdget_part(part);
|
||||
if (!bdevp)
|
||||
goto out_put_part;
|
||||
|
||||
mutex_lock(&bdevp->bd_mutex);
|
||||
mutex_lock(&part->bd_mutex);
|
||||
mutex_lock_nested(&bdev->bd_mutex, 1);
|
||||
|
||||
ret = -EINVAL;
|
||||
if (start != part->start_sect)
|
||||
if (start != part->bd_start_sect)
|
||||
goto out_unlock;
|
||||
|
||||
ret = -EBUSY;
|
||||
if (partition_overlaps(bdev->bd_disk, start, length, partno))
|
||||
goto out_unlock;
|
||||
|
||||
part_nr_sects_write(part, length);
|
||||
bd_set_nr_sectors(bdevp, length);
|
||||
bdev_set_nr_sectors(part, length);
|
||||
|
||||
ret = 0;
|
||||
out_unlock:
|
||||
mutex_unlock(&bdevp->bd_mutex);
|
||||
mutex_unlock(&part->bd_mutex);
|
||||
mutex_unlock(&bdev->bd_mutex);
|
||||
bdput(bdevp);
|
||||
out_put_part:
|
||||
disk_put_part(part);
|
||||
bdput(part);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -627,7 +539,7 @@ static bool disk_unlock_native_capacity(struct gendisk *disk)
|
||||
int blk_drop_partitions(struct block_device *bdev)
|
||||
{
|
||||
struct disk_part_iter piter;
|
||||
struct hd_struct *part;
|
||||
struct block_device *part;
|
||||
|
||||
if (bdev->bd_part_count)
|
||||
return -EBUSY;
|
||||
@@ -652,7 +564,7 @@ static bool blk_add_partition(struct gendisk *disk, struct block_device *bdev,
|
||||
{
|
||||
sector_t size = state->parts[p].size;
|
||||
sector_t from = state->parts[p].from;
|
||||
struct hd_struct *part;
|
||||
struct block_device *part;
|
||||
|
||||
if (!size)
|
||||
return true;
|
||||
@@ -692,7 +604,7 @@ static bool blk_add_partition(struct gendisk *disk, struct block_device *bdev,
|
||||
|
||||
if (IS_BUILTIN(CONFIG_BLK_DEV_MD) &&
|
||||
(state->parts[p].flags & ADDPART_FLAG_RAID))
|
||||
md_autodetect_dev(part_to_dev(part)->devt);
|
||||
md_autodetect_dev(part->bd_dev);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+56
-44
@@ -201,7 +201,7 @@ struct amiga_floppy_struct {
|
||||
int busy; /* true when drive is active */
|
||||
int dirty; /* true when trackbuf is not on disk */
|
||||
int status; /* current error code for unit */
|
||||
struct gendisk *gendisk;
|
||||
struct gendisk *gendisk[2];
|
||||
struct blk_mq_tag_set tag_set;
|
||||
};
|
||||
|
||||
@@ -1669,6 +1669,11 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (unit[drive].type->code == FD_NODRIVE) {
|
||||
mutex_unlock(&amiflop_mutex);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (mode & (FMODE_READ|FMODE_WRITE)) {
|
||||
bdev_check_media_change(bdev);
|
||||
if (mode & FMODE_WRITE) {
|
||||
@@ -1695,7 +1700,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
|
||||
unit[drive].dtype=&data_types[system];
|
||||
unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
|
||||
data_types[system].sects*unit[drive].type->sect_mult;
|
||||
set_capacity(unit[drive].gendisk, unit[drive].blocks);
|
||||
set_capacity(unit[drive].gendisk[system], unit[drive].blocks);
|
||||
|
||||
printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,
|
||||
unit[drive].type->name, data_types[system].name);
|
||||
@@ -1772,36 +1777,68 @@ static const struct blk_mq_ops amiflop_mq_ops = {
|
||||
.queue_rq = amiflop_queue_rq,
|
||||
};
|
||||
|
||||
static struct gendisk *fd_alloc_disk(int drive)
|
||||
static int fd_alloc_disk(int drive, int system)
|
||||
{
|
||||
struct gendisk *disk;
|
||||
|
||||
disk = alloc_disk(1);
|
||||
if (!disk)
|
||||
goto out;
|
||||
|
||||
disk->queue = blk_mq_init_sq_queue(&unit[drive].tag_set, &amiflop_mq_ops,
|
||||
2, BLK_MQ_F_SHOULD_MERGE);
|
||||
if (IS_ERR(disk->queue)) {
|
||||
disk->queue = NULL;
|
||||
disk->queue = blk_mq_init_queue(&unit[drive].tag_set);
|
||||
if (IS_ERR(disk->queue))
|
||||
goto out_put_disk;
|
||||
}
|
||||
|
||||
unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL);
|
||||
if (!unit[drive].trackbuf)
|
||||
goto out_cleanup_queue;
|
||||
disk->major = FLOPPY_MAJOR;
|
||||
disk->first_minor = drive + system;
|
||||
disk->fops = &floppy_fops;
|
||||
disk->events = DISK_EVENT_MEDIA_CHANGE;
|
||||
if (system)
|
||||
sprintf(disk->disk_name, "fd%d_msdos", drive);
|
||||
else
|
||||
sprintf(disk->disk_name, "fd%d", drive);
|
||||
disk->private_data = &unit[drive];
|
||||
set_capacity(disk, 880 * 2);
|
||||
|
||||
return disk;
|
||||
unit[drive].gendisk[system] = disk;
|
||||
add_disk(disk);
|
||||
return 0;
|
||||
|
||||
out_cleanup_queue:
|
||||
blk_cleanup_queue(disk->queue);
|
||||
disk->queue = NULL;
|
||||
blk_mq_free_tag_set(&unit[drive].tag_set);
|
||||
out_put_disk:
|
||||
disk->queue = NULL;
|
||||
put_disk(disk);
|
||||
out:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int fd_alloc_drive(int drive)
|
||||
{
|
||||
unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL);
|
||||
if (!unit[drive].trackbuf)
|
||||
goto out;
|
||||
|
||||
memset(&unit[drive].tag_set, 0, sizeof(unit[drive].tag_set));
|
||||
unit[drive].tag_set.ops = &amiflop_mq_ops;
|
||||
unit[drive].tag_set.nr_hw_queues = 1;
|
||||
unit[drive].tag_set.nr_maps = 1;
|
||||
unit[drive].tag_set.queue_depth = 2;
|
||||
unit[drive].tag_set.numa_node = NUMA_NO_NODE;
|
||||
unit[drive].tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
|
||||
if (blk_mq_alloc_tag_set(&unit[drive].tag_set))
|
||||
goto out_cleanup_trackbuf;
|
||||
|
||||
pr_cont(" fd%d", drive);
|
||||
|
||||
if (fd_alloc_disk(drive, 0) || fd_alloc_disk(drive, 1))
|
||||
goto out_cleanup_tagset;
|
||||
return 0;
|
||||
|
||||
out_cleanup_tagset:
|
||||
blk_mq_free_tag_set(&unit[drive].tag_set);
|
||||
out_cleanup_trackbuf:
|
||||
kfree(unit[drive].trackbuf);
|
||||
out:
|
||||
unit[drive].type->code = FD_NODRIVE;
|
||||
return NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int __init fd_probe_drives(void)
|
||||
@@ -1812,29 +1849,16 @@ static int __init fd_probe_drives(void)
|
||||
drives=0;
|
||||
nomem=0;
|
||||
for(drive=0;drive<FD_MAX_UNITS;drive++) {
|
||||
struct gendisk *disk;
|
||||
fd_probe(drive);
|
||||
if (unit[drive].type->code == FD_NODRIVE)
|
||||
continue;
|
||||
|
||||
disk = fd_alloc_disk(drive);
|
||||
if (!disk) {
|
||||
if (fd_alloc_drive(drive) < 0) {
|
||||
pr_cont(" no mem for fd%d", drive);
|
||||
nomem = 1;
|
||||
continue;
|
||||
}
|
||||
unit[drive].gendisk = disk;
|
||||
drives++;
|
||||
|
||||
pr_cont(" fd%d",drive);
|
||||
disk->major = FLOPPY_MAJOR;
|
||||
disk->first_minor = drive;
|
||||
disk->fops = &floppy_fops;
|
||||
disk->events = DISK_EVENT_MEDIA_CHANGE;
|
||||
sprintf(disk->disk_name, "fd%d", drive);
|
||||
disk->private_data = &unit[drive];
|
||||
set_capacity(disk, 880*2);
|
||||
add_disk(disk);
|
||||
}
|
||||
if ((drives > 0) || (nomem == 0)) {
|
||||
if (drives == 0)
|
||||
@@ -1846,15 +1870,6 @@ static int __init fd_probe_drives(void)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct kobject *floppy_find(dev_t dev, int *part, void *data)
|
||||
{
|
||||
int drive = *part & 3;
|
||||
if (unit[drive].type->code == FD_NODRIVE)
|
||||
return NULL;
|
||||
*part = 0;
|
||||
return get_disk_and_module(unit[drive].gendisk);
|
||||
}
|
||||
|
||||
static int __init amiga_floppy_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i, ret;
|
||||
@@ -1884,9 +1899,6 @@ static int __init amiga_floppy_probe(struct platform_device *pdev)
|
||||
if (fd_probe_drives() < 1) /* No usable drives */
|
||||
goto out_probe;
|
||||
|
||||
blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE,
|
||||
floppy_find, NULL, NULL);
|
||||
|
||||
/* initialize variables */
|
||||
timer_setup(&motor_on_timer, motor_on_callback, 0);
|
||||
motor_on_timer.expires = 0;
|
||||
|
||||
@@ -890,19 +890,13 @@ void
|
||||
aoecmd_sleepwork(struct work_struct *work)
|
||||
{
|
||||
struct aoedev *d = container_of(work, struct aoedev, work);
|
||||
struct block_device *bd;
|
||||
u64 ssize;
|
||||
|
||||
if (d->flags & DEVFL_GDALLOC)
|
||||
aoeblk_gdalloc(d);
|
||||
|
||||
if (d->flags & DEVFL_NEWSIZE) {
|
||||
ssize = get_capacity(d->gd);
|
||||
bd = bdget_disk(d->gd, 0);
|
||||
if (bd) {
|
||||
bd_set_nr_sectors(bd, ssize);
|
||||
bdput(bd);
|
||||
}
|
||||
set_capacity_and_notify(d->gd, d->ssize);
|
||||
|
||||
spin_lock_irq(&d->lock);
|
||||
d->flags |= DEVFL_UP;
|
||||
d->flags &= ~DEVFL_NEWSIZE;
|
||||
@@ -971,10 +965,9 @@ ataid_complete(struct aoedev *d, struct aoetgt *t, unsigned char *id)
|
||||
d->geo.start = 0;
|
||||
if (d->flags & (DEVFL_GDALLOC|DEVFL_NEWSIZE))
|
||||
return;
|
||||
if (d->gd != NULL) {
|
||||
set_capacity(d->gd, ssize);
|
||||
if (d->gd != NULL)
|
||||
d->flags |= DEVFL_NEWSIZE;
|
||||
} else
|
||||
else
|
||||
d->flags |= DEVFL_GDALLOC;
|
||||
schedule_work(&d->work);
|
||||
}
|
||||
|
||||
Loaded 20 of 110 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user