diff options
Diffstat (limited to 'drivers/md/dm-mpath.c')
| -rw-r--r-- | drivers/md/dm-mpath.c | 67 | 
1 files changed, 51 insertions, 16 deletions
| diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index c8faa2b85842..f7810cc869ac 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -458,6 +458,38 @@ do {									\  } while (0)  /* + * Check whether bios must be queued in the device-mapper core rather + * than here in the target. + * + * If MPATHF_QUEUE_IF_NO_PATH and MPATHF_SAVED_QUEUE_IF_NO_PATH hold + * the same value then we are not between multipath_presuspend() + * and multipath_resume() calls and we have no need to check + * for the DMF_NOFLUSH_SUSPENDING flag. + */ +static bool __must_push_back(struct multipath *m, unsigned long flags) +{ +	return ((test_bit(MPATHF_QUEUE_IF_NO_PATH, &flags) != +		 test_bit(MPATHF_SAVED_QUEUE_IF_NO_PATH, &flags)) && +		dm_noflush_suspending(m->ti)); +} + +/* + * Following functions use READ_ONCE to get atomic access to + * all m->flags to avoid taking spinlock + */ +static bool must_push_back_rq(struct multipath *m) +{ +	unsigned long flags = READ_ONCE(m->flags); +	return test_bit(MPATHF_QUEUE_IF_NO_PATH, &flags) || __must_push_back(m, flags); +} + +static bool must_push_back_bio(struct multipath *m) +{ +	unsigned long flags = READ_ONCE(m->flags); +	return __must_push_back(m, flags); +} + +/*   * Map cloned requests (request-based multipath)   */  static int multipath_clone_and_map(struct dm_target *ti, struct request *rq, @@ -478,7 +510,7 @@ static int multipath_clone_and_map(struct dm_target *ti, struct request *rq,  		pgpath = choose_pgpath(m, nr_bytes);  	if (!pgpath) { -		if (test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) +		if (must_push_back_rq(m))  			return DM_MAPIO_DELAY_REQUEUE;  		dm_report_EIO(m);	/* Failed */  		return DM_MAPIO_KILL; @@ -553,7 +585,7 @@ static int __multipath_map_bio(struct multipath *m, struct bio *bio, struct dm_m  	}  	if (!pgpath) { -		if (test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) +		if (must_push_back_bio(m))  			return DM_MAPIO_REQUEUE;  		dm_report_EIO(m);  		return DM_MAPIO_KILL; @@ -651,8 +683,7 @@ static int queue_if_no_path(struct multipath *m, bool queue_if_no_path,  	assign_bit(MPATHF_SAVED_QUEUE_IF_NO_PATH, &m->flags,  		   (save_old_value && test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) ||  		   (!save_old_value && queue_if_no_path)); -	assign_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags, -		   queue_if_no_path || dm_noflush_suspending(m->ti)); +	assign_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags, queue_if_no_path);  	spin_unlock_irqrestore(&m->lock, flags);  	if (!queue_if_no_path) { @@ -1486,7 +1517,7 @@ static int multipath_end_io(struct dm_target *ti, struct request *clone,  			fail_path(pgpath);  		if (atomic_read(&m->nr_valid_paths) == 0 && -		    !test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) { +		    !must_push_back_rq(m)) {  			if (error == BLK_STS_IOERR)  				dm_report_EIO(m);  			/* complete with the original error */ @@ -1521,8 +1552,12 @@ static int multipath_end_io_bio(struct dm_target *ti, struct bio *clone,  	if (atomic_read(&m->nr_valid_paths) == 0 &&  	    !test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) { -		dm_report_EIO(m); -		*error = BLK_STS_IOERR; +		if (must_push_back_bio(m)) { +			r = DM_ENDIO_REQUEUE; +		} else { +			dm_report_EIO(m); +			*error = BLK_STS_IOERR; +		}  		goto done;  	} @@ -1957,13 +1992,6 @@ static int __init dm_multipath_init(void)  {  	int r; -	r = dm_register_target(&multipath_target); -	if (r < 0) { -		DMERR("request-based register failed %d", r); -		r = -EINVAL; -		goto bad_register_target; -	} -  	kmultipathd = alloc_workqueue("kmpathd", WQ_MEM_RECLAIM, 0);  	if (!kmultipathd) {  		DMERR("failed to create workqueue kmpathd"); @@ -1985,13 +2013,20 @@ static int __init dm_multipath_init(void)  		goto bad_alloc_kmpath_handlerd;  	} +	r = dm_register_target(&multipath_target); +	if (r < 0) { +		DMERR("request-based register failed %d", r); +		r = -EINVAL; +		goto bad_register_target; +	} +  	return 0; +bad_register_target: +	destroy_workqueue(kmpath_handlerd);  bad_alloc_kmpath_handlerd:  	destroy_workqueue(kmultipathd);  bad_alloc_kmultipathd: -	dm_unregister_target(&multipath_target); -bad_register_target:  	return r;  } |