1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * PAV alias management for the DASD ECKD discipline |
4 | * |
5 | * Copyright IBM Corp. 2007 |
6 | * Author(s): Stefan Weinhuber <wein@de.ibm.com> |
7 | */ |
8 | |
9 | #define KMSG_COMPONENT "dasd-eckd" |
10 | |
11 | #include <linux/list.h> |
12 | #include <linux/slab.h> |
13 | #include <asm/ebcdic.h> |
14 | #include "dasd_int.h" |
15 | #include "dasd_eckd.h" |
16 | |
17 | #ifdef PRINTK_HEADER |
18 | #undef PRINTK_HEADER |
19 | #endif /* PRINTK_HEADER */ |
20 | #define "dasd(eckd):" |
21 | |
22 | |
23 | /* |
24 | * General concept of alias management: |
25 | * - PAV and DASD alias management is specific to the eckd discipline. |
26 | * - A device is connected to an lcu as long as the device exists. |
27 | * dasd_alias_make_device_known_to_lcu will be called wenn the |
28 | * device is checked by the eckd discipline and |
29 | * dasd_alias_disconnect_device_from_lcu will be called |
30 | * before the device is deleted. |
31 | * - The dasd_alias_add_device / dasd_alias_remove_device |
32 | * functions mark the point when a device is 'ready for service'. |
33 | * - A summary unit check is a rare occasion, but it is mandatory to |
34 | * support it. It requires some complex recovery actions before the |
35 | * devices can be used again (see dasd_alias_handle_summary_unit_check). |
36 | * - dasd_alias_get_start_dev will find an alias device that can be used |
37 | * instead of the base device and does some (very simple) load balancing. |
38 | * This is the function that gets called for each I/O, so when improving |
39 | * something, this function should get faster or better, the rest has just |
40 | * to be correct. |
41 | */ |
42 | |
43 | |
44 | static void summary_unit_check_handling_work(struct work_struct *); |
45 | static void lcu_update_work(struct work_struct *); |
46 | static int _schedule_lcu_update(struct alias_lcu *, struct dasd_device *); |
47 | |
48 | static struct alias_root aliastree = { |
49 | .serverlist = LIST_HEAD_INIT(aliastree.serverlist), |
50 | .lock = __SPIN_LOCK_UNLOCKED(aliastree.lock), |
51 | }; |
52 | |
53 | static struct alias_server *_find_server(struct dasd_uid *uid) |
54 | { |
55 | struct alias_server *pos; |
56 | list_for_each_entry(pos, &aliastree.serverlist, server) { |
57 | if (!strncmp(pos->uid.vendor, uid->vendor, |
58 | sizeof(uid->vendor)) |
59 | && !strncmp(pos->uid.serial, uid->serial, |
60 | sizeof(uid->serial))) |
61 | return pos; |
62 | } |
63 | return NULL; |
64 | } |
65 | |
66 | static struct alias_lcu *_find_lcu(struct alias_server *server, |
67 | struct dasd_uid *uid) |
68 | { |
69 | struct alias_lcu *pos; |
70 | list_for_each_entry(pos, &server->lculist, lcu) { |
71 | if (pos->uid.ssid == uid->ssid) |
72 | return pos; |
73 | } |
74 | return NULL; |
75 | } |
76 | |
77 | static struct alias_pav_group *_find_group(struct alias_lcu *lcu, |
78 | struct dasd_uid *uid) |
79 | { |
80 | struct alias_pav_group *pos; |
81 | __u8 search_unit_addr; |
82 | |
83 | /* for hyper pav there is only one group */ |
84 | if (lcu->pav == HYPER_PAV) { |
85 | if (list_empty(head: &lcu->grouplist)) |
86 | return NULL; |
87 | else |
88 | return list_first_entry(&lcu->grouplist, |
89 | struct alias_pav_group, group); |
90 | } |
91 | |
92 | /* for base pav we have to find the group that matches the base */ |
93 | if (uid->type == UA_BASE_DEVICE) |
94 | search_unit_addr = uid->real_unit_addr; |
95 | else |
96 | search_unit_addr = uid->base_unit_addr; |
97 | list_for_each_entry(pos, &lcu->grouplist, group) { |
98 | if (pos->uid.base_unit_addr == search_unit_addr && |
99 | !strncmp(pos->uid.vduit, uid->vduit, sizeof(uid->vduit))) |
100 | return pos; |
101 | } |
102 | return NULL; |
103 | } |
104 | |
105 | static struct alias_server *_allocate_server(struct dasd_uid *uid) |
106 | { |
107 | struct alias_server *server; |
108 | |
109 | server = kzalloc(size: sizeof(*server), GFP_KERNEL); |
110 | if (!server) |
111 | return ERR_PTR(error: -ENOMEM); |
112 | memcpy(server->uid.vendor, uid->vendor, sizeof(uid->vendor)); |
113 | memcpy(server->uid.serial, uid->serial, sizeof(uid->serial)); |
114 | INIT_LIST_HEAD(list: &server->server); |
115 | INIT_LIST_HEAD(list: &server->lculist); |
116 | return server; |
117 | } |
118 | |
119 | static void _free_server(struct alias_server *server) |
120 | { |
121 | kfree(objp: server); |
122 | } |
123 | |
124 | static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid) |
125 | { |
126 | struct alias_lcu *lcu; |
127 | |
128 | lcu = kzalloc(size: sizeof(*lcu), GFP_KERNEL); |
129 | if (!lcu) |
130 | return ERR_PTR(error: -ENOMEM); |
131 | lcu->uac = kzalloc(size: sizeof(*(lcu->uac)), GFP_KERNEL | GFP_DMA); |
132 | if (!lcu->uac) |
133 | goto out_err1; |
134 | lcu->rsu_cqr = kzalloc(size: sizeof(*lcu->rsu_cqr), GFP_KERNEL | GFP_DMA); |
135 | if (!lcu->rsu_cqr) |
136 | goto out_err2; |
137 | lcu->rsu_cqr->cpaddr = kzalloc(sizeof(struct ccw1), |
138 | GFP_KERNEL | GFP_DMA); |
139 | if (!lcu->rsu_cqr->cpaddr) |
140 | goto out_err3; |
141 | lcu->rsu_cqr->data = kzalloc(size: 16, GFP_KERNEL | GFP_DMA); |
142 | if (!lcu->rsu_cqr->data) |
143 | goto out_err4; |
144 | |
145 | memcpy(lcu->uid.vendor, uid->vendor, sizeof(uid->vendor)); |
146 | memcpy(lcu->uid.serial, uid->serial, sizeof(uid->serial)); |
147 | lcu->uid.ssid = uid->ssid; |
148 | lcu->pav = NO_PAV; |
149 | lcu->flags = NEED_UAC_UPDATE | UPDATE_PENDING; |
150 | INIT_LIST_HEAD(list: &lcu->lcu); |
151 | INIT_LIST_HEAD(list: &lcu->inactive_devices); |
152 | INIT_LIST_HEAD(list: &lcu->active_devices); |
153 | INIT_LIST_HEAD(list: &lcu->grouplist); |
154 | INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work); |
155 | INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work); |
156 | spin_lock_init(&lcu->lock); |
157 | init_completion(x: &lcu->lcu_setup); |
158 | return lcu; |
159 | |
160 | out_err4: |
161 | kfree(objp: lcu->rsu_cqr->cpaddr); |
162 | out_err3: |
163 | kfree(objp: lcu->rsu_cqr); |
164 | out_err2: |
165 | kfree(objp: lcu->uac); |
166 | out_err1: |
167 | kfree(objp: lcu); |
168 | return ERR_PTR(error: -ENOMEM); |
169 | } |
170 | |
171 | static void _free_lcu(struct alias_lcu *lcu) |
172 | { |
173 | kfree(objp: lcu->rsu_cqr->data); |
174 | kfree(objp: lcu->rsu_cqr->cpaddr); |
175 | kfree(objp: lcu->rsu_cqr); |
176 | kfree(objp: lcu->uac); |
177 | kfree(objp: lcu); |
178 | } |
179 | |
180 | /* |
181 | * This is the function that will allocate all the server and lcu data, |
182 | * so this function must be called first for a new device. |
183 | * If the return value is 1, the lcu was already known before, if it |
184 | * is 0, this is a new lcu. |
185 | * Negative return code indicates that something went wrong (e.g. -ENOMEM) |
186 | */ |
187 | int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) |
188 | { |
189 | struct dasd_eckd_private *private = device->private; |
190 | unsigned long flags; |
191 | struct alias_server *server, *newserver; |
192 | struct alias_lcu *lcu, *newlcu; |
193 | struct dasd_uid uid; |
194 | |
195 | device->discipline->get_uid(device, &uid); |
196 | spin_lock_irqsave(&aliastree.lock, flags); |
197 | server = _find_server(uid: &uid); |
198 | if (!server) { |
199 | spin_unlock_irqrestore(lock: &aliastree.lock, flags); |
200 | newserver = _allocate_server(uid: &uid); |
201 | if (IS_ERR(ptr: newserver)) |
202 | return PTR_ERR(ptr: newserver); |
203 | spin_lock_irqsave(&aliastree.lock, flags); |
204 | server = _find_server(uid: &uid); |
205 | if (!server) { |
206 | list_add(new: &newserver->server, head: &aliastree.serverlist); |
207 | server = newserver; |
208 | } else { |
209 | /* someone was faster */ |
210 | _free_server(server: newserver); |
211 | } |
212 | } |
213 | |
214 | lcu = _find_lcu(server, uid: &uid); |
215 | if (!lcu) { |
216 | spin_unlock_irqrestore(lock: &aliastree.lock, flags); |
217 | newlcu = _allocate_lcu(uid: &uid); |
218 | if (IS_ERR(ptr: newlcu)) |
219 | return PTR_ERR(ptr: newlcu); |
220 | spin_lock_irqsave(&aliastree.lock, flags); |
221 | lcu = _find_lcu(server, uid: &uid); |
222 | if (!lcu) { |
223 | list_add(new: &newlcu->lcu, head: &server->lculist); |
224 | lcu = newlcu; |
225 | } else { |
226 | /* someone was faster */ |
227 | _free_lcu(lcu: newlcu); |
228 | } |
229 | } |
230 | spin_lock(lock: &lcu->lock); |
231 | list_add(new: &device->alias_list, head: &lcu->inactive_devices); |
232 | private->lcu = lcu; |
233 | spin_unlock(lock: &lcu->lock); |
234 | spin_unlock_irqrestore(lock: &aliastree.lock, flags); |
235 | |
236 | return 0; |
237 | } |
238 | |
239 | /* |
240 | * This function removes a device from the scope of alias management. |
241 | * The complicated part is to make sure that it is not in use by |
242 | * any of the workers. If necessary cancel the work. |
243 | */ |
244 | void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device) |
245 | { |
246 | struct dasd_eckd_private *private = device->private; |
247 | unsigned long flags; |
248 | struct alias_lcu *lcu; |
249 | struct alias_server *server; |
250 | int was_pending; |
251 | struct dasd_uid uid; |
252 | |
253 | lcu = private->lcu; |
254 | /* nothing to do if already disconnected */ |
255 | if (!lcu) |
256 | return; |
257 | device->discipline->get_uid(device, &uid); |
258 | spin_lock_irqsave(&lcu->lock, flags); |
259 | /* make sure that the workers don't use this device */ |
260 | if (device == lcu->suc_data.device) { |
261 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
262 | cancel_work_sync(work: &lcu->suc_data.worker); |
263 | spin_lock_irqsave(&lcu->lock, flags); |
264 | if (device == lcu->suc_data.device) { |
265 | dasd_put_device(device); |
266 | lcu->suc_data.device = NULL; |
267 | } |
268 | } |
269 | was_pending = 0; |
270 | if (device == lcu->ruac_data.device) { |
271 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
272 | was_pending = 1; |
273 | cancel_delayed_work_sync(dwork: &lcu->ruac_data.dwork); |
274 | spin_lock_irqsave(&lcu->lock, flags); |
275 | if (device == lcu->ruac_data.device) { |
276 | dasd_put_device(device); |
277 | lcu->ruac_data.device = NULL; |
278 | } |
279 | } |
280 | private->lcu = NULL; |
281 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
282 | |
283 | spin_lock_irqsave(&aliastree.lock, flags); |
284 | spin_lock(lock: &lcu->lock); |
285 | list_del_init(entry: &device->alias_list); |
286 | if (list_empty(head: &lcu->grouplist) && |
287 | list_empty(head: &lcu->active_devices) && |
288 | list_empty(head: &lcu->inactive_devices)) { |
289 | list_del(entry: &lcu->lcu); |
290 | spin_unlock(lock: &lcu->lock); |
291 | _free_lcu(lcu); |
292 | lcu = NULL; |
293 | } else { |
294 | if (was_pending) |
295 | _schedule_lcu_update(lcu, NULL); |
296 | spin_unlock(lock: &lcu->lock); |
297 | } |
298 | server = _find_server(uid: &uid); |
299 | if (server && list_empty(head: &server->lculist)) { |
300 | list_del(entry: &server->server); |
301 | _free_server(server); |
302 | } |
303 | spin_unlock_irqrestore(lock: &aliastree.lock, flags); |
304 | } |
305 | |
306 | /* |
307 | * This function assumes that the unit address configuration stored |
308 | * in the lcu is up to date and will update the device uid before |
309 | * adding it to a pav group. |
310 | */ |
311 | |
312 | static int _add_device_to_lcu(struct alias_lcu *lcu, |
313 | struct dasd_device *device, |
314 | struct dasd_device *pos) |
315 | { |
316 | |
317 | struct dasd_eckd_private *private = device->private; |
318 | struct alias_pav_group *group; |
319 | struct dasd_uid uid; |
320 | |
321 | spin_lock(lock: get_ccwdev_lock(device->cdev)); |
322 | private->uid.type = lcu->uac->unit[private->uid.real_unit_addr].ua_type; |
323 | private->uid.base_unit_addr = |
324 | lcu->uac->unit[private->uid.real_unit_addr].base_ua; |
325 | uid = private->uid; |
326 | spin_unlock(lock: get_ccwdev_lock(device->cdev)); |
327 | /* if we have no PAV anyway, we don't need to bother with PAV groups */ |
328 | if (lcu->pav == NO_PAV) { |
329 | list_move(list: &device->alias_list, head: &lcu->active_devices); |
330 | return 0; |
331 | } |
332 | group = _find_group(lcu, uid: &uid); |
333 | if (!group) { |
334 | group = kzalloc(size: sizeof(*group), GFP_ATOMIC); |
335 | if (!group) |
336 | return -ENOMEM; |
337 | memcpy(group->uid.vendor, uid.vendor, sizeof(uid.vendor)); |
338 | memcpy(group->uid.serial, uid.serial, sizeof(uid.serial)); |
339 | group->uid.ssid = uid.ssid; |
340 | if (uid.type == UA_BASE_DEVICE) |
341 | group->uid.base_unit_addr = uid.real_unit_addr; |
342 | else |
343 | group->uid.base_unit_addr = uid.base_unit_addr; |
344 | memcpy(group->uid.vduit, uid.vduit, sizeof(uid.vduit)); |
345 | INIT_LIST_HEAD(list: &group->group); |
346 | INIT_LIST_HEAD(list: &group->baselist); |
347 | INIT_LIST_HEAD(list: &group->aliaslist); |
348 | list_add(new: &group->group, head: &lcu->grouplist); |
349 | } |
350 | if (uid.type == UA_BASE_DEVICE) |
351 | list_move(list: &device->alias_list, head: &group->baselist); |
352 | else |
353 | list_move(list: &device->alias_list, head: &group->aliaslist); |
354 | private->pavgroup = group; |
355 | return 0; |
356 | }; |
357 | |
358 | static void _remove_device_from_lcu(struct alias_lcu *lcu, |
359 | struct dasd_device *device) |
360 | { |
361 | struct dasd_eckd_private *private = device->private; |
362 | struct alias_pav_group *group; |
363 | |
364 | list_move(list: &device->alias_list, head: &lcu->inactive_devices); |
365 | group = private->pavgroup; |
366 | if (!group) |
367 | return; |
368 | private->pavgroup = NULL; |
369 | if (list_empty(head: &group->baselist) && list_empty(head: &group->aliaslist)) { |
370 | list_del(entry: &group->group); |
371 | kfree(objp: group); |
372 | return; |
373 | } |
374 | if (group->next == device) |
375 | group->next = NULL; |
376 | }; |
377 | |
378 | static int |
379 | suborder_not_supported(struct dasd_ccw_req *cqr) |
380 | { |
381 | char *sense; |
382 | char reason; |
383 | char msg_format; |
384 | char msg_no; |
385 | |
386 | /* |
387 | * intrc values ENODEV, ENOLINK and EPERM |
388 | * will be optained from sleep_on to indicate that no |
389 | * IO operation can be started |
390 | */ |
391 | if (cqr->intrc == -ENODEV) |
392 | return 1; |
393 | |
394 | if (cqr->intrc == -ENOLINK) |
395 | return 1; |
396 | |
397 | if (cqr->intrc == -EPERM) |
398 | return 1; |
399 | |
400 | sense = dasd_get_sense(&cqr->irb); |
401 | if (!sense) |
402 | return 0; |
403 | |
404 | reason = sense[0]; |
405 | msg_format = (sense[7] & 0xF0); |
406 | msg_no = (sense[7] & 0x0F); |
407 | |
408 | /* command reject, Format 0 MSG 4 - invalid parameter */ |
409 | if ((reason == 0x80) && (msg_format == 0x00) && (msg_no == 0x04)) |
410 | return 1; |
411 | |
412 | return 0; |
413 | } |
414 | |
415 | static int read_unit_address_configuration(struct dasd_device *device, |
416 | struct alias_lcu *lcu) |
417 | { |
418 | struct dasd_psf_prssd_data *; |
419 | struct dasd_ccw_req *cqr; |
420 | struct ccw1 *ccw; |
421 | int rc; |
422 | unsigned long flags; |
423 | |
424 | cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, |
425 | (sizeof(struct dasd_psf_prssd_data)), |
426 | device, NULL); |
427 | if (IS_ERR(ptr: cqr)) |
428 | return PTR_ERR(ptr: cqr); |
429 | cqr->startdev = device; |
430 | cqr->memdev = device; |
431 | clear_bit(DASD_CQR_FLAGS_USE_ERP, addr: &cqr->flags); |
432 | cqr->retries = 10; |
433 | cqr->expires = 20 * HZ; |
434 | |
435 | /* Prepare for Read Subsystem Data */ |
436 | prssdp = (struct dasd_psf_prssd_data *) cqr->data; |
437 | memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); |
438 | prssdp->order = PSF_ORDER_PRSSD; |
439 | prssdp->suborder = 0x0e; /* Read unit address configuration */ |
440 | /* all other bytes of prssdp must be zero */ |
441 | |
442 | ccw = cqr->cpaddr; |
443 | ccw->cmd_code = DASD_ECKD_CCW_PSF; |
444 | ccw->count = sizeof(struct dasd_psf_prssd_data); |
445 | ccw->flags |= CCW_FLAG_CC; |
446 | ccw->cda = (__u32)virt_to_phys(address: prssdp); |
447 | |
448 | /* Read Subsystem Data - feature codes */ |
449 | memset(lcu->uac, 0, sizeof(*(lcu->uac))); |
450 | |
451 | ccw++; |
452 | ccw->cmd_code = DASD_ECKD_CCW_RSSD; |
453 | ccw->count = sizeof(*(lcu->uac)); |
454 | ccw->cda = (__u32)virt_to_phys(address: lcu->uac); |
455 | |
456 | cqr->buildclk = get_tod_clock(); |
457 | cqr->status = DASD_CQR_FILLED; |
458 | |
459 | /* need to unset flag here to detect race with summary unit check */ |
460 | spin_lock_irqsave(&lcu->lock, flags); |
461 | lcu->flags &= ~NEED_UAC_UPDATE; |
462 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
463 | |
464 | rc = dasd_sleep_on(cqr); |
465 | if (!rc) |
466 | goto out; |
467 | |
468 | if (suborder_not_supported(cqr)) { |
469 | /* suborder not supported or device unusable for IO */ |
470 | rc = -EOPNOTSUPP; |
471 | } else { |
472 | /* IO failed but should be retried */ |
473 | spin_lock_irqsave(&lcu->lock, flags); |
474 | lcu->flags |= NEED_UAC_UPDATE; |
475 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
476 | } |
477 | out: |
478 | dasd_sfree_request(cqr, cqr->memdev); |
479 | return rc; |
480 | } |
481 | |
482 | static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu) |
483 | { |
484 | unsigned long flags; |
485 | struct alias_pav_group *pavgroup, *tempgroup; |
486 | struct dasd_device *device, *tempdev; |
487 | int i, rc; |
488 | struct dasd_eckd_private *private; |
489 | |
490 | spin_lock_irqsave(&lcu->lock, flags); |
491 | list_for_each_entry_safe(pavgroup, tempgroup, &lcu->grouplist, group) { |
492 | list_for_each_entry_safe(device, tempdev, &pavgroup->baselist, |
493 | alias_list) { |
494 | list_move(list: &device->alias_list, head: &lcu->active_devices); |
495 | private = device->private; |
496 | private->pavgroup = NULL; |
497 | } |
498 | list_for_each_entry_safe(device, tempdev, &pavgroup->aliaslist, |
499 | alias_list) { |
500 | list_move(list: &device->alias_list, head: &lcu->active_devices); |
501 | private = device->private; |
502 | private->pavgroup = NULL; |
503 | } |
504 | list_del(entry: &pavgroup->group); |
505 | kfree(objp: pavgroup); |
506 | } |
507 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
508 | |
509 | rc = read_unit_address_configuration(device: refdev, lcu); |
510 | if (rc) |
511 | return rc; |
512 | |
513 | spin_lock_irqsave(&lcu->lock, flags); |
514 | /* |
515 | * there is another update needed skip the remaining handling |
516 | * the data might already be outdated |
517 | * but especially do not add the device to an LCU with pending |
518 | * update |
519 | */ |
520 | if (lcu->flags & NEED_UAC_UPDATE) |
521 | goto out; |
522 | lcu->pav = NO_PAV; |
523 | for (i = 0; i < MAX_DEVICES_PER_LCU; ++i) { |
524 | switch (lcu->uac->unit[i].ua_type) { |
525 | case UA_BASE_PAV_ALIAS: |
526 | lcu->pav = BASE_PAV; |
527 | break; |
528 | case UA_HYPER_PAV_ALIAS: |
529 | lcu->pav = HYPER_PAV; |
530 | break; |
531 | } |
532 | if (lcu->pav != NO_PAV) |
533 | break; |
534 | } |
535 | |
536 | list_for_each_entry_safe(device, tempdev, &lcu->active_devices, |
537 | alias_list) { |
538 | _add_device_to_lcu(lcu, device, pos: refdev); |
539 | } |
540 | out: |
541 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
542 | return 0; |
543 | } |
544 | |
545 | static void lcu_update_work(struct work_struct *work) |
546 | { |
547 | struct alias_lcu *lcu; |
548 | struct read_uac_work_data *ruac_data; |
549 | struct dasd_device *device; |
550 | unsigned long flags; |
551 | int rc; |
552 | |
553 | ruac_data = container_of(work, struct read_uac_work_data, dwork.work); |
554 | lcu = container_of(ruac_data, struct alias_lcu, ruac_data); |
555 | device = ruac_data->device; |
556 | rc = _lcu_update(refdev: device, lcu); |
557 | /* |
558 | * Need to check flags again, as there could have been another |
559 | * prepare_update or a new device a new device while we were still |
560 | * processing the data |
561 | */ |
562 | spin_lock_irqsave(&lcu->lock, flags); |
563 | if ((rc && (rc != -EOPNOTSUPP)) || (lcu->flags & NEED_UAC_UPDATE)) { |
564 | DBF_DEV_EVENT(DBF_WARNING, device, "could not update" |
565 | " alias data in lcu (rc = %d), retry later" , rc); |
566 | if (!schedule_delayed_work(dwork: &lcu->ruac_data.dwork, delay: 30*HZ)) |
567 | dasd_put_device(device); |
568 | } else { |
569 | dasd_put_device(device); |
570 | lcu->ruac_data.device = NULL; |
571 | lcu->flags &= ~UPDATE_PENDING; |
572 | } |
573 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
574 | } |
575 | |
576 | static int _schedule_lcu_update(struct alias_lcu *lcu, |
577 | struct dasd_device *device) |
578 | { |
579 | struct dasd_device *usedev = NULL; |
580 | struct alias_pav_group *group; |
581 | |
582 | lcu->flags |= NEED_UAC_UPDATE; |
583 | if (lcu->ruac_data.device) { |
584 | /* already scheduled or running */ |
585 | return 0; |
586 | } |
587 | if (device && !list_empty(head: &device->alias_list)) |
588 | usedev = device; |
589 | |
590 | if (!usedev && !list_empty(head: &lcu->grouplist)) { |
591 | group = list_first_entry(&lcu->grouplist, |
592 | struct alias_pav_group, group); |
593 | if (!list_empty(head: &group->baselist)) |
594 | usedev = list_first_entry(&group->baselist, |
595 | struct dasd_device, |
596 | alias_list); |
597 | else if (!list_empty(head: &group->aliaslist)) |
598 | usedev = list_first_entry(&group->aliaslist, |
599 | struct dasd_device, |
600 | alias_list); |
601 | } |
602 | if (!usedev && !list_empty(head: &lcu->active_devices)) { |
603 | usedev = list_first_entry(&lcu->active_devices, |
604 | struct dasd_device, alias_list); |
605 | } |
606 | /* |
607 | * if we haven't found a proper device yet, give up for now, the next |
608 | * device that will be set active will trigger an lcu update |
609 | */ |
610 | if (!usedev) |
611 | return -EINVAL; |
612 | dasd_get_device(device: usedev); |
613 | lcu->ruac_data.device = usedev; |
614 | if (!schedule_delayed_work(dwork: &lcu->ruac_data.dwork, delay: 0)) |
615 | dasd_put_device(device: usedev); |
616 | return 0; |
617 | } |
618 | |
619 | int dasd_alias_add_device(struct dasd_device *device) |
620 | { |
621 | struct dasd_eckd_private *private = device->private; |
622 | __u8 uaddr = private->uid.real_unit_addr; |
623 | struct alias_lcu *lcu = private->lcu; |
624 | unsigned long flags; |
625 | int rc; |
626 | |
627 | rc = 0; |
628 | spin_lock_irqsave(&lcu->lock, flags); |
629 | /* |
630 | * Check if device and lcu type differ. If so, the uac data may be |
631 | * outdated and needs to be updated. |
632 | */ |
633 | if (private->uid.type != lcu->uac->unit[uaddr].ua_type) { |
634 | lcu->flags |= UPDATE_PENDING; |
635 | DBF_DEV_EVENT(DBF_WARNING, device, "%s" , |
636 | "uid type mismatch - trigger rescan" ); |
637 | } |
638 | if (!(lcu->flags & UPDATE_PENDING)) { |
639 | rc = _add_device_to_lcu(lcu, device, pos: device); |
640 | if (rc) |
641 | lcu->flags |= UPDATE_PENDING; |
642 | } |
643 | if (lcu->flags & UPDATE_PENDING) { |
644 | list_move(list: &device->alias_list, head: &lcu->active_devices); |
645 | private->pavgroup = NULL; |
646 | _schedule_lcu_update(lcu, device); |
647 | } |
648 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
649 | return rc; |
650 | } |
651 | |
652 | int dasd_alias_update_add_device(struct dasd_device *device) |
653 | { |
654 | struct dasd_eckd_private *private = device->private; |
655 | |
656 | private->lcu->flags |= UPDATE_PENDING; |
657 | return dasd_alias_add_device(device); |
658 | } |
659 | |
660 | int dasd_alias_remove_device(struct dasd_device *device) |
661 | { |
662 | struct dasd_eckd_private *private = device->private; |
663 | struct alias_lcu *lcu = private->lcu; |
664 | unsigned long flags; |
665 | |
666 | /* nothing to do if already removed */ |
667 | if (!lcu) |
668 | return 0; |
669 | spin_lock_irqsave(&lcu->lock, flags); |
670 | _remove_device_from_lcu(lcu, device); |
671 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
672 | return 0; |
673 | } |
674 | |
675 | struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device) |
676 | { |
677 | struct dasd_eckd_private *alias_priv, *private = base_device->private; |
678 | struct alias_lcu *lcu = private->lcu; |
679 | struct dasd_device *alias_device; |
680 | struct alias_pav_group *group; |
681 | unsigned long flags; |
682 | |
683 | if (!lcu) |
684 | return NULL; |
685 | if (lcu->pav == NO_PAV || |
686 | lcu->flags & (NEED_UAC_UPDATE | UPDATE_PENDING)) |
687 | return NULL; |
688 | if (unlikely(!(private->features.feature[8] & 0x01))) { |
689 | /* |
690 | * PAV enabled but prefix not, very unlikely |
691 | * seems to be a lost pathgroup |
692 | * use base device to do IO |
693 | */ |
694 | DBF_DEV_EVENT(DBF_ERR, base_device, "%s" , |
695 | "Prefix not enabled with PAV enabled\n" ); |
696 | return NULL; |
697 | } |
698 | |
699 | spin_lock_irqsave(&lcu->lock, flags); |
700 | group = private->pavgroup; |
701 | if (!group) { |
702 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
703 | return NULL; |
704 | } |
705 | alias_device = group->next; |
706 | if (!alias_device) { |
707 | if (list_empty(head: &group->aliaslist)) { |
708 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
709 | return NULL; |
710 | } else { |
711 | alias_device = list_first_entry(&group->aliaslist, |
712 | struct dasd_device, |
713 | alias_list); |
714 | } |
715 | } |
716 | if (list_is_last(list: &alias_device->alias_list, head: &group->aliaslist)) |
717 | group->next = list_first_entry(&group->aliaslist, |
718 | struct dasd_device, alias_list); |
719 | else |
720 | group->next = list_first_entry(&alias_device->alias_list, |
721 | struct dasd_device, alias_list); |
722 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
723 | alias_priv = alias_device->private; |
724 | if ((alias_priv->count < private->count) && !alias_device->stopped && |
725 | !test_bit(DASD_FLAG_OFFLINE, &alias_device->flags)) |
726 | return alias_device; |
727 | else |
728 | return NULL; |
729 | } |
730 | |
731 | /* |
732 | * Summary unit check handling depends on the way alias devices |
733 | * are handled so it is done here rather then in dasd_eckd.c |
734 | */ |
735 | static int reset_summary_unit_check(struct alias_lcu *lcu, |
736 | struct dasd_device *device, |
737 | char reason) |
738 | { |
739 | struct dasd_ccw_req *cqr; |
740 | int rc = 0; |
741 | struct ccw1 *ccw; |
742 | |
743 | cqr = lcu->rsu_cqr; |
744 | memcpy((char *) &cqr->magic, "ECKD" , 4); |
745 | ASCEBC((char *) &cqr->magic, 4); |
746 | ccw = cqr->cpaddr; |
747 | ccw->cmd_code = DASD_ECKD_CCW_RSCK; |
748 | ccw->flags = CCW_FLAG_SLI; |
749 | ccw->count = 16; |
750 | ccw->cda = (__u32)virt_to_phys(address: cqr->data); |
751 | ((char *)cqr->data)[0] = reason; |
752 | |
753 | clear_bit(DASD_CQR_FLAGS_USE_ERP, addr: &cqr->flags); |
754 | cqr->retries = 255; /* set retry counter to enable basic ERP */ |
755 | cqr->startdev = device; |
756 | cqr->memdev = device; |
757 | cqr->block = NULL; |
758 | cqr->expires = 5 * HZ; |
759 | cqr->buildclk = get_tod_clock(); |
760 | cqr->status = DASD_CQR_FILLED; |
761 | |
762 | rc = dasd_sleep_on_immediatly(cqr); |
763 | return rc; |
764 | } |
765 | |
766 | static void _restart_all_base_devices_on_lcu(struct alias_lcu *lcu) |
767 | { |
768 | struct alias_pav_group *pavgroup; |
769 | struct dasd_device *device; |
770 | struct dasd_eckd_private *private; |
771 | |
772 | /* active and inactive list can contain alias as well as base devices */ |
773 | list_for_each_entry(device, &lcu->active_devices, alias_list) { |
774 | private = device->private; |
775 | if (private->uid.type != UA_BASE_DEVICE) |
776 | continue; |
777 | dasd_schedule_block_bh(device->block); |
778 | dasd_schedule_device_bh(device); |
779 | } |
780 | list_for_each_entry(device, &lcu->inactive_devices, alias_list) { |
781 | private = device->private; |
782 | if (private->uid.type != UA_BASE_DEVICE) |
783 | continue; |
784 | dasd_schedule_block_bh(device->block); |
785 | dasd_schedule_device_bh(device); |
786 | } |
787 | list_for_each_entry(pavgroup, &lcu->grouplist, group) { |
788 | list_for_each_entry(device, &pavgroup->baselist, alias_list) { |
789 | dasd_schedule_block_bh(device->block); |
790 | dasd_schedule_device_bh(device); |
791 | } |
792 | } |
793 | } |
794 | |
795 | static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu) |
796 | { |
797 | struct alias_pav_group *pavgroup; |
798 | struct dasd_device *device, *temp; |
799 | struct dasd_eckd_private *private; |
800 | unsigned long flags; |
801 | LIST_HEAD(active); |
802 | |
803 | /* |
804 | * Problem here ist that dasd_flush_device_queue may wait |
805 | * for termination of a request to complete. We can't keep |
806 | * the lcu lock during that time, so we must assume that |
807 | * the lists may have changed. |
808 | * Idea: first gather all active alias devices in a separate list, |
809 | * then flush the first element of this list unlocked, and afterwards |
810 | * check if it is still on the list before moving it to the |
811 | * active_devices list. |
812 | */ |
813 | |
814 | spin_lock_irqsave(&lcu->lock, flags); |
815 | list_for_each_entry_safe(device, temp, &lcu->active_devices, |
816 | alias_list) { |
817 | private = device->private; |
818 | if (private->uid.type == UA_BASE_DEVICE) |
819 | continue; |
820 | list_move(list: &device->alias_list, head: &active); |
821 | } |
822 | |
823 | list_for_each_entry(pavgroup, &lcu->grouplist, group) { |
824 | list_splice_init(list: &pavgroup->aliaslist, head: &active); |
825 | } |
826 | while (!list_empty(head: &active)) { |
827 | device = list_first_entry(&active, struct dasd_device, |
828 | alias_list); |
829 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
830 | dasd_flush_device_queue(device); |
831 | spin_lock_irqsave(&lcu->lock, flags); |
832 | /* |
833 | * only move device around if it wasn't moved away while we |
834 | * were waiting for the flush |
835 | */ |
836 | if (device == list_first_entry(&active, |
837 | struct dasd_device, alias_list)) { |
838 | list_move(list: &device->alias_list, head: &lcu->active_devices); |
839 | private = device->private; |
840 | private->pavgroup = NULL; |
841 | } |
842 | } |
843 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
844 | } |
845 | |
846 | static void _stop_all_devices_on_lcu(struct alias_lcu *lcu) |
847 | { |
848 | struct alias_pav_group *pavgroup; |
849 | struct dasd_device *device; |
850 | |
851 | list_for_each_entry(device, &lcu->active_devices, alias_list) { |
852 | spin_lock(lock: get_ccwdev_lock(device->cdev)); |
853 | dasd_device_set_stop_bits(device, DASD_STOPPED_SU); |
854 | spin_unlock(lock: get_ccwdev_lock(device->cdev)); |
855 | } |
856 | list_for_each_entry(device, &lcu->inactive_devices, alias_list) { |
857 | spin_lock(lock: get_ccwdev_lock(device->cdev)); |
858 | dasd_device_set_stop_bits(device, DASD_STOPPED_SU); |
859 | spin_unlock(lock: get_ccwdev_lock(device->cdev)); |
860 | } |
861 | list_for_each_entry(pavgroup, &lcu->grouplist, group) { |
862 | list_for_each_entry(device, &pavgroup->baselist, alias_list) { |
863 | spin_lock(lock: get_ccwdev_lock(device->cdev)); |
864 | dasd_device_set_stop_bits(device, DASD_STOPPED_SU); |
865 | spin_unlock(lock: get_ccwdev_lock(device->cdev)); |
866 | } |
867 | list_for_each_entry(device, &pavgroup->aliaslist, alias_list) { |
868 | spin_lock(lock: get_ccwdev_lock(device->cdev)); |
869 | dasd_device_set_stop_bits(device, DASD_STOPPED_SU); |
870 | spin_unlock(lock: get_ccwdev_lock(device->cdev)); |
871 | } |
872 | } |
873 | } |
874 | |
875 | static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu) |
876 | { |
877 | struct alias_pav_group *pavgroup; |
878 | struct dasd_device *device; |
879 | |
880 | list_for_each_entry(device, &lcu->active_devices, alias_list) { |
881 | spin_lock(lock: get_ccwdev_lock(device->cdev)); |
882 | dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); |
883 | spin_unlock(lock: get_ccwdev_lock(device->cdev)); |
884 | } |
885 | list_for_each_entry(device, &lcu->inactive_devices, alias_list) { |
886 | spin_lock(lock: get_ccwdev_lock(device->cdev)); |
887 | dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); |
888 | spin_unlock(lock: get_ccwdev_lock(device->cdev)); |
889 | } |
890 | list_for_each_entry(pavgroup, &lcu->grouplist, group) { |
891 | list_for_each_entry(device, &pavgroup->baselist, alias_list) { |
892 | spin_lock(lock: get_ccwdev_lock(device->cdev)); |
893 | dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); |
894 | spin_unlock(lock: get_ccwdev_lock(device->cdev)); |
895 | } |
896 | list_for_each_entry(device, &pavgroup->aliaslist, alias_list) { |
897 | spin_lock(lock: get_ccwdev_lock(device->cdev)); |
898 | dasd_device_remove_stop_bits(device, DASD_STOPPED_SU); |
899 | spin_unlock(lock: get_ccwdev_lock(device->cdev)); |
900 | } |
901 | } |
902 | } |
903 | |
904 | static void summary_unit_check_handling_work(struct work_struct *work) |
905 | { |
906 | struct alias_lcu *lcu; |
907 | struct summary_unit_check_work_data *suc_data; |
908 | unsigned long flags; |
909 | struct dasd_device *device; |
910 | |
911 | suc_data = container_of(work, struct summary_unit_check_work_data, |
912 | worker); |
913 | lcu = container_of(suc_data, struct alias_lcu, suc_data); |
914 | device = suc_data->device; |
915 | |
916 | /* 1. flush alias devices */ |
917 | flush_all_alias_devices_on_lcu(lcu); |
918 | |
919 | /* 2. reset summary unit check */ |
920 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); |
921 | dasd_device_remove_stop_bits(device, |
922 | (DASD_STOPPED_SU | DASD_STOPPED_PENDING)); |
923 | spin_unlock_irqrestore(lock: get_ccwdev_lock(device->cdev), flags); |
924 | reset_summary_unit_check(lcu, device, reason: suc_data->reason); |
925 | |
926 | spin_lock_irqsave(&lcu->lock, flags); |
927 | _unstop_all_devices_on_lcu(lcu); |
928 | _restart_all_base_devices_on_lcu(lcu); |
929 | /* 3. read new alias configuration */ |
930 | _schedule_lcu_update(lcu, device); |
931 | lcu->suc_data.device = NULL; |
932 | dasd_put_device(device); |
933 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
934 | } |
935 | |
936 | void dasd_alias_handle_summary_unit_check(struct work_struct *work) |
937 | { |
938 | struct dasd_device *device = container_of(work, struct dasd_device, |
939 | suc_work); |
940 | struct dasd_eckd_private *private = device->private; |
941 | struct alias_lcu *lcu; |
942 | unsigned long flags; |
943 | |
944 | lcu = private->lcu; |
945 | if (!lcu) { |
946 | DBF_DEV_EVENT(DBF_WARNING, device, "%s" , |
947 | "device not ready to handle summary" |
948 | " unit check (no lcu structure)" ); |
949 | goto out; |
950 | } |
951 | spin_lock_irqsave(&lcu->lock, flags); |
952 | /* If this device is about to be removed just return and wait for |
953 | * the next interrupt on a different device |
954 | */ |
955 | if (list_empty(head: &device->alias_list)) { |
956 | DBF_DEV_EVENT(DBF_WARNING, device, "%s" , |
957 | "device is in offline processing," |
958 | " don't do summary unit check handling" ); |
959 | goto out_unlock; |
960 | } |
961 | if (lcu->suc_data.device) { |
962 | /* already scheduled or running */ |
963 | DBF_DEV_EVENT(DBF_WARNING, device, "%s" , |
964 | "previous instance of summary unit check worker" |
965 | " still pending" ); |
966 | goto out_unlock; |
967 | } |
968 | _stop_all_devices_on_lcu(lcu); |
969 | /* prepare for lcu_update */ |
970 | lcu->flags |= NEED_UAC_UPDATE | UPDATE_PENDING; |
971 | lcu->suc_data.reason = private->suc_reason; |
972 | lcu->suc_data.device = device; |
973 | dasd_get_device(device); |
974 | if (!schedule_work(work: &lcu->suc_data.worker)) |
975 | dasd_put_device(device); |
976 | out_unlock: |
977 | spin_unlock_irqrestore(lock: &lcu->lock, flags); |
978 | out: |
979 | clear_bit(DASD_FLAG_SUC, addr: &device->flags); |
980 | dasd_put_device(device); |
981 | }; |
982 | |