blob: ed70852cc9154666a0bffeb95d813c2cbd762268 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * File...........: linux/drivers/s390/block/dasd_devmap.c
3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
4 * Horst Hummel <Horst.Hummel@de.ibm.com>
5 * Carsten Otte <Cotte@de.ibm.com>
6 * Martin Schwidefsky <schwidefsky@de.ibm.com>
7 * Bugreports.to..: <Linux390@de.ibm.com>
8 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
9 *
10 * Device mapping and dasd= parameter parsing functions. All devmap
11 * functions may not be called from interrupt context. In particular
12 * dasd_get_device is a no-no from interrupt context.
13 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 */
15
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/ctype.h>
17#include <linux/init.h>
Rusty Russell8d3b33f2006-03-25 03:07:05 -080018#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
20#include <asm/debug.h>
21#include <asm/uaccess.h>
22
23/* This is ugly... */
24#define PRINTK_HEADER "dasd_devmap:"
25
26#include "dasd_int.h"
27
Christoph Lametere18b8902006-12-06 20:33:20 -080028struct kmem_cache *dasd_page_cache;
Horst Hummel40545572006-06-29 15:08:18 +020029EXPORT_SYMBOL_GPL(dasd_page_cache);
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31/*
32 * dasd_devmap_t is used to store the features and the relation
33 * between device number and device index. To find a dasd_devmap_t
34 * that corresponds to a device number of a device index each
35 * dasd_devmap_t is added to two linked lists, one to search by
36 * the device number and one to search by the device index. As
37 * soon as big minor numbers are available the device index list
38 * can be removed since the device number will then be identical
39 * to the device index.
40 */
41struct dasd_devmap {
42 struct list_head list;
43 char bus_id[BUS_ID_SIZE];
44 unsigned int devindex;
45 unsigned short features;
46 struct dasd_device *device;
Horst Hummel3d052592006-04-27 18:40:28 -070047 struct dasd_uid uid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070048};
49
50/*
Peter Oberparleiterb18a60e2006-08-16 13:49:33 +020051 * dasd_server_ssid_map contains a globally unique storage server subsystem ID.
52 * dasd_server_ssid_list contains the list of all subsystem IDs accessed by
53 * the DASD device driver.
Horst Hummel40545572006-06-29 15:08:18 +020054 */
Peter Oberparleiterb18a60e2006-08-16 13:49:33 +020055struct dasd_server_ssid_map {
Horst Hummel40545572006-06-29 15:08:18 +020056 struct list_head list;
Horst Hummel8e79a442006-08-24 13:22:36 +020057 struct system_id {
Horst Hummel40545572006-06-29 15:08:18 +020058 char vendor[4];
59 char serial[15];
Horst Hummel8e79a442006-08-24 13:22:36 +020060 __u16 ssid;
Horst Hummel40545572006-06-29 15:08:18 +020061 } sid;
62};
63
Peter Oberparleiterb18a60e2006-08-16 13:49:33 +020064static struct list_head dasd_server_ssid_list;
Horst Hummel40545572006-06-29 15:08:18 +020065
66/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 * Parameter parsing functions for dasd= parameter. The syntax is:
68 * <devno> : (0x)?[0-9a-fA-F]+
69 * <busid> : [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+
70 * <feature> : ro
71 * <feature_list> : \(<feature>(:<feature>)*\)
72 * <devno-range> : <devno>(-<devno>)?<feature_list>?
73 * <busid-range> : <busid>(-<busid>)?<feature_list>?
74 * <devices> : <devno-range>|<busid-range>
75 * <dasd_module> : dasd_diag_mod|dasd_eckd_mod|dasd_fba_mod
76 *
77 * <dasd> : autodetect|probeonly|<devices>(,<devices>)*
78 */
79
80int dasd_probeonly = 0; /* is true, when probeonly mode is active */
81int dasd_autodetect = 0; /* is true, when autodetection is active */
Horst Hummel40545572006-06-29 15:08:18 +020082int dasd_nopav = 0; /* is true, when PAV is disabled */
83EXPORT_SYMBOL_GPL(dasd_nopav);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
85/*
86 * char *dasd[] is intended to hold the ranges supplied by the dasd= statement
87 * it is named 'dasd' to directly be filled by insmod with the comma separated
88 * strings when running as a module.
89 */
90static char *dasd[256];
Rusty Russell8d3b33f2006-03-25 03:07:05 -080091module_param_array(dasd, charp, NULL, 0);
92
Linus Torvalds1da177e2005-04-16 15:20:36 -070093/*
Horst Hummeld0710c72006-08-10 15:45:16 +020094 * Single spinlock to protect devmap and servermap structures and lists.
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 */
96static DEFINE_SPINLOCK(dasd_devmap_lock);
97
98/*
99 * Hash lists for devmap structures.
100 */
101static struct list_head dasd_hashlists[256];
102int dasd_max_devindex;
103
104static struct dasd_devmap *dasd_add_busid(char *, int);
105
106static inline int
107dasd_hash_busid(char *bus_id)
108{
109 int hash, i;
110
111 hash = 0;
112 for (i = 0; (i < BUS_ID_SIZE) && *bus_id; i++, bus_id++)
113 hash += *bus_id;
114 return hash & 0xff;
115}
116
117#ifndef MODULE
118/*
119 * The parameter parsing functions for builtin-drivers are called
120 * before kmalloc works. Store the pointers to the parameters strings
121 * into dasd[] for later processing.
122 */
123static int __init
124dasd_call_setup(char *str)
125{
126 static int count = 0;
127
128 if (count < 256)
129 dasd[count++] = str;
130 return 1;
131}
132
133__setup ("dasd=", dasd_call_setup);
134#endif /* #ifndef MODULE */
135
136/*
137 * Read a device busid/devno from a string.
138 */
Heiko Carstens4d284ca2007-02-05 21:18:53 +0100139static int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140dasd_busid(char **str, int *id0, int *id1, int *devno)
141{
142 int val, old_style;
Horst Hummel138c0142006-06-29 14:58:12 +0200143
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 /* check for leading '0x' */
145 old_style = 0;
146 if ((*str)[0] == '0' && (*str)[1] == 'x') {
147 *str += 2;
148 old_style = 1;
149 }
150 if (!isxdigit((*str)[0])) /* We require at least one hex digit */
151 return -EINVAL;
152 val = simple_strtoul(*str, str, 16);
153 if (old_style || (*str)[0] != '.') {
154 *id0 = *id1 = 0;
155 if (val < 0 || val > 0xffff)
156 return -EINVAL;
157 *devno = val;
158 return 0;
159 }
160 /* New style x.y.z busid */
161 if (val < 0 || val > 0xff)
162 return -EINVAL;
163 *id0 = val;
164 (*str)++;
165 if (!isxdigit((*str)[0])) /* We require at least one hex digit */
166 return -EINVAL;
167 val = simple_strtoul(*str, str, 16);
168 if (val < 0 || val > 0xff || (*str)++[0] != '.')
169 return -EINVAL;
170 *id1 = val;
171 if (!isxdigit((*str)[0])) /* We require at least one hex digit */
172 return -EINVAL;
173 val = simple_strtoul(*str, str, 16);
174 if (val < 0 || val > 0xffff)
175 return -EINVAL;
176 *devno = val;
177 return 0;
178}
179
180/*
181 * Read colon separated list of dasd features. Currently there is
182 * only one: "ro" for read-only devices. The default feature set
183 * is empty (value 0).
184 */
Heiko Carstens4d284ca2007-02-05 21:18:53 +0100185static int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186dasd_feature_list(char *str, char **endp)
187{
188 int features, len, rc;
189
190 rc = 0;
191 if (*str != '(') {
192 *endp = str;
193 return DASD_FEATURE_DEFAULT;
194 }
195 str++;
196 features = 0;
197
198 while (1) {
Horst Hummel138c0142006-06-29 14:58:12 +0200199 for (len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 str[len] && str[len] != ':' && str[len] != ')'; len++);
201 if (len == 2 && !strncmp(str, "ro", 2))
202 features |= DASD_FEATURE_READONLY;
203 else if (len == 4 && !strncmp(str, "diag", 4))
204 features |= DASD_FEATURE_USEDIAG;
Horst Hummel9575bf22006-12-08 15:54:15 +0100205 else if (len == 6 && !strncmp(str, "erplog", 6))
206 features |= DASD_FEATURE_ERPLOG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 else {
208 MESSAGE(KERN_WARNING,
209 "unsupported feature: %*s, "
210 "ignoring setting", len, str);
211 rc = -EINVAL;
212 }
213 str += len;
214 if (*str != ':')
215 break;
216 str++;
217 }
218 if (*str != ')') {
219 MESSAGE(KERN_WARNING, "%s",
220 "missing ')' in dasd parameter string\n");
221 rc = -EINVAL;
222 } else
223 str++;
224 *endp = str;
225 if (rc != 0)
226 return rc;
227 return features;
228}
229
230/*
231 * Try to match the first element on the comma separated parse string
232 * with one of the known keywords. If a keyword is found, take the approprate
233 * action and return a pointer to the residual string. If the first element
234 * could not be matched to any keyword then return an error code.
235 */
236static char *
237dasd_parse_keyword( char *parsestring ) {
238
239 char *nextcomma, *residual_str;
240 int length;
241
242 nextcomma = strchr(parsestring,',');
243 if (nextcomma) {
244 length = nextcomma - parsestring;
245 residual_str = nextcomma + 1;
246 } else {
247 length = strlen(parsestring);
248 residual_str = parsestring + length;
249 }
Horst Hummel40545572006-06-29 15:08:18 +0200250 if (strncmp("autodetect", parsestring, length) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 dasd_autodetect = 1;
252 MESSAGE (KERN_INFO, "%s",
253 "turning to autodetection mode");
254 return residual_str;
255 }
Horst Hummel40545572006-06-29 15:08:18 +0200256 if (strncmp("probeonly", parsestring, length) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 dasd_probeonly = 1;
258 MESSAGE(KERN_INFO, "%s",
259 "turning to probeonly mode");
260 return residual_str;
261 }
Horst Hummel40545572006-06-29 15:08:18 +0200262 if (strncmp("nopav", parsestring, length) == 0) {
Peter Oberparleiterdcd707b2006-09-20 15:59:52 +0200263 if (MACHINE_IS_VM)
264 MESSAGE(KERN_INFO, "%s", "'nopav' not supported on VM");
265 else {
266 dasd_nopav = 1;
267 MESSAGE(KERN_INFO, "%s", "disable PAV mode");
268 }
Horst Hummel40545572006-06-29 15:08:18 +0200269 return residual_str;
270 }
271 if (strncmp("fixedbuffers", parsestring, length) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 if (dasd_page_cache)
273 return residual_str;
274 dasd_page_cache =
Heiko Carstens2f6c55f2006-08-16 13:49:27 +0200275 kmem_cache_create("dasd_page_cache", PAGE_SIZE,
276 PAGE_SIZE, SLAB_CACHE_DMA,
277 NULL, NULL );
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 if (!dasd_page_cache)
279 MESSAGE(KERN_WARNING, "%s", "Failed to create slab, "
280 "fixed buffer mode disabled.");
281 else
282 MESSAGE (KERN_INFO, "%s",
283 "turning on fixed buffer mode");
284 return residual_str;
285 }
286 return ERR_PTR(-EINVAL);
287}
288
289/*
290 * Try to interprete the first element on the comma separated parse string
291 * as a device number or a range of devices. If the interpretation is
292 * successfull, create the matching dasd_devmap entries and return a pointer
293 * to the residual string.
294 * If interpretation fails or in case of an error, return an error code.
295 */
296static char *
297dasd_parse_range( char *parsestring ) {
298
299 struct dasd_devmap *devmap;
300 int from, from_id0, from_id1;
301 int to, to_id0, to_id1;
302 int features, rc;
303 char bus_id[BUS_ID_SIZE+1], *str;
304
305 str = parsestring;
306 rc = dasd_busid(&str, &from_id0, &from_id1, &from);
307 if (rc == 0) {
308 to = from;
309 to_id0 = from_id0;
310 to_id1 = from_id1;
311 if (*str == '-') {
312 str++;
313 rc = dasd_busid(&str, &to_id0, &to_id1, &to);
314 }
315 }
316 if (rc == 0 &&
317 (from_id0 != to_id0 || from_id1 != to_id1 || from > to))
318 rc = -EINVAL;
319 if (rc) {
320 MESSAGE(KERN_ERR, "Invalid device range %s", parsestring);
321 return ERR_PTR(rc);
322 }
323 features = dasd_feature_list(str, &str);
324 if (features < 0)
325 return ERR_PTR(-EINVAL);
Horst Hummel40545572006-06-29 15:08:18 +0200326 /* each device in dasd= parameter should be set initially online */
327 features |= DASD_FEATURE_INITIAL_ONLINE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 while (from <= to) {
329 sprintf(bus_id, "%01x.%01x.%04x",
330 from_id0, from_id1, from++);
331 devmap = dasd_add_busid(bus_id, features);
332 if (IS_ERR(devmap))
333 return (char *)devmap;
334 }
335 if (*str == ',')
336 return str + 1;
337 if (*str == '\0')
338 return str;
339 MESSAGE(KERN_WARNING,
340 "junk at end of dasd parameter string: %s\n", str);
341 return ERR_PTR(-EINVAL);
342}
343
Heiko Carstens4d284ca2007-02-05 21:18:53 +0100344static char *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345dasd_parse_next_element( char *parsestring ) {
346 char * residual_str;
347 residual_str = dasd_parse_keyword(parsestring);
348 if (!IS_ERR(residual_str))
349 return residual_str;
350 residual_str = dasd_parse_range(parsestring);
351 return residual_str;
352}
353
354/*
355 * Parse parameters stored in dasd[]
356 * The 'dasd=...' parameter allows to specify a comma separated list of
357 * keywords and device ranges. When the dasd driver is build into the kernel,
358 * the complete list will be stored as one element of the dasd[] array.
359 * When the dasd driver is build as a module, then the list is broken into
360 * it's elements and each dasd[] entry contains one element.
361 */
362int
363dasd_parse(void)
364{
365 int rc, i;
366 char *parsestring;
367
368 rc = 0;
369 for (i = 0; i < 256; i++) {
370 if (dasd[i] == NULL)
371 break;
372 parsestring = dasd[i];
373 /* loop over the comma separated list in the parsestring */
374 while (*parsestring) {
375 parsestring = dasd_parse_next_element(parsestring);
376 if(IS_ERR(parsestring)) {
377 rc = PTR_ERR(parsestring);
378 break;
379 }
380 }
381 if (rc) {
382 DBF_EVENT(DBF_ALERT, "%s", "invalid range found");
383 break;
384 }
385 }
386 return rc;
387}
388
389/*
390 * Add a devmap for the device specified by busid. It is possible that
391 * the devmap already exists (dasd= parameter). The order of the devices
392 * added through this function will define the kdevs for the individual
Horst Hummel138c0142006-06-29 14:58:12 +0200393 * devices.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 */
395static struct dasd_devmap *
396dasd_add_busid(char *bus_id, int features)
397{
398 struct dasd_devmap *devmap, *new, *tmp;
399 int hash;
400
401 new = (struct dasd_devmap *)
Horst Hummel138c0142006-06-29 14:58:12 +0200402 kzalloc(sizeof(struct dasd_devmap), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 if (!new)
404 return ERR_PTR(-ENOMEM);
405 spin_lock(&dasd_devmap_lock);
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200406 devmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 hash = dasd_hash_busid(bus_id);
408 list_for_each_entry(tmp, &dasd_hashlists[hash], list)
409 if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
410 devmap = tmp;
411 break;
412 }
413 if (!devmap) {
414 /* This bus_id is new. */
415 new->devindex = dasd_max_devindex++;
416 strncpy(new->bus_id, bus_id, BUS_ID_SIZE);
417 new->features = features;
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200418 new->device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 list_add(&new->list, &dasd_hashlists[hash]);
420 devmap = new;
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200421 new = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 }
423 spin_unlock(&dasd_devmap_lock);
Jesper Juhl17fd6822005-11-07 01:01:30 -0800424 kfree(new);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 return devmap;
426}
427
428/*
429 * Find devmap for device with given bus_id.
430 */
431static struct dasd_devmap *
432dasd_find_busid(char *bus_id)
433{
434 struct dasd_devmap *devmap, *tmp;
435 int hash;
436
437 spin_lock(&dasd_devmap_lock);
438 devmap = ERR_PTR(-ENODEV);
439 hash = dasd_hash_busid(bus_id);
440 list_for_each_entry(tmp, &dasd_hashlists[hash], list) {
441 if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
442 devmap = tmp;
443 break;
444 }
445 }
446 spin_unlock(&dasd_devmap_lock);
447 return devmap;
448}
449
450/*
451 * Check if busid has been added to the list of dasd ranges.
452 */
453int
454dasd_busid_known(char *bus_id)
455{
456 return IS_ERR(dasd_find_busid(bus_id)) ? -ENOENT : 0;
457}
458
459/*
460 * Forget all about the device numbers added so far.
461 * This may only be called at module unload or system shutdown.
462 */
463static void
464dasd_forget_ranges(void)
465{
466 struct dasd_devmap *devmap, *n;
467 int i;
468
469 spin_lock(&dasd_devmap_lock);
470 for (i = 0; i < 256; i++) {
471 list_for_each_entry_safe(devmap, n, &dasd_hashlists[i], list) {
Eric Sesterhenn606f4422006-03-26 18:33:07 +0200472 BUG_ON(devmap->device != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 list_del(&devmap->list);
474 kfree(devmap);
475 }
476 }
477 spin_unlock(&dasd_devmap_lock);
478}
479
480/*
481 * Find the device struct by its device index.
482 */
483struct dasd_device *
484dasd_device_from_devindex(int devindex)
485{
486 struct dasd_devmap *devmap, *tmp;
487 struct dasd_device *device;
488 int i;
489
490 spin_lock(&dasd_devmap_lock);
Heiko Carstensd2c993d2006-07-12 16:41:55 +0200491 devmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 for (i = 0; (i < 256) && !devmap; i++)
493 list_for_each_entry(tmp, &dasd_hashlists[i], list)
494 if (tmp->devindex == devindex) {
495 /* Found the devmap for the device. */
496 devmap = tmp;
497 break;
498 }
499 if (devmap && devmap->device) {
500 device = devmap->device;
501 dasd_get_device(device);
502 } else
503 device = ERR_PTR(-ENODEV);
504 spin_unlock(&dasd_devmap_lock);
505 return device;
506}
507
508/*
509 * Return devmap for cdev. If no devmap exists yet, create one and
510 * connect it to the cdev.
511 */
512static struct dasd_devmap *
513dasd_devmap_from_cdev(struct ccw_device *cdev)
514{
515 struct dasd_devmap *devmap;
516
517 devmap = dasd_find_busid(cdev->dev.bus_id);
518 if (IS_ERR(devmap))
519 devmap = dasd_add_busid(cdev->dev.bus_id,
520 DASD_FEATURE_DEFAULT);
521 return devmap;
522}
523
524/*
525 * Create a dasd device structure for cdev.
526 */
527struct dasd_device *
528dasd_create_device(struct ccw_device *cdev)
529{
530 struct dasd_devmap *devmap;
531 struct dasd_device *device;
Martin Schwidefskya00bfd72006-09-20 15:59:05 +0200532 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 int rc;
534
535 devmap = dasd_devmap_from_cdev(cdev);
536 if (IS_ERR(devmap))
537 return (void *) devmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
539 device = dasd_alloc_device();
540 if (IS_ERR(device))
541 return device;
Martin Schwidefskya00bfd72006-09-20 15:59:05 +0200542 atomic_set(&device->ref_count, 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
544 spin_lock(&dasd_devmap_lock);
545 if (!devmap->device) {
546 devmap->device = device;
547 device->devindex = devmap->devindex;
Horst Hummelc6eb7b72005-09-03 15:57:58 -0700548 device->features = devmap->features;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 get_device(&cdev->dev);
550 device->cdev = cdev;
551 rc = 0;
552 } else
553 /* Someone else was faster. */
554 rc = -EBUSY;
555 spin_unlock(&dasd_devmap_lock);
556
557 if (rc) {
558 dasd_free_device(device);
559 return ERR_PTR(rc);
560 }
Martin Schwidefskya00bfd72006-09-20 15:59:05 +0200561
562 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
563 cdev->dev.driver_data = device;
564 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
565
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 return device;
567}
568
569/*
570 * Wait queue for dasd_delete_device waits.
571 */
572static DECLARE_WAIT_QUEUE_HEAD(dasd_delete_wq);
573
574/*
575 * Remove a dasd device structure. The passed referenced
576 * is destroyed.
577 */
578void
579dasd_delete_device(struct dasd_device *device)
580{
581 struct ccw_device *cdev;
582 struct dasd_devmap *devmap;
Martin Schwidefskya00bfd72006-09-20 15:59:05 +0200583 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
585 /* First remove device pointer from devmap. */
586 devmap = dasd_find_busid(device->cdev->dev.bus_id);
Eric Sesterhenn606f4422006-03-26 18:33:07 +0200587 BUG_ON(IS_ERR(devmap));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 spin_lock(&dasd_devmap_lock);
589 if (devmap->device != device) {
590 spin_unlock(&dasd_devmap_lock);
591 dasd_put_device(device);
592 return;
593 }
594 devmap->device = NULL;
595 spin_unlock(&dasd_devmap_lock);
596
Martin Schwidefskya00bfd72006-09-20 15:59:05 +0200597 /* Disconnect dasd_device structure from ccw_device structure. */
598 spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
599 device->cdev->dev.driver_data = NULL;
600 spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
601
602 /*
603 * Drop ref_count by 3, one for the devmap reference, one for
604 * the cdev reference and one for the passed reference.
605 */
606 atomic_sub(3, &device->ref_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
608 /* Wait for reference counter to drop to zero. */
609 wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
610
611 /* Disconnect dasd_device structure from ccw_device structure. */
612 cdev = device->cdev;
613 device->cdev = NULL;
614
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 /* Put ccw_device structure. */
616 put_device(&cdev->dev);
617
618 /* Now the device structure can be freed. */
619 dasd_free_device(device);
620}
621
622/*
623 * Reference counter dropped to zero. Wake up waiter
624 * in dasd_delete_device.
625 */
626void
627dasd_put_device_wake(struct dasd_device *device)
628{
629 wake_up(&dasd_delete_wq);
630}
631
632/*
633 * Return dasd_device structure associated with cdev.
Martin Schwidefskya00bfd72006-09-20 15:59:05 +0200634 * This function needs to be called with the ccw device
635 * lock held. It can be used from interrupt context.
636 */
637struct dasd_device *
638dasd_device_from_cdev_locked(struct ccw_device *cdev)
639{
640 struct dasd_device *device = cdev->dev.driver_data;
641
642 if (!device)
643 return ERR_PTR(-ENODEV);
644 dasd_get_device(device);
645 return device;
646}
647
648/*
649 * Return dasd_device structure associated with cdev.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 */
651struct dasd_device *
652dasd_device_from_cdev(struct ccw_device *cdev)
653{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 struct dasd_device *device;
Martin Schwidefskya00bfd72006-09-20 15:59:05 +0200655 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
Martin Schwidefskya00bfd72006-09-20 15:59:05 +0200657 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
658 device = dasd_device_from_cdev_locked(cdev);
659 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 return device;
661}
662
663/*
664 * SECTION: files in sysfs
665 */
666
667/*
668 * readonly controls the readonly status of a dasd
669 */
670static ssize_t
Yani Ioannoue404e272005-05-17 06:42:58 -0400671dasd_ro_show(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672{
673 struct dasd_devmap *devmap;
674 int ro_flag;
675
676 devmap = dasd_find_busid(dev->bus_id);
677 if (!IS_ERR(devmap))
678 ro_flag = (devmap->features & DASD_FEATURE_READONLY) != 0;
679 else
680 ro_flag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_READONLY) != 0;
681 return snprintf(buf, PAGE_SIZE, ro_flag ? "1\n" : "0\n");
682}
683
684static ssize_t
Horst Hummel138c0142006-06-29 14:58:12 +0200685dasd_ro_store(struct device *dev, struct device_attribute *attr,
686 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687{
688 struct dasd_devmap *devmap;
Horst Hummel01376f42006-12-04 15:39:50 +0100689 int val;
690 char *endp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
692 devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
693 if (IS_ERR(devmap))
694 return PTR_ERR(devmap);
Horst Hummel01376f42006-12-04 15:39:50 +0100695
696 val = simple_strtoul(buf, &endp, 0);
697 if (((endp + 1) < (buf + count)) || (val > 1))
698 return -EINVAL;
699
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 spin_lock(&dasd_devmap_lock);
Horst Hummel01376f42006-12-04 15:39:50 +0100701 if (val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 devmap->features |= DASD_FEATURE_READONLY;
703 else
704 devmap->features &= ~DASD_FEATURE_READONLY;
Horst Hummelc6eb7b72005-09-03 15:57:58 -0700705 if (devmap->device)
706 devmap->device->features = devmap->features;
Horst Hummelf24acd42005-05-01 08:58:59 -0700707 if (devmap->device && devmap->device->gdp)
Horst Hummel01376f42006-12-04 15:39:50 +0100708 set_disk_ro(devmap->device->gdp, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 spin_unlock(&dasd_devmap_lock);
710 return count;
711}
712
713static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
Horst Hummel9575bf22006-12-08 15:54:15 +0100714/*
715 * erplog controls the logging of ERP related data
716 * (e.g. failing channel programs).
717 */
718static ssize_t
719dasd_erplog_show(struct device *dev, struct device_attribute *attr, char *buf)
720{
721 struct dasd_devmap *devmap;
722 int erplog;
723
724 devmap = dasd_find_busid(dev->bus_id);
725 if (!IS_ERR(devmap))
726 erplog = (devmap->features & DASD_FEATURE_ERPLOG) != 0;
727 else
728 erplog = (DASD_FEATURE_DEFAULT & DASD_FEATURE_ERPLOG) != 0;
729 return snprintf(buf, PAGE_SIZE, erplog ? "1\n" : "0\n");
730}
731
732static ssize_t
733dasd_erplog_store(struct device *dev, struct device_attribute *attr,
734 const char *buf, size_t count)
735{
736 struct dasd_devmap *devmap;
737 int val;
738 char *endp;
739
740 devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
741 if (IS_ERR(devmap))
742 return PTR_ERR(devmap);
743
744 val = simple_strtoul(buf, &endp, 0);
745 if (((endp + 1) < (buf + count)) || (val > 1))
746 return -EINVAL;
747
748 spin_lock(&dasd_devmap_lock);
749 if (val)
750 devmap->features |= DASD_FEATURE_ERPLOG;
751 else
752 devmap->features &= ~DASD_FEATURE_ERPLOG;
753 if (devmap->device)
754 devmap->device->features = devmap->features;
755 spin_unlock(&dasd_devmap_lock);
756 return count;
757}
758
759static DEVICE_ATTR(erplog, 0644, dasd_erplog_show, dasd_erplog_store);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
761/*
762 * use_diag controls whether the driver should use diag rather than ssch
763 * to talk to the device
764 */
Horst Hummel138c0142006-06-29 14:58:12 +0200765static ssize_t
Yani Ioannoue404e272005-05-17 06:42:58 -0400766dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767{
768 struct dasd_devmap *devmap;
769 int use_diag;
770
771 devmap = dasd_find_busid(dev->bus_id);
772 if (!IS_ERR(devmap))
773 use_diag = (devmap->features & DASD_FEATURE_USEDIAG) != 0;
774 else
775 use_diag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USEDIAG) != 0;
776 return sprintf(buf, use_diag ? "1\n" : "0\n");
777}
778
779static ssize_t
Horst Hummel138c0142006-06-29 14:58:12 +0200780dasd_use_diag_store(struct device *dev, struct device_attribute *attr,
781 const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782{
783 struct dasd_devmap *devmap;
784 ssize_t rc;
Horst Hummel01376f42006-12-04 15:39:50 +0100785 int val;
786 char *endp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
788 devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
789 if (IS_ERR(devmap))
790 return PTR_ERR(devmap);
Horst Hummel01376f42006-12-04 15:39:50 +0100791
792 val = simple_strtoul(buf, &endp, 0);
793 if (((endp + 1) < (buf + count)) || (val > 1))
794 return -EINVAL;
795
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 spin_lock(&dasd_devmap_lock);
797 /* Changing diag discipline flag is only allowed in offline state. */
798 rc = count;
799 if (!devmap->device) {
Horst Hummel01376f42006-12-04 15:39:50 +0100800 if (val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 devmap->features |= DASD_FEATURE_USEDIAG;
802 else
803 devmap->features &= ~DASD_FEATURE_USEDIAG;
804 } else
805 rc = -EPERM;
806 spin_unlock(&dasd_devmap_lock);
807 return rc;
808}
809
Horst Hummel138c0142006-06-29 14:58:12 +0200810static DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
812static ssize_t
Horst Hummel138c0142006-06-29 14:58:12 +0200813dasd_discipline_show(struct device *dev, struct device_attribute *attr,
814 char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815{
Martin Schwidefskya00bfd72006-09-20 15:59:05 +0200816 struct dasd_device *device;
817 ssize_t len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Martin Schwidefskya00bfd72006-09-20 15:59:05 +0200819 device = dasd_device_from_cdev(to_ccwdev(dev));
820 if (!IS_ERR(device) && device->discipline) {
821 len = snprintf(buf, PAGE_SIZE, "%s\n",
822 device->discipline->name);
823 dasd_put_device(device);
824 } else
825 len = snprintf(buf, PAGE_SIZE, "none\n");
826 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827}
828
829static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
830
Horst Hummel3d052592006-04-27 18:40:28 -0700831static ssize_t
832dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
833{
834 struct dasd_devmap *devmap;
835 int alias;
836
837 devmap = dasd_find_busid(dev->bus_id);
838 spin_lock(&dasd_devmap_lock);
839 if (!IS_ERR(devmap))
840 alias = devmap->uid.alias;
841 else
842 alias = 0;
843 spin_unlock(&dasd_devmap_lock);
844
845 return sprintf(buf, alias ? "1\n" : "0\n");
846}
847
848static DEVICE_ATTR(alias, 0444, dasd_alias_show, NULL);
849
850static ssize_t
851dasd_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
852{
853 struct dasd_devmap *devmap;
854 char *vendor;
855
856 devmap = dasd_find_busid(dev->bus_id);
857 spin_lock(&dasd_devmap_lock);
858 if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0)
859 vendor = devmap->uid.vendor;
860 else
861 vendor = "";
862 spin_unlock(&dasd_devmap_lock);
863
864 return snprintf(buf, PAGE_SIZE, "%s\n", vendor);
865}
866
867static DEVICE_ATTR(vendor, 0444, dasd_vendor_show, NULL);
868
869#define UID_STRLEN ( /* vendor */ 3 + 1 + /* serial */ 14 + 1 +\
870 /* SSID */ 4 + 1 + /* unit addr */ 2 + 1)
871
872static ssize_t
873dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
874{
875 struct dasd_devmap *devmap;
876 char uid[UID_STRLEN];
877
878 devmap = dasd_find_busid(dev->bus_id);
879 spin_lock(&dasd_devmap_lock);
880 if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0)
881 snprintf(uid, sizeof(uid), "%s.%s.%04x.%02x",
882 devmap->uid.vendor, devmap->uid.serial,
883 devmap->uid.ssid, devmap->uid.unit_addr);
884 else
885 uid[0] = 0;
886 spin_unlock(&dasd_devmap_lock);
887
888 return snprintf(buf, PAGE_SIZE, "%s\n", uid);
889}
890
891static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL);
892
Stefan Weinhuber20c64462006-03-24 03:15:25 -0800893/*
894 * extended error-reporting
895 */
896static ssize_t
897dasd_eer_show(struct device *dev, struct device_attribute *attr, char *buf)
898{
899 struct dasd_devmap *devmap;
900 int eer_flag;
901
902 devmap = dasd_find_busid(dev->bus_id);
903 if (!IS_ERR(devmap) && devmap->device)
904 eer_flag = dasd_eer_enabled(devmap->device);
905 else
906 eer_flag = 0;
907 return snprintf(buf, PAGE_SIZE, eer_flag ? "1\n" : "0\n");
908}
909
910static ssize_t
911dasd_eer_store(struct device *dev, struct device_attribute *attr,
912 const char *buf, size_t count)
913{
914 struct dasd_devmap *devmap;
Horst Hummel01376f42006-12-04 15:39:50 +0100915 int val, rc;
916 char *endp;
Stefan Weinhuber20c64462006-03-24 03:15:25 -0800917
918 devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
919 if (IS_ERR(devmap))
920 return PTR_ERR(devmap);
921 if (!devmap->device)
Horst Hummel01376f42006-12-04 15:39:50 +0100922 return -ENODEV;
923
924 val = simple_strtoul(buf, &endp, 0);
925 if (((endp + 1) < (buf + count)) || (val > 1))
926 return -EINVAL;
927
Horst Hummel645c98c2006-12-04 15:40:18 +0100928 if (val) {
Stefan Weinhuber20c64462006-03-24 03:15:25 -0800929 rc = dasd_eer_enable(devmap->device);
Horst Hummel645c98c2006-12-04 15:40:18 +0100930 if (rc)
931 return rc;
932 } else
Stefan Weinhuber20c64462006-03-24 03:15:25 -0800933 dasd_eer_disable(devmap->device);
Horst Hummel645c98c2006-12-04 15:40:18 +0100934 return count;
Stefan Weinhuber20c64462006-03-24 03:15:25 -0800935}
936
937static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store);
938
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939static struct attribute * dasd_attrs[] = {
940 &dev_attr_readonly.attr,
941 &dev_attr_discipline.attr,
Horst Hummel3d052592006-04-27 18:40:28 -0700942 &dev_attr_alias.attr,
943 &dev_attr_vendor.attr,
944 &dev_attr_uid.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 &dev_attr_use_diag.attr,
Stefan Weinhuber20c64462006-03-24 03:15:25 -0800946 &dev_attr_eer_enabled.attr,
Horst Hummel9575bf22006-12-08 15:54:15 +0100947 &dev_attr_erplog.attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 NULL,
949};
950
951static struct attribute_group dasd_attr_group = {
952 .attrs = dasd_attrs,
953};
954
Horst Hummel40545572006-06-29 15:08:18 +0200955/*
Horst Hummel3d052592006-04-27 18:40:28 -0700956 * Return copy of the device unique identifier.
957 */
958int
959dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid)
960{
961 struct dasd_devmap *devmap;
962
963 devmap = dasd_find_busid(cdev->dev.bus_id);
964 if (IS_ERR(devmap))
965 return PTR_ERR(devmap);
966 spin_lock(&dasd_devmap_lock);
967 *uid = devmap->uid;
968 spin_unlock(&dasd_devmap_lock);
969 return 0;
970}
971
972/*
973 * Register the given device unique identifier into devmap struct.
Peter Oberparleiterb18a60e2006-08-16 13:49:33 +0200974 * In addition check if the related storage server subsystem ID is already
975 * contained in the dasd_server_ssid_list. If subsystem ID is not contained,
976 * create new entry.
Horst Hummel40545572006-06-29 15:08:18 +0200977 * Return 0 if server was already in serverlist,
978 * 1 if the server was added successful
979 * <0 in case of error.
Horst Hummel3d052592006-04-27 18:40:28 -0700980 */
981int
982dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
983{
984 struct dasd_devmap *devmap;
Peter Oberparleiterb18a60e2006-08-16 13:49:33 +0200985 struct dasd_server_ssid_map *srv, *tmp;
Horst Hummel3d052592006-04-27 18:40:28 -0700986
987 devmap = dasd_find_busid(cdev->dev.bus_id);
988 if (IS_ERR(devmap))
989 return PTR_ERR(devmap);
Horst Hummeld0710c72006-08-10 15:45:16 +0200990
Peter Oberparleiterb18a60e2006-08-16 13:49:33 +0200991 /* generate entry for server_ssid_map */
992 srv = (struct dasd_server_ssid_map *)
993 kzalloc(sizeof(struct dasd_server_ssid_map), GFP_KERNEL);
Horst Hummeld0710c72006-08-10 15:45:16 +0200994 if (!srv)
995 return -ENOMEM;
996 strncpy(srv->sid.vendor, uid->vendor, sizeof(srv->sid.vendor) - 1);
997 strncpy(srv->sid.serial, uid->serial, sizeof(srv->sid.serial) - 1);
Horst Hummel8e79a442006-08-24 13:22:36 +0200998 srv->sid.ssid = uid->ssid;
Horst Hummeld0710c72006-08-10 15:45:16 +0200999
1000 /* server is already contained ? */
Horst Hummel3d052592006-04-27 18:40:28 -07001001 spin_lock(&dasd_devmap_lock);
1002 devmap->uid = *uid;
Peter Oberparleiterb18a60e2006-08-16 13:49:33 +02001003 list_for_each_entry(tmp, &dasd_server_ssid_list, list) {
Horst Hummeld0710c72006-08-10 15:45:16 +02001004 if (!memcmp(&srv->sid, &tmp->sid,
Horst Hummel8e79a442006-08-24 13:22:36 +02001005 sizeof(struct system_id))) {
Horst Hummeld0710c72006-08-10 15:45:16 +02001006 kfree(srv);
1007 srv = NULL;
1008 break;
1009 }
1010 }
1011
1012 /* add servermap to serverlist */
1013 if (srv)
Peter Oberparleiterb18a60e2006-08-16 13:49:33 +02001014 list_add(&srv->list, &dasd_server_ssid_list);
Horst Hummel3d052592006-04-27 18:40:28 -07001015 spin_unlock(&dasd_devmap_lock);
Horst Hummeld0710c72006-08-10 15:45:16 +02001016
1017 return (srv ? 1 : 0);
Horst Hummel3d052592006-04-27 18:40:28 -07001018}
Horst Hummel40545572006-06-29 15:08:18 +02001019EXPORT_SYMBOL_GPL(dasd_set_uid);
Horst Hummel3d052592006-04-27 18:40:28 -07001020
Horst Hummelf24acd42005-05-01 08:58:59 -07001021/*
1022 * Return value of the specified feature.
1023 */
1024int
1025dasd_get_feature(struct ccw_device *cdev, int feature)
1026{
1027 struct dasd_devmap *devmap;
1028
1029 devmap = dasd_find_busid(cdev->dev.bus_id);
1030 if (IS_ERR(devmap))
Horst Hummel40545572006-06-29 15:08:18 +02001031 return PTR_ERR(devmap);
Horst Hummelf24acd42005-05-01 08:58:59 -07001032
1033 return ((devmap->features & feature) != 0);
1034}
1035
1036/*
1037 * Set / reset given feature.
1038 * Flag indicates wether to set (!=0) or the reset (=0) the feature.
1039 */
1040int
1041dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
1042{
1043 struct dasd_devmap *devmap;
1044
1045 devmap = dasd_find_busid(cdev->dev.bus_id);
1046 if (IS_ERR(devmap))
Horst Hummel40545572006-06-29 15:08:18 +02001047 return PTR_ERR(devmap);
Horst Hummelf24acd42005-05-01 08:58:59 -07001048
1049 spin_lock(&dasd_devmap_lock);
1050 if (flag)
1051 devmap->features |= feature;
1052 else
1053 devmap->features &= ~feature;
Horst Hummelc6eb7b72005-09-03 15:57:58 -07001054 if (devmap->device)
1055 devmap->device->features = devmap->features;
Horst Hummelf24acd42005-05-01 08:58:59 -07001056 spin_unlock(&dasd_devmap_lock);
1057 return 0;
1058}
1059
1060
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061int
1062dasd_add_sysfs_files(struct ccw_device *cdev)
1063{
1064 return sysfs_create_group(&cdev->dev.kobj, &dasd_attr_group);
1065}
1066
1067void
1068dasd_remove_sysfs_files(struct ccw_device *cdev)
1069{
1070 sysfs_remove_group(&cdev->dev.kobj, &dasd_attr_group);
1071}
1072
1073
1074int
1075dasd_devmap_init(void)
1076{
1077 int i;
1078
1079 /* Initialize devmap structures. */
1080 dasd_max_devindex = 0;
1081 for (i = 0; i < 256; i++)
1082 INIT_LIST_HEAD(&dasd_hashlists[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083
Horst Hummel40545572006-06-29 15:08:18 +02001084 /* Initialize servermap structure. */
Peter Oberparleiterb18a60e2006-08-16 13:49:33 +02001085 INIT_LIST_HEAD(&dasd_server_ssid_list);
Horst Hummel40545572006-06-29 15:08:18 +02001086 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087}
1088
1089void
1090dasd_devmap_exit(void)
1091{
1092 dasd_forget_ranges();
1093}