162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * multipath.c : Multiple Devices driver for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1999, 2000, 2001 Ingo Molnar, Red Hat 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 1996, 1997, 1998 Ingo Molnar, Miguel de Icaza, Gadi Oxman 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * MULTIPATH management functions. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * derived from raid1.c. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/blkdev.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/raid/md_u.h> 1762306a36Sopenharmony_ci#include <linux/seq_file.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include "md.h" 2062306a36Sopenharmony_ci#include "md-multipath.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MAX_WORK_PER_DISK 128 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define NR_RESERVED_BUFS 32 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int multipath_map (struct mpconf *conf) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci int i, disks = conf->raid_disks; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci /* 3162306a36Sopenharmony_ci * Later we do read balancing on the read side 3262306a36Sopenharmony_ci * now we use the first available disk. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci rcu_read_lock(); 3662306a36Sopenharmony_ci for (i = 0; i < disks; i++) { 3762306a36Sopenharmony_ci struct md_rdev *rdev = rcu_dereference(conf->multipaths[i].rdev); 3862306a36Sopenharmony_ci if (rdev && test_bit(In_sync, &rdev->flags) && 3962306a36Sopenharmony_ci !test_bit(Faulty, &rdev->flags)) { 4062306a36Sopenharmony_ci atomic_inc(&rdev->nr_pending); 4162306a36Sopenharmony_ci rcu_read_unlock(); 4262306a36Sopenharmony_ci return i; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci rcu_read_unlock(); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci pr_crit_ratelimited("multipath_map(): no more operational IO paths?\n"); 4862306a36Sopenharmony_ci return (-1); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void multipath_reschedule_retry (struct multipath_bh *mp_bh) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci unsigned long flags; 5462306a36Sopenharmony_ci struct mddev *mddev = mp_bh->mddev; 5562306a36Sopenharmony_ci struct mpconf *conf = mddev->private; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci spin_lock_irqsave(&conf->device_lock, flags); 5862306a36Sopenharmony_ci list_add(&mp_bh->retry_list, &conf->retry_list); 5962306a36Sopenharmony_ci spin_unlock_irqrestore(&conf->device_lock, flags); 6062306a36Sopenharmony_ci md_wakeup_thread(mddev->thread); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * multipath_end_bh_io() is called when we have finished servicing a multipathed 6562306a36Sopenharmony_ci * operation and are ready to return a success/failure code to the buffer 6662306a36Sopenharmony_ci * cache layer. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistatic void multipath_end_bh_io(struct multipath_bh *mp_bh, blk_status_t status) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct bio *bio = mp_bh->master_bio; 7162306a36Sopenharmony_ci struct mpconf *conf = mp_bh->mddev->private; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci bio->bi_status = status; 7462306a36Sopenharmony_ci bio_endio(bio); 7562306a36Sopenharmony_ci mempool_free(mp_bh, &conf->pool); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void multipath_end_request(struct bio *bio) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct multipath_bh *mp_bh = bio->bi_private; 8162306a36Sopenharmony_ci struct mpconf *conf = mp_bh->mddev->private; 8262306a36Sopenharmony_ci struct md_rdev *rdev = conf->multipaths[mp_bh->path].rdev; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (!bio->bi_status) 8562306a36Sopenharmony_ci multipath_end_bh_io(mp_bh, 0); 8662306a36Sopenharmony_ci else if (!(bio->bi_opf & REQ_RAHEAD)) { 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * oops, IO error: 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci md_error (mp_bh->mddev, rdev); 9162306a36Sopenharmony_ci pr_info("multipath: %pg: rescheduling sector %llu\n", 9262306a36Sopenharmony_ci rdev->bdev, 9362306a36Sopenharmony_ci (unsigned long long)bio->bi_iter.bi_sector); 9462306a36Sopenharmony_ci multipath_reschedule_retry(mp_bh); 9562306a36Sopenharmony_ci } else 9662306a36Sopenharmony_ci multipath_end_bh_io(mp_bh, bio->bi_status); 9762306a36Sopenharmony_ci rdev_dec_pending(rdev, conf->mddev); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic bool multipath_make_request(struct mddev *mddev, struct bio * bio) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct mpconf *conf = mddev->private; 10362306a36Sopenharmony_ci struct multipath_bh * mp_bh; 10462306a36Sopenharmony_ci struct multipath_info *multipath; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (unlikely(bio->bi_opf & REQ_PREFLUSH) 10762306a36Sopenharmony_ci && md_flush_request(mddev, bio)) 10862306a36Sopenharmony_ci return true; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci md_account_bio(mddev, &bio); 11162306a36Sopenharmony_ci mp_bh = mempool_alloc(&conf->pool, GFP_NOIO); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci mp_bh->master_bio = bio; 11462306a36Sopenharmony_ci mp_bh->mddev = mddev; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci mp_bh->path = multipath_map(conf); 11762306a36Sopenharmony_ci if (mp_bh->path < 0) { 11862306a36Sopenharmony_ci bio_io_error(bio); 11962306a36Sopenharmony_ci mempool_free(mp_bh, &conf->pool); 12062306a36Sopenharmony_ci return true; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci multipath = conf->multipaths + mp_bh->path; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci bio_init_clone(multipath->rdev->bdev, &mp_bh->bio, bio, GFP_NOIO); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci mp_bh->bio.bi_iter.bi_sector += multipath->rdev->data_offset; 12762306a36Sopenharmony_ci mp_bh->bio.bi_opf |= REQ_FAILFAST_TRANSPORT; 12862306a36Sopenharmony_ci mp_bh->bio.bi_end_io = multipath_end_request; 12962306a36Sopenharmony_ci mp_bh->bio.bi_private = mp_bh; 13062306a36Sopenharmony_ci mddev_check_write_zeroes(mddev, &mp_bh->bio); 13162306a36Sopenharmony_ci submit_bio_noacct(&mp_bh->bio); 13262306a36Sopenharmony_ci return true; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void multipath_status(struct seq_file *seq, struct mddev *mddev) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct mpconf *conf = mddev->private; 13862306a36Sopenharmony_ci int i; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci seq_printf (seq, " [%d/%d] [", conf->raid_disks, 14162306a36Sopenharmony_ci conf->raid_disks - mddev->degraded); 14262306a36Sopenharmony_ci rcu_read_lock(); 14362306a36Sopenharmony_ci for (i = 0; i < conf->raid_disks; i++) { 14462306a36Sopenharmony_ci struct md_rdev *rdev = rcu_dereference(conf->multipaths[i].rdev); 14562306a36Sopenharmony_ci seq_printf (seq, "%s", rdev && test_bit(In_sync, &rdev->flags) ? "U" : "_"); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci rcu_read_unlock(); 14862306a36Sopenharmony_ci seq_putc(seq, ']'); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* 15262306a36Sopenharmony_ci * Careful, this can execute in IRQ contexts as well! 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic void multipath_error (struct mddev *mddev, struct md_rdev *rdev) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct mpconf *conf = mddev->private; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (conf->raid_disks - mddev->degraded <= 1) { 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * Uh oh, we can do nothing if this is our last path, but 16162306a36Sopenharmony_ci * first check if this is a queued request for a device 16262306a36Sopenharmony_ci * which has just failed. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci pr_warn("multipath: only one IO path left and IO error.\n"); 16562306a36Sopenharmony_ci /* leave it active... it's all we have */ 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * Mark disk as unusable 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci if (test_and_clear_bit(In_sync, &rdev->flags)) { 17262306a36Sopenharmony_ci unsigned long flags; 17362306a36Sopenharmony_ci spin_lock_irqsave(&conf->device_lock, flags); 17462306a36Sopenharmony_ci mddev->degraded++; 17562306a36Sopenharmony_ci spin_unlock_irqrestore(&conf->device_lock, flags); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci set_bit(Faulty, &rdev->flags); 17862306a36Sopenharmony_ci set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags); 17962306a36Sopenharmony_ci pr_err("multipath: IO failure on %pg, disabling IO path.\n" 18062306a36Sopenharmony_ci "multipath: Operation continuing on %d IO paths.\n", 18162306a36Sopenharmony_ci rdev->bdev, 18262306a36Sopenharmony_ci conf->raid_disks - mddev->degraded); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void print_multipath_conf (struct mpconf *conf) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci int i; 18862306a36Sopenharmony_ci struct multipath_info *tmp; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci pr_debug("MULTIPATH conf printout:\n"); 19162306a36Sopenharmony_ci if (!conf) { 19262306a36Sopenharmony_ci pr_debug("(conf==NULL)\n"); 19362306a36Sopenharmony_ci return; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci pr_debug(" --- wd:%d rd:%d\n", conf->raid_disks - conf->mddev->degraded, 19662306a36Sopenharmony_ci conf->raid_disks); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci for (i = 0; i < conf->raid_disks; i++) { 19962306a36Sopenharmony_ci tmp = conf->multipaths + i; 20062306a36Sopenharmony_ci if (tmp->rdev) 20162306a36Sopenharmony_ci pr_debug(" disk%d, o:%d, dev:%pg\n", 20262306a36Sopenharmony_ci i,!test_bit(Faulty, &tmp->rdev->flags), 20362306a36Sopenharmony_ci tmp->rdev->bdev); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int multipath_add_disk(struct mddev *mddev, struct md_rdev *rdev) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct mpconf *conf = mddev->private; 21062306a36Sopenharmony_ci int err = -EEXIST; 21162306a36Sopenharmony_ci int path; 21262306a36Sopenharmony_ci struct multipath_info *p; 21362306a36Sopenharmony_ci int first = 0; 21462306a36Sopenharmony_ci int last = mddev->raid_disks - 1; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (rdev->raid_disk >= 0) 21762306a36Sopenharmony_ci first = last = rdev->raid_disk; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci print_multipath_conf(conf); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci for (path = first; path <= last; path++) 22262306a36Sopenharmony_ci if ((p=conf->multipaths+path)->rdev == NULL) { 22362306a36Sopenharmony_ci disk_stack_limits(mddev->gendisk, rdev->bdev, 22462306a36Sopenharmony_ci rdev->data_offset << 9); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci err = md_integrity_add_rdev(rdev, mddev); 22762306a36Sopenharmony_ci if (err) 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci spin_lock_irq(&conf->device_lock); 23062306a36Sopenharmony_ci mddev->degraded--; 23162306a36Sopenharmony_ci rdev->raid_disk = path; 23262306a36Sopenharmony_ci set_bit(In_sync, &rdev->flags); 23362306a36Sopenharmony_ci spin_unlock_irq(&conf->device_lock); 23462306a36Sopenharmony_ci rcu_assign_pointer(p->rdev, rdev); 23562306a36Sopenharmony_ci err = 0; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci print_multipath_conf(conf); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return err; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int multipath_remove_disk(struct mddev *mddev, struct md_rdev *rdev) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct mpconf *conf = mddev->private; 24762306a36Sopenharmony_ci int err = 0; 24862306a36Sopenharmony_ci int number = rdev->raid_disk; 24962306a36Sopenharmony_ci struct multipath_info *p = conf->multipaths + number; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci print_multipath_conf(conf); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (rdev == p->rdev) { 25462306a36Sopenharmony_ci if (test_bit(In_sync, &rdev->flags) || 25562306a36Sopenharmony_ci atomic_read(&rdev->nr_pending)) { 25662306a36Sopenharmony_ci pr_warn("hot-remove-disk, slot %d is identified but is still operational!\n", number); 25762306a36Sopenharmony_ci err = -EBUSY; 25862306a36Sopenharmony_ci goto abort; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci p->rdev = NULL; 26162306a36Sopenharmony_ci if (!test_bit(RemoveSynchronized, &rdev->flags)) { 26262306a36Sopenharmony_ci synchronize_rcu(); 26362306a36Sopenharmony_ci if (atomic_read(&rdev->nr_pending)) { 26462306a36Sopenharmony_ci /* lost the race, try later */ 26562306a36Sopenharmony_ci err = -EBUSY; 26662306a36Sopenharmony_ci p->rdev = rdev; 26762306a36Sopenharmony_ci goto abort; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci err = md_integrity_register(mddev); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ciabort: 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci print_multipath_conf(conf); 27562306a36Sopenharmony_ci return err; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/* 27962306a36Sopenharmony_ci * This is a kernel thread which: 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * 1. Retries failed read operations on working multipaths. 28262306a36Sopenharmony_ci * 2. Updates the raid superblock when problems encounter. 28362306a36Sopenharmony_ci * 3. Performs writes following reads for array syncronising. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic void multipathd(struct md_thread *thread) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct mddev *mddev = thread->mddev; 28962306a36Sopenharmony_ci struct multipath_bh *mp_bh; 29062306a36Sopenharmony_ci struct bio *bio; 29162306a36Sopenharmony_ci unsigned long flags; 29262306a36Sopenharmony_ci struct mpconf *conf = mddev->private; 29362306a36Sopenharmony_ci struct list_head *head = &conf->retry_list; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci md_check_recovery(mddev); 29662306a36Sopenharmony_ci for (;;) { 29762306a36Sopenharmony_ci spin_lock_irqsave(&conf->device_lock, flags); 29862306a36Sopenharmony_ci if (list_empty(head)) 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci mp_bh = list_entry(head->prev, struct multipath_bh, retry_list); 30162306a36Sopenharmony_ci list_del(head->prev); 30262306a36Sopenharmony_ci spin_unlock_irqrestore(&conf->device_lock, flags); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci bio = &mp_bh->bio; 30562306a36Sopenharmony_ci bio->bi_iter.bi_sector = mp_bh->master_bio->bi_iter.bi_sector; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if ((mp_bh->path = multipath_map (conf))<0) { 30862306a36Sopenharmony_ci pr_err("multipath: %pg: unrecoverable IO read error for block %llu\n", 30962306a36Sopenharmony_ci bio->bi_bdev, 31062306a36Sopenharmony_ci (unsigned long long)bio->bi_iter.bi_sector); 31162306a36Sopenharmony_ci multipath_end_bh_io(mp_bh, BLK_STS_IOERR); 31262306a36Sopenharmony_ci } else { 31362306a36Sopenharmony_ci pr_err("multipath: %pg: redirecting sector %llu to another IO path\n", 31462306a36Sopenharmony_ci bio->bi_bdev, 31562306a36Sopenharmony_ci (unsigned long long)bio->bi_iter.bi_sector); 31662306a36Sopenharmony_ci *bio = *(mp_bh->master_bio); 31762306a36Sopenharmony_ci bio->bi_iter.bi_sector += 31862306a36Sopenharmony_ci conf->multipaths[mp_bh->path].rdev->data_offset; 31962306a36Sopenharmony_ci bio_set_dev(bio, conf->multipaths[mp_bh->path].rdev->bdev); 32062306a36Sopenharmony_ci bio->bi_opf |= REQ_FAILFAST_TRANSPORT; 32162306a36Sopenharmony_ci bio->bi_end_io = multipath_end_request; 32262306a36Sopenharmony_ci bio->bi_private = mp_bh; 32362306a36Sopenharmony_ci submit_bio_noacct(bio); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci spin_unlock_irqrestore(&conf->device_lock, flags); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic sector_t multipath_size(struct mddev *mddev, sector_t sectors, int raid_disks) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci WARN_ONCE(sectors || raid_disks, 33262306a36Sopenharmony_ci "%s does not support generic reshape\n", __func__); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return mddev->dev_sectors; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic int multipath_run (struct mddev *mddev) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct mpconf *conf; 34062306a36Sopenharmony_ci int disk_idx; 34162306a36Sopenharmony_ci struct multipath_info *disk; 34262306a36Sopenharmony_ci struct md_rdev *rdev; 34362306a36Sopenharmony_ci int working_disks; 34462306a36Sopenharmony_ci int ret; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (md_check_no_bitmap(mddev)) 34762306a36Sopenharmony_ci return -EINVAL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (mddev->level != LEVEL_MULTIPATH) { 35062306a36Sopenharmony_ci pr_warn("multipath: %s: raid level not set to multipath IO (%d)\n", 35162306a36Sopenharmony_ci mdname(mddev), mddev->level); 35262306a36Sopenharmony_ci goto out; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * copy the already verified devices into our private MULTIPATH 35662306a36Sopenharmony_ci * bookkeeping area. [whatever we allocate in multipath_run(), 35762306a36Sopenharmony_ci * should be freed in multipath_free()] 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci conf = kzalloc(sizeof(struct mpconf), GFP_KERNEL); 36162306a36Sopenharmony_ci mddev->private = conf; 36262306a36Sopenharmony_ci if (!conf) 36362306a36Sopenharmony_ci goto out; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci conf->multipaths = kcalloc(mddev->raid_disks, 36662306a36Sopenharmony_ci sizeof(struct multipath_info), 36762306a36Sopenharmony_ci GFP_KERNEL); 36862306a36Sopenharmony_ci if (!conf->multipaths) 36962306a36Sopenharmony_ci goto out_free_conf; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci working_disks = 0; 37262306a36Sopenharmony_ci rdev_for_each(rdev, mddev) { 37362306a36Sopenharmony_ci disk_idx = rdev->raid_disk; 37462306a36Sopenharmony_ci if (disk_idx < 0 || 37562306a36Sopenharmony_ci disk_idx >= mddev->raid_disks) 37662306a36Sopenharmony_ci continue; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci disk = conf->multipaths + disk_idx; 37962306a36Sopenharmony_ci disk->rdev = rdev; 38062306a36Sopenharmony_ci disk_stack_limits(mddev->gendisk, rdev->bdev, 38162306a36Sopenharmony_ci rdev->data_offset << 9); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (!test_bit(Faulty, &rdev->flags)) 38462306a36Sopenharmony_ci working_disks++; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci conf->raid_disks = mddev->raid_disks; 38862306a36Sopenharmony_ci conf->mddev = mddev; 38962306a36Sopenharmony_ci spin_lock_init(&conf->device_lock); 39062306a36Sopenharmony_ci INIT_LIST_HEAD(&conf->retry_list); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (!working_disks) { 39362306a36Sopenharmony_ci pr_warn("multipath: no operational IO paths for %s\n", 39462306a36Sopenharmony_ci mdname(mddev)); 39562306a36Sopenharmony_ci goto out_free_conf; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci mddev->degraded = conf->raid_disks - working_disks; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci ret = mempool_init_kmalloc_pool(&conf->pool, NR_RESERVED_BUFS, 40062306a36Sopenharmony_ci sizeof(struct multipath_bh)); 40162306a36Sopenharmony_ci if (ret) 40262306a36Sopenharmony_ci goto out_free_conf; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci rcu_assign_pointer(mddev->thread, 40562306a36Sopenharmony_ci md_register_thread(multipathd, mddev, "multipath")); 40662306a36Sopenharmony_ci if (!mddev->thread) 40762306a36Sopenharmony_ci goto out_free_conf; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci pr_info("multipath: array %s active with %d out of %d IO paths\n", 41062306a36Sopenharmony_ci mdname(mddev), conf->raid_disks - mddev->degraded, 41162306a36Sopenharmony_ci mddev->raid_disks); 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * Ok, everything is just fine now 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ci md_set_array_sectors(mddev, multipath_size(mddev, 0, 0)); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (md_integrity_register(mddev)) 41862306a36Sopenharmony_ci goto out_free_conf; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ciout_free_conf: 42362306a36Sopenharmony_ci mempool_exit(&conf->pool); 42462306a36Sopenharmony_ci kfree(conf->multipaths); 42562306a36Sopenharmony_ci kfree(conf); 42662306a36Sopenharmony_ci mddev->private = NULL; 42762306a36Sopenharmony_ciout: 42862306a36Sopenharmony_ci return -EIO; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic void multipath_free(struct mddev *mddev, void *priv) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct mpconf *conf = priv; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci mempool_exit(&conf->pool); 43662306a36Sopenharmony_ci kfree(conf->multipaths); 43762306a36Sopenharmony_ci kfree(conf); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic struct md_personality multipath_personality = 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci .name = "multipath", 44362306a36Sopenharmony_ci .level = LEVEL_MULTIPATH, 44462306a36Sopenharmony_ci .owner = THIS_MODULE, 44562306a36Sopenharmony_ci .make_request = multipath_make_request, 44662306a36Sopenharmony_ci .run = multipath_run, 44762306a36Sopenharmony_ci .free = multipath_free, 44862306a36Sopenharmony_ci .status = multipath_status, 44962306a36Sopenharmony_ci .error_handler = multipath_error, 45062306a36Sopenharmony_ci .hot_add_disk = multipath_add_disk, 45162306a36Sopenharmony_ci .hot_remove_disk= multipath_remove_disk, 45262306a36Sopenharmony_ci .size = multipath_size, 45362306a36Sopenharmony_ci}; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int __init multipath_init (void) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci return register_md_personality (&multipath_personality); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic void __exit multipath_exit (void) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci unregister_md_personality (&multipath_personality); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cimodule_init(multipath_init); 46662306a36Sopenharmony_cimodule_exit(multipath_exit); 46762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 46862306a36Sopenharmony_ciMODULE_DESCRIPTION("simple multi-path personality for MD (deprecated)"); 46962306a36Sopenharmony_ciMODULE_ALIAS("md-personality-7"); /* MULTIPATH */ 47062306a36Sopenharmony_ciMODULE_ALIAS("md-multipath"); 47162306a36Sopenharmony_ciMODULE_ALIAS("md-level--4"); 472