| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 2 |  * zfcp device driver | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3 |  * | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 4 |  * Registration and callback for the s390 common I/O layer. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 5 |  * | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 6 |  * Copyright IBM Corporation 2002, 2008 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7 |  */ | 
 | 8 |  | 
| Christof Schmitt | ecf39d4 | 2008-12-25 13:39:53 +0100 | [diff] [blame] | 9 | #define KMSG_COMPONENT "zfcp" | 
 | 10 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | 
 | 11 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 12 | #include "zfcp_ext.h" | 
 | 13 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 14 | /** | 
 | 15 |  * zfcp_ccw_probe - probe function of zfcp driver | 
 | 16 |  * @ccw_device: pointer to belonging ccw device | 
 | 17 |  * | 
 | 18 |  * This function gets called by the common i/o layer and sets up the initial | 
 | 19 |  * data structures for each fcp adapter, which was detected by the system. | 
 | 20 |  * Also the sysfs files for this adapter will be created by this function. | 
 | 21 |  * In addition the nameserver port will be added to the ports of the adapter | 
 | 22 |  * and its sysfs representation will be created too. | 
 | 23 |  */ | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 24 | static int zfcp_ccw_probe(struct ccw_device *ccw_device) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 25 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 26 | 	int retval = 0; | 
 | 27 |  | 
 | 28 | 	down(&zfcp_data.config_sema); | 
| Swen Schillig | 317e6b6 | 2008-07-02 10:56:37 +0200 | [diff] [blame] | 29 | 	if (zfcp_adapter_enqueue(ccw_device)) { | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 30 | 		dev_err(&ccw_device->dev, | 
| Christof Schmitt | ff3b24f | 2008-10-01 12:42:15 +0200 | [diff] [blame] | 31 | 			"Setting up data structures for the " | 
 | 32 | 			"FCP adapter failed\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 33 | 		retval = -EINVAL; | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 34 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 35 | 	up(&zfcp_data.config_sema); | 
 | 36 | 	return retval; | 
 | 37 | } | 
 | 38 |  | 
 | 39 | /** | 
 | 40 |  * zfcp_ccw_remove - remove function of zfcp driver | 
 | 41 |  * @ccw_device: pointer to belonging ccw device | 
 | 42 |  * | 
 | 43 |  * This function gets called by the common i/o layer and removes an adapter | 
 | 44 |  * from the system. Task of this function is to get rid of all units and | 
 | 45 |  * ports that belong to this adapter. And in addition all resources of this | 
 | 46 |  * adapter will be freed too. | 
 | 47 |  */ | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 48 | static void zfcp_ccw_remove(struct ccw_device *ccw_device) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 49 | { | 
 | 50 | 	struct zfcp_adapter *adapter; | 
 | 51 | 	struct zfcp_port *port, *p; | 
 | 52 | 	struct zfcp_unit *unit, *u; | 
| Christof Schmitt | 0406289 | 2008-10-01 12:42:20 +0200 | [diff] [blame] | 53 | 	LIST_HEAD(unit_remove_lh); | 
 | 54 | 	LIST_HEAD(port_remove_lh); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 55 |  | 
 | 56 | 	ccw_device_set_offline(ccw_device); | 
 | 57 | 	down(&zfcp_data.config_sema); | 
 | 58 | 	adapter = dev_get_drvdata(&ccw_device->dev); | 
 | 59 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 | 	write_lock_irq(&zfcp_data.config_lock); | 
 | 61 | 	list_for_each_entry_safe(port, p, &adapter->port_list_head, list) { | 
 | 62 | 		list_for_each_entry_safe(unit, u, &port->unit_list_head, list) { | 
| Christof Schmitt | 0406289 | 2008-10-01 12:42:20 +0200 | [diff] [blame] | 63 | 			list_move(&unit->list, &unit_remove_lh); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 64 | 			atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, | 
 | 65 | 					&unit->status); | 
 | 66 | 		} | 
| Christof Schmitt | 0406289 | 2008-10-01 12:42:20 +0200 | [diff] [blame] | 67 | 		list_move(&port->list, &port_remove_lh); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 68 | 		atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); | 
 | 69 | 	} | 
 | 70 | 	atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); | 
 | 71 | 	write_unlock_irq(&zfcp_data.config_lock); | 
 | 72 |  | 
| Christof Schmitt | 0406289 | 2008-10-01 12:42:20 +0200 | [diff] [blame] | 73 | 	list_for_each_entry_safe(port, p, &port_remove_lh, list) { | 
 | 74 | 		list_for_each_entry_safe(unit, u, &unit_remove_lh, list) { | 
| Swen Schillig | 44cc76f | 2008-10-01 12:42:16 +0200 | [diff] [blame] | 75 | 			if (atomic_read(&unit->status) & | 
 | 76 | 			    ZFCP_STATUS_UNIT_REGISTERED) | 
| Christof Schmitt | e39c887 | 2007-11-05 12:37:46 +0100 | [diff] [blame] | 77 | 				scsi_remove_device(unit->device); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 | 			zfcp_unit_dequeue(unit); | 
 | 79 | 		} | 
 | 80 | 		zfcp_port_dequeue(port); | 
 | 81 | 	} | 
| Swen Schillig | 44cc76f | 2008-10-01 12:42:16 +0200 | [diff] [blame] | 82 | 	wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 | 	zfcp_adapter_dequeue(adapter); | 
 | 84 |  | 
 | 85 | 	up(&zfcp_data.config_sema); | 
 | 86 | } | 
 | 87 |  | 
 | 88 | /** | 
 | 89 |  * zfcp_ccw_set_online - set_online function of zfcp driver | 
 | 90 |  * @ccw_device: pointer to belonging ccw device | 
 | 91 |  * | 
 | 92 |  * This function gets called by the common i/o layer and sets an adapter | 
 | 93 |  * into state online. Setting an fcp device online means that it will be | 
 | 94 |  * registered with the SCSI stack, that the QDIO queues will be set up | 
 | 95 |  * and that the adapter will be opened (asynchronously). | 
 | 96 |  */ | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 97 | static int zfcp_ccw_set_online(struct ccw_device *ccw_device) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 98 | { | 
 | 99 | 	struct zfcp_adapter *adapter; | 
 | 100 | 	int retval; | 
 | 101 |  | 
 | 102 | 	down(&zfcp_data.config_sema); | 
 | 103 | 	adapter = dev_get_drvdata(&ccw_device->dev); | 
 | 104 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 105 | 	retval = zfcp_erp_thread_setup(adapter); | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 106 | 	if (retval) | 
| Christof Schmitt | ff17a29 | 2007-08-28 09:31:41 +0200 | [diff] [blame] | 107 | 		goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 108 |  | 
| Volker Sameske | fea9d6c | 2006-08-02 11:05:16 +0200 | [diff] [blame] | 109 | 	/* initialize request counter */ | 
 | 110 | 	BUG_ON(!zfcp_reqlist_isempty(adapter)); | 
 | 111 | 	adapter->req_no = 0; | 
 | 112 |  | 
| Martin Peschke | 1f6f712 | 2008-04-18 12:51:55 +0200 | [diff] [blame] | 113 | 	zfcp_erp_modify_adapter_status(adapter, 10, NULL, | 
| Martin Peschke | 698ec016 | 2008-03-27 14:22:02 +0100 | [diff] [blame] | 114 | 				       ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET); | 
| Martin Peschke | 1f6f712 | 2008-04-18 12:51:55 +0200 | [diff] [blame] | 115 | 	zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 85, | 
 | 116 | 				NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 | 	zfcp_erp_wait(adapter); | 
| Christof Schmitt | 77fd949 | 2008-11-04 16:35:10 +0100 | [diff] [blame] | 118 | 	up(&zfcp_data.config_sema); | 
 | 119 | 	flush_work(&adapter->scan_work); | 
 | 120 | 	return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 121 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 122 |  out: | 
 | 123 | 	up(&zfcp_data.config_sema); | 
 | 124 | 	return retval; | 
 | 125 | } | 
 | 126 |  | 
 | 127 | /** | 
 | 128 |  * zfcp_ccw_set_offline - set_offline function of zfcp driver | 
 | 129 |  * @ccw_device: pointer to belonging ccw device | 
 | 130 |  * | 
 | 131 |  * This function gets called by the common i/o layer and sets an adapter | 
| Michael Loehr | 9f28745 | 2007-05-09 11:01:24 +0200 | [diff] [blame] | 132 |  * into state offline. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 133 |  */ | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 134 | static int zfcp_ccw_set_offline(struct ccw_device *ccw_device) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 135 | { | 
 | 136 | 	struct zfcp_adapter *adapter; | 
 | 137 |  | 
 | 138 | 	down(&zfcp_data.config_sema); | 
 | 139 | 	adapter = dev_get_drvdata(&ccw_device->dev); | 
| Martin Peschke | 1f6f712 | 2008-04-18 12:51:55 +0200 | [diff] [blame] | 140 | 	zfcp_erp_adapter_shutdown(adapter, 0, 86, NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 141 | 	zfcp_erp_wait(adapter); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 142 | 	zfcp_erp_thread_kill(adapter); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | 	up(&zfcp_data.config_sema); | 
 | 144 | 	return 0; | 
 | 145 | } | 
 | 146 |  | 
 | 147 | /** | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 148 |  * zfcp_ccw_notify - ccw notify function | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 149 |  * @ccw_device: pointer to belonging ccw device | 
 | 150 |  * @event: indicates if adapter was detached or attached | 
 | 151 |  * | 
 | 152 |  * This function gets called by the common i/o layer if an adapter has gone | 
 | 153 |  * or reappeared. | 
 | 154 |  */ | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 155 | static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 156 | { | 
| Christof Schmitt | f48bf7f | 2008-08-21 13:43:34 +0200 | [diff] [blame] | 157 | 	struct zfcp_adapter *adapter = dev_get_drvdata(&ccw_device->dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 158 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 159 | 	switch (event) { | 
 | 160 | 	case CIO_GONE: | 
| Christof Schmitt | ff3b24f | 2008-10-01 12:42:15 +0200 | [diff] [blame] | 161 | 		dev_warn(&adapter->ccw_device->dev, | 
 | 162 | 			 "The FCP device has been detached\n"); | 
| Martin Peschke | 1f6f712 | 2008-04-18 12:51:55 +0200 | [diff] [blame] | 163 | 		zfcp_erp_adapter_shutdown(adapter, 0, 87, NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 164 | 		break; | 
 | 165 | 	case CIO_NO_PATH: | 
| Christof Schmitt | ff3b24f | 2008-10-01 12:42:15 +0200 | [diff] [blame] | 166 | 		dev_warn(&adapter->ccw_device->dev, | 
 | 167 | 			 "The CHPID for the FCP device is offline\n"); | 
| Martin Peschke | 1f6f712 | 2008-04-18 12:51:55 +0200 | [diff] [blame] | 168 | 		zfcp_erp_adapter_shutdown(adapter, 0, 88, NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 169 | 		break; | 
 | 170 | 	case CIO_OPER: | 
| Christof Schmitt | ff3b24f | 2008-10-01 12:42:15 +0200 | [diff] [blame] | 171 | 		dev_info(&adapter->ccw_device->dev, | 
 | 172 | 			 "The FCP device is operational again\n"); | 
| Martin Peschke | 1f6f712 | 2008-04-18 12:51:55 +0200 | [diff] [blame] | 173 | 		zfcp_erp_modify_adapter_status(adapter, 11, NULL, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 174 | 					       ZFCP_STATUS_COMMON_RUNNING, | 
 | 175 | 					       ZFCP_SET); | 
| Martin Peschke | 1f6f712 | 2008-04-18 12:51:55 +0200 | [diff] [blame] | 176 | 		zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, | 
 | 177 | 					89, NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 178 | 		break; | 
 | 179 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 180 | 	return 1; | 
 | 181 | } | 
 | 182 |  | 
 | 183 | /** | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 184 |  * zfcp_ccw_shutdown - handle shutdown from cio | 
 | 185 |  * @cdev: device for adapter to shutdown. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 186 |  */ | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 187 | static void zfcp_ccw_shutdown(struct ccw_device *cdev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 188 | { | 
 | 189 | 	struct zfcp_adapter *adapter; | 
 | 190 |  | 
 | 191 | 	down(&zfcp_data.config_sema); | 
| Cornelia Huck | 958974f | 2007-10-12 16:11:21 +0200 | [diff] [blame] | 192 | 	adapter = dev_get_drvdata(&cdev->dev); | 
| Martin Peschke | 1f6f712 | 2008-04-18 12:51:55 +0200 | [diff] [blame] | 193 | 	zfcp_erp_adapter_shutdown(adapter, 0, 90, NULL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 194 | 	zfcp_erp_wait(adapter); | 
 | 195 | 	up(&zfcp_data.config_sema); | 
 | 196 | } | 
 | 197 |  | 
| Christof Schmitt | fa04c28 | 2008-06-10 18:20:56 +0200 | [diff] [blame] | 198 | static struct ccw_device_id zfcp_ccw_device_id[] = { | 
 | 199 | 	{ CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) }, | 
 | 200 | 	{ CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x4) }, /* priv. */ | 
 | 201 | 	{}, | 
 | 202 | }; | 
 | 203 |  | 
 | 204 | MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id); | 
 | 205 |  | 
 | 206 | static struct ccw_driver zfcp_ccw_driver = { | 
 | 207 | 	.owner       = THIS_MODULE, | 
 | 208 | 	.name        = "zfcp", | 
 | 209 | 	.ids         = zfcp_ccw_device_id, | 
 | 210 | 	.probe       = zfcp_ccw_probe, | 
 | 211 | 	.remove      = zfcp_ccw_remove, | 
 | 212 | 	.set_online  = zfcp_ccw_set_online, | 
 | 213 | 	.set_offline = zfcp_ccw_set_offline, | 
 | 214 | 	.notify      = zfcp_ccw_notify, | 
 | 215 | 	.shutdown    = zfcp_ccw_shutdown, | 
 | 216 | }; | 
 | 217 |  | 
 | 218 | /** | 
 | 219 |  * zfcp_ccw_register - ccw register function | 
 | 220 |  * | 
 | 221 |  * Registers the driver at the common i/o layer. This function will be called | 
 | 222 |  * at module load time/system start. | 
 | 223 |  */ | 
 | 224 | int __init zfcp_ccw_register(void) | 
 | 225 | { | 
 | 226 | 	return ccw_driver_register(&zfcp_ccw_driver); | 
 | 227 | } | 
| Swen Schillig | a1b449d | 2008-10-01 12:42:19 +0200 | [diff] [blame] | 228 |  | 
 | 229 | /** | 
 | 230 |  * zfcp_get_adapter_by_busid - find zfcp_adapter struct | 
 | 231 |  * @busid: bus id string of zfcp adapter to find | 
 | 232 |  */ | 
 | 233 | struct zfcp_adapter *zfcp_get_adapter_by_busid(char *busid) | 
 | 234 | { | 
 | 235 | 	struct ccw_device *ccw_device; | 
 | 236 | 	struct zfcp_adapter *adapter = NULL; | 
 | 237 |  | 
 | 238 | 	ccw_device = get_ccwdev_by_busid(&zfcp_ccw_driver, busid); | 
 | 239 | 	if (ccw_device) { | 
 | 240 | 		adapter = dev_get_drvdata(&ccw_device->dev); | 
 | 241 | 		put_device(&ccw_device->dev); | 
 | 242 | 	} | 
 | 243 | 	return adapter; | 
 | 244 | } |