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