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