diff options
author | Horst Hummel <horst.hummel@de.ibm.com> | 2005-09-03 18:58:00 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@evo.osdl.org> | 2005-09-05 03:06:27 -0400 |
commit | fd49f41aa0c125ec649c56a45337b3024d6b1736 (patch) | |
tree | 7400ce289ba77474c3adf6a5a7f1ff8d5215cc28 /drivers | |
parent | c6eb7b7703ac4b3401b74f411c8c51ded214bf19 (diff) |
[PATCH] s390: 64 bit diag250 support
Add support for diag 250 access to dasd devices for 64 bit kernels. In
addition fix detach/attach for diag disks. The VM control block needs to get
recreated by a call to mdsk_init_io.
Signed-off-by: Horst Hummel <horst.hummel@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/block/Kconfig | 2 | ||||
-rw-r--r-- | drivers/s390/block/dasd_diag.c | 334 | ||||
-rw-r--r-- | drivers/s390/block/dasd_diag.h | 105 |
3 files changed, 307 insertions, 134 deletions
diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig index dc1c89dbdb8f..6e7d7b06421d 100644 --- a/drivers/s390/block/Kconfig +++ b/drivers/s390/block/Kconfig | |||
@@ -49,7 +49,7 @@ config DASD_FBA | |||
49 | 49 | ||
50 | config DASD_DIAG | 50 | config DASD_DIAG |
51 | tristate "Support for DIAG access to Disks" | 51 | tristate "Support for DIAG access to Disks" |
52 | depends on DASD && ARCH_S390X = 'n' | 52 | depends on DASD && ( ARCH_S390X = 'n' || EXPERIMENTAL) |
53 | help | 53 | help |
54 | Select this option if you want to use Diagnose250 command to access | 54 | Select this option if you want to use Diagnose250 command to access |
55 | Disks under VM. If you are not running under VM or unsure what it is, | 55 | Disks under VM. If you are not running under VM or unsure what it is, |
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 127699830fa1..7478423b53bb 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c | |||
@@ -6,17 +6,18 @@ | |||
6 | * Bugreports.to..: <Linux390@de.ibm.com> | 6 | * Bugreports.to..: <Linux390@de.ibm.com> |
7 | * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 | 7 | * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 |
8 | * | 8 | * |
9 | * $Revision: 1.42 $ | 9 | * $Revision: 1.49 $ |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/config.h> | 12 | #include <linux/config.h> |
13 | #include <linux/stddef.h> | 13 | #include <linux/stddef.h> |
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/hdreg.h> /* HDIO_GETGEO */ | 16 | #include <linux/hdreg.h> |
17 | #include <linux/bio.h> | 17 | #include <linux/bio.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/jiffies.h> | ||
20 | 21 | ||
21 | #include <asm/dasd.h> | 22 | #include <asm/dasd.h> |
22 | #include <asm/debug.h> | 23 | #include <asm/debug.h> |
@@ -28,58 +29,89 @@ | |||
28 | #include "dasd_int.h" | 29 | #include "dasd_int.h" |
29 | #include "dasd_diag.h" | 30 | #include "dasd_diag.h" |
30 | 31 | ||
31 | #ifdef PRINTK_HEADER | ||
32 | #undef PRINTK_HEADER | ||
33 | #endif /* PRINTK_HEADER */ | ||
34 | #define PRINTK_HEADER "dasd(diag):" | 32 | #define PRINTK_HEADER "dasd(diag):" |
35 | 33 | ||
36 | MODULE_LICENSE("GPL"); | 34 | MODULE_LICENSE("GPL"); |
37 | 35 | ||
36 | /* The maximum number of blocks per request (max_blocks) is dependent on the | ||
37 | * amount of storage that is available in the static I/O buffer for each | ||
38 | * device. Currently each device gets 2 pages. We want to fit two requests | ||
39 | * into the available memory so that we can immediately start the next if one | ||
40 | * finishes. */ | ||
41 | #define DIAG_MAX_BLOCKS (((2 * PAGE_SIZE - sizeof(struct dasd_ccw_req) - \ | ||
42 | sizeof(struct dasd_diag_req)) / \ | ||
43 | sizeof(struct dasd_diag_bio)) / 2) | ||
44 | #define DIAG_MAX_RETRIES 32 | ||
45 | #define DIAG_TIMEOUT 50 * HZ | ||
46 | |||
38 | struct dasd_discipline dasd_diag_discipline; | 47 | struct dasd_discipline dasd_diag_discipline; |
39 | 48 | ||
40 | struct dasd_diag_private { | 49 | struct dasd_diag_private { |
41 | struct dasd_diag_characteristics rdc_data; | 50 | struct dasd_diag_characteristics rdc_data; |
42 | struct dasd_diag_rw_io iob; | 51 | struct dasd_diag_rw_io iob; |
43 | struct dasd_diag_init_io iib; | 52 | struct dasd_diag_init_io iib; |
44 | unsigned int pt_block; | 53 | blocknum_t pt_block; |
45 | }; | 54 | }; |
46 | 55 | ||
47 | struct dasd_diag_req { | 56 | struct dasd_diag_req { |
48 | int block_count; | 57 | unsigned int block_count; |
49 | struct dasd_diag_bio bio[0]; | 58 | struct dasd_diag_bio bio[0]; |
50 | }; | 59 | }; |
51 | 60 | ||
61 | static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */ | ||
62 | |||
63 | /* Perform DIAG250 call with block I/O parameter list iob (input and output) | ||
64 | * and function code cmd. | ||
65 | * In case of an exception return 3. Otherwise return result of bitwise OR of | ||
66 | * resulting condition code and DIAG return code. */ | ||
52 | static __inline__ int | 67 | static __inline__ int |
53 | dia250(void *iob, int cmd) | 68 | dia250(void *iob, int cmd) |
54 | { | 69 | { |
70 | typedef struct { | ||
71 | char _[max(sizeof (struct dasd_diag_init_io), | ||
72 | sizeof (struct dasd_diag_rw_io))]; | ||
73 | } addr_type; | ||
55 | int rc; | 74 | int rc; |
56 | 75 | ||
57 | __asm__ __volatile__(" lhi %0,3\n" | 76 | __asm__ __volatile__( |
58 | " lr 0,%2\n" | 77 | #ifdef CONFIG_ARCH_S390X |
59 | " diag 0,%1,0x250\n" | 78 | " lghi %0,3\n" |
60 | "0: ipm %0\n" | 79 | " lgr 0,%3\n" |
61 | " srl %0,28\n" | 80 | " diag 0,%2,0x250\n" |
62 | " or %0,1\n" | 81 | "0: ipm %0\n" |
63 | "1:\n" | 82 | " srl %0,28\n" |
64 | #ifndef CONFIG_ARCH_S390X | 83 | " or %0,1\n" |
65 | ".section __ex_table,\"a\"\n" | 84 | "1:\n" |
66 | " .align 4\n" | 85 | ".section __ex_table,\"a\"\n" |
67 | " .long 0b,1b\n" | 86 | " .align 8\n" |
68 | ".previous\n" | 87 | " .quad 0b,1b\n" |
88 | ".previous\n" | ||
69 | #else | 89 | #else |
70 | ".section __ex_table,\"a\"\n" | 90 | " lhi %0,3\n" |
71 | " .align 8\n" | 91 | " lr 0,%3\n" |
72 | " .quad 0b,1b\n" | 92 | " diag 0,%2,0x250\n" |
73 | ".previous\n" | 93 | "0: ipm %0\n" |
94 | " srl %0,28\n" | ||
95 | " or %0,1\n" | ||
96 | "1:\n" | ||
97 | ".section __ex_table,\"a\"\n" | ||
98 | " .align 4\n" | ||
99 | " .long 0b,1b\n" | ||
100 | ".previous\n" | ||
74 | #endif | 101 | #endif |
75 | : "=&d" (rc) | 102 | : "=&d" (rc), "=m" (*(addr_type *) iob) |
76 | : "d" (cmd), "d" ((void *) __pa(iob)) | 103 | : "d" (cmd), "d" (iob), "m" (*(addr_type *) iob) |
77 | : "0", "1", "cc"); | 104 | : "0", "1", "cc"); |
78 | return rc; | 105 | return rc; |
79 | } | 106 | } |
80 | 107 | ||
108 | /* Initialize block I/O to DIAG device using the specified blocksize and | ||
109 | * block offset. On success, return zero and set end_block to contain the | ||
110 | * number of blocks on the device minus the specified offset. Return non-zero | ||
111 | * otherwise. */ | ||
81 | static __inline__ int | 112 | static __inline__ int |
82 | mdsk_init_io(struct dasd_device * device, int blocksize, int offset, int size) | 113 | mdsk_init_io(struct dasd_device *device, unsigned int blocksize, |
114 | blocknum_t offset, blocknum_t *end_block) | ||
83 | { | 115 | { |
84 | struct dasd_diag_private *private; | 116 | struct dasd_diag_private *private; |
85 | struct dasd_diag_init_io *iib; | 117 | struct dasd_diag_init_io *iib; |
@@ -92,14 +124,18 @@ mdsk_init_io(struct dasd_device * device, int blocksize, int offset, int size) | |||
92 | iib->dev_nr = _ccw_device_get_device_number(device->cdev); | 124 | iib->dev_nr = _ccw_device_get_device_number(device->cdev); |
93 | iib->block_size = blocksize; | 125 | iib->block_size = blocksize; |
94 | iib->offset = offset; | 126 | iib->offset = offset; |
95 | iib->start_block = 0; | 127 | iib->flaga = DASD_DIAG_FLAGA_DEFAULT; |
96 | iib->end_block = size; | ||
97 | 128 | ||
98 | rc = dia250(iib, INIT_BIO); | 129 | rc = dia250(iib, INIT_BIO); |
99 | 130 | ||
100 | return rc & 3; | 131 | if ((rc & 3) == 0 && end_block) |
132 | *end_block = iib->end_block; | ||
133 | |||
134 | return rc; | ||
101 | } | 135 | } |
102 | 136 | ||
137 | /* Remove block I/O environment for device. Return zero on success, non-zero | ||
138 | * otherwise. */ | ||
103 | static __inline__ int | 139 | static __inline__ int |
104 | mdsk_term_io(struct dasd_device * device) | 140 | mdsk_term_io(struct dasd_device * device) |
105 | { | 141 | { |
@@ -112,9 +148,25 @@ mdsk_term_io(struct dasd_device * device) | |||
112 | memset(iib, 0, sizeof (struct dasd_diag_init_io)); | 148 | memset(iib, 0, sizeof (struct dasd_diag_init_io)); |
113 | iib->dev_nr = _ccw_device_get_device_number(device->cdev); | 149 | iib->dev_nr = _ccw_device_get_device_number(device->cdev); |
114 | rc = dia250(iib, TERM_BIO); | 150 | rc = dia250(iib, TERM_BIO); |
115 | return rc & 3; | 151 | return rc; |
152 | } | ||
153 | |||
154 | /* Error recovery for failed DIAG requests - try to reestablish the DIAG | ||
155 | * environment. */ | ||
156 | static void | ||
157 | dasd_diag_erp(struct dasd_device *device) | ||
158 | { | ||
159 | int rc; | ||
160 | |||
161 | mdsk_term_io(device); | ||
162 | rc = mdsk_init_io(device, device->bp_block, 0, NULL); | ||
163 | if (rc) | ||
164 | DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, " | ||
165 | "rc=%d", rc); | ||
116 | } | 166 | } |
117 | 167 | ||
168 | /* Start a given request at the device. Return zero on success, non-zero | ||
169 | * otherwise. */ | ||
118 | static int | 170 | static int |
119 | dasd_start_diag(struct dasd_ccw_req * cqr) | 171 | dasd_start_diag(struct dasd_ccw_req * cqr) |
120 | { | 172 | { |
@@ -124,32 +176,66 @@ dasd_start_diag(struct dasd_ccw_req * cqr) | |||
124 | int rc; | 176 | int rc; |
125 | 177 | ||
126 | device = cqr->device; | 178 | device = cqr->device; |
179 | if (cqr->retries < 0) { | ||
180 | DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p " | ||
181 | "- no retry left)", cqr); | ||
182 | cqr->status = DASD_CQR_FAILED; | ||
183 | return -EIO; | ||
184 | } | ||
127 | private = (struct dasd_diag_private *) device->private; | 185 | private = (struct dasd_diag_private *) device->private; |
128 | dreq = (struct dasd_diag_req *) cqr->data; | 186 | dreq = (struct dasd_diag_req *) cqr->data; |
129 | 187 | ||
130 | private->iob.dev_nr = _ccw_device_get_device_number(device->cdev); | 188 | private->iob.dev_nr = _ccw_device_get_device_number(device->cdev); |
131 | private->iob.key = 0; | 189 | private->iob.key = 0; |
132 | private->iob.flags = 2; /* do asynchronous io */ | 190 | private->iob.flags = DASD_DIAG_RWFLAG_ASYNC; |
133 | private->iob.block_count = dreq->block_count; | 191 | private->iob.block_count = dreq->block_count; |
134 | private->iob.interrupt_params = (u32)(addr_t) cqr; | 192 | private->iob.interrupt_params = (addr_t) cqr; |
135 | private->iob.bio_list = __pa(dreq->bio); | 193 | private->iob.bio_list = __pa(dreq->bio); |
194 | private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT; | ||
136 | 195 | ||
137 | cqr->startclk = get_clock(); | 196 | cqr->startclk = get_clock(); |
197 | cqr->starttime = jiffies; | ||
198 | cqr->retries--; | ||
138 | 199 | ||
139 | rc = dia250(&private->iob, RW_BIO); | 200 | rc = dia250(&private->iob, RW_BIO); |
140 | if (rc > 8) { | 201 | switch (rc) { |
141 | DEV_MESSAGE(KERN_WARNING, device, "dia250 returned CC %d", rc); | 202 | case 0: /* Synchronous I/O finished successfully */ |
142 | cqr->status = DASD_CQR_ERROR; | 203 | cqr->stopclk = get_clock(); |
143 | } else if (rc == 0) { | ||
144 | cqr->status = DASD_CQR_DONE; | 204 | cqr->status = DASD_CQR_DONE; |
145 | dasd_schedule_bh(device); | 205 | /* Indicate to calling function that only a dasd_schedule_bh() |
146 | } else { | 206 | and no timer is needed */ |
207 | rc = -EACCES; | ||
208 | break; | ||
209 | case 8: /* Asynchronous I/O was started */ | ||
147 | cqr->status = DASD_CQR_IN_IO; | 210 | cqr->status = DASD_CQR_IN_IO; |
148 | rc = 0; | 211 | rc = 0; |
212 | break; | ||
213 | default: /* Error condition */ | ||
214 | cqr->status = DASD_CQR_QUEUED; | ||
215 | DEV_MESSAGE(KERN_WARNING, device, "dia250 returned rc=%d", rc); | ||
216 | dasd_diag_erp(device); | ||
217 | rc = -EIO; | ||
218 | break; | ||
149 | } | 219 | } |
150 | return rc; | 220 | return rc; |
151 | } | 221 | } |
152 | 222 | ||
223 | /* Terminate given request at the device. */ | ||
224 | static int | ||
225 | dasd_diag_term_IO(struct dasd_ccw_req * cqr) | ||
226 | { | ||
227 | struct dasd_device *device; | ||
228 | |||
229 | device = cqr->device; | ||
230 | mdsk_term_io(device); | ||
231 | mdsk_init_io(device, device->bp_block, 0, NULL); | ||
232 | cqr->status = DASD_CQR_CLEAR; | ||
233 | cqr->stopclk = get_clock(); | ||
234 | dasd_schedule_bh(device); | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | /* Handle external interruption. */ | ||
153 | static void | 239 | static void |
154 | dasd_ext_handler(struct pt_regs *regs, __u16 code) | 240 | dasd_ext_handler(struct pt_regs *regs, __u16 code) |
155 | { | 241 | { |
@@ -157,25 +243,27 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code) | |||
157 | struct dasd_device *device; | 243 | struct dasd_device *device; |
158 | unsigned long long expires; | 244 | unsigned long long expires; |
159 | unsigned long flags; | 245 | unsigned long flags; |
160 | char status; | 246 | u8 int_code, status; |
161 | int ip; | 247 | addr_t ip; |
162 | 248 | int rc; | |
163 | /* | ||
164 | * Get the external interruption subcode. VM stores | ||
165 | * this in the 'cpu address' field associated with | ||
166 | * the external interrupt. For diag 250 the subcode | ||
167 | * needs to be 3. | ||
168 | */ | ||
169 | if ((S390_lowcore.cpu_addr & 0xff00) != 0x0300) | ||
170 | return; | ||
171 | status = *((char *) &S390_lowcore.ext_params + 5); | ||
172 | ip = S390_lowcore.ext_params; | ||
173 | 249 | ||
250 | int_code = *((u8 *) DASD_DIAG_LC_INT_CODE); | ||
251 | status = *((u8 *) DASD_DIAG_LC_INT_STATUS); | ||
252 | switch (int_code) { | ||
253 | case DASD_DIAG_CODE_31BIT: | ||
254 | ip = (addr_t) *((u32 *) DASD_DIAG_LC_INT_PARM_31BIT); | ||
255 | break; | ||
256 | case DASD_DIAG_CODE_64BIT: | ||
257 | ip = (addr_t) *((u64 *) DASD_DIAG_LC_INT_PARM_64BIT); | ||
258 | break; | ||
259 | default: | ||
260 | return; | ||
261 | } | ||
174 | if (!ip) { /* no intparm: unsolicited interrupt */ | 262 | if (!ip) { /* no intparm: unsolicited interrupt */ |
175 | MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt"); | 263 | MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt"); |
176 | return; | 264 | return; |
177 | } | 265 | } |
178 | cqr = (struct dasd_ccw_req *)(addr_t) ip; | 266 | cqr = (struct dasd_ccw_req *) ip; |
179 | device = (struct dasd_device *) cqr->device; | 267 | device = (struct dasd_device *) cqr->device; |
180 | if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { | 268 | if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { |
181 | DEV_MESSAGE(KERN_WARNING, device, | 269 | DEV_MESSAGE(KERN_WARNING, device, |
@@ -188,6 +276,15 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code) | |||
188 | /* get irq lock to modify request queue */ | 276 | /* get irq lock to modify request queue */ |
189 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | 277 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); |
190 | 278 | ||
279 | /* Check for a pending clear operation */ | ||
280 | if (cqr->status == DASD_CQR_CLEAR) { | ||
281 | cqr->status = DASD_CQR_QUEUED; | ||
282 | dasd_clear_timer(device); | ||
283 | dasd_schedule_bh(device); | ||
284 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | ||
285 | return; | ||
286 | } | ||
287 | |||
191 | cqr->stopclk = get_clock(); | 288 | cqr->stopclk = get_clock(); |
192 | 289 | ||
193 | expires = 0; | 290 | expires = 0; |
@@ -198,16 +295,22 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code) | |||
198 | next = list_entry(device->ccw_queue.next, | 295 | next = list_entry(device->ccw_queue.next, |
199 | struct dasd_ccw_req, list); | 296 | struct dasd_ccw_req, list); |
200 | if (next->status == DASD_CQR_QUEUED) { | 297 | if (next->status == DASD_CQR_QUEUED) { |
201 | if (dasd_start_diag(next) == 0) | 298 | rc = dasd_start_diag(next); |
299 | if (rc == 0) | ||
202 | expires = next->expires; | 300 | expires = next->expires; |
203 | else | 301 | else if (rc != -EACCES) |
204 | DEV_MESSAGE(KERN_WARNING, device, "%s", | 302 | DEV_MESSAGE(KERN_WARNING, device, "%s", |
205 | "Interrupt fastpath " | 303 | "Interrupt fastpath " |
206 | "failed!"); | 304 | "failed!"); |
207 | } | 305 | } |
208 | } | 306 | } |
209 | } else | 307 | } else { |
210 | cqr->status = DASD_CQR_FAILED; | 308 | cqr->status = DASD_CQR_QUEUED; |
309 | DEV_MESSAGE(KERN_WARNING, device, "interrupt status for " | ||
310 | "request %p was %d (%d retries left)", cqr, status, | ||
311 | cqr->retries); | ||
312 | dasd_diag_erp(device); | ||
313 | } | ||
211 | 314 | ||
212 | if (expires != 0) | 315 | if (expires != 0) |
213 | dasd_set_timer(device, expires); | 316 | dasd_set_timer(device, expires); |
@@ -218,14 +321,17 @@ dasd_ext_handler(struct pt_regs *regs, __u16 code) | |||
218 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | 321 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); |
219 | } | 322 | } |
220 | 323 | ||
324 | /* Check whether device can be controlled by DIAG discipline. Return zero on | ||
325 | * success, non-zero otherwise. */ | ||
221 | static int | 326 | static int |
222 | dasd_diag_check_device(struct dasd_device *device) | 327 | dasd_diag_check_device(struct dasd_device *device) |
223 | { | 328 | { |
224 | struct dasd_diag_private *private; | 329 | struct dasd_diag_private *private; |
225 | struct dasd_diag_characteristics *rdc_data; | 330 | struct dasd_diag_characteristics *rdc_data; |
226 | struct dasd_diag_bio bio; | 331 | struct dasd_diag_bio bio; |
227 | long *label; | 332 | struct dasd_diag_cms_label *label; |
228 | int sb, bsize; | 333 | blocknum_t end_block; |
334 | unsigned int sb, bsize; | ||
229 | int rc; | 335 | int rc; |
230 | 336 | ||
231 | private = (struct dasd_diag_private *) device->private; | 337 | private = (struct dasd_diag_private *) device->private; |
@@ -244,8 +350,11 @@ dasd_diag_check_device(struct dasd_device *device) | |||
244 | rdc_data->rdc_len = sizeof (struct dasd_diag_characteristics); | 350 | rdc_data->rdc_len = sizeof (struct dasd_diag_characteristics); |
245 | 351 | ||
246 | rc = diag210((struct diag210 *) rdc_data); | 352 | rc = diag210((struct diag210 *) rdc_data); |
247 | if (rc) | 353 | if (rc) { |
354 | DEV_MESSAGE(KERN_WARNING, device, "failed to retrieve device " | ||
355 | "information (rc=%d)", rc); | ||
248 | return -ENOTSUPP; | 356 | return -ENOTSUPP; |
357 | } | ||
249 | 358 | ||
250 | /* Figure out position of label block */ | 359 | /* Figure out position of label block */ |
251 | switch (private->rdc_data.vdev_class) { | 360 | switch (private->rdc_data.vdev_class) { |
@@ -256,6 +365,8 @@ dasd_diag_check_device(struct dasd_device *device) | |||
256 | private->pt_block = 2; | 365 | private->pt_block = 2; |
257 | break; | 366 | break; |
258 | default: | 367 | default: |
368 | DEV_MESSAGE(KERN_WARNING, device, "unsupported device class " | ||
369 | "(class=%d)", private->rdc_data.vdev_class); | ||
259 | return -ENOTSUPP; | 370 | return -ENOTSUPP; |
260 | } | 371 | } |
261 | 372 | ||
@@ -269,15 +380,17 @@ dasd_diag_check_device(struct dasd_device *device) | |||
269 | mdsk_term_io(device); | 380 | mdsk_term_io(device); |
270 | 381 | ||
271 | /* figure out blocksize of device */ | 382 | /* figure out blocksize of device */ |
272 | label = (long *) get_zeroed_page(GFP_KERNEL); | 383 | label = (struct dasd_diag_cms_label *) get_zeroed_page(GFP_KERNEL); |
273 | if (label == NULL) { | 384 | if (label == NULL) { |
274 | DEV_MESSAGE(KERN_WARNING, device, "%s", | 385 | DEV_MESSAGE(KERN_WARNING, device, "%s", |
275 | "No memory to allocate initialization request"); | 386 | "No memory to allocate initialization request"); |
276 | return -ENOMEM; | 387 | return -ENOMEM; |
277 | } | 388 | } |
389 | rc = 0; | ||
390 | end_block = 0; | ||
278 | /* try all sizes - needed for ECKD devices */ | 391 | /* try all sizes - needed for ECKD devices */ |
279 | for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) { | 392 | for (bsize = 512; bsize <= PAGE_SIZE; bsize <<= 1) { |
280 | mdsk_init_io(device, bsize, 0, 64); | 393 | mdsk_init_io(device, bsize, 0, &end_block); |
281 | memset(&bio, 0, sizeof (struct dasd_diag_bio)); | 394 | memset(&bio, 0, sizeof (struct dasd_diag_bio)); |
282 | bio.type = MDSK_READ_REQ; | 395 | bio.type = MDSK_READ_REQ; |
283 | bio.block_number = private->pt_block + 1; | 396 | bio.block_number = private->pt_block + 1; |
@@ -289,37 +402,45 @@ dasd_diag_check_device(struct dasd_device *device) | |||
289 | private->iob.block_count = 1; | 402 | private->iob.block_count = 1; |
290 | private->iob.interrupt_params = 0; | 403 | private->iob.interrupt_params = 0; |
291 | private->iob.bio_list = __pa(&bio); | 404 | private->iob.bio_list = __pa(&bio); |
292 | if (dia250(&private->iob, RW_BIO) == 0) | 405 | private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT; |
406 | rc = dia250(&private->iob, RW_BIO); | ||
407 | if (rc == 0 || rc == 3) | ||
293 | break; | 408 | break; |
294 | mdsk_term_io(device); | 409 | mdsk_term_io(device); |
295 | } | 410 | } |
296 | if (bsize <= PAGE_SIZE && label[0] == 0xc3d4e2f1) { | 411 | if (rc == 3) { |
297 | /* get formatted blocksize from label block */ | 412 | DEV_MESSAGE(KERN_WARNING, device, "%s", "DIAG call failed"); |
298 | bsize = (int) label[3]; | 413 | rc = -EOPNOTSUPP; |
299 | device->blocks = label[7]; | 414 | } else if (rc != 0) { |
415 | DEV_MESSAGE(KERN_WARNING, device, "device access failed " | ||
416 | "(rc=%d)", rc); | ||
417 | rc = -EIO; | ||
418 | } else { | ||
419 | if (memcmp(label->label_id, DASD_DIAG_CMS1, | ||
420 | sizeof(DASD_DIAG_CMS1)) == 0) { | ||
421 | /* get formatted blocksize from label block */ | ||
422 | bsize = (unsigned int) label->block_size; | ||
423 | device->blocks = (unsigned long) label->block_count; | ||
424 | } else | ||
425 | device->blocks = end_block; | ||
300 | device->bp_block = bsize; | 426 | device->bp_block = bsize; |
301 | device->s2b_shift = 0; /* bits to shift 512 to get a block */ | 427 | device->s2b_shift = 0; /* bits to shift 512 to get a block */ |
302 | for (sb = 512; sb < bsize; sb = sb << 1) | 428 | for (sb = 512; sb < bsize; sb = sb << 1) |
303 | device->s2b_shift++; | 429 | device->s2b_shift++; |
304 | 430 | ||
305 | DEV_MESSAGE(KERN_INFO, device, | 431 | DEV_MESSAGE(KERN_INFO, device, |
306 | "capacity (%dkB blks): %ldkB", | 432 | "(%ld B/blk): %ldkB", |
307 | (device->bp_block >> 10), | 433 | (unsigned long) device->bp_block, |
308 | (device->blocks << device->s2b_shift) >> 1); | 434 | (unsigned long) (device->blocks << |
435 | device->s2b_shift) >> 1); | ||
309 | rc = 0; | 436 | rc = 0; |
310 | } else { | ||
311 | if (bsize > PAGE_SIZE) | ||
312 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
313 | "DIAG access failed"); | ||
314 | else | ||
315 | DEV_MESSAGE(KERN_WARNING, device, "%s", | ||
316 | "volume is not CMS formatted"); | ||
317 | rc = -EMEDIUMTYPE; | ||
318 | } | 437 | } |
319 | free_page((long) label); | 438 | free_page((long) label); |
320 | return rc; | 439 | return rc; |
321 | } | 440 | } |
322 | 441 | ||
442 | /* Fill in virtual disk geometry for device. Return zero on success, non-zero | ||
443 | * otherwise. */ | ||
323 | static int | 444 | static int |
324 | dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) | 445 | dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) |
325 | { | 446 | { |
@@ -349,6 +470,8 @@ dasd_diag_erp_postaction(struct dasd_ccw_req * cqr) | |||
349 | return dasd_default_erp_postaction; | 470 | return dasd_default_erp_postaction; |
350 | } | 471 | } |
351 | 472 | ||
473 | /* Create DASD request from block device request. Return pointer to new | ||
474 | * request on success, ERR_PTR otherwise. */ | ||
352 | static struct dasd_ccw_req * | 475 | static struct dasd_ccw_req * |
353 | dasd_diag_build_cp(struct dasd_device * device, struct request *req) | 476 | dasd_diag_build_cp(struct dasd_device * device, struct request *req) |
354 | { | 477 | { |
@@ -358,9 +481,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) | |||
358 | struct bio *bio; | 481 | struct bio *bio; |
359 | struct bio_vec *bv; | 482 | struct bio_vec *bv; |
360 | char *dst; | 483 | char *dst; |
361 | int count, datasize; | 484 | unsigned int count, datasize; |
362 | sector_t recid, first_rec, last_rec; | 485 | sector_t recid, first_rec, last_rec; |
363 | unsigned blksize, off; | 486 | unsigned int blksize, off; |
364 | unsigned char rw_cmd; | 487 | unsigned char rw_cmd; |
365 | int i; | 488 | int i; |
366 | 489 | ||
@@ -413,13 +536,16 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) | |||
413 | } | 536 | } |
414 | } | 537 | } |
415 | } | 538 | } |
539 | cqr->retries = DIAG_MAX_RETRIES; | ||
416 | cqr->buildclk = get_clock(); | 540 | cqr->buildclk = get_clock(); |
417 | cqr->device = device; | 541 | cqr->device = device; |
418 | cqr->expires = 50 * HZ; /* 50 seconds */ | 542 | cqr->expires = DIAG_TIMEOUT; |
419 | cqr->status = DASD_CQR_FILLED; | 543 | cqr->status = DASD_CQR_FILLED; |
420 | return cqr; | 544 | return cqr; |
421 | } | 545 | } |
422 | 546 | ||
547 | /* Release DASD request. Return non-zero if request was successful, zero | ||
548 | * otherwise. */ | ||
423 | static int | 549 | static int |
424 | dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) | 550 | dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) |
425 | { | 551 | { |
@@ -430,6 +556,7 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) | |||
430 | return status; | 556 | return status; |
431 | } | 557 | } |
432 | 558 | ||
559 | /* Fill in IOCTL data for device. */ | ||
433 | static int | 560 | static int |
434 | dasd_diag_fill_info(struct dasd_device * device, | 561 | dasd_diag_fill_info(struct dasd_device * device, |
435 | struct dasd_information2_t * info) | 562 | struct dasd_information2_t * info) |
@@ -437,7 +564,7 @@ dasd_diag_fill_info(struct dasd_device * device, | |||
437 | struct dasd_diag_private *private; | 564 | struct dasd_diag_private *private; |
438 | 565 | ||
439 | private = (struct dasd_diag_private *) device->private; | 566 | private = (struct dasd_diag_private *) device->private; |
440 | info->label_block = private->pt_block; | 567 | info->label_block = (unsigned int) private->pt_block; |
441 | info->FBA_layout = 1; | 568 | info->FBA_layout = 1; |
442 | info->format = DASD_FORMAT_LDL; | 569 | info->format = DASD_FORMAT_LDL; |
443 | info->characteristics_size = sizeof (struct dasd_diag_characteristics); | 570 | info->characteristics_size = sizeof (struct dasd_diag_characteristics); |
@@ -456,26 +583,15 @@ dasd_diag_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, | |||
456 | "dump sense not available for DIAG data"); | 583 | "dump sense not available for DIAG data"); |
457 | } | 584 | } |
458 | 585 | ||
459 | /* | ||
460 | * max_blocks is dependent on the amount of storage that is available | ||
461 | * in the static io buffer for each device. Currently each device has | ||
462 | * 8192 bytes (=2 pages). dasd diag is only relevant for 31 bit. | ||
463 | * The struct dasd_ccw_req has 96 bytes, the struct dasd_diag_req has | ||
464 | * 8 bytes and the struct dasd_diag_bio for each block has 16 bytes. | ||
465 | * That makes: | ||
466 | * (8192 - 96 - 8) / 16 = 505.5 blocks at maximum. | ||
467 | * We want to fit two into the available memory so that we can immediately | ||
468 | * start the next request if one finishes off. That makes 252.75 blocks | ||
469 | * for one request. Give a little safety and the result is 240. | ||
470 | */ | ||
471 | struct dasd_discipline dasd_diag_discipline = { | 586 | struct dasd_discipline dasd_diag_discipline = { |
472 | .owner = THIS_MODULE, | 587 | .owner = THIS_MODULE, |
473 | .name = "DIAG", | 588 | .name = "DIAG", |
474 | .ebcname = "DIAG", | 589 | .ebcname = "DIAG", |
475 | .max_blocks = 240, | 590 | .max_blocks = DIAG_MAX_BLOCKS, |
476 | .check_device = dasd_diag_check_device, | 591 | .check_device = dasd_diag_check_device, |
477 | .fill_geometry = dasd_diag_fill_geometry, | 592 | .fill_geometry = dasd_diag_fill_geometry, |
478 | .start_IO = dasd_start_diag, | 593 | .start_IO = dasd_start_diag, |
594 | .term_IO = dasd_diag_term_IO, | ||
479 | .examine_error = dasd_diag_examine_error, | 595 | .examine_error = dasd_diag_examine_error, |
480 | .erp_action = dasd_diag_erp_action, | 596 | .erp_action = dasd_diag_erp_action, |
481 | .erp_postaction = dasd_diag_erp_postaction, | 597 | .erp_postaction = dasd_diag_erp_postaction, |
@@ -493,7 +609,7 @@ dasd_diag_init(void) | |||
493 | "Machine is not VM: %s " | 609 | "Machine is not VM: %s " |
494 | "discipline not initializing", | 610 | "discipline not initializing", |
495 | dasd_diag_discipline.name); | 611 | dasd_diag_discipline.name); |
496 | return -EINVAL; | 612 | return -ENODEV; |
497 | } | 613 | } |
498 | ASCEBC(dasd_diag_discipline.ebcname, 4); | 614 | ASCEBC(dasd_diag_discipline.ebcname, 4); |
499 | 615 | ||
@@ -506,13 +622,6 @@ dasd_diag_init(void) | |||
506 | static void __exit | 622 | static void __exit |
507 | dasd_diag_cleanup(void) | 623 | dasd_diag_cleanup(void) |
508 | { | 624 | { |
509 | if (!MACHINE_IS_VM) { | ||
510 | MESSAGE_LOG(KERN_INFO, | ||
511 | "Machine is not VM: %s " | ||
512 | "discipline not cleaned", | ||
513 | dasd_diag_discipline.name); | ||
514 | return; | ||
515 | } | ||
516 | unregister_external_interrupt(0x2603, dasd_ext_handler); | 625 | unregister_external_interrupt(0x2603, dasd_ext_handler); |
517 | ctl_clear_bit(0, 9); | 626 | ctl_clear_bit(0, 9); |
518 | dasd_diag_discipline_pointer = NULL; | 627 | dasd_diag_discipline_pointer = NULL; |
@@ -520,22 +629,3 @@ dasd_diag_cleanup(void) | |||
520 | 629 | ||
521 | module_init(dasd_diag_init); | 630 | module_init(dasd_diag_init); |
522 | module_exit(dasd_diag_cleanup); | 631 | module_exit(dasd_diag_cleanup); |
523 | |||
524 | /* | ||
525 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
526 | * Emacs will notice this stuff at the end of the file and automatically | ||
527 | * adjust the settings for this buffer only. This must remain at the end | ||
528 | * of the file. | ||
529 | * --------------------------------------------------------------------------- | ||
530 | * Local variables: | ||
531 | * c-indent-level: 4 | ||
532 | * c-brace-imaginary-offset: 0 | ||
533 | * c-brace-offset: -4 | ||
534 | * c-argdecl-indent: 4 | ||
535 | * c-label-offset: -4 | ||
536 | * c-continued-statement-offset: 4 | ||
537 | * c-continued-brace-offset: 0 | ||
538 | * indent-tabs-mode: 1 | ||
539 | * tab-width: 8 | ||
540 | * End: | ||
541 | */ | ||
diff --git a/drivers/s390/block/dasd_diag.h b/drivers/s390/block/dasd_diag.h index a0c38e303979..b26eb28df4bf 100644 --- a/drivers/s390/block/dasd_diag.h +++ b/drivers/s390/block/dasd_diag.h | |||
@@ -6,7 +6,7 @@ | |||
6 | * Bugreports.to..: <Linux390@de.ibm.com> | 6 | * Bugreports.to..: <Linux390@de.ibm.com> |
7 | * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 | 7 | * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 |
8 | * | 8 | * |
9 | * $Revision: 1.6 $ | 9 | * $Revision: 1.7 $ |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #define MDSK_WRITE_REQ 0x01 | 12 | #define MDSK_WRITE_REQ 0x01 |
@@ -19,6 +19,18 @@ | |||
19 | #define DEV_CLASS_FBA 0x01 | 19 | #define DEV_CLASS_FBA 0x01 |
20 | #define DEV_CLASS_ECKD 0x04 | 20 | #define DEV_CLASS_ECKD 0x04 |
21 | 21 | ||
22 | #define DASD_DIAG_LC_INT_CODE 132 | ||
23 | #define DASD_DIAG_LC_INT_STATUS 133 | ||
24 | #define DASD_DIAG_LC_INT_PARM_31BIT 128 | ||
25 | #define DASD_DIAG_LC_INT_PARM_64BIT 4536 | ||
26 | #define DASD_DIAG_CODE_31BIT 0x03 | ||
27 | #define DASD_DIAG_CODE_64BIT 0x07 | ||
28 | |||
29 | #define DASD_DIAG_RWFLAG_ASYNC 0x02 | ||
30 | #define DASD_DIAG_RWFLAG_NOCACHE 0x01 | ||
31 | |||
32 | #define DASD_DIAG_FLAGA_FORMAT_64BIT 0x80 | ||
33 | |||
22 | struct dasd_diag_characteristics { | 34 | struct dasd_diag_characteristics { |
23 | u16 dev_nr; | 35 | u16 dev_nr; |
24 | u16 rdc_len; | 36 | u16 rdc_len; |
@@ -32,35 +44,106 @@ struct dasd_diag_characteristics { | |||
32 | u8 rdev_features; | 44 | u8 rdev_features; |
33 | } __attribute__ ((packed, aligned(4))); | 45 | } __attribute__ ((packed, aligned(4))); |
34 | 46 | ||
47 | struct dasd_diag_cms_label { | ||
48 | u8 label_id[4]; | ||
49 | u8 vol_id[6]; | ||
50 | u16 version_id; | ||
51 | u32 block_size; | ||
52 | u32 origin_ptr; | ||
53 | u32 usable_count; | ||
54 | u32 formatted_count; | ||
55 | u32 block_count; | ||
56 | u32 used_count; | ||
57 | u32 fst_size; | ||
58 | u32 fst_count; | ||
59 | u8 format_date[6]; | ||
60 | u8 reserved1[2]; | ||
61 | u32 disk_offset; | ||
62 | u32 map_block; | ||
63 | u32 hblk_disp; | ||
64 | u32 user_disp; | ||
65 | u8 reserved2[4]; | ||
66 | u8 segment_name[8]; | ||
67 | } __attribute__ ((packed)); | ||
68 | |||
69 | #ifdef CONFIG_ARCH_S390X | ||
70 | #define DASD_DIAG_FLAGA_DEFAULT DASD_DIAG_FLAGA_FORMAT_64BIT | ||
71 | |||
72 | typedef u64 blocknum_t; | ||
73 | typedef s64 sblocknum_t; | ||
74 | |||
75 | struct dasd_diag_bio { | ||
76 | u8 type; | ||
77 | u8 status; | ||
78 | u8 spare1[2]; | ||
79 | u32 alet; | ||
80 | blocknum_t block_number; | ||
81 | u64 buffer; | ||
82 | } __attribute__ ((packed, aligned(8))); | ||
83 | |||
84 | struct dasd_diag_init_io { | ||
85 | u16 dev_nr; | ||
86 | u8 flaga; | ||
87 | u8 spare1[21]; | ||
88 | u32 block_size; | ||
89 | u8 spare2[4]; | ||
90 | blocknum_t offset; | ||
91 | sblocknum_t start_block; | ||
92 | blocknum_t end_block; | ||
93 | u8 spare3[8]; | ||
94 | } __attribute__ ((packed, aligned(8))); | ||
95 | |||
96 | struct dasd_diag_rw_io { | ||
97 | u16 dev_nr; | ||
98 | u8 flaga; | ||
99 | u8 spare1[21]; | ||
100 | u8 key; | ||
101 | u8 flags; | ||
102 | u8 spare2[2]; | ||
103 | u32 block_count; | ||
104 | u32 alet; | ||
105 | u8 spare3[4]; | ||
106 | u64 interrupt_params; | ||
107 | u64 bio_list; | ||
108 | u8 spare4[8]; | ||
109 | } __attribute__ ((packed, aligned(8))); | ||
110 | #else /* CONFIG_ARCH_S390X */ | ||
111 | #define DASD_DIAG_FLAGA_DEFAULT 0x0 | ||
112 | |||
113 | typedef u32 blocknum_t; | ||
114 | typedef s32 sblocknum_t; | ||
115 | |||
35 | struct dasd_diag_bio { | 116 | struct dasd_diag_bio { |
36 | u8 type; | 117 | u8 type; |
37 | u8 status; | 118 | u8 status; |
38 | u16 spare1; | 119 | u16 spare1; |
39 | u32 block_number; | 120 | blocknum_t block_number; |
40 | u32 alet; | 121 | u32 alet; |
41 | u32 buffer; | 122 | u32 buffer; |
42 | } __attribute__ ((packed, aligned(8))); | 123 | } __attribute__ ((packed, aligned(8))); |
43 | 124 | ||
44 | struct dasd_diag_init_io { | 125 | struct dasd_diag_init_io { |
45 | u16 dev_nr; | 126 | u16 dev_nr; |
46 | u16 spare1[11]; | 127 | u8 flaga; |
128 | u8 spare1[21]; | ||
47 | u32 block_size; | 129 | u32 block_size; |
48 | u32 offset; | 130 | blocknum_t offset; |
49 | u32 start_block; | 131 | sblocknum_t start_block; |
50 | u32 end_block; | 132 | blocknum_t end_block; |
51 | u32 spare2[6]; | 133 | u8 spare2[24]; |
52 | } __attribute__ ((packed, aligned(8))); | 134 | } __attribute__ ((packed, aligned(8))); |
53 | 135 | ||
54 | struct dasd_diag_rw_io { | 136 | struct dasd_diag_rw_io { |
55 | u16 dev_nr; | 137 | u16 dev_nr; |
56 | u16 spare1[11]; | 138 | u8 flaga; |
139 | u8 spare1[21]; | ||
57 | u8 key; | 140 | u8 key; |
58 | u8 flags; | 141 | u8 flags; |
59 | u16 spare2; | 142 | u8 spare2[2]; |
60 | u32 block_count; | 143 | u32 block_count; |
61 | u32 alet; | 144 | u32 alet; |
62 | u32 bio_list; | 145 | u32 bio_list; |
63 | u32 interrupt_params; | 146 | u32 interrupt_params; |
64 | u32 spare3[5]; | 147 | u8 spare3[20]; |
65 | } __attribute__ ((packed, aligned(8))); | 148 | } __attribute__ ((packed, aligned(8))); |
66 | 149 | #endif /* CONFIG_ARCH_S390X */ | |