xref: /openbmc/linux/drivers/s390/block/dasd_alias.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
28e09f215SStefan Weinhuber /*
38e09f215SStefan Weinhuber  * PAV alias management for the DASD ECKD discipline
48e09f215SStefan Weinhuber  *
5a53c8fabSHeiko Carstens  * Copyright IBM Corp. 2007
68e09f215SStefan Weinhuber  * Author(s): Stefan Weinhuber <wein@de.ibm.com>
78e09f215SStefan Weinhuber  */
88e09f215SStefan Weinhuber 
9ca99dab0SStefan Haberland #define KMSG_COMPONENT "dasd-eckd"
10fc19f381SStefan Haberland 
118e09f215SStefan Weinhuber #include <linux/list.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
138e09f215SStefan Weinhuber #include <asm/ebcdic.h>
148e09f215SStefan Weinhuber #include "dasd_int.h"
158e09f215SStefan Weinhuber #include "dasd_eckd.h"
168e09f215SStefan Weinhuber 
178e09f215SStefan Weinhuber #ifdef PRINTK_HEADER
188e09f215SStefan Weinhuber #undef PRINTK_HEADER
198e09f215SStefan Weinhuber #endif				/* PRINTK_HEADER */
208e09f215SStefan Weinhuber #define PRINTK_HEADER "dasd(eckd):"
218e09f215SStefan Weinhuber 
228e09f215SStefan Weinhuber 
238e09f215SStefan Weinhuber /*
248e09f215SStefan Weinhuber  * General concept of alias management:
258e09f215SStefan Weinhuber  * - PAV and DASD alias management is specific to the eckd discipline.
268e09f215SStefan Weinhuber  * - A device is connected to an lcu as long as the device exists.
278e09f215SStefan Weinhuber  *   dasd_alias_make_device_known_to_lcu will be called wenn the
288e09f215SStefan Weinhuber  *   device is checked by the eckd discipline and
298e09f215SStefan Weinhuber  *   dasd_alias_disconnect_device_from_lcu will be called
308e09f215SStefan Weinhuber  *   before the device is deleted.
318e09f215SStefan Weinhuber  * - The dasd_alias_add_device / dasd_alias_remove_device
328e09f215SStefan Weinhuber  *   functions mark the point when a device is 'ready for service'.
338e09f215SStefan Weinhuber  * - A summary unit check is a rare occasion, but it is mandatory to
348e09f215SStefan Weinhuber  *   support it. It requires some complex recovery actions before the
358e09f215SStefan Weinhuber  *   devices can be used again (see dasd_alias_handle_summary_unit_check).
368e09f215SStefan Weinhuber  * - dasd_alias_get_start_dev will find an alias device that can be used
378e09f215SStefan Weinhuber  *   instead of the base device and does some (very simple) load balancing.
388e09f215SStefan Weinhuber  *   This is the function that gets called for each I/O, so when improving
398e09f215SStefan Weinhuber  *   something, this function should get faster or better, the rest has just
408e09f215SStefan Weinhuber  *   to be correct.
418e09f215SStefan Weinhuber  */
428e09f215SStefan Weinhuber 
438e09f215SStefan Weinhuber 
448e09f215SStefan Weinhuber static void summary_unit_check_handling_work(struct work_struct *);
458e09f215SStefan Weinhuber static void lcu_update_work(struct work_struct *);
468e09f215SStefan Weinhuber static int _schedule_lcu_update(struct alias_lcu *, struct dasd_device *);
478e09f215SStefan Weinhuber 
488e09f215SStefan Weinhuber static struct alias_root aliastree = {
498e09f215SStefan Weinhuber 	.serverlist = LIST_HEAD_INIT(aliastree.serverlist),
508e09f215SStefan Weinhuber 	.lock = __SPIN_LOCK_UNLOCKED(aliastree.lock),
518e09f215SStefan Weinhuber };
528e09f215SStefan Weinhuber 
_find_server(struct dasd_uid * uid)538e09f215SStefan Weinhuber static struct alias_server *_find_server(struct dasd_uid *uid)
548e09f215SStefan Weinhuber {
558e09f215SStefan Weinhuber 	struct alias_server *pos;
568e09f215SStefan Weinhuber 	list_for_each_entry(pos, &aliastree.serverlist, server) {
578e09f215SStefan Weinhuber 		if (!strncmp(pos->uid.vendor, uid->vendor,
588e09f215SStefan Weinhuber 			     sizeof(uid->vendor))
598e09f215SStefan Weinhuber 		    && !strncmp(pos->uid.serial, uid->serial,
608e09f215SStefan Weinhuber 				sizeof(uid->serial)))
618e09f215SStefan Weinhuber 			return pos;
623b974874SPeter Senna Tschudin 	}
638e09f215SStefan Weinhuber 	return NULL;
648e09f215SStefan Weinhuber }
658e09f215SStefan Weinhuber 
_find_lcu(struct alias_server * server,struct dasd_uid * uid)668e09f215SStefan Weinhuber static struct alias_lcu *_find_lcu(struct alias_server *server,
678e09f215SStefan Weinhuber 				   struct dasd_uid *uid)
688e09f215SStefan Weinhuber {
698e09f215SStefan Weinhuber 	struct alias_lcu *pos;
708e09f215SStefan Weinhuber 	list_for_each_entry(pos, &server->lculist, lcu) {
718e09f215SStefan Weinhuber 		if (pos->uid.ssid == uid->ssid)
728e09f215SStefan Weinhuber 			return pos;
733b974874SPeter Senna Tschudin 	}
748e09f215SStefan Weinhuber 	return NULL;
758e09f215SStefan Weinhuber }
768e09f215SStefan Weinhuber 
_find_group(struct alias_lcu * lcu,struct dasd_uid * uid)778e09f215SStefan Weinhuber static struct alias_pav_group *_find_group(struct alias_lcu *lcu,
788e09f215SStefan Weinhuber 					   struct dasd_uid *uid)
798e09f215SStefan Weinhuber {
808e09f215SStefan Weinhuber 	struct alias_pav_group *pos;
818e09f215SStefan Weinhuber 	__u8 search_unit_addr;
828e09f215SStefan Weinhuber 
838e09f215SStefan Weinhuber 	/* for hyper pav there is only one group */
848e09f215SStefan Weinhuber 	if (lcu->pav == HYPER_PAV) {
858e09f215SStefan Weinhuber 		if (list_empty(&lcu->grouplist))
868e09f215SStefan Weinhuber 			return NULL;
878e09f215SStefan Weinhuber 		else
888e09f215SStefan Weinhuber 			return list_first_entry(&lcu->grouplist,
898e09f215SStefan Weinhuber 						struct alias_pav_group, group);
908e09f215SStefan Weinhuber 	}
918e09f215SStefan Weinhuber 
928e09f215SStefan Weinhuber 	/* for base pav we have to find the group that matches the base */
938e09f215SStefan Weinhuber 	if (uid->type == UA_BASE_DEVICE)
948e09f215SStefan Weinhuber 		search_unit_addr = uid->real_unit_addr;
958e09f215SStefan Weinhuber 	else
968e09f215SStefan Weinhuber 		search_unit_addr = uid->base_unit_addr;
978e09f215SStefan Weinhuber 	list_for_each_entry(pos, &lcu->grouplist, group) {
984abb08c2SStefan Weinhuber 		if (pos->uid.base_unit_addr == search_unit_addr &&
994abb08c2SStefan Weinhuber 		    !strncmp(pos->uid.vduit, uid->vduit, sizeof(uid->vduit)))
1008e09f215SStefan Weinhuber 			return pos;
1013b974874SPeter Senna Tschudin 	}
1028e09f215SStefan Weinhuber 	return NULL;
1038e09f215SStefan Weinhuber }
1048e09f215SStefan Weinhuber 
_allocate_server(struct dasd_uid * uid)1058e09f215SStefan Weinhuber static struct alias_server *_allocate_server(struct dasd_uid *uid)
1068e09f215SStefan Weinhuber {
1078e09f215SStefan Weinhuber 	struct alias_server *server;
1088e09f215SStefan Weinhuber 
1098e09f215SStefan Weinhuber 	server = kzalloc(sizeof(*server), GFP_KERNEL);
1108e09f215SStefan Weinhuber 	if (!server)
1118e09f215SStefan Weinhuber 		return ERR_PTR(-ENOMEM);
1128e09f215SStefan Weinhuber 	memcpy(server->uid.vendor, uid->vendor, sizeof(uid->vendor));
1138e09f215SStefan Weinhuber 	memcpy(server->uid.serial, uid->serial, sizeof(uid->serial));
1148e09f215SStefan Weinhuber 	INIT_LIST_HEAD(&server->server);
1158e09f215SStefan Weinhuber 	INIT_LIST_HEAD(&server->lculist);
1168e09f215SStefan Weinhuber 	return server;
1178e09f215SStefan Weinhuber }
1188e09f215SStefan Weinhuber 
_free_server(struct alias_server * server)1198e09f215SStefan Weinhuber static void _free_server(struct alias_server *server)
1208e09f215SStefan Weinhuber {
1218e09f215SStefan Weinhuber 	kfree(server);
1228e09f215SStefan Weinhuber }
1238e09f215SStefan Weinhuber 
_allocate_lcu(struct dasd_uid * uid)1248e09f215SStefan Weinhuber static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid)
1258e09f215SStefan Weinhuber {
1268e09f215SStefan Weinhuber 	struct alias_lcu *lcu;
1278e09f215SStefan Weinhuber 
1288e09f215SStefan Weinhuber 	lcu = kzalloc(sizeof(*lcu), GFP_KERNEL);
1298e09f215SStefan Weinhuber 	if (!lcu)
1308e09f215SStefan Weinhuber 		return ERR_PTR(-ENOMEM);
1318e09f215SStefan Weinhuber 	lcu->uac = kzalloc(sizeof(*(lcu->uac)), GFP_KERNEL | GFP_DMA);
1328e09f215SStefan Weinhuber 	if (!lcu->uac)
1338e09f215SStefan Weinhuber 		goto out_err1;
1348e09f215SStefan Weinhuber 	lcu->rsu_cqr = kzalloc(sizeof(*lcu->rsu_cqr), GFP_KERNEL | GFP_DMA);
1358e09f215SStefan Weinhuber 	if (!lcu->rsu_cqr)
1368e09f215SStefan Weinhuber 		goto out_err2;
1378e09f215SStefan Weinhuber 	lcu->rsu_cqr->cpaddr = kzalloc(sizeof(struct ccw1),
1388e09f215SStefan Weinhuber 				       GFP_KERNEL | GFP_DMA);
1398e09f215SStefan Weinhuber 	if (!lcu->rsu_cqr->cpaddr)
1408e09f215SStefan Weinhuber 		goto out_err3;
1418e09f215SStefan Weinhuber 	lcu->rsu_cqr->data = kzalloc(16, GFP_KERNEL | GFP_DMA);
1428e09f215SStefan Weinhuber 	if (!lcu->rsu_cqr->data)
1438e09f215SStefan Weinhuber 		goto out_err4;
1448e09f215SStefan Weinhuber 
1458e09f215SStefan Weinhuber 	memcpy(lcu->uid.vendor, uid->vendor, sizeof(uid->vendor));
1468e09f215SStefan Weinhuber 	memcpy(lcu->uid.serial, uid->serial, sizeof(uid->serial));
1478e09f215SStefan Weinhuber 	lcu->uid.ssid = uid->ssid;
1488e09f215SStefan Weinhuber 	lcu->pav = NO_PAV;
1498e09f215SStefan Weinhuber 	lcu->flags = NEED_UAC_UPDATE | UPDATE_PENDING;
1508e09f215SStefan Weinhuber 	INIT_LIST_HEAD(&lcu->lcu);
1518e09f215SStefan Weinhuber 	INIT_LIST_HEAD(&lcu->inactive_devices);
1528e09f215SStefan Weinhuber 	INIT_LIST_HEAD(&lcu->active_devices);
1538e09f215SStefan Weinhuber 	INIT_LIST_HEAD(&lcu->grouplist);
1548e09f215SStefan Weinhuber 	INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work);
1558e09f215SStefan Weinhuber 	INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work);
1568e09f215SStefan Weinhuber 	spin_lock_init(&lcu->lock);
157f4ac1d02SStefan Weinhuber 	init_completion(&lcu->lcu_setup);
1588e09f215SStefan Weinhuber 	return lcu;
1598e09f215SStefan Weinhuber 
1608e09f215SStefan Weinhuber out_err4:
1618e09f215SStefan Weinhuber 	kfree(lcu->rsu_cqr->cpaddr);
1628e09f215SStefan Weinhuber out_err3:
1638e09f215SStefan Weinhuber 	kfree(lcu->rsu_cqr);
1648e09f215SStefan Weinhuber out_err2:
1658e09f215SStefan Weinhuber 	kfree(lcu->uac);
1668e09f215SStefan Weinhuber out_err1:
1678e09f215SStefan Weinhuber 	kfree(lcu);
1688e09f215SStefan Weinhuber 	return ERR_PTR(-ENOMEM);
1698e09f215SStefan Weinhuber }
1708e09f215SStefan Weinhuber 
_free_lcu(struct alias_lcu * lcu)1718e09f215SStefan Weinhuber static void _free_lcu(struct alias_lcu *lcu)
1728e09f215SStefan Weinhuber {
1738e09f215SStefan Weinhuber 	kfree(lcu->rsu_cqr->data);
1748e09f215SStefan Weinhuber 	kfree(lcu->rsu_cqr->cpaddr);
1758e09f215SStefan Weinhuber 	kfree(lcu->rsu_cqr);
1768e09f215SStefan Weinhuber 	kfree(lcu->uac);
1778e09f215SStefan Weinhuber 	kfree(lcu);
1788e09f215SStefan Weinhuber }
1798e09f215SStefan Weinhuber 
1808e09f215SStefan Weinhuber /*
1818e09f215SStefan Weinhuber  * This is the function that will allocate all the server and lcu data,
1828e09f215SStefan Weinhuber  * so this function must be called first for a new device.
1838e09f215SStefan Weinhuber  * If the return value is 1, the lcu was already known before, if it
1848e09f215SStefan Weinhuber  * is 0, this is a new lcu.
1858e09f215SStefan Weinhuber  * Negative return code indicates that something went wrong (e.g. -ENOMEM)
1868e09f215SStefan Weinhuber  */
dasd_alias_make_device_known_to_lcu(struct dasd_device * device)1878e09f215SStefan Weinhuber int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
1888e09f215SStefan Weinhuber {
189543691a4SSebastian Ott 	struct dasd_eckd_private *private = device->private;
1908e09f215SStefan Weinhuber 	unsigned long flags;
1918e09f215SStefan Weinhuber 	struct alias_server *server, *newserver;
1928e09f215SStefan Weinhuber 	struct alias_lcu *lcu, *newlcu;
1932dedf0d9SStefan Haberland 	struct dasd_uid uid;
1948e09f215SStefan Weinhuber 
1952dedf0d9SStefan Haberland 	device->discipline->get_uid(device, &uid);
1968e09f215SStefan Weinhuber 	spin_lock_irqsave(&aliastree.lock, flags);
1972dedf0d9SStefan Haberland 	server = _find_server(&uid);
1988e09f215SStefan Weinhuber 	if (!server) {
1998e09f215SStefan Weinhuber 		spin_unlock_irqrestore(&aliastree.lock, flags);
2002dedf0d9SStefan Haberland 		newserver = _allocate_server(&uid);
2018e09f215SStefan Weinhuber 		if (IS_ERR(newserver))
2028e09f215SStefan Weinhuber 			return PTR_ERR(newserver);
2038e09f215SStefan Weinhuber 		spin_lock_irqsave(&aliastree.lock, flags);
2042dedf0d9SStefan Haberland 		server = _find_server(&uid);
2058e09f215SStefan Weinhuber 		if (!server) {
2068e09f215SStefan Weinhuber 			list_add(&newserver->server, &aliastree.serverlist);
2078e09f215SStefan Weinhuber 			server = newserver;
2088e09f215SStefan Weinhuber 		} else {
2098e09f215SStefan Weinhuber 			/* someone was faster */
2108e09f215SStefan Weinhuber 			_free_server(newserver);
2118e09f215SStefan Weinhuber 		}
2128e09f215SStefan Weinhuber 	}
2138e09f215SStefan Weinhuber 
2142dedf0d9SStefan Haberland 	lcu = _find_lcu(server, &uid);
2158e09f215SStefan Weinhuber 	if (!lcu) {
2168e09f215SStefan Weinhuber 		spin_unlock_irqrestore(&aliastree.lock, flags);
2172dedf0d9SStefan Haberland 		newlcu = _allocate_lcu(&uid);
2188e09f215SStefan Weinhuber 		if (IS_ERR(newlcu))
2196d53cfe5SRoel Kluin 			return PTR_ERR(newlcu);
2208e09f215SStefan Weinhuber 		spin_lock_irqsave(&aliastree.lock, flags);
2212dedf0d9SStefan Haberland 		lcu = _find_lcu(server, &uid);
2228e09f215SStefan Weinhuber 		if (!lcu) {
2238e09f215SStefan Weinhuber 			list_add(&newlcu->lcu, &server->lculist);
2248e09f215SStefan Weinhuber 			lcu = newlcu;
2258e09f215SStefan Weinhuber 		} else {
2268e09f215SStefan Weinhuber 			/* someone was faster */
2278e09f215SStefan Weinhuber 			_free_lcu(newlcu);
2288e09f215SStefan Weinhuber 		}
2298e09f215SStefan Weinhuber 	}
2308e09f215SStefan Weinhuber 	spin_lock(&lcu->lock);
2318e09f215SStefan Weinhuber 	list_add(&device->alias_list, &lcu->inactive_devices);
2328e09f215SStefan Weinhuber 	private->lcu = lcu;
2338e09f215SStefan Weinhuber 	spin_unlock(&lcu->lock);
2348e09f215SStefan Weinhuber 	spin_unlock_irqrestore(&aliastree.lock, flags);
2358e09f215SStefan Weinhuber 
236f9f8d02fSStefan Haberland 	return 0;
237f4ac1d02SStefan Weinhuber }
238f4ac1d02SStefan Weinhuber 
239f4ac1d02SStefan Weinhuber /*
2408e09f215SStefan Weinhuber  * This function removes a device from the scope of alias management.
2418e09f215SStefan Weinhuber  * The complicated part is to make sure that it is not in use by
2428e09f215SStefan Weinhuber  * any of the workers. If necessary cancel the work.
2438e09f215SStefan Weinhuber  */
dasd_alias_disconnect_device_from_lcu(struct dasd_device * device)2448e09f215SStefan Weinhuber void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device)
2458e09f215SStefan Weinhuber {
246543691a4SSebastian Ott 	struct dasd_eckd_private *private = device->private;
2478e09f215SStefan Weinhuber 	unsigned long flags;
2488e09f215SStefan Weinhuber 	struct alias_lcu *lcu;
2498e09f215SStefan Weinhuber 	struct alias_server *server;
2508e09f215SStefan Weinhuber 	int was_pending;
2512dedf0d9SStefan Haberland 	struct dasd_uid uid;
2528e09f215SStefan Weinhuber 
2538e09f215SStefan Weinhuber 	lcu = private->lcu;
254f602f6d6SStefan Haberland 	/* nothing to do if already disconnected */
255f602f6d6SStefan Haberland 	if (!lcu)
256f602f6d6SStefan Haberland 		return;
2572dedf0d9SStefan Haberland 	device->discipline->get_uid(device, &uid);
2588e09f215SStefan Weinhuber 	spin_lock_irqsave(&lcu->lock, flags);
2598e09f215SStefan Weinhuber 	/* make sure that the workers don't use this device */
2608e09f215SStefan Weinhuber 	if (device == lcu->suc_data.device) {
2618e09f215SStefan Weinhuber 		spin_unlock_irqrestore(&lcu->lock, flags);
2628e09f215SStefan Weinhuber 		cancel_work_sync(&lcu->suc_data.worker);
2638e09f215SStefan Weinhuber 		spin_lock_irqsave(&lcu->lock, flags);
2649d862abaSStefan Haberland 		if (device == lcu->suc_data.device) {
2659d862abaSStefan Haberland 			dasd_put_device(device);
2668e09f215SStefan Weinhuber 			lcu->suc_data.device = NULL;
2678e09f215SStefan Weinhuber 		}
2689d862abaSStefan Haberland 	}
2698e09f215SStefan Weinhuber 	was_pending = 0;
2708e09f215SStefan Weinhuber 	if (device == lcu->ruac_data.device) {
2718e09f215SStefan Weinhuber 		spin_unlock_irqrestore(&lcu->lock, flags);
2728e09f215SStefan Weinhuber 		was_pending = 1;
2738e09f215SStefan Weinhuber 		cancel_delayed_work_sync(&lcu->ruac_data.dwork);
2748e09f215SStefan Weinhuber 		spin_lock_irqsave(&lcu->lock, flags);
2759d862abaSStefan Haberland 		if (device == lcu->ruac_data.device) {
2769d862abaSStefan Haberland 			dasd_put_device(device);
2778e09f215SStefan Weinhuber 			lcu->ruac_data.device = NULL;
2788e09f215SStefan Weinhuber 		}
2799d862abaSStefan Haberland 	}
2808e09f215SStefan Weinhuber 	private->lcu = NULL;
2818e09f215SStefan Weinhuber 	spin_unlock_irqrestore(&lcu->lock, flags);
2828e09f215SStefan Weinhuber 
2838e09f215SStefan Weinhuber 	spin_lock_irqsave(&aliastree.lock, flags);
2848e09f215SStefan Weinhuber 	spin_lock(&lcu->lock);
28553a7f655SStefan Haberland 	list_del_init(&device->alias_list);
2868e09f215SStefan Weinhuber 	if (list_empty(&lcu->grouplist) &&
2878e09f215SStefan Weinhuber 	    list_empty(&lcu->active_devices) &&
2888e09f215SStefan Weinhuber 	    list_empty(&lcu->inactive_devices)) {
2898e09f215SStefan Weinhuber 		list_del(&lcu->lcu);
2908e09f215SStefan Weinhuber 		spin_unlock(&lcu->lock);
2918e09f215SStefan Weinhuber 		_free_lcu(lcu);
2928e09f215SStefan Weinhuber 		lcu = NULL;
2938e09f215SStefan Weinhuber 	} else {
2948e09f215SStefan Weinhuber 		if (was_pending)
2958e09f215SStefan Weinhuber 			_schedule_lcu_update(lcu, NULL);
2968e09f215SStefan Weinhuber 		spin_unlock(&lcu->lock);
2978e09f215SStefan Weinhuber 	}
2982dedf0d9SStefan Haberland 	server = _find_server(&uid);
2998e09f215SStefan Weinhuber 	if (server && list_empty(&server->lculist)) {
3008e09f215SStefan Weinhuber 		list_del(&server->server);
3018e09f215SStefan Weinhuber 		_free_server(server);
3028e09f215SStefan Weinhuber 	}
3038e09f215SStefan Weinhuber 	spin_unlock_irqrestore(&aliastree.lock, flags);
3048e09f215SStefan Weinhuber }
3058e09f215SStefan Weinhuber 
3068e09f215SStefan Weinhuber /*
3078e09f215SStefan Weinhuber  * This function assumes that the unit address configuration stored
3088e09f215SStefan Weinhuber  * in the lcu is up to date and will update the device uid before
3098e09f215SStefan Weinhuber  * adding it to a pav group.
3108e09f215SStefan Weinhuber  */
3112dedf0d9SStefan Haberland 
_add_device_to_lcu(struct alias_lcu * lcu,struct dasd_device * device,struct dasd_device * pos)3128e09f215SStefan Weinhuber static int _add_device_to_lcu(struct alias_lcu *lcu,
3132dedf0d9SStefan Haberland 			      struct dasd_device *device,
3142dedf0d9SStefan Haberland 			      struct dasd_device *pos)
3158e09f215SStefan Weinhuber {
3168e09f215SStefan Weinhuber 
317543691a4SSebastian Ott 	struct dasd_eckd_private *private = device->private;
3188e09f215SStefan Weinhuber 	struct alias_pav_group *group;
3192dedf0d9SStefan Haberland 	struct dasd_uid uid;
3208e09f215SStefan Weinhuber 
32159a9ed5fSStefan Haberland 	spin_lock(get_ccwdev_lock(device->cdev));
3222dedf0d9SStefan Haberland 	private->uid.type = lcu->uac->unit[private->uid.real_unit_addr].ua_type;
3232dedf0d9SStefan Haberland 	private->uid.base_unit_addr =
3242dedf0d9SStefan Haberland 		lcu->uac->unit[private->uid.real_unit_addr].base_ua;
3252dedf0d9SStefan Haberland 	uid = private->uid;
32659a9ed5fSStefan Haberland 	spin_unlock(get_ccwdev_lock(device->cdev));
3278e09f215SStefan Weinhuber 	/* if we have no PAV anyway, we don't need to bother with PAV groups */
3288e09f215SStefan Weinhuber 	if (lcu->pav == NO_PAV) {
3298e09f215SStefan Weinhuber 		list_move(&device->alias_list, &lcu->active_devices);
3308e09f215SStefan Weinhuber 		return 0;
3318e09f215SStefan Weinhuber 	}
3322dedf0d9SStefan Haberland 	group = _find_group(lcu, &uid);
3338e09f215SStefan Weinhuber 	if (!group) {
3348e09f215SStefan Weinhuber 		group = kzalloc(sizeof(*group), GFP_ATOMIC);
3358e09f215SStefan Weinhuber 		if (!group)
3368e09f215SStefan Weinhuber 			return -ENOMEM;
3372dedf0d9SStefan Haberland 		memcpy(group->uid.vendor, uid.vendor, sizeof(uid.vendor));
3382dedf0d9SStefan Haberland 		memcpy(group->uid.serial, uid.serial, sizeof(uid.serial));
3392dedf0d9SStefan Haberland 		group->uid.ssid = uid.ssid;
3402dedf0d9SStefan Haberland 		if (uid.type == UA_BASE_DEVICE)
3412dedf0d9SStefan Haberland 			group->uid.base_unit_addr = uid.real_unit_addr;
3428e09f215SStefan Weinhuber 		else
3432dedf0d9SStefan Haberland 			group->uid.base_unit_addr = uid.base_unit_addr;
3442dedf0d9SStefan Haberland 		memcpy(group->uid.vduit, uid.vduit, sizeof(uid.vduit));
3458e09f215SStefan Weinhuber 		INIT_LIST_HEAD(&group->group);
3468e09f215SStefan Weinhuber 		INIT_LIST_HEAD(&group->baselist);
3478e09f215SStefan Weinhuber 		INIT_LIST_HEAD(&group->aliaslist);
3488e09f215SStefan Weinhuber 		list_add(&group->group, &lcu->grouplist);
3498e09f215SStefan Weinhuber 	}
3502dedf0d9SStefan Haberland 	if (uid.type == UA_BASE_DEVICE)
3518e09f215SStefan Weinhuber 		list_move(&device->alias_list, &group->baselist);
3528e09f215SStefan Weinhuber 	else
3538e09f215SStefan Weinhuber 		list_move(&device->alias_list, &group->aliaslist);
3548e09f215SStefan Weinhuber 	private->pavgroup = group;
3558e09f215SStefan Weinhuber 	return 0;
3568e09f215SStefan Weinhuber };
3578e09f215SStefan Weinhuber 
_remove_device_from_lcu(struct alias_lcu * lcu,struct dasd_device * device)3588e09f215SStefan Weinhuber static void _remove_device_from_lcu(struct alias_lcu *lcu,
3598e09f215SStefan Weinhuber 				    struct dasd_device *device)
3608e09f215SStefan Weinhuber {
361543691a4SSebastian Ott 	struct dasd_eckd_private *private = device->private;
3628e09f215SStefan Weinhuber 	struct alias_pav_group *group;
3638e09f215SStefan Weinhuber 
3648e09f215SStefan Weinhuber 	list_move(&device->alias_list, &lcu->inactive_devices);
3658e09f215SStefan Weinhuber 	group = private->pavgroup;
3668e09f215SStefan Weinhuber 	if (!group)
3678e09f215SStefan Weinhuber 		return;
3688e09f215SStefan Weinhuber 	private->pavgroup = NULL;
3698e09f215SStefan Weinhuber 	if (list_empty(&group->baselist) && list_empty(&group->aliaslist)) {
3708e09f215SStefan Weinhuber 		list_del(&group->group);
3718e09f215SStefan Weinhuber 		kfree(group);
3728e09f215SStefan Weinhuber 		return;
3738e09f215SStefan Weinhuber 	}
3748e09f215SStefan Weinhuber 	if (group->next == device)
3758e09f215SStefan Weinhuber 		group->next = NULL;
3768e09f215SStefan Weinhuber };
3778e09f215SStefan Weinhuber 
37803429f34SStefan Haberland static int
suborder_not_supported(struct dasd_ccw_req * cqr)37903429f34SStefan Haberland suborder_not_supported(struct dasd_ccw_req *cqr)
38003429f34SStefan Haberland {
38103429f34SStefan Haberland 	char *sense;
38203429f34SStefan Haberland 	char reason;
38303429f34SStefan Haberland 	char msg_format;
38403429f34SStefan Haberland 	char msg_no;
38503429f34SStefan Haberland 
38641995342SStefan Haberland 	/*
38741995342SStefan Haberland 	 * intrc values ENODEV, ENOLINK and EPERM
38841995342SStefan Haberland 	 * will be optained from sleep_on to indicate that no
38941995342SStefan Haberland 	 * IO operation can be started
39041995342SStefan Haberland 	 */
39141995342SStefan Haberland 	if (cqr->intrc == -ENODEV)
39241995342SStefan Haberland 		return 1;
39341995342SStefan Haberland 
39441995342SStefan Haberland 	if (cqr->intrc == -ENOLINK)
39541995342SStefan Haberland 		return 1;
39641995342SStefan Haberland 
39741995342SStefan Haberland 	if (cqr->intrc == -EPERM)
39841995342SStefan Haberland 		return 1;
39941995342SStefan Haberland 
40003429f34SStefan Haberland 	sense = dasd_get_sense(&cqr->irb);
40103429f34SStefan Haberland 	if (!sense)
40203429f34SStefan Haberland 		return 0;
40303429f34SStefan Haberland 
40403429f34SStefan Haberland 	reason = sense[0];
40503429f34SStefan Haberland 	msg_format = (sense[7] & 0xF0);
40603429f34SStefan Haberland 	msg_no = (sense[7] & 0x0F);
40703429f34SStefan Haberland 
40803429f34SStefan Haberland 	/* command reject, Format 0 MSG 4 - invalid parameter */
40903429f34SStefan Haberland 	if ((reason == 0x80) && (msg_format == 0x00) && (msg_no == 0x04))
41003429f34SStefan Haberland 		return 1;
41103429f34SStefan Haberland 
41203429f34SStefan Haberland 	return 0;
41303429f34SStefan Haberland }
41403429f34SStefan Haberland 
read_unit_address_configuration(struct dasd_device * device,struct alias_lcu * lcu)4158e09f215SStefan Weinhuber static int read_unit_address_configuration(struct dasd_device *device,
4168e09f215SStefan Weinhuber 					   struct alias_lcu *lcu)
4178e09f215SStefan Weinhuber {
4188e09f215SStefan Weinhuber 	struct dasd_psf_prssd_data *prssdp;
4198e09f215SStefan Weinhuber 	struct dasd_ccw_req *cqr;
4208e09f215SStefan Weinhuber 	struct ccw1 *ccw;
4218e09f215SStefan Weinhuber 	int rc;
4228e09f215SStefan Weinhuber 	unsigned long flags;
4238e09f215SStefan Weinhuber 
424ec530174SSebastian Ott 	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */	+ 1 /* RSSD */,
4258e09f215SStefan Weinhuber 				   (sizeof(struct dasd_psf_prssd_data)),
426ec530174SSebastian Ott 				   device, NULL);
4278e09f215SStefan Weinhuber 	if (IS_ERR(cqr))
4288e09f215SStefan Weinhuber 		return PTR_ERR(cqr);
4298e09f215SStefan Weinhuber 	cqr->startdev = device;
4308e09f215SStefan Weinhuber 	cqr->memdev = device;
4318e09f215SStefan Weinhuber 	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
4328e09f215SStefan Weinhuber 	cqr->retries = 10;
4338e09f215SStefan Weinhuber 	cqr->expires = 20 * HZ;
4348e09f215SStefan Weinhuber 
4358e09f215SStefan Weinhuber 	/* Prepare for Read Subsystem Data */
4368e09f215SStefan Weinhuber 	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
4378e09f215SStefan Weinhuber 	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
4388e09f215SStefan Weinhuber 	prssdp->order = PSF_ORDER_PRSSD;
4398e09f215SStefan Weinhuber 	prssdp->suborder = 0x0e;	/* Read unit address configuration */
4408e09f215SStefan Weinhuber 	/* all other bytes of prssdp must be zero */
4418e09f215SStefan Weinhuber 
4428e09f215SStefan Weinhuber 	ccw = cqr->cpaddr;
4438e09f215SStefan Weinhuber 	ccw->cmd_code = DASD_ECKD_CCW_PSF;
4448e09f215SStefan Weinhuber 	ccw->count = sizeof(struct dasd_psf_prssd_data);
4458e09f215SStefan Weinhuber 	ccw->flags |= CCW_FLAG_CC;
446*b87c52e4SAlexander Gordeev 	ccw->cda = (__u32)virt_to_phys(prssdp);
4478e09f215SStefan Weinhuber 
4488e09f215SStefan Weinhuber 	/* Read Subsystem Data - feature codes */
4498e09f215SStefan Weinhuber 	memset(lcu->uac, 0, sizeof(*(lcu->uac)));
4508e09f215SStefan Weinhuber 
4518e09f215SStefan Weinhuber 	ccw++;
4528e09f215SStefan Weinhuber 	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
4538e09f215SStefan Weinhuber 	ccw->count = sizeof(*(lcu->uac));
454*b87c52e4SAlexander Gordeev 	ccw->cda = (__u32)virt_to_phys(lcu->uac);
4558e09f215SStefan Weinhuber 
4561aae0560SHeiko Carstens 	cqr->buildclk = get_tod_clock();
4578e09f215SStefan Weinhuber 	cqr->status = DASD_CQR_FILLED;
4588e09f215SStefan Weinhuber 
4598e09f215SStefan Weinhuber 	/* need to unset flag here to detect race with summary unit check */
4608e09f215SStefan Weinhuber 	spin_lock_irqsave(&lcu->lock, flags);
4618e09f215SStefan Weinhuber 	lcu->flags &= ~NEED_UAC_UPDATE;
4628e09f215SStefan Weinhuber 	spin_unlock_irqrestore(&lcu->lock, flags);
4638e09f215SStefan Weinhuber 
4648e09f215SStefan Weinhuber 	rc = dasd_sleep_on(cqr);
465658a337aSStefan Haberland 	if (!rc)
466658a337aSStefan Haberland 		goto out;
467658a337aSStefan Haberland 
468658a337aSStefan Haberland 	if (suborder_not_supported(cqr)) {
469658a337aSStefan Haberland 		/* suborder not supported or device unusable for IO */
470658a337aSStefan Haberland 		rc = -EOPNOTSUPP;
471658a337aSStefan Haberland 	} else {
472658a337aSStefan Haberland 		/* IO failed but should be retried */
4738e09f215SStefan Weinhuber 		spin_lock_irqsave(&lcu->lock, flags);
4748e09f215SStefan Weinhuber 		lcu->flags |= NEED_UAC_UPDATE;
4758e09f215SStefan Weinhuber 		spin_unlock_irqrestore(&lcu->lock, flags);
4768e09f215SStefan Weinhuber 	}
477658a337aSStefan Haberland out:
478ec530174SSebastian Ott 	dasd_sfree_request(cqr, cqr->memdev);
4798e09f215SStefan Weinhuber 	return rc;
4808e09f215SStefan Weinhuber }
4818e09f215SStefan Weinhuber 
_lcu_update(struct dasd_device * refdev,struct alias_lcu * lcu)4828e09f215SStefan Weinhuber static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu)
4838e09f215SStefan Weinhuber {
4848e09f215SStefan Weinhuber 	unsigned long flags;
4858e09f215SStefan Weinhuber 	struct alias_pav_group *pavgroup, *tempgroup;
4868e09f215SStefan Weinhuber 	struct dasd_device *device, *tempdev;
4878e09f215SStefan Weinhuber 	int i, rc;
4888e09f215SStefan Weinhuber 	struct dasd_eckd_private *private;
4898e09f215SStefan Weinhuber 
4908e09f215SStefan Weinhuber 	spin_lock_irqsave(&lcu->lock, flags);
4918e09f215SStefan Weinhuber 	list_for_each_entry_safe(pavgroup, tempgroup, &lcu->grouplist, group) {
4928e09f215SStefan Weinhuber 		list_for_each_entry_safe(device, tempdev, &pavgroup->baselist,
4938e09f215SStefan Weinhuber 					 alias_list) {
4948e09f215SStefan Weinhuber 			list_move(&device->alias_list, &lcu->active_devices);
495543691a4SSebastian Ott 			private = device->private;
4968e09f215SStefan Weinhuber 			private->pavgroup = NULL;
4978e09f215SStefan Weinhuber 		}
4988e09f215SStefan Weinhuber 		list_for_each_entry_safe(device, tempdev, &pavgroup->aliaslist,
4998e09f215SStefan Weinhuber 					 alias_list) {
5008e09f215SStefan Weinhuber 			list_move(&device->alias_list, &lcu->active_devices);
501543691a4SSebastian Ott 			private = device->private;
5028e09f215SStefan Weinhuber 			private->pavgroup = NULL;
5038e09f215SStefan Weinhuber 		}
5048e09f215SStefan Weinhuber 		list_del(&pavgroup->group);
5058e09f215SStefan Weinhuber 		kfree(pavgroup);
5068e09f215SStefan Weinhuber 	}
5078e09f215SStefan Weinhuber 	spin_unlock_irqrestore(&lcu->lock, flags);
5088e09f215SStefan Weinhuber 
5098e09f215SStefan Weinhuber 	rc = read_unit_address_configuration(refdev, lcu);
5108e09f215SStefan Weinhuber 	if (rc)
5118e09f215SStefan Weinhuber 		return rc;
5128e09f215SStefan Weinhuber 
51359a9ed5fSStefan Haberland 	spin_lock_irqsave(&lcu->lock, flags);
514a29ea016SStefan Haberland 	/*
515a29ea016SStefan Haberland 	 * there is another update needed skip the remaining handling
516a29ea016SStefan Haberland 	 * the data might already be outdated
517a29ea016SStefan Haberland 	 * but especially do not add the device to an LCU with pending
518a29ea016SStefan Haberland 	 * update
519a29ea016SStefan Haberland 	 */
520a29ea016SStefan Haberland 	if (lcu->flags & NEED_UAC_UPDATE)
521a29ea016SStefan Haberland 		goto out;
5228e09f215SStefan Weinhuber 	lcu->pav = NO_PAV;
5238e09f215SStefan Weinhuber 	for (i = 0; i < MAX_DEVICES_PER_LCU; ++i) {
5248e09f215SStefan Weinhuber 		switch (lcu->uac->unit[i].ua_type) {
5258e09f215SStefan Weinhuber 		case UA_BASE_PAV_ALIAS:
5268e09f215SStefan Weinhuber 			lcu->pav = BASE_PAV;
5278e09f215SStefan Weinhuber 			break;
5288e09f215SStefan Weinhuber 		case UA_HYPER_PAV_ALIAS:
5298e09f215SStefan Weinhuber 			lcu->pav = HYPER_PAV;
5308e09f215SStefan Weinhuber 			break;
5318e09f215SStefan Weinhuber 		}
5328e09f215SStefan Weinhuber 		if (lcu->pav != NO_PAV)
5338e09f215SStefan Weinhuber 			break;
5348e09f215SStefan Weinhuber 	}
5358e09f215SStefan Weinhuber 
5368e09f215SStefan Weinhuber 	list_for_each_entry_safe(device, tempdev, &lcu->active_devices,
5378e09f215SStefan Weinhuber 				 alias_list) {
5382dedf0d9SStefan Haberland 		_add_device_to_lcu(lcu, device, refdev);
5398e09f215SStefan Weinhuber 	}
540a29ea016SStefan Haberland out:
5419bfefde7SStefan Haberland 	spin_unlock_irqrestore(&lcu->lock, flags);
5428e09f215SStefan Weinhuber 	return 0;
5438e09f215SStefan Weinhuber }
5448e09f215SStefan Weinhuber 
lcu_update_work(struct work_struct * work)5458e09f215SStefan Weinhuber static void lcu_update_work(struct work_struct *work)
5468e09f215SStefan Weinhuber {
5478e09f215SStefan Weinhuber 	struct alias_lcu *lcu;
5488e09f215SStefan Weinhuber 	struct read_uac_work_data *ruac_data;
5498e09f215SStefan Weinhuber 	struct dasd_device *device;
5508e09f215SStefan Weinhuber 	unsigned long flags;
5518e09f215SStefan Weinhuber 	int rc;
5528e09f215SStefan Weinhuber 
5538e09f215SStefan Weinhuber 	ruac_data = container_of(work, struct read_uac_work_data, dwork.work);
5548e09f215SStefan Weinhuber 	lcu = container_of(ruac_data, struct alias_lcu, ruac_data);
5558e09f215SStefan Weinhuber 	device = ruac_data->device;
5568e09f215SStefan Weinhuber 	rc = _lcu_update(device, lcu);
5578e09f215SStefan Weinhuber 	/*
5588e09f215SStefan Weinhuber 	 * Need to check flags again, as there could have been another
5598e09f215SStefan Weinhuber 	 * prepare_update or a new device a new device while we were still
5608e09f215SStefan Weinhuber 	 * processing the data
5618e09f215SStefan Weinhuber 	 */
5628e09f215SStefan Weinhuber 	spin_lock_irqsave(&lcu->lock, flags);
56303429f34SStefan Haberland 	if ((rc && (rc != -EOPNOTSUPP)) || (lcu->flags & NEED_UAC_UPDATE)) {
564fc19f381SStefan Haberland 		DBF_DEV_EVENT(DBF_WARNING, device, "could not update"
5658e09f215SStefan Weinhuber 			    " alias data in lcu (rc = %d), retry later", rc);
5669d862abaSStefan Haberland 		if (!schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ))
5679d862abaSStefan Haberland 			dasd_put_device(device);
5688e09f215SStefan Weinhuber 	} else {
5699d862abaSStefan Haberland 		dasd_put_device(device);
5708e09f215SStefan Weinhuber 		lcu->ruac_data.device = NULL;
5718e09f215SStefan Weinhuber 		lcu->flags &= ~UPDATE_PENDING;
5728e09f215SStefan Weinhuber 	}
5738e09f215SStefan Weinhuber 	spin_unlock_irqrestore(&lcu->lock, flags);
5748e09f215SStefan Weinhuber }
5758e09f215SStefan Weinhuber 
_schedule_lcu_update(struct alias_lcu * lcu,struct dasd_device * device)5768e09f215SStefan Weinhuber static int _schedule_lcu_update(struct alias_lcu *lcu,
5778e09f215SStefan Weinhuber 				struct dasd_device *device)
5788e09f215SStefan Weinhuber {
5798e09f215SStefan Weinhuber 	struct dasd_device *usedev = NULL;
5808e09f215SStefan Weinhuber 	struct alias_pav_group *group;
5818e09f215SStefan Weinhuber 
5828e09f215SStefan Weinhuber 	lcu->flags |= NEED_UAC_UPDATE;
5838e09f215SStefan Weinhuber 	if (lcu->ruac_data.device) {
5848e09f215SStefan Weinhuber 		/* already scheduled or running */
5858e09f215SStefan Weinhuber 		return 0;
5868e09f215SStefan Weinhuber 	}
5878e09f215SStefan Weinhuber 	if (device && !list_empty(&device->alias_list))
5888e09f215SStefan Weinhuber 		usedev = device;
5898e09f215SStefan Weinhuber 
5908e09f215SStefan Weinhuber 	if (!usedev && !list_empty(&lcu->grouplist)) {
5918e09f215SStefan Weinhuber 		group = list_first_entry(&lcu->grouplist,
5928e09f215SStefan Weinhuber 					 struct alias_pav_group, group);
5938e09f215SStefan Weinhuber 		if (!list_empty(&group->baselist))
5948e09f215SStefan Weinhuber 			usedev = list_first_entry(&group->baselist,
5958e09f215SStefan Weinhuber 						  struct dasd_device,
5968e09f215SStefan Weinhuber 						  alias_list);
5978e09f215SStefan Weinhuber 		else if (!list_empty(&group->aliaslist))
5988e09f215SStefan Weinhuber 			usedev = list_first_entry(&group->aliaslist,
5998e09f215SStefan Weinhuber 						  struct dasd_device,
6008e09f215SStefan Weinhuber 						  alias_list);
6018e09f215SStefan Weinhuber 	}
6028e09f215SStefan Weinhuber 	if (!usedev && !list_empty(&lcu->active_devices)) {
6038e09f215SStefan Weinhuber 		usedev = list_first_entry(&lcu->active_devices,
6048e09f215SStefan Weinhuber 					  struct dasd_device, alias_list);
6058e09f215SStefan Weinhuber 	}
6068e09f215SStefan Weinhuber 	/*
6078e09f215SStefan Weinhuber 	 * if we haven't found a proper device yet, give up for now, the next
6088e09f215SStefan Weinhuber 	 * device that will be set active will trigger an lcu update
6098e09f215SStefan Weinhuber 	 */
6108e09f215SStefan Weinhuber 	if (!usedev)
6118e09f215SStefan Weinhuber 		return -EINVAL;
6129d862abaSStefan Haberland 	dasd_get_device(usedev);
6138e09f215SStefan Weinhuber 	lcu->ruac_data.device = usedev;
6149d862abaSStefan Haberland 	if (!schedule_delayed_work(&lcu->ruac_data.dwork, 0))
6159d862abaSStefan Haberland 		dasd_put_device(usedev);
6168e09f215SStefan Weinhuber 	return 0;
6178e09f215SStefan Weinhuber }
6188e09f215SStefan Weinhuber 
dasd_alias_add_device(struct dasd_device * device)6198e09f215SStefan Weinhuber int dasd_alias_add_device(struct dasd_device *device)
6208e09f215SStefan Weinhuber {
621543691a4SSebastian Ott 	struct dasd_eckd_private *private = device->private;
6225d27a2bfSStefan Haberland 	__u8 uaddr = private->uid.real_unit_addr;
6235d27a2bfSStefan Haberland 	struct alias_lcu *lcu = private->lcu;
6248e09f215SStefan Weinhuber 	unsigned long flags;
6258e09f215SStefan Weinhuber 	int rc;
6268e09f215SStefan Weinhuber 
6278e09f215SStefan Weinhuber 	rc = 0;
62859a9ed5fSStefan Haberland 	spin_lock_irqsave(&lcu->lock, flags);
6295d27a2bfSStefan Haberland 	/*
6305d27a2bfSStefan Haberland 	 * Check if device and lcu type differ. If so, the uac data may be
6315d27a2bfSStefan Haberland 	 * outdated and needs to be updated.
6325d27a2bfSStefan Haberland 	 */
6335d27a2bfSStefan Haberland 	if (private->uid.type !=  lcu->uac->unit[uaddr].ua_type) {
6345d27a2bfSStefan Haberland 		lcu->flags |= UPDATE_PENDING;
6355d27a2bfSStefan Haberland 		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
6365d27a2bfSStefan Haberland 			      "uid type mismatch - trigger rescan");
6375d27a2bfSStefan Haberland 	}
6388e09f215SStefan Weinhuber 	if (!(lcu->flags & UPDATE_PENDING)) {
6392dedf0d9SStefan Haberland 		rc = _add_device_to_lcu(lcu, device, device);
6408e09f215SStefan Weinhuber 		if (rc)
6418e09f215SStefan Weinhuber 			lcu->flags |= UPDATE_PENDING;
6428e09f215SStefan Weinhuber 	}
6438e09f215SStefan Weinhuber 	if (lcu->flags & UPDATE_PENDING) {
6448e09f215SStefan Weinhuber 		list_move(&device->alias_list, &lcu->active_devices);
6450ede91f8SStefan Haberland 		private->pavgroup = NULL;
6468e09f215SStefan Weinhuber 		_schedule_lcu_update(lcu, device);
6478e09f215SStefan Weinhuber 	}
64859a9ed5fSStefan Haberland 	spin_unlock_irqrestore(&lcu->lock, flags);
6498e09f215SStefan Weinhuber 	return rc;
6508e09f215SStefan Weinhuber }
6518e09f215SStefan Weinhuber 
dasd_alias_update_add_device(struct dasd_device * device)652501183f2SStefan Haberland int dasd_alias_update_add_device(struct dasd_device *device)
653501183f2SStefan Haberland {
654543691a4SSebastian Ott 	struct dasd_eckd_private *private = device->private;
655543691a4SSebastian Ott 
656501183f2SStefan Haberland 	private->lcu->flags |= UPDATE_PENDING;
657501183f2SStefan Haberland 	return dasd_alias_add_device(device);
658501183f2SStefan Haberland }
659501183f2SStefan Haberland 
dasd_alias_remove_device(struct dasd_device * device)6608e09f215SStefan Weinhuber int dasd_alias_remove_device(struct dasd_device *device)
6618e09f215SStefan Weinhuber {
662543691a4SSebastian Ott 	struct dasd_eckd_private *private = device->private;
663543691a4SSebastian Ott 	struct alias_lcu *lcu = private->lcu;
6648e09f215SStefan Weinhuber 	unsigned long flags;
6658e09f215SStefan Weinhuber 
666f602f6d6SStefan Haberland 	/* nothing to do if already removed */
667f602f6d6SStefan Haberland 	if (!lcu)
668f602f6d6SStefan Haberland 		return 0;
6698e09f215SStefan Weinhuber 	spin_lock_irqsave(&lcu->lock, flags);
6708e09f215SStefan Weinhuber 	_remove_device_from_lcu(lcu, device);
6718e09f215SStefan Weinhuber 	spin_unlock_irqrestore(&lcu->lock, flags);
6728e09f215SStefan Weinhuber 	return 0;
6738e09f215SStefan Weinhuber }
6748e09f215SStefan Weinhuber 
dasd_alias_get_start_dev(struct dasd_device * base_device)6758e09f215SStefan Weinhuber struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device)
6768e09f215SStefan Weinhuber {
677543691a4SSebastian Ott 	struct dasd_eckd_private *alias_priv, *private = base_device->private;
678543691a4SSebastian Ott 	struct alias_lcu *lcu = private->lcu;
6798e09f215SStefan Weinhuber 	struct dasd_device *alias_device;
680db7ba071SStefan Haberland 	struct alias_pav_group *group;
6818e09f215SStefan Weinhuber 	unsigned long flags;
6828e09f215SStefan Weinhuber 
683db7ba071SStefan Haberland 	if (!lcu)
6848e09f215SStefan Weinhuber 		return NULL;
6858e09f215SStefan Weinhuber 	if (lcu->pav == NO_PAV ||
6868e09f215SStefan Weinhuber 	    lcu->flags & (NEED_UAC_UPDATE | UPDATE_PENDING))
6878e09f215SStefan Weinhuber 		return NULL;
688b38f27e8SStefan Haberland 	if (unlikely(!(private->features.feature[8] & 0x01))) {
689b38f27e8SStefan Haberland 		/*
690b38f27e8SStefan Haberland 		 * PAV enabled but prefix not, very unlikely
691b38f27e8SStefan Haberland 		 * seems to be a lost pathgroup
692b38f27e8SStefan Haberland 		 * use base device to do IO
693b38f27e8SStefan Haberland 		 */
694b38f27e8SStefan Haberland 		DBF_DEV_EVENT(DBF_ERR, base_device, "%s",
695b38f27e8SStefan Haberland 			      "Prefix not enabled with PAV enabled\n");
696b38f27e8SStefan Haberland 		return NULL;
697b38f27e8SStefan Haberland 	}
6988e09f215SStefan Weinhuber 
6998e09f215SStefan Weinhuber 	spin_lock_irqsave(&lcu->lock, flags);
700db7ba071SStefan Haberland 	group = private->pavgroup;
701db7ba071SStefan Haberland 	if (!group) {
702db7ba071SStefan Haberland 		spin_unlock_irqrestore(&lcu->lock, flags);
703db7ba071SStefan Haberland 		return NULL;
704db7ba071SStefan Haberland 	}
7058e09f215SStefan Weinhuber 	alias_device = group->next;
7068e09f215SStefan Weinhuber 	if (!alias_device) {
7078e09f215SStefan Weinhuber 		if (list_empty(&group->aliaslist)) {
7088e09f215SStefan Weinhuber 			spin_unlock_irqrestore(&lcu->lock, flags);
7098e09f215SStefan Weinhuber 			return NULL;
7108e09f215SStefan Weinhuber 		} else {
7118e09f215SStefan Weinhuber 			alias_device = list_first_entry(&group->aliaslist,
7128e09f215SStefan Weinhuber 							struct dasd_device,
7138e09f215SStefan Weinhuber 							alias_list);
7148e09f215SStefan Weinhuber 		}
7158e09f215SStefan Weinhuber 	}
7168e09f215SStefan Weinhuber 	if (list_is_last(&alias_device->alias_list, &group->aliaslist))
7178e09f215SStefan Weinhuber 		group->next = list_first_entry(&group->aliaslist,
7188e09f215SStefan Weinhuber 					       struct dasd_device, alias_list);
7198e09f215SStefan Weinhuber 	else
7208e09f215SStefan Weinhuber 		group->next = list_first_entry(&alias_device->alias_list,
7218e09f215SStefan Weinhuber 					       struct dasd_device, alias_list);
7228e09f215SStefan Weinhuber 	spin_unlock_irqrestore(&lcu->lock, flags);
723543691a4SSebastian Ott 	alias_priv = alias_device->private;
724f81a49d1SStefan Haberland 	if ((alias_priv->count < private->count) && !alias_device->stopped &&
725f81a49d1SStefan Haberland 	    !test_bit(DASD_FLAG_OFFLINE, &alias_device->flags))
7268e09f215SStefan Weinhuber 		return alias_device;
7278e09f215SStefan Weinhuber 	else
7288e09f215SStefan Weinhuber 		return NULL;
7298e09f215SStefan Weinhuber }
7308e09f215SStefan Weinhuber 
7318e09f215SStefan Weinhuber /*
7328e09f215SStefan Weinhuber  * Summary unit check handling depends on the way alias devices
7338e09f215SStefan Weinhuber  * are handled so it is done here rather then in dasd_eckd.c
7348e09f215SStefan Weinhuber  */
reset_summary_unit_check(struct alias_lcu * lcu,struct dasd_device * device,char reason)7358e09f215SStefan Weinhuber static int reset_summary_unit_check(struct alias_lcu *lcu,
7368e09f215SStefan Weinhuber 				    struct dasd_device *device,
7378e09f215SStefan Weinhuber 				    char reason)
7388e09f215SStefan Weinhuber {
7398e09f215SStefan Weinhuber 	struct dasd_ccw_req *cqr;
7408e09f215SStefan Weinhuber 	int rc = 0;
741f3eb5384SStefan Weinhuber 	struct ccw1 *ccw;
7428e09f215SStefan Weinhuber 
7438e09f215SStefan Weinhuber 	cqr = lcu->rsu_cqr;
7444e0f5e91SVasily Gorbik 	memcpy((char *) &cqr->magic, "ECKD", 4);
7458e09f215SStefan Weinhuber 	ASCEBC((char *) &cqr->magic, 4);
746f3eb5384SStefan Weinhuber 	ccw = cqr->cpaddr;
747f3eb5384SStefan Weinhuber 	ccw->cmd_code = DASD_ECKD_CCW_RSCK;
748020bf042SStefan Haberland 	ccw->flags = CCW_FLAG_SLI;
749f3eb5384SStefan Weinhuber 	ccw->count = 16;
750*b87c52e4SAlexander Gordeev 	ccw->cda = (__u32)virt_to_phys(cqr->data);
7518e09f215SStefan Weinhuber 	((char *)cqr->data)[0] = reason;
7528e09f215SStefan Weinhuber 
7538e09f215SStefan Weinhuber 	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
7548e09f215SStefan Weinhuber 	cqr->retries = 255;	/* set retry counter to enable basic ERP */
7558e09f215SStefan Weinhuber 	cqr->startdev = device;
7568e09f215SStefan Weinhuber 	cqr->memdev = device;
7578e09f215SStefan Weinhuber 	cqr->block = NULL;
7588e09f215SStefan Weinhuber 	cqr->expires = 5 * HZ;
7591aae0560SHeiko Carstens 	cqr->buildclk = get_tod_clock();
7608e09f215SStefan Weinhuber 	cqr->status = DASD_CQR_FILLED;
7618e09f215SStefan Weinhuber 
7628e09f215SStefan Weinhuber 	rc = dasd_sleep_on_immediatly(cqr);
7638e09f215SStefan Weinhuber 	return rc;
7648e09f215SStefan Weinhuber }
7658e09f215SStefan Weinhuber 
_restart_all_base_devices_on_lcu(struct alias_lcu * lcu)7668e09f215SStefan Weinhuber static void _restart_all_base_devices_on_lcu(struct alias_lcu *lcu)
7678e09f215SStefan Weinhuber {
7688e09f215SStefan Weinhuber 	struct alias_pav_group *pavgroup;
7698e09f215SStefan Weinhuber 	struct dasd_device *device;
7708e09f215SStefan Weinhuber 	struct dasd_eckd_private *private;
7718e09f215SStefan Weinhuber 
7728e09f215SStefan Weinhuber 	/* active and inactive list can contain alias as well as base devices */
7738e09f215SStefan Weinhuber 	list_for_each_entry(device, &lcu->active_devices, alias_list) {
774543691a4SSebastian Ott 		private = device->private;
7759bfefde7SStefan Haberland 		if (private->uid.type != UA_BASE_DEVICE)
7768e09f215SStefan Weinhuber 			continue;
7778e09f215SStefan Weinhuber 		dasd_schedule_block_bh(device->block);
7788e09f215SStefan Weinhuber 		dasd_schedule_device_bh(device);
7798e09f215SStefan Weinhuber 	}
7808e09f215SStefan Weinhuber 	list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
781543691a4SSebastian Ott 		private = device->private;
7829bfefde7SStefan Haberland 		if (private->uid.type != UA_BASE_DEVICE)
7838e09f215SStefan Weinhuber 			continue;
7848e09f215SStefan Weinhuber 		dasd_schedule_block_bh(device->block);
7858e09f215SStefan Weinhuber 		dasd_schedule_device_bh(device);
7868e09f215SStefan Weinhuber 	}
7878e09f215SStefan Weinhuber 	list_for_each_entry(pavgroup, &lcu->grouplist, group) {
7888e09f215SStefan Weinhuber 		list_for_each_entry(device, &pavgroup->baselist, alias_list) {
7898e09f215SStefan Weinhuber 			dasd_schedule_block_bh(device->block);
7908e09f215SStefan Weinhuber 			dasd_schedule_device_bh(device);
7918e09f215SStefan Weinhuber 		}
7928e09f215SStefan Weinhuber 	}
7938e09f215SStefan Weinhuber }
7948e09f215SStefan Weinhuber 
flush_all_alias_devices_on_lcu(struct alias_lcu * lcu)7958e09f215SStefan Weinhuber static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu)
7968e09f215SStefan Weinhuber {
7978e09f215SStefan Weinhuber 	struct alias_pav_group *pavgroup;
7988e09f215SStefan Weinhuber 	struct dasd_device *device, *temp;
7998e09f215SStefan Weinhuber 	struct dasd_eckd_private *private;
8008e09f215SStefan Weinhuber 	unsigned long flags;
8018e09f215SStefan Weinhuber 	LIST_HEAD(active);
8028e09f215SStefan Weinhuber 
8038e09f215SStefan Weinhuber 	/*
8048e09f215SStefan Weinhuber 	 * Problem here ist that dasd_flush_device_queue may wait
8058e09f215SStefan Weinhuber 	 * for termination of a request to complete. We can't keep
8068e09f215SStefan Weinhuber 	 * the lcu lock during that time, so we must assume that
8078e09f215SStefan Weinhuber 	 * the lists may have changed.
8088e09f215SStefan Weinhuber 	 * Idea: first gather all active alias devices in a separate list,
8098e09f215SStefan Weinhuber 	 * then flush the first element of this list unlocked, and afterwards
8108e09f215SStefan Weinhuber 	 * check if it is still on the list before moving it to the
8118e09f215SStefan Weinhuber 	 * active_devices list.
8128e09f215SStefan Weinhuber 	 */
8138e09f215SStefan Weinhuber 
8148e09f215SStefan Weinhuber 	spin_lock_irqsave(&lcu->lock, flags);
8158e09f215SStefan Weinhuber 	list_for_each_entry_safe(device, temp, &lcu->active_devices,
8168e09f215SStefan Weinhuber 				 alias_list) {
817543691a4SSebastian Ott 		private = device->private;
8188e09f215SStefan Weinhuber 		if (private->uid.type == UA_BASE_DEVICE)
8198e09f215SStefan Weinhuber 			continue;
8208e09f215SStefan Weinhuber 		list_move(&device->alias_list, &active);
8218e09f215SStefan Weinhuber 	}
8228e09f215SStefan Weinhuber 
8238e09f215SStefan Weinhuber 	list_for_each_entry(pavgroup, &lcu->grouplist, group) {
8248e09f215SStefan Weinhuber 		list_splice_init(&pavgroup->aliaslist, &active);
8258e09f215SStefan Weinhuber 	}
8268e09f215SStefan Weinhuber 	while (!list_empty(&active)) {
8278e09f215SStefan Weinhuber 		device = list_first_entry(&active, struct dasd_device,
8288e09f215SStefan Weinhuber 					  alias_list);
8298e09f215SStefan Weinhuber 		spin_unlock_irqrestore(&lcu->lock, flags);
8304bca698fSSebastian Ott 		dasd_flush_device_queue(device);
8318e09f215SStefan Weinhuber 		spin_lock_irqsave(&lcu->lock, flags);
8328e09f215SStefan Weinhuber 		/*
8338e09f215SStefan Weinhuber 		 * only move device around if it wasn't moved away while we
8348e09f215SStefan Weinhuber 		 * were waiting for the flush
8358e09f215SStefan Weinhuber 		 */
8368e09f215SStefan Weinhuber 		if (device == list_first_entry(&active,
8376933c35aSStefan Haberland 					       struct dasd_device, alias_list)) {
8388e09f215SStefan Weinhuber 			list_move(&device->alias_list, &lcu->active_devices);
839543691a4SSebastian Ott 			private = device->private;
8406933c35aSStefan Haberland 			private->pavgroup = NULL;
8416933c35aSStefan Haberland 		}
8428e09f215SStefan Weinhuber 	}
8438e09f215SStefan Weinhuber 	spin_unlock_irqrestore(&lcu->lock, flags);
8448e09f215SStefan Weinhuber }
8458e09f215SStefan Weinhuber 
_stop_all_devices_on_lcu(struct alias_lcu * lcu)8469bfefde7SStefan Haberland static void _stop_all_devices_on_lcu(struct alias_lcu *lcu)
8478e09f215SStefan Weinhuber {
8488e09f215SStefan Weinhuber 	struct alias_pav_group *pavgroup;
8499bfefde7SStefan Haberland 	struct dasd_device *device;
8508e09f215SStefan Weinhuber 
85159a9ed5fSStefan Haberland 	list_for_each_entry(device, &lcu->active_devices, alias_list) {
85259a9ed5fSStefan Haberland 		spin_lock(get_ccwdev_lock(device->cdev));
8539bfefde7SStefan Haberland 		dasd_device_set_stop_bits(device, DASD_STOPPED_SU);
85459a9ed5fSStefan Haberland 		spin_unlock(get_ccwdev_lock(device->cdev));
85559a9ed5fSStefan Haberland 	}
85659a9ed5fSStefan Haberland 	list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
85759a9ed5fSStefan Haberland 		spin_lock(get_ccwdev_lock(device->cdev));
8589bfefde7SStefan Haberland 		dasd_device_set_stop_bits(device, DASD_STOPPED_SU);
85959a9ed5fSStefan Haberland 		spin_unlock(get_ccwdev_lock(device->cdev));
86059a9ed5fSStefan Haberland 	}
8618e09f215SStefan Weinhuber 	list_for_each_entry(pavgroup, &lcu->grouplist, group) {
86259a9ed5fSStefan Haberland 		list_for_each_entry(device, &pavgroup->baselist, alias_list) {
86359a9ed5fSStefan Haberland 			spin_lock(get_ccwdev_lock(device->cdev));
8649bfefde7SStefan Haberland 			dasd_device_set_stop_bits(device, DASD_STOPPED_SU);
86559a9ed5fSStefan Haberland 			spin_unlock(get_ccwdev_lock(device->cdev));
86659a9ed5fSStefan Haberland 		}
86759a9ed5fSStefan Haberland 		list_for_each_entry(device, &pavgroup->aliaslist, alias_list) {
86859a9ed5fSStefan Haberland 			spin_lock(get_ccwdev_lock(device->cdev));
8699bfefde7SStefan Haberland 			dasd_device_set_stop_bits(device, DASD_STOPPED_SU);
87059a9ed5fSStefan Haberland 			spin_unlock(get_ccwdev_lock(device->cdev));
87159a9ed5fSStefan Haberland 		}
8728e09f215SStefan Weinhuber 	}
8738e09f215SStefan Weinhuber }
8748e09f215SStefan Weinhuber 
_unstop_all_devices_on_lcu(struct alias_lcu * lcu)8758e09f215SStefan Weinhuber static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu)
8768e09f215SStefan Weinhuber {
8778e09f215SStefan Weinhuber 	struct alias_pav_group *pavgroup;
8788e09f215SStefan Weinhuber 	struct dasd_device *device;
8798e09f215SStefan Weinhuber 
88059a9ed5fSStefan Haberland 	list_for_each_entry(device, &lcu->active_devices, alias_list) {
88159a9ed5fSStefan Haberland 		spin_lock(get_ccwdev_lock(device->cdev));
882eb6e199bSStefan Weinhuber 		dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
88359a9ed5fSStefan Haberland 		spin_unlock(get_ccwdev_lock(device->cdev));
88459a9ed5fSStefan Haberland 	}
88559a9ed5fSStefan Haberland 	list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
88659a9ed5fSStefan Haberland 		spin_lock(get_ccwdev_lock(device->cdev));
887eb6e199bSStefan Weinhuber 		dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
88859a9ed5fSStefan Haberland 		spin_unlock(get_ccwdev_lock(device->cdev));
88959a9ed5fSStefan Haberland 	}
8908e09f215SStefan Weinhuber 	list_for_each_entry(pavgroup, &lcu->grouplist, group) {
89159a9ed5fSStefan Haberland 		list_for_each_entry(device, &pavgroup->baselist, alias_list) {
89259a9ed5fSStefan Haberland 			spin_lock(get_ccwdev_lock(device->cdev));
893eb6e199bSStefan Weinhuber 			dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
89459a9ed5fSStefan Haberland 			spin_unlock(get_ccwdev_lock(device->cdev));
89559a9ed5fSStefan Haberland 		}
89659a9ed5fSStefan Haberland 		list_for_each_entry(device, &pavgroup->aliaslist, alias_list) {
89759a9ed5fSStefan Haberland 			spin_lock(get_ccwdev_lock(device->cdev));
898eb6e199bSStefan Weinhuber 			dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
89959a9ed5fSStefan Haberland 			spin_unlock(get_ccwdev_lock(device->cdev));
90059a9ed5fSStefan Haberland 		}
9018e09f215SStefan Weinhuber 	}
9028e09f215SStefan Weinhuber }
9038e09f215SStefan Weinhuber 
summary_unit_check_handling_work(struct work_struct * work)9048e09f215SStefan Weinhuber static void summary_unit_check_handling_work(struct work_struct *work)
9058e09f215SStefan Weinhuber {
9068e09f215SStefan Weinhuber 	struct alias_lcu *lcu;
9078e09f215SStefan Weinhuber 	struct summary_unit_check_work_data *suc_data;
9088e09f215SStefan Weinhuber 	unsigned long flags;
9098e09f215SStefan Weinhuber 	struct dasd_device *device;
9108e09f215SStefan Weinhuber 
9118e09f215SStefan Weinhuber 	suc_data = container_of(work, struct summary_unit_check_work_data,
9128e09f215SStefan Weinhuber 				worker);
9138e09f215SStefan Weinhuber 	lcu = container_of(suc_data, struct alias_lcu, suc_data);
9148e09f215SStefan Weinhuber 	device = suc_data->device;
9158e09f215SStefan Weinhuber 
9168e09f215SStefan Weinhuber 	/* 1. flush alias devices */
9178e09f215SStefan Weinhuber 	flush_all_alias_devices_on_lcu(lcu);
9188e09f215SStefan Weinhuber 
9198e09f215SStefan Weinhuber 	/* 2. reset summary unit check */
9208e09f215SStefan Weinhuber 	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
921eb6e199bSStefan Weinhuber 	dasd_device_remove_stop_bits(device,
922eb6e199bSStefan Weinhuber 				     (DASD_STOPPED_SU | DASD_STOPPED_PENDING));
9238e09f215SStefan Weinhuber 	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
9248e09f215SStefan Weinhuber 	reset_summary_unit_check(lcu, device, suc_data->reason);
9258e09f215SStefan Weinhuber 
92659a9ed5fSStefan Haberland 	spin_lock_irqsave(&lcu->lock, flags);
9278e09f215SStefan Weinhuber 	_unstop_all_devices_on_lcu(lcu);
9288e09f215SStefan Weinhuber 	_restart_all_base_devices_on_lcu(lcu);
9298e09f215SStefan Weinhuber 	/* 3. read new alias configuration */
9308e09f215SStefan Weinhuber 	_schedule_lcu_update(lcu, device);
9318e09f215SStefan Weinhuber 	lcu->suc_data.device = NULL;
9329d862abaSStefan Haberland 	dasd_put_device(device);
9338e09f215SStefan Weinhuber 	spin_unlock_irqrestore(&lcu->lock, flags);
9348e09f215SStefan Weinhuber }
9358e09f215SStefan Weinhuber 
dasd_alias_handle_summary_unit_check(struct work_struct * work)93659a9ed5fSStefan Haberland void dasd_alias_handle_summary_unit_check(struct work_struct *work)
9378e09f215SStefan Weinhuber {
93859a9ed5fSStefan Haberland 	struct dasd_device *device = container_of(work, struct dasd_device,
93959a9ed5fSStefan Haberland 						  suc_work);
940543691a4SSebastian Ott 	struct dasd_eckd_private *private = device->private;
9418e09f215SStefan Weinhuber 	struct alias_lcu *lcu;
94259a9ed5fSStefan Haberland 	unsigned long flags;
9438e09f215SStefan Weinhuber 
9448e09f215SStefan Weinhuber 	lcu = private->lcu;
9458e09f215SStefan Weinhuber 	if (!lcu) {
946fc19f381SStefan Haberland 		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
9478e09f215SStefan Weinhuber 			    "device not ready to handle summary"
9488e09f215SStefan Weinhuber 			    " unit check (no lcu structure)");
94959a9ed5fSStefan Haberland 		goto out;
9508e09f215SStefan Weinhuber 	}
95159a9ed5fSStefan Haberland 	spin_lock_irqsave(&lcu->lock, flags);
9528e09f215SStefan Weinhuber 	/* If this device is about to be removed just return and wait for
9538e09f215SStefan Weinhuber 	 * the next interrupt on a different device
9548e09f215SStefan Weinhuber 	 */
9558e09f215SStefan Weinhuber 	if (list_empty(&device->alias_list)) {
956fc19f381SStefan Haberland 		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
9578e09f215SStefan Weinhuber 			    "device is in offline processing,"
9588e09f215SStefan Weinhuber 			    " don't do summary unit check handling");
95959a9ed5fSStefan Haberland 		goto out_unlock;
9608e09f215SStefan Weinhuber 	}
9618e09f215SStefan Weinhuber 	if (lcu->suc_data.device) {
9628e09f215SStefan Weinhuber 		/* already scheduled or running */
963fc19f381SStefan Haberland 		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
9648e09f215SStefan Weinhuber 			    "previous instance of summary unit check worker"
9658e09f215SStefan Weinhuber 			    " still pending");
96659a9ed5fSStefan Haberland 		goto out_unlock;
9678e09f215SStefan Weinhuber 	}
9689bfefde7SStefan Haberland 	_stop_all_devices_on_lcu(lcu);
9699bfefde7SStefan Haberland 	/* prepare for lcu_update */
97059a9ed5fSStefan Haberland 	lcu->flags |= NEED_UAC_UPDATE | UPDATE_PENDING;
97159a9ed5fSStefan Haberland 	lcu->suc_data.reason = private->suc_reason;
9728e09f215SStefan Weinhuber 	lcu->suc_data.device = device;
9739d862abaSStefan Haberland 	dasd_get_device(device);
9749d862abaSStefan Haberland 	if (!schedule_work(&lcu->suc_data.worker))
9759d862abaSStefan Haberland 		dasd_put_device(device);
97659a9ed5fSStefan Haberland out_unlock:
97759a9ed5fSStefan Haberland 	spin_unlock_irqrestore(&lcu->lock, flags);
97859a9ed5fSStefan Haberland out:
97959a9ed5fSStefan Haberland 	clear_bit(DASD_FLAG_SUC, &device->flags);
98059a9ed5fSStefan Haberland 	dasd_put_device(device);
9818e09f215SStefan Weinhuber };
982