xref: /openbmc/linux/drivers/scsi/hosts.c (revision 46eeaa11bdd1bc9e077bdf741d32ca7235d263c6)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  hosts.c Copyright (C) 1992 Drew Eckhardt
41da177e4SLinus Torvalds  *          Copyright (C) 1993, 1994, 1995 Eric Youngdale
51da177e4SLinus Torvalds  *          Copyright (C) 2002-2003 Christoph Hellwig
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  mid to lowlevel SCSI driver interface
81da177e4SLinus Torvalds  *      Initial versions: Drew Eckhardt
91da177e4SLinus Torvalds  *      Subsequent revisions: Eric Youngdale
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *  <drew@colorado.edu>
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  *  Jiffies wrap fixes (host->resetting), 3 Dec 1998 Andrea Arcangeli
141da177e4SLinus Torvalds  *  Added QLOGIC QLA1280 SCSI controller kernel host support.
151da177e4SLinus Torvalds  *     August 4, 1999 Fred Lewis, Intel DuPont
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  *  Updated to reflect the new initialization scheme for the higher
181da177e4SLinus Torvalds  *  level of scsi drivers (sd/sr/st)
191da177e4SLinus Torvalds  *  September 17, 2000 Torben Mathiasen <tmm@image.dk>
201da177e4SLinus Torvalds  *
211da177e4SLinus Torvalds  *  Restructured scsi_host lists and associated functions.
221da177e4SLinus Torvalds  *  September 04, 2002 Mike Anderson (andmike@us.ibm.com)
231da177e4SLinus Torvalds  */
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #include <linux/module.h>
261da177e4SLinus Torvalds #include <linux/blkdev.h>
271da177e4SLinus Torvalds #include <linux/kernel.h>
285a0e3ad6STejun Heo #include <linux/slab.h>
29c5478defSChristoph Hellwig #include <linux/kthread.h>
301da177e4SLinus Torvalds #include <linux/string.h>
311da177e4SLinus Torvalds #include <linux/mm.h>
321da177e4SLinus Torvalds #include <linux/init.h>
331da177e4SLinus Torvalds #include <linux/completion.h>
341da177e4SLinus Torvalds #include <linux/transport_class.h>
35d052d1beSRussell King #include <linux/platform_device.h>
36bc4f2401SAlan Stern #include <linux/pm_runtime.h>
37126a4fe0SLee Duncan #include <linux/idr.h>
381da177e4SLinus Torvalds #include <scsi/scsi_device.h>
391da177e4SLinus Torvalds #include <scsi/scsi_host.h>
401da177e4SLinus Torvalds #include <scsi/scsi_transport.h>
416eb045e0SMing Lei #include <scsi/scsi_cmnd.h>
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #include "scsi_priv.h"
441da177e4SLinus Torvalds #include "scsi_logging.h"
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 
4798663067SBart Van Assche static int shost_eh_deadline = -1;
4898663067SBart Van Assche 
4998663067SBart Van Assche module_param_named(eh_deadline, shost_eh_deadline, int, S_IRUGO|S_IWUSR);
5098663067SBart Van Assche MODULE_PARM_DESC(eh_deadline,
5198663067SBart Van Assche 		 "SCSI EH timeout in seconds (should be between 0 and 2^31-1)");
5298663067SBart Van Assche 
53126a4fe0SLee Duncan static DEFINE_IDA(host_index_ida);
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 
scsi_host_cls_release(struct device * dev)56ee959b00STony Jones static void scsi_host_cls_release(struct device *dev)
571da177e4SLinus Torvalds {
58ee959b00STony Jones 	put_device(&class_to_shost(dev)->shost_gendev);
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds static struct class shost_class = {
621da177e4SLinus Torvalds 	.name		= "scsi_host",
63ee959b00STony Jones 	.dev_release	= scsi_host_cls_release,
640a84486dSBart Van Assche 	.dev_groups	= scsi_shost_groups,
651da177e4SLinus Torvalds };
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds /**
68eb44820cSRob Landley  *	scsi_host_set_state - Take the given host through the host state model.
69d3301874SMike Anderson  *	@shost:	scsi host to change the state of.
70d3301874SMike Anderson  *	@state:	state to change to.
71d3301874SMike Anderson  *
72d3301874SMike Anderson  *	Returns zero if unsuccessful or an error if the requested
73d3301874SMike Anderson  *	transition is illegal.
74d3301874SMike Anderson  **/
scsi_host_set_state(struct Scsi_Host * shost,enum scsi_host_state state)75d3301874SMike Anderson int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state)
76d3301874SMike Anderson {
77d3301874SMike Anderson 	enum scsi_host_state oldstate = shost->shost_state;
78d3301874SMike Anderson 
79d3301874SMike Anderson 	if (state == oldstate)
80d3301874SMike Anderson 		return 0;
81d3301874SMike Anderson 
82d3301874SMike Anderson 	switch (state) {
83d3301874SMike Anderson 	case SHOST_CREATED:
84d3301874SMike Anderson 		/* There are no legal states that come back to
85d3301874SMike Anderson 		 * created.  This is the manually initialised start
86d3301874SMike Anderson 		 * state */
87d3301874SMike Anderson 		goto illegal;
88d3301874SMike Anderson 
89d3301874SMike Anderson 	case SHOST_RUNNING:
90d3301874SMike Anderson 		switch (oldstate) {
91d3301874SMike Anderson 		case SHOST_CREATED:
92d3301874SMike Anderson 		case SHOST_RECOVERY:
93d3301874SMike Anderson 			break;
94d3301874SMike Anderson 		default:
95d3301874SMike Anderson 			goto illegal;
96d3301874SMike Anderson 		}
97d3301874SMike Anderson 		break;
98d3301874SMike Anderson 
99d3301874SMike Anderson 	case SHOST_RECOVERY:
100d3301874SMike Anderson 		switch (oldstate) {
101d3301874SMike Anderson 		case SHOST_RUNNING:
102d3301874SMike Anderson 			break;
103d3301874SMike Anderson 		default:
104d3301874SMike Anderson 			goto illegal;
105d3301874SMike Anderson 		}
106d3301874SMike Anderson 		break;
107d3301874SMike Anderson 
108d3301874SMike Anderson 	case SHOST_CANCEL:
109d3301874SMike Anderson 		switch (oldstate) {
110d3301874SMike Anderson 		case SHOST_CREATED:
111d3301874SMike Anderson 		case SHOST_RUNNING:
112939647eeSJames Bottomley 		case SHOST_CANCEL_RECOVERY:
113d3301874SMike Anderson 			break;
114d3301874SMike Anderson 		default:
115d3301874SMike Anderson 			goto illegal;
116d3301874SMike Anderson 		}
117d3301874SMike Anderson 		break;
118d3301874SMike Anderson 
119d3301874SMike Anderson 	case SHOST_DEL:
120d3301874SMike Anderson 		switch (oldstate) {
121d3301874SMike Anderson 		case SHOST_CANCEL:
122939647eeSJames Bottomley 		case SHOST_DEL_RECOVERY:
123d3301874SMike Anderson 			break;
124d3301874SMike Anderson 		default:
125d3301874SMike Anderson 			goto illegal;
126d3301874SMike Anderson 		}
127d3301874SMike Anderson 		break;
128d3301874SMike Anderson 
129939647eeSJames Bottomley 	case SHOST_CANCEL_RECOVERY:
130939647eeSJames Bottomley 		switch (oldstate) {
131939647eeSJames Bottomley 		case SHOST_CANCEL:
132939647eeSJames Bottomley 		case SHOST_RECOVERY:
133939647eeSJames Bottomley 			break;
134939647eeSJames Bottomley 		default:
135939647eeSJames Bottomley 			goto illegal;
136939647eeSJames Bottomley 		}
137939647eeSJames Bottomley 		break;
138939647eeSJames Bottomley 
139939647eeSJames Bottomley 	case SHOST_DEL_RECOVERY:
140939647eeSJames Bottomley 		switch (oldstate) {
141939647eeSJames Bottomley 		case SHOST_CANCEL_RECOVERY:
142939647eeSJames Bottomley 			break;
143939647eeSJames Bottomley 		default:
144939647eeSJames Bottomley 			goto illegal;
145939647eeSJames Bottomley 		}
146939647eeSJames Bottomley 		break;
147d3301874SMike Anderson 	}
148d3301874SMike Anderson 	shost->shost_state = state;
149d3301874SMike Anderson 	return 0;
150d3301874SMike Anderson 
151d3301874SMike Anderson  illegal:
152d3301874SMike Anderson 	SCSI_LOG_ERROR_RECOVERY(1,
1539ccfc756SJames Bottomley 				shost_printk(KERN_ERR, shost,
154d3301874SMike Anderson 					     "Illegal host state transition"
155d3301874SMike Anderson 					     "%s->%s\n",
156d3301874SMike Anderson 					     scsi_host_state_name(oldstate),
157d3301874SMike Anderson 					     scsi_host_state_name(state)));
158d3301874SMike Anderson 	return -EINVAL;
159d3301874SMike Anderson }
160d3301874SMike Anderson 
161d3301874SMike Anderson /**
1621da177e4SLinus Torvalds  * scsi_remove_host - remove a scsi host
1631da177e4SLinus Torvalds  * @shost:	a pointer to a scsi host to remove
1641da177e4SLinus Torvalds  **/
scsi_remove_host(struct Scsi_Host * shost)1651da177e4SLinus Torvalds void scsi_remove_host(struct Scsi_Host *shost)
1661da177e4SLinus Torvalds {
167939647eeSJames Bottomley 	unsigned long flags;
168bc4f2401SAlan Stern 
1690b950672SArjan van de Ven 	mutex_lock(&shost->scan_mutex);
170939647eeSJames Bottomley 	spin_lock_irqsave(shost->host_lock, flags);
171939647eeSJames Bottomley 	if (scsi_host_set_state(shost, SHOST_CANCEL))
172939647eeSJames Bottomley 		if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) {
173939647eeSJames Bottomley 			spin_unlock_irqrestore(shost->host_lock, flags);
1740b950672SArjan van de Ven 			mutex_unlock(&shost->scan_mutex);
175939647eeSJames Bottomley 			return;
176939647eeSJames Bottomley 		}
177939647eeSJames Bottomley 	spin_unlock_irqrestore(shost->host_lock, flags);
178bc4f2401SAlan Stern 
179bc4f2401SAlan Stern 	scsi_autopm_get_host(shost);
180e494f6a7SHannes Reinecke 	flush_workqueue(shost->tmf_work_q);
1811da177e4SLinus Torvalds 	scsi_forget_host(shost);
1824e46bf89SAlexey Kuznetsov 	mutex_unlock(&shost->scan_mutex);
1831da177e4SLinus Torvalds 	scsi_proc_host_rm(shost);
184fc663711SBart Van Assche 	scsi_proc_hostdir_rm(shost->hostt);
1851da177e4SLinus Torvalds 
1868fe4ce58SBart Van Assche 	/*
1878fe4ce58SBart Van Assche 	 * New SCSI devices cannot be attached anymore because of the SCSI host
1888fe4ce58SBart Van Assche 	 * state so drop the tag set refcnt. Wait until the tag set refcnt drops
1898fe4ce58SBart Van Assche 	 * to zero because .exit_cmd_priv implementations may need the host
1908fe4ce58SBart Van Assche 	 * pointer.
1918fe4ce58SBart Van Assche 	 */
1928fe4ce58SBart Van Assche 	kref_put(&shost->tagset_refcnt, scsi_mq_free_tags);
1938fe4ce58SBart Van Assche 	wait_for_completion(&shost->tagset_freed);
1948fe4ce58SBart Van Assche 
195939647eeSJames Bottomley 	spin_lock_irqsave(shost->host_lock, flags);
196939647eeSJames Bottomley 	if (scsi_host_set_state(shost, SHOST_DEL))
197939647eeSJames Bottomley 		BUG_ON(scsi_host_set_state(shost, SHOST_DEL_RECOVERY));
198939647eeSJames Bottomley 	spin_unlock_irqrestore(shost->host_lock, flags);
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	transport_unregister_device(&shost->shost_gendev);
201ee959b00STony Jones 	device_unregister(&shost->shost_dev);
2021da177e4SLinus Torvalds 	device_del(&shost->shost_gendev);
2031da177e4SLinus Torvalds }
2041da177e4SLinus Torvalds EXPORT_SYMBOL(scsi_remove_host);
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds /**
207d139b9bdSJames Bottomley  * scsi_add_host_with_dma - add a scsi host with dma device
2081da177e4SLinus Torvalds  * @shost:	scsi host pointer to add
2091da177e4SLinus Torvalds  * @dev:	a struct device of type scsi class
210d139b9bdSJames Bottomley  * @dma_dev:	dma device for the host
211d139b9bdSJames Bottomley  *
212d139b9bdSJames Bottomley  * Note: You rarely need to worry about this unless you're in a
213d139b9bdSJames Bottomley  * virtualised host environments, so use the simpler scsi_add_host()
214d139b9bdSJames Bottomley  * function instead.
2151da177e4SLinus Torvalds  *
2161da177e4SLinus Torvalds  * Return value:
2171da177e4SLinus Torvalds  * 	0 on success / != 0 for error
2181da177e4SLinus Torvalds  **/
scsi_add_host_with_dma(struct Scsi_Host * shost,struct device * dev,struct device * dma_dev)219d139b9bdSJames Bottomley int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
220d139b9bdSJames Bottomley 			   struct device *dma_dev)
2211da177e4SLinus Torvalds {
222e0d3f2c6SBart Van Assche 	const struct scsi_host_template *sht = shost->hostt;
2231da177e4SLinus Torvalds 	int error = -EINVAL;
2241da177e4SLinus Torvalds 
22591921e01SHannes Reinecke 	shost_printk(KERN_INFO, shost, "%s\n",
2261da177e4SLinus Torvalds 			sht->info ? sht->info(shost) : sht->name);
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	if (!shost->can_queue) {
22991921e01SHannes Reinecke 		shost_printk(KERN_ERR, shost,
23091921e01SHannes Reinecke 			     "can_queue = 0 no longer supported\n");
231542bd137SJames Bottomley 		goto fail;
2321da177e4SLinus Torvalds 	}
2331da177e4SLinus Torvalds 
23450b6cb35SDexuan Cui 	/* Use min_t(int, ...) in case shost->can_queue exceeds SHRT_MAX */
23550b6cb35SDexuan Cui 	shost->cmd_per_lun = min_t(int, shost->cmd_per_lun,
236ea2f0f77SJohn Garry 				   shost->can_queue);
237ea2f0f77SJohn Garry 
2380a6ac4eeSChristoph Hellwig 	error = scsi_init_sense_cache(shost);
2390a6ac4eeSChristoph Hellwig 	if (error)
2400a6ac4eeSChristoph Hellwig 		goto fail;
2410a6ac4eeSChristoph Hellwig 
2421da177e4SLinus Torvalds 	if (!shost->shost_gendev.parent)
2431da177e4SLinus Torvalds 		shost->shost_gendev.parent = dev ? dev : &platform_bus;
2443c8d9a95SJames Bottomley 	if (!dma_dev)
2453c8d9a95SJames Bottomley 		dma_dev = shost->shost_gendev.parent;
2463c8d9a95SJames Bottomley 
247d139b9bdSJames Bottomley 	shost->dma_dev = dma_dev;
2481da177e4SLinus Torvalds 
249bb7d1283SJohn Garry 	if (dma_dev->dma_mask) {
250bb7d1283SJohn Garry 		shost->max_sectors = min_t(unsigned int, shost->max_sectors,
251bb7d1283SJohn Garry 				dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT);
252bb7d1283SJohn Garry 	}
253bb7d1283SJohn Garry 
254973dac8aSJohn Garry 	error = scsi_mq_setup_tags(shost);
255973dac8aSJohn Garry 	if (error)
256973dac8aSJohn Garry 		goto fail;
257973dac8aSJohn Garry 
2588fe4ce58SBart Van Assche 	kref_init(&shost->tagset_refcnt);
2598fe4ce58SBart Van Assche 	init_completion(&shost->tagset_freed);
2608fe4ce58SBart Van Assche 
2615c6fab9dSMika Westerberg 	/*
2625c6fab9dSMika Westerberg 	 * Increase usage count temporarily here so that calling
2635c6fab9dSMika Westerberg 	 * scsi_autopm_put_host() will trigger runtime idle if there is
2645c6fab9dSMika Westerberg 	 * nothing else preventing suspending the device.
2655c6fab9dSMika Westerberg 	 */
2665c6fab9dSMika Westerberg 	pm_runtime_get_noresume(&shost->shost_gendev);
267bc4f2401SAlan Stern 	pm_runtime_set_active(&shost->shost_gendev);
268bc4f2401SAlan Stern 	pm_runtime_enable(&shost->shost_gendev);
269bc4f2401SAlan Stern 	device_enable_async_suspend(&shost->shost_gendev);
270bc4f2401SAlan Stern 
2710d5644b7SHeiner Kallweit 	error = device_add(&shost->shost_gendev);
2720d5644b7SHeiner Kallweit 	if (error)
273e9c787e6SChristoph Hellwig 		goto out_disable_runtime_pm;
2740d5644b7SHeiner Kallweit 
275d3301874SMike Anderson 	scsi_host_set_state(shost, SHOST_RUNNING);
2761da177e4SLinus Torvalds 	get_device(shost->shost_gendev.parent);
2771da177e4SLinus Torvalds 
2784cb077d9SRafael J. Wysocki 	device_enable_async_suspend(&shost->shost_dev);
2794cb077d9SRafael J. Wysocki 
28011714026SMing Lei 	get_device(&shost->shost_gendev);
281ee959b00STony Jones 	error = device_add(&shost->shost_dev);
2821da177e4SLinus Torvalds 	if (error)
2831da177e4SLinus Torvalds 		goto out_del_gendev;
2841da177e4SLinus Torvalds 
28577cca462SJames Smart 	if (shost->transportt->host_size) {
28677cca462SJames Smart 		shost->shost_data = kzalloc(shost->transportt->host_size,
28777cca462SJames Smart 					 GFP_KERNEL);
28877cca462SJames Smart 		if (shost->shost_data == NULL) {
28977cca462SJames Smart 			error = -ENOMEM;
290ee959b00STony Jones 			goto out_del_dev;
29177cca462SJames Smart 		}
29277cca462SJames Smart 	}
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds 	if (shost->transportt->create_work_queue) {
295aab0de24SKay Sievers 		snprintf(shost->work_q_name, sizeof(shost->work_q_name),
296aab0de24SKay Sievers 			 "scsi_wq_%d", shost->host_no);
29762921300SBob Liu 		shost->work_q = alloc_workqueue("%s",
29862921300SBob Liu 			WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND,
29962921300SBob Liu 			1, shost->work_q_name);
30062921300SBob Liu 
30177cca462SJames Smart 		if (!shost->work_q) {
30277cca462SJames Smart 			error = -EINVAL;
3033719f4ffSMing Lei 			goto out_del_dev;
3041da177e4SLinus Torvalds 		}
30577cca462SJames Smart 	}
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	error = scsi_sysfs_add_host(shost);
3081da177e4SLinus Torvalds 	if (error)
3093719f4ffSMing Lei 		goto out_del_dev;
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	scsi_proc_host_add(shost);
3125c6fab9dSMika Westerberg 	scsi_autopm_put_host(shost);
3131da177e4SLinus Torvalds 	return error;
3141da177e4SLinus Torvalds 
3153719f4ffSMing Lei 	/*
3162b36209cSBart Van Assche 	 * Any host allocation in this function will be freed in
3172b36209cSBart Van Assche 	 * scsi_host_dev_release().
3183719f4ffSMing Lei 	 */
319ee959b00STony Jones  out_del_dev:
320ee959b00STony Jones 	device_del(&shost->shost_dev);
3211da177e4SLinus Torvalds  out_del_gendev:
32211714026SMing Lei 	/*
32311714026SMing Lei 	 * Host state is SHOST_RUNNING so we have to explicitly release
32411714026SMing Lei 	 * ->shost_dev.
32511714026SMing Lei 	 */
32611714026SMing Lei 	put_device(&shost->shost_dev);
3271da177e4SLinus Torvalds 	device_del(&shost->shost_gendev);
328e9c787e6SChristoph Hellwig  out_disable_runtime_pm:
3290d5644b7SHeiner Kallweit 	device_disable_async_suspend(&shost->shost_gendev);
3300d5644b7SHeiner Kallweit 	pm_runtime_disable(&shost->shost_gendev);
3310d5644b7SHeiner Kallweit 	pm_runtime_set_suspended(&shost->shost_gendev);
3320d5644b7SHeiner Kallweit 	pm_runtime_put_noidle(&shost->shost_gendev);
3338fe4ce58SBart Van Assche 	kref_put(&shost->tagset_refcnt, scsi_mq_free_tags);
334542bd137SJames Bottomley  fail:
3351da177e4SLinus Torvalds 	return error;
3361da177e4SLinus Torvalds }
337d139b9bdSJames Bottomley EXPORT_SYMBOL(scsi_add_host_with_dma);
3381da177e4SLinus Torvalds 
scsi_host_dev_release(struct device * dev)3391da177e4SLinus Torvalds static void scsi_host_dev_release(struct device *dev)
3401da177e4SLinus Torvalds {
3411da177e4SLinus Torvalds 	struct Scsi_Host *shost = dev_to_shost(dev);
3421da177e4SLinus Torvalds 	struct device *parent = dev->parent;
3431da177e4SLinus Torvalds 
3442dde5c8dSXiang Chen 	/* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */
3453bd6f43fSBart Van Assche 	rcu_barrier();
3463bd6f43fSBart Van Assche 
347e494f6a7SHannes Reinecke 	if (shost->tmf_work_q)
348e494f6a7SHannes Reinecke 		destroy_workqueue(shost->tmf_work_q);
349c5478defSChristoph Hellwig 	if (shost->ehandler)
350c5478defSChristoph Hellwig 		kthread_stop(shost->ehandler);
3511da177e4SLinus Torvalds 	if (shost->work_q)
3521da177e4SLinus Torvalds 		destroy_workqueue(shost->work_q);
3531da177e4SLinus Torvalds 
354b49493f9SBart Van Assche 	if (shost->shost_state == SHOST_CREATED) {
355b49493f9SBart Van Assche 		/*
356*d4c34782SGuilherme G. Piccoli 		 * Free the shost_dev device name and remove the proc host dir
357*d4c34782SGuilherme G. Piccoli 		 * here if scsi_host_{alloc,put}() have been called but neither
358f4407e60SBart Van Assche 		 * scsi_host_add() nor scsi_remove_host() has been called.
359b49493f9SBart Van Assche 		 * This avoids that the memory allocated for the shost_dev
360*d4c34782SGuilherme G. Piccoli 		 * name as well as the proc dir structure are leaked.
361b49493f9SBart Van Assche 		 */
362*d4c34782SGuilherme G. Piccoli 		scsi_proc_hostdir_rm(shost->hostt);
363b49493f9SBart Van Assche 		kfree(dev_name(&shost->shost_dev));
364b49493f9SBart Van Assche 	}
365b49493f9SBart Van Assche 
3661da177e4SLinus Torvalds 	kfree(shost->shost_data);
3671da177e4SLinus Torvalds 
3683fd3a52cSkeliu 	ida_free(&host_index_ida, shost->host_no);
369126a4fe0SLee Duncan 
3701e0d4e62SMing Lei 	if (shost->shost_state != SHOST_CREATED)
3711da177e4SLinus Torvalds 		put_device(parent);
3721da177e4SLinus Torvalds 	kfree(shost);
3731da177e4SLinus Torvalds }
3741da177e4SLinus Torvalds 
375453cd0f3SAdrian Bunk static struct device_type scsi_host_type = {
376b0ed4336SHannes Reinecke 	.name =		"scsi_host",
377b0ed4336SHannes Reinecke 	.release =	scsi_host_dev_release,
378b0ed4336SHannes Reinecke };
379b0ed4336SHannes Reinecke 
3801da177e4SLinus Torvalds /**
3811da177e4SLinus Torvalds  * scsi_host_alloc - register a scsi host adapter instance.
3821da177e4SLinus Torvalds  * @sht:	pointer to scsi host template
3831da177e4SLinus Torvalds  * @privsize:	extra bytes to allocate for driver
3841da177e4SLinus Torvalds  *
3851da177e4SLinus Torvalds  * Note:
3861da177e4SLinus Torvalds  * 	Allocate a new Scsi_Host and perform basic initialization.
3871da177e4SLinus Torvalds  * 	The host is not published to the scsi midlayer until scsi_add_host
3881da177e4SLinus Torvalds  * 	is called.
3891da177e4SLinus Torvalds  *
3901da177e4SLinus Torvalds  * Return value:
3911da177e4SLinus Torvalds  * 	Pointer to a new Scsi_Host
3921da177e4SLinus Torvalds  **/
scsi_host_alloc(const struct scsi_host_template * sht,int privsize)393e0d3f2c6SBart Van Assche struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int privsize)
3941da177e4SLinus Torvalds {
3951da177e4SLinus Torvalds 	struct Scsi_Host *shost;
3960a84486dSBart Van Assche 	int index;
3971da177e4SLinus Torvalds 
398aaff5ebaSChristoph Hellwig 	shost = kzalloc(sizeof(struct Scsi_Host) + privsize, GFP_KERNEL);
3991da177e4SLinus Torvalds 	if (!shost)
4001da177e4SLinus Torvalds 		return NULL;
4011da177e4SLinus Torvalds 
4024f777ed2SChristoph Hellwig 	shost->host_lock = &shost->default_lock;
4034f777ed2SChristoph Hellwig 	spin_lock_init(shost->host_lock);
404d3301874SMike Anderson 	shost->shost_state = SHOST_CREATED;
4051da177e4SLinus Torvalds 	INIT_LIST_HEAD(&shost->__devices);
4061da177e4SLinus Torvalds 	INIT_LIST_HEAD(&shost->__targets);
4075ae17501SEwan D. Milne 	INIT_LIST_HEAD(&shost->eh_abort_list);
4081da177e4SLinus Torvalds 	INIT_LIST_HEAD(&shost->eh_cmd_q);
4091da177e4SLinus Torvalds 	INIT_LIST_HEAD(&shost->starved_list);
4101da177e4SLinus Torvalds 	init_waitqueue_head(&shost->host_wait);
4110b950672SArjan van de Ven 	mutex_init(&shost->scan_mutex);
4121da177e4SLinus Torvalds 
4133fd3a52cSkeliu 	index = ida_alloc(&host_index_ida, GFP_KERNEL);
41466a834d0SMing Lei 	if (index < 0) {
41566a834d0SMing Lei 		kfree(shost);
41666a834d0SMing Lei 		return NULL;
41766a834d0SMing Lei 	}
418126a4fe0SLee Duncan 	shost->host_no = index;
419126a4fe0SLee Duncan 
4201da177e4SLinus Torvalds 	shost->dma_channel = 0xff;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	/* These three are default values which can be overridden */
4231da177e4SLinus Torvalds 	shost->max_channel = 0;
4241da177e4SLinus Torvalds 	shost->max_id = 8;
4251da177e4SLinus Torvalds 	shost->max_lun = 8;
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	/* Give each shost a default transportt */
4281da177e4SLinus Torvalds 	shost->transportt = &blank_transport_template;
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	/*
4311da177e4SLinus Torvalds 	 * All drivers right now should be able to handle 12 byte
4321da177e4SLinus Torvalds 	 * commands.  Every so often there are requests for 16 byte
4331da177e4SLinus Torvalds 	 * commands, but individual low-level drivers need to certify that
4341da177e4SLinus Torvalds 	 * they actually do something sensible with such commands.
4351da177e4SLinus Torvalds 	 */
4361da177e4SLinus Torvalds 	shost->max_cmd_len = 12;
4371da177e4SLinus Torvalds 	shost->hostt = sht;
4381da177e4SLinus Torvalds 	shost->this_id = sht->this_id;
4391da177e4SLinus Torvalds 	shost->can_queue = sht->can_queue;
4401da177e4SLinus Torvalds 	shost->sg_tablesize = sht->sg_tablesize;
44113f05c8dSMartin K. Petersen 	shost->sg_prot_tablesize = sht->sg_prot_tablesize;
4421da177e4SLinus Torvalds 	shost->cmd_per_lun = sht->cmd_per_lun;
44354b2b50cSMartin K. Petersen 	shost->no_write_same = sht->no_write_same;
444bdb01301SHannes Reinecke 	shost->host_tagset = sht->host_tagset;
445b125bb99SBart Van Assche 	shost->queuecommand_may_block = sht->queuecommand_may_block;
4461da177e4SLinus Torvalds 
447ad469a57SHannes Reinecke 	if (shost_eh_deadline == -1 || !sht->eh_host_reset_handler)
448bb3b621aSRen Mingxin 		shost->eh_deadline = -1;
449bb3b621aSRen Mingxin 	else if ((ulong) shost_eh_deadline * HZ > INT_MAX) {
450bb3b621aSRen Mingxin 		shost_printk(KERN_WARNING, shost,
451bb3b621aSRen Mingxin 			     "eh_deadline %u too large, setting to %u\n",
452bb3b621aSRen Mingxin 			     shost_eh_deadline, INT_MAX / HZ);
453bb3b621aSRen Mingxin 		shost->eh_deadline = INT_MAX;
454bb3b621aSRen Mingxin 	} else
455bb3b621aSRen Mingxin 		shost->eh_deadline = shost_eh_deadline * HZ;
456bb3b621aSRen Mingxin 
4577a39ac3fSJames Bottomley 	if (sht->supported_mode == MODE_UNKNOWN)
4587a39ac3fSJames Bottomley 		/* means we didn't set it ... default to INITIATOR */
4597a39ac3fSJames Bottomley 		shost->active_mode = MODE_INITIATOR;
4607a39ac3fSJames Bottomley 	else
4617a39ac3fSJames Bottomley 		shost->active_mode = sht->supported_mode;
4627a39ac3fSJames Bottomley 
4631da177e4SLinus Torvalds 	if (sht->max_host_blocked)
4641da177e4SLinus Torvalds 		shost->max_host_blocked = sht->max_host_blocked;
4651da177e4SLinus Torvalds 	else
4661da177e4SLinus Torvalds 		shost->max_host_blocked = SCSI_DEFAULT_HOST_BLOCKED;
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 	/*
4691da177e4SLinus Torvalds 	 * If the driver imposes no hard sector transfer limit, start at
4701da177e4SLinus Torvalds 	 * machine infinity initially.
4711da177e4SLinus Torvalds 	 */
4721da177e4SLinus Torvalds 	if (sht->max_sectors)
4731da177e4SLinus Torvalds 		shost->max_sectors = sht->max_sectors;
4741da177e4SLinus Torvalds 	else
4751da177e4SLinus Torvalds 		shost->max_sectors = SCSI_DEFAULT_MAX_SECTORS;
4761da177e4SLinus Torvalds 
47750c2e910SChristoph Hellwig 	if (sht->max_segment_size)
47850c2e910SChristoph Hellwig 		shost->max_segment_size = sht->max_segment_size;
47950c2e910SChristoph Hellwig 	else
48050c2e910SChristoph Hellwig 		shost->max_segment_size = BLK_MAX_SEGMENT_SIZE;
48150c2e910SChristoph Hellwig 
4821da177e4SLinus Torvalds 	/*
4831da177e4SLinus Torvalds 	 * assume a 4GB boundary, if not set
4841da177e4SLinus Torvalds 	 */
4851da177e4SLinus Torvalds 	if (sht->dma_boundary)
4861da177e4SLinus Torvalds 		shost->dma_boundary = sht->dma_boundary;
4871da177e4SLinus Torvalds 	else
4881da177e4SLinus Torvalds 		shost->dma_boundary = 0xffffffff;
4891da177e4SLinus Torvalds 
4907ad388d8SChristoph Hellwig 	if (sht->virt_boundary_mask)
4917ad388d8SChristoph Hellwig 		shost->virt_boundary_mask = sht->virt_boundary_mask;
4927ad388d8SChristoph Hellwig 
4931da177e4SLinus Torvalds 	device_initialize(&shost->shost_gendev);
49471610f55SKay Sievers 	dev_set_name(&shost->shost_gendev, "host%d", shost->host_no);
495b0ed4336SHannes Reinecke 	shost->shost_gendev.bus = &scsi_bus_type;
496b0ed4336SHannes Reinecke 	shost->shost_gendev.type = &scsi_host_type;
497a19a93e4SBart Van Assche 	scsi_enable_async_suspend(&shost->shost_gendev);
4981da177e4SLinus Torvalds 
499ee959b00STony Jones 	device_initialize(&shost->shost_dev);
500ee959b00STony Jones 	shost->shost_dev.parent = &shost->shost_gendev;
501ee959b00STony Jones 	shost->shost_dev.class = &shost_class;
50271610f55SKay Sievers 	dev_set_name(&shost->shost_dev, "host%d", shost->host_no);
5030a84486dSBart Van Assche 	shost->shost_dev.groups = sht->shost_groups;
5041da177e4SLinus Torvalds 
505c5478defSChristoph Hellwig 	shost->ehandler = kthread_run(scsi_error_handler, shost,
506c5478defSChristoph Hellwig 			"scsi_eh_%d", shost->host_no);
507c5478defSChristoph Hellwig 	if (IS_ERR(shost->ehandler)) {
50891921e01SHannes Reinecke 		shost_printk(KERN_WARNING, shost,
50991921e01SHannes Reinecke 			"error handler thread failed to spawn, error = %ld\n",
51091921e01SHannes Reinecke 			PTR_ERR(shost->ehandler));
51193aa71adSTyrel Datwyler 		shost->ehandler = NULL;
51266a834d0SMing Lei 		goto fail;
513c5478defSChristoph Hellwig 	}
5141da177e4SLinus Torvalds 
515e494f6a7SHannes Reinecke 	shost->tmf_work_q = alloc_workqueue("scsi_tmf_%d",
51662921300SBob Liu 					WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS,
517e494f6a7SHannes Reinecke 					   1, shost->host_no);
518e494f6a7SHannes Reinecke 	if (!shost->tmf_work_q) {
519a222b1e2SHannes Reinecke 		shost_printk(KERN_WARNING, shost,
520a222b1e2SHannes Reinecke 			     "failed to create tmf workq\n");
52166a834d0SMing Lei 		goto fail;
522e494f6a7SHannes Reinecke 	}
523ecca3f9bSBart Van Assche 	if (scsi_proc_hostdir_add(shost->hostt) < 0)
524ecca3f9bSBart Van Assche 		goto fail;
5251da177e4SLinus Torvalds 	return shost;
52666a834d0SMing Lei  fail:
52766a834d0SMing Lei 	/*
52866a834d0SMing Lei 	 * Host state is still SHOST_CREATED and that is enough to release
52966a834d0SMing Lei 	 * ->shost_gendev. scsi_host_dev_release() will free
53066a834d0SMing Lei 	 * dev_name(&shost->shost_dev).
53166a834d0SMing Lei 	 */
53266a834d0SMing Lei 	put_device(&shost->shost_gendev);
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 	return NULL;
5351da177e4SLinus Torvalds }
5361da177e4SLinus Torvalds EXPORT_SYMBOL(scsi_host_alloc);
5371da177e4SLinus Torvalds 
__scsi_host_match(struct device * dev,const void * data)5389f3b795aSMichał Mirosław static int __scsi_host_match(struct device *dev, const void *data)
5399c770108SDave Young {
5409c770108SDave Young 	struct Scsi_Host *p;
54162ec2092STony Battersby 	const unsigned int *hostnum = data;
5429c770108SDave Young 
543ee959b00STony Jones 	p = class_to_shost(dev);
5449c770108SDave Young 	return p->host_no == *hostnum;
5459c770108SDave Young }
5469c770108SDave Young 
5471da177e4SLinus Torvalds /**
5481da177e4SLinus Torvalds  * scsi_host_lookup - get a reference to a Scsi_Host by host no
5491da177e4SLinus Torvalds  * @hostnum:	host number to locate
5501da177e4SLinus Torvalds  *
5511da177e4SLinus Torvalds  * Return value:
5521da177e4SLinus Torvalds  *	A pointer to located Scsi_Host or NULL.
5533ed78972SMike Christie  *
5543ed78972SMike Christie  *	The caller must do a scsi_host_put() to drop the reference
5553ed78972SMike Christie  *	that scsi_host_get() took. The put_device() below dropped
5563ed78972SMike Christie  *	the reference from class_find_device().
5571da177e4SLinus Torvalds  **/
scsi_host_lookup(unsigned int hostnum)55862ec2092STony Battersby struct Scsi_Host *scsi_host_lookup(unsigned int hostnum)
5591da177e4SLinus Torvalds {
560ee959b00STony Jones 	struct device *cdev;
561315cb0adSJames Smart 	struct Scsi_Host *shost = NULL;
5621da177e4SLinus Torvalds 
563695794aeSGreg Kroah-Hartman 	cdev = class_find_device(&shost_class, NULL, &hostnum,
564695794aeSGreg Kroah-Hartman 				 __scsi_host_match);
5653ed78972SMike Christie 	if (cdev) {
5669c770108SDave Young 		shost = scsi_host_get(class_to_shost(cdev));
5673ed78972SMike Christie 		put_device(cdev);
5683ed78972SMike Christie 	}
5691da177e4SLinus Torvalds 	return shost;
5701da177e4SLinus Torvalds }
5711da177e4SLinus Torvalds EXPORT_SYMBOL(scsi_host_lookup);
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds /**
5741da177e4SLinus Torvalds  * scsi_host_get - inc a Scsi_Host ref count
5751da177e4SLinus Torvalds  * @shost:	Pointer to Scsi_Host to inc.
5761da177e4SLinus Torvalds  **/
scsi_host_get(struct Scsi_Host * shost)5771da177e4SLinus Torvalds struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost)
5781da177e4SLinus Torvalds {
579d3301874SMike Anderson 	if ((shost->shost_state == SHOST_DEL) ||
5801da177e4SLinus Torvalds 		!get_device(&shost->shost_gendev))
5811da177e4SLinus Torvalds 		return NULL;
5821da177e4SLinus Torvalds 	return shost;
5831da177e4SLinus Torvalds }
5841da177e4SLinus Torvalds EXPORT_SYMBOL(scsi_host_get);
5851da177e4SLinus Torvalds 
scsi_host_check_in_flight(struct request * rq,void * data)5862dd6532eSJohn Garry static bool scsi_host_check_in_flight(struct request *rq, void *data)
5876eb045e0SMing Lei {
5886eb045e0SMing Lei 	int *count = data;
5896eb045e0SMing Lei 	struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq);
5906eb045e0SMing Lei 
5916eb045e0SMing Lei 	if (test_bit(SCMD_STATE_INFLIGHT, &cmd->state))
5926eb045e0SMing Lei 		(*count)++;
5936eb045e0SMing Lei 
5946eb045e0SMing Lei 	return true;
5956eb045e0SMing Lei }
5966eb045e0SMing Lei 
5971da177e4SLinus Torvalds /**
598c84b023aSMing Lei  * scsi_host_busy - Return the host busy counter
599c84b023aSMing Lei  * @shost:	Pointer to Scsi_Host to inc.
600c84b023aSMing Lei  **/
scsi_host_busy(struct Scsi_Host * shost)601c84b023aSMing Lei int scsi_host_busy(struct Scsi_Host *shost)
602c84b023aSMing Lei {
6036eb045e0SMing Lei 	int cnt = 0;
6046eb045e0SMing Lei 
6056eb045e0SMing Lei 	blk_mq_tagset_busy_iter(&shost->tag_set,
6066eb045e0SMing Lei 				scsi_host_check_in_flight, &cnt);
6076eb045e0SMing Lei 	return cnt;
608c84b023aSMing Lei }
609c84b023aSMing Lei EXPORT_SYMBOL(scsi_host_busy);
610c84b023aSMing Lei 
611c84b023aSMing Lei /**
6121da177e4SLinus Torvalds  * scsi_host_put - dec a Scsi_Host ref count
6131da177e4SLinus Torvalds  * @shost:	Pointer to Scsi_Host to dec.
6141da177e4SLinus Torvalds  **/
scsi_host_put(struct Scsi_Host * shost)6151da177e4SLinus Torvalds void scsi_host_put(struct Scsi_Host *shost)
6161da177e4SLinus Torvalds {
6171da177e4SLinus Torvalds 	put_device(&shost->shost_gendev);
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds EXPORT_SYMBOL(scsi_host_put);
6201da177e4SLinus Torvalds 
scsi_init_hosts(void)6211da177e4SLinus Torvalds int scsi_init_hosts(void)
6221da177e4SLinus Torvalds {
6231da177e4SLinus Torvalds 	return class_register(&shost_class);
6241da177e4SLinus Torvalds }
6251da177e4SLinus Torvalds 
scsi_exit_hosts(void)6261da177e4SLinus Torvalds void scsi_exit_hosts(void)
6271da177e4SLinus Torvalds {
6281da177e4SLinus Torvalds 	class_unregister(&shost_class);
629126a4fe0SLee Duncan 	ida_destroy(&host_index_ida);
6301da177e4SLinus Torvalds }
6311da177e4SLinus Torvalds 
scsi_is_host_device(const struct device * dev)6321da177e4SLinus Torvalds int scsi_is_host_device(const struct device *dev)
6331da177e4SLinus Torvalds {
634b0ed4336SHannes Reinecke 	return dev->type == &scsi_host_type;
6351da177e4SLinus Torvalds }
6361da177e4SLinus Torvalds EXPORT_SYMBOL(scsi_is_host_device);
6371da177e4SLinus Torvalds 
6381da177e4SLinus Torvalds /**
6391da177e4SLinus Torvalds  * scsi_queue_work - Queue work to the Scsi_Host workqueue.
6401da177e4SLinus Torvalds  * @shost:	Pointer to Scsi_Host.
6411da177e4SLinus Torvalds  * @work:	Work to queue for execution.
6421da177e4SLinus Torvalds  *
6431da177e4SLinus Torvalds  * Return value:
644dd7e2f22SMichael Reed  * 	1 - work queued for execution
645dd7e2f22SMichael Reed  *	0 - work is already queued
646dd7e2f22SMichael Reed  *	-EINVAL - work queue doesn't exist
6471da177e4SLinus Torvalds  **/
scsi_queue_work(struct Scsi_Host * shost,struct work_struct * work)6481da177e4SLinus Torvalds int scsi_queue_work(struct Scsi_Host *shost, struct work_struct *work)
6491da177e4SLinus Torvalds {
6501da177e4SLinus Torvalds 	if (unlikely(!shost->work_q)) {
65191921e01SHannes Reinecke 		shost_printk(KERN_ERR, shost,
6521da177e4SLinus Torvalds 			"ERROR: Scsi host '%s' attempted to queue scsi-work, "
6531da177e4SLinus Torvalds 			"when no workqueue created.\n", shost->hostt->name);
6541da177e4SLinus Torvalds 		dump_stack();
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds 		return -EINVAL;
6571da177e4SLinus Torvalds 	}
6581da177e4SLinus Torvalds 
6591da177e4SLinus Torvalds 	return queue_work(shost->work_q, work);
6601da177e4SLinus Torvalds }
6611da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(scsi_queue_work);
6621da177e4SLinus Torvalds 
6631da177e4SLinus Torvalds /**
6641da177e4SLinus Torvalds  * scsi_flush_work - Flush a Scsi_Host's workqueue.
6651da177e4SLinus Torvalds  * @shost:	Pointer to Scsi_Host.
6661da177e4SLinus Torvalds  **/
scsi_flush_work(struct Scsi_Host * shost)6671da177e4SLinus Torvalds void scsi_flush_work(struct Scsi_Host *shost)
6681da177e4SLinus Torvalds {
6691da177e4SLinus Torvalds 	if (!shost->work_q) {
67091921e01SHannes Reinecke 		shost_printk(KERN_ERR, shost,
6711da177e4SLinus Torvalds 			"ERROR: Scsi host '%s' attempted to flush scsi-work, "
6721da177e4SLinus Torvalds 			"when no workqueue created.\n", shost->hostt->name);
6731da177e4SLinus Torvalds 		dump_stack();
6741da177e4SLinus Torvalds 		return;
6751da177e4SLinus Torvalds 	}
6761da177e4SLinus Torvalds 
6771da177e4SLinus Torvalds 	flush_workqueue(shost->work_q);
6781da177e4SLinus Torvalds }
6791da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(scsi_flush_work);
680466552b9SHannes Reinecke 
complete_all_cmds_iter(struct request * rq,void * data)6812dd6532eSJohn Garry static bool complete_all_cmds_iter(struct request *rq, void *data)
682466552b9SHannes Reinecke {
683466552b9SHannes Reinecke 	struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
68462af0ee9SBart Van Assche 	enum scsi_host_status status = *(enum scsi_host_status *)data;
685466552b9SHannes Reinecke 
686466552b9SHannes Reinecke 	scsi_dma_unmap(scmd);
68762af0ee9SBart Van Assche 	scmd->result = 0;
68862af0ee9SBart Van Assche 	set_host_byte(scmd, status);
68911b68e36SBart Van Assche 	scsi_done(scmd);
690466552b9SHannes Reinecke 	return true;
691466552b9SHannes Reinecke }
692466552b9SHannes Reinecke 
693466552b9SHannes Reinecke /**
694466552b9SHannes Reinecke  * scsi_host_complete_all_commands - Terminate all running commands
695466552b9SHannes Reinecke  * @shost:	Scsi Host on which commands should be terminated
696466552b9SHannes Reinecke  * @status:	Status to be set for the terminated commands
697466552b9SHannes Reinecke  *
698466552b9SHannes Reinecke  * There is no protection against modification of the number
699466552b9SHannes Reinecke  * of outstanding commands. It is the responsibility of the
700466552b9SHannes Reinecke  * caller to ensure that concurrent I/O submission and/or
701466552b9SHannes Reinecke  * completion is stopped when calling this function.
702466552b9SHannes Reinecke  */
scsi_host_complete_all_commands(struct Scsi_Host * shost,enum scsi_host_status status)70362af0ee9SBart Van Assche void scsi_host_complete_all_commands(struct Scsi_Host *shost,
70462af0ee9SBart Van Assche 				     enum scsi_host_status status)
705466552b9SHannes Reinecke {
706466552b9SHannes Reinecke 	blk_mq_tagset_busy_iter(&shost->tag_set, complete_all_cmds_iter,
707466552b9SHannes Reinecke 				&status);
708466552b9SHannes Reinecke }
709466552b9SHannes Reinecke EXPORT_SYMBOL_GPL(scsi_host_complete_all_commands);
710dcece99eSHannes Reinecke 
711dcece99eSHannes Reinecke struct scsi_host_busy_iter_data {
7122dd6532eSJohn Garry 	bool (*fn)(struct scsi_cmnd *, void *);
713dcece99eSHannes Reinecke 	void *priv;
714dcece99eSHannes Reinecke };
715dcece99eSHannes Reinecke 
__scsi_host_busy_iter_fn(struct request * req,void * priv)7162dd6532eSJohn Garry static bool __scsi_host_busy_iter_fn(struct request *req, void *priv)
717dcece99eSHannes Reinecke {
718dcece99eSHannes Reinecke 	struct scsi_host_busy_iter_data *iter_data = priv;
719dcece99eSHannes Reinecke 	struct scsi_cmnd *sc = blk_mq_rq_to_pdu(req);
720dcece99eSHannes Reinecke 
7212dd6532eSJohn Garry 	return iter_data->fn(sc, iter_data->priv);
722dcece99eSHannes Reinecke }
723dcece99eSHannes Reinecke 
724dcece99eSHannes Reinecke /**
725dcece99eSHannes Reinecke  * scsi_host_busy_iter - Iterate over all busy commands
726dcece99eSHannes Reinecke  * @shost:	Pointer to Scsi_Host.
727dcece99eSHannes Reinecke  * @fn:		Function to call on each busy command
728dcece99eSHannes Reinecke  * @priv:	Data pointer passed to @fn
729dcece99eSHannes Reinecke  *
730dcece99eSHannes Reinecke  * If locking against concurrent command completions is required
731dcece99eSHannes Reinecke  * ithas to be provided by the caller
732dcece99eSHannes Reinecke  **/
scsi_host_busy_iter(struct Scsi_Host * shost,bool (* fn)(struct scsi_cmnd *,void *),void * priv)733dcece99eSHannes Reinecke void scsi_host_busy_iter(struct Scsi_Host *shost,
7342dd6532eSJohn Garry 			 bool (*fn)(struct scsi_cmnd *, void *),
735dcece99eSHannes Reinecke 			 void *priv)
736dcece99eSHannes Reinecke {
737dcece99eSHannes Reinecke 	struct scsi_host_busy_iter_data iter_data = {
738dcece99eSHannes Reinecke 		.fn = fn,
739dcece99eSHannes Reinecke 		.priv = priv,
740dcece99eSHannes Reinecke 	};
741dcece99eSHannes Reinecke 
742dcece99eSHannes Reinecke 	blk_mq_tagset_busy_iter(&shost->tag_set, __scsi_host_busy_iter_fn,
743dcece99eSHannes Reinecke 				&iter_data);
744dcece99eSHannes Reinecke }
745dcece99eSHannes Reinecke EXPORT_SYMBOL_GPL(scsi_host_busy_iter);
746