diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /drivers/s390/cio/device_pgid.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'drivers/s390/cio/device_pgid.c')
-rw-r--r-- | drivers/s390/cio/device_pgid.c | 970 |
1 files changed, 472 insertions, 498 deletions
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index fc5ca1dd52b3..6facb5499a65 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c | |||
@@ -1,594 +1,568 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/cio/device_pgid.c | 2 | * CCW device PGID and path verification I/O handling. |
3 | * | 3 | * |
4 | * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, | 4 | * Copyright IBM Corp. 2002,2009 |
5 | * IBM Corporation | 5 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> |
6 | * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) | 6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
7 | * Martin Schwidefsky (schwidefsky@de.ibm.com) | 7 | * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> |
8 | * | ||
9 | * Path Group ID functions. | ||
10 | */ | 8 | */ |
11 | 9 | ||
12 | #include <linux/module.h> | 10 | #include <linux/kernel.h> |
13 | #include <linux/init.h> | 11 | #include <linux/string.h> |
14 | 12 | #include <linux/types.h> | |
13 | #include <linux/errno.h> | ||
14 | #include <linux/bitops.h> | ||
15 | #include <asm/ccwdev.h> | 15 | #include <asm/ccwdev.h> |
16 | #include <asm/cio.h> | 16 | #include <asm/cio.h> |
17 | #include <asm/delay.h> | ||
18 | #include <asm/lowcore.h> | ||
19 | 17 | ||
20 | #include "cio.h" | 18 | #include "cio.h" |
21 | #include "cio_debug.h" | 19 | #include "cio_debug.h" |
22 | #include "css.h" | ||
23 | #include "device.h" | 20 | #include "device.h" |
24 | #include "ioasm.h" | ||
25 | #include "io_sch.h" | 21 | #include "io_sch.h" |
26 | 22 | ||
23 | #define PGID_RETRIES 256 | ||
24 | #define PGID_TIMEOUT (10 * HZ) | ||
25 | |||
27 | /* | 26 | /* |
28 | * Helper function called from interrupt context to decide whether an | 27 | * Process path verification data and report result. |
29 | * operation should be tried again. | ||
30 | */ | 28 | */ |
31 | static int __ccw_device_should_retry(union scsw *scsw) | 29 | static void verify_done(struct ccw_device *cdev, int rc) |
32 | { | 30 | { |
33 | /* CC is only valid if start function bit is set. */ | 31 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
34 | if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1) | 32 | struct ccw_dev_id *id = &cdev->private->dev_id; |
35 | return 1; | 33 | int mpath = cdev->private->flags.mpath; |
36 | /* No more activity. For sense and set PGID we stubbornly try again. */ | 34 | int pgroup = cdev->private->flags.pgroup; |
37 | if (!scsw->cmd.actl) | 35 | |
38 | return 1; | 36 | if (rc) |
39 | return 0; | 37 | goto out; |
38 | /* Ensure consistent multipathing state at device and channel. */ | ||
39 | if (sch->config.mp != mpath) { | ||
40 | sch->config.mp = mpath; | ||
41 | rc = cio_commit_config(sch); | ||
42 | } | ||
43 | out: | ||
44 | CIO_MSG_EVENT(2, "vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d " | ||
45 | "vpm=%02x\n", id->ssid, id->devno, rc, pgroup, mpath, | ||
46 | sch->vpm); | ||
47 | ccw_device_verify_done(cdev, rc); | ||
40 | } | 48 | } |
41 | 49 | ||
42 | /* | 50 | /* |
43 | * Start Sense Path Group ID helper function. Used in ccw_device_recog | 51 | * Create channel program to perform a NOOP. |
44 | * and ccw_device_sense_pgid. | ||
45 | */ | 52 | */ |
46 | static int | 53 | static void nop_build_cp(struct ccw_device *cdev) |
47 | __ccw_device_sense_pgid_start(struct ccw_device *cdev) | ||
48 | { | 54 | { |
49 | struct subchannel *sch; | 55 | struct ccw_request *req = &cdev->private->req; |
50 | struct ccw1 *ccw; | 56 | struct ccw1 *cp = cdev->private->iccws; |
51 | int ret; | 57 | |
52 | int i; | 58 | cp->cmd_code = CCW_CMD_NOOP; |
53 | 59 | cp->cda = 0; | |
54 | sch = to_subchannel(cdev->dev.parent); | 60 | cp->count = 0; |
55 | /* Return if we already checked on all paths. */ | 61 | cp->flags = CCW_FLAG_SLI; |
56 | if (cdev->private->imask == 0) | 62 | req->cp = cp; |
57 | return (sch->lpm == 0) ? -ENODEV : -EACCES; | ||
58 | i = 8 - ffs(cdev->private->imask); | ||
59 | |||
60 | /* Setup sense path group id channel program. */ | ||
61 | ccw = cdev->private->iccws; | ||
62 | ccw->cmd_code = CCW_CMD_SENSE_PGID; | ||
63 | ccw->count = sizeof (struct pgid); | ||
64 | ccw->flags = CCW_FLAG_SLI; | ||
65 | |||
66 | /* Reset device status. */ | ||
67 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | ||
68 | /* Try on every path. */ | ||
69 | ret = -ENODEV; | ||
70 | while (cdev->private->imask != 0) { | ||
71 | /* Try every path multiple times. */ | ||
72 | ccw->cda = (__u32) __pa (&cdev->private->pgid[i]); | ||
73 | if (cdev->private->iretry > 0) { | ||
74 | cdev->private->iretry--; | ||
75 | /* Reset internal retry indication. */ | ||
76 | cdev->private->flags.intretry = 0; | ||
77 | ret = cio_start (sch, cdev->private->iccws, | ||
78 | cdev->private->imask); | ||
79 | /* ret is 0, -EBUSY, -EACCES or -ENODEV */ | ||
80 | if (ret != -EACCES) | ||
81 | return ret; | ||
82 | CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel " | ||
83 | "0.%x.%04x, lpm %02X, became 'not " | ||
84 | "operational'\n", | ||
85 | cdev->private->dev_id.devno, | ||
86 | sch->schid.ssid, | ||
87 | sch->schid.sch_no, cdev->private->imask); | ||
88 | |||
89 | } | ||
90 | cdev->private->imask >>= 1; | ||
91 | cdev->private->iretry = 5; | ||
92 | i++; | ||
93 | } | ||
94 | |||
95 | return ret; | ||
96 | } | 63 | } |
97 | 64 | ||
98 | void | 65 | /* |
99 | ccw_device_sense_pgid_start(struct ccw_device *cdev) | 66 | * Perform NOOP on a single path. |
67 | */ | ||
68 | static void nop_do(struct ccw_device *cdev) | ||
100 | { | 69 | { |
101 | int ret; | 70 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
102 | 71 | struct ccw_request *req = &cdev->private->req; | |
103 | /* Set a timeout of 60s */ | 72 | |
104 | ccw_device_set_timeout(cdev, 60*HZ); | 73 | /* Adjust lpm. */ |
105 | 74 | req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm); | |
106 | cdev->private->state = DEV_STATE_SENSE_PGID; | 75 | if (!req->lpm) |
107 | cdev->private->imask = 0x80; | 76 | goto out_nopath; |
108 | cdev->private->iretry = 5; | 77 | nop_build_cp(cdev); |
109 | memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid)); | 78 | ccw_request_start(cdev); |
110 | ret = __ccw_device_sense_pgid_start(cdev); | 79 | return; |
111 | if (ret && ret != -EBUSY) | 80 | |
112 | ccw_device_sense_pgid_done(cdev, ret); | 81 | out_nopath: |
82 | verify_done(cdev, sch->vpm ? 0 : -EACCES); | ||
113 | } | 83 | } |
114 | 84 | ||
115 | /* | 85 | /* |
116 | * Called from interrupt context to check if a valid answer | 86 | * Adjust NOOP I/O status. |
117 | * to Sense Path Group ID was received. | ||
118 | */ | 87 | */ |
119 | static int | 88 | static enum io_status nop_filter(struct ccw_device *cdev, void *data, |
120 | __ccw_device_check_sense_pgid(struct ccw_device *cdev) | 89 | struct irb *irb, enum io_status status) |
121 | { | 90 | { |
122 | struct subchannel *sch; | 91 | /* Only subchannel status might indicate a path error. */ |
123 | struct irb *irb; | 92 | if (status == IO_STATUS_ERROR && irb->scsw.cmd.cstat == 0) |
124 | int i; | 93 | return IO_DONE; |
125 | 94 | return status; | |
126 | sch = to_subchannel(cdev->dev.parent); | ||
127 | irb = &cdev->private->irb; | ||
128 | if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { | ||
129 | /* Retry Sense PGID if requested. */ | ||
130 | if (cdev->private->flags.intretry) { | ||
131 | cdev->private->flags.intretry = 0; | ||
132 | return -EAGAIN; | ||
133 | } | ||
134 | return -ETIME; | ||
135 | } | ||
136 | if (irb->esw.esw0.erw.cons && | ||
137 | (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) { | ||
138 | /* | ||
139 | * If the device doesn't support the Sense Path Group ID | ||
140 | * command further retries wouldn't help ... | ||
141 | */ | ||
142 | return -EOPNOTSUPP; | ||
143 | } | ||
144 | if (irb->esw.esw0.erw.cons) { | ||
145 | CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, " | ||
146 | "lpum %02X, cnt %02d, sns : " | ||
147 | "%02X%02X%02X%02X %02X%02X%02X%02X ...\n", | ||
148 | cdev->private->dev_id.ssid, | ||
149 | cdev->private->dev_id.devno, | ||
150 | irb->esw.esw0.sublog.lpum, | ||
151 | irb->esw.esw0.erw.scnt, | ||
152 | irb->ecw[0], irb->ecw[1], | ||
153 | irb->ecw[2], irb->ecw[3], | ||
154 | irb->ecw[4], irb->ecw[5], | ||
155 | irb->ecw[6], irb->ecw[7]); | ||
156 | return -EAGAIN; | ||
157 | } | ||
158 | if (irb->scsw.cmd.cc == 3) { | ||
159 | u8 lpm; | ||
160 | |||
161 | lpm = to_io_private(sch)->orb.cmd.lpm; | ||
162 | CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x," | ||
163 | " lpm %02X, became 'not operational'\n", | ||
164 | cdev->private->dev_id.devno, sch->schid.ssid, | ||
165 | sch->schid.sch_no, lpm); | ||
166 | return -EACCES; | ||
167 | } | ||
168 | i = 8 - ffs(cdev->private->imask); | ||
169 | if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { | ||
170 | CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x " | ||
171 | "is reserved by someone else\n", | ||
172 | cdev->private->dev_id.devno, sch->schid.ssid, | ||
173 | sch->schid.sch_no); | ||
174 | return -EUSERS; | ||
175 | } | ||
176 | return 0; | ||
177 | } | 95 | } |
178 | 96 | ||
179 | /* | 97 | /* |
180 | * Got interrupt for Sense Path Group ID. | 98 | * Process NOOP request result for a single path. |
181 | */ | 99 | */ |
182 | void | 100 | static void nop_callback(struct ccw_device *cdev, void *data, int rc) |
183 | ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event) | ||
184 | { | 101 | { |
185 | struct subchannel *sch; | 102 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
186 | struct irb *irb; | 103 | struct ccw_request *req = &cdev->private->req; |
187 | int ret; | 104 | |
188 | 105 | if (rc == 0) | |
189 | irb = (struct irb *) __LC_IRB; | 106 | sch->vpm |= req->lpm; |
190 | 107 | else if (rc != -EACCES) | |
191 | if (irb->scsw.cmd.stctl == | 108 | goto err; |
192 | (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { | 109 | req->lpm >>= 1; |
193 | if (__ccw_device_should_retry(&irb->scsw)) { | 110 | nop_do(cdev); |
194 | ret = __ccw_device_sense_pgid_start(cdev); | 111 | return; |
195 | if (ret && ret != -EBUSY) | 112 | |
196 | ccw_device_sense_pgid_done(cdev, ret); | 113 | err: |
197 | } | 114 | verify_done(cdev, rc); |
198 | return; | ||
199 | } | ||
200 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) | ||
201 | return; | ||
202 | sch = to_subchannel(cdev->dev.parent); | ||
203 | ret = __ccw_device_check_sense_pgid(cdev); | ||
204 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | ||
205 | switch (ret) { | ||
206 | /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */ | ||
207 | case -EOPNOTSUPP: /* Sense Path Group ID not supported */ | ||
208 | ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP); | ||
209 | break; | ||
210 | case -ETIME: /* Sense path group id stopped by timeout. */ | ||
211 | ccw_device_sense_pgid_done(cdev, -ETIME); | ||
212 | break; | ||
213 | case -EACCES: /* channel is not operational. */ | ||
214 | sch->lpm &= ~cdev->private->imask; | ||
215 | /* Fall through. */ | ||
216 | case 0: /* Sense Path Group ID successful. */ | ||
217 | cdev->private->imask >>= 1; | ||
218 | cdev->private->iretry = 5; | ||
219 | /* Fall through. */ | ||
220 | case -EAGAIN: /* Try again. */ | ||
221 | ret = __ccw_device_sense_pgid_start(cdev); | ||
222 | if (ret != 0 && ret != -EBUSY) | ||
223 | ccw_device_sense_pgid_done(cdev, ret); | ||
224 | break; | ||
225 | case -EUSERS: /* device is reserved for someone else. */ | ||
226 | ccw_device_sense_pgid_done(cdev, -EUSERS); | ||
227 | break; | ||
228 | } | ||
229 | } | 115 | } |
230 | 116 | ||
231 | /* | 117 | /* |
232 | * Path Group ID helper function. | 118 | * Create channel program to perform SET PGID on a single path. |
233 | */ | 119 | */ |
234 | static int | 120 | static void spid_build_cp(struct ccw_device *cdev, u8 fn) |
235 | __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) | ||
236 | { | 121 | { |
237 | struct subchannel *sch; | 122 | struct ccw_request *req = &cdev->private->req; |
238 | struct ccw1 *ccw; | 123 | struct ccw1 *cp = cdev->private->iccws; |
239 | int ret; | 124 | int i = 8 - ffs(req->lpm); |
240 | 125 | struct pgid *pgid = &cdev->private->pgid[i]; | |
241 | sch = to_subchannel(cdev->dev.parent); | 126 | |
242 | 127 | pgid->inf.fc = fn; | |
243 | /* Setup sense path group id channel program. */ | 128 | cp->cmd_code = CCW_CMD_SET_PGID; |
244 | cdev->private->pgid[0].inf.fc = func; | 129 | cp->cda = (u32) (addr_t) pgid; |
245 | ccw = cdev->private->iccws; | 130 | cp->count = sizeof(*pgid); |
246 | if (cdev->private->flags.pgid_single) | 131 | cp->flags = CCW_FLAG_SLI; |
247 | cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH; | 132 | req->cp = cp; |
248 | else | ||
249 | cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH; | ||
250 | ccw->cmd_code = CCW_CMD_SET_PGID; | ||
251 | ccw->cda = (__u32) __pa (&cdev->private->pgid[0]); | ||
252 | ccw->count = sizeof (struct pgid); | ||
253 | ccw->flags = CCW_FLAG_SLI; | ||
254 | |||
255 | /* Reset device status. */ | ||
256 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | ||
257 | |||
258 | /* Try multiple times. */ | ||
259 | ret = -EACCES; | ||
260 | if (cdev->private->iretry > 0) { | ||
261 | cdev->private->iretry--; | ||
262 | /* Reset internal retry indication. */ | ||
263 | cdev->private->flags.intretry = 0; | ||
264 | ret = cio_start (sch, cdev->private->iccws, | ||
265 | cdev->private->imask); | ||
266 | /* We expect an interrupt in case of success or busy | ||
267 | * indication. */ | ||
268 | if ((ret == 0) || (ret == -EBUSY)) | ||
269 | return ret; | ||
270 | } | ||
271 | /* PGID command failed on this path. */ | ||
272 | CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel " | ||
273 | "0.%x.%04x, lpm %02X, became 'not operational'\n", | ||
274 | cdev->private->dev_id.devno, sch->schid.ssid, | ||
275 | sch->schid.sch_no, cdev->private->imask); | ||
276 | return ret; | ||
277 | } | 133 | } |
278 | 134 | ||
279 | /* | 135 | /* |
280 | * Helper function to send a nop ccw down a path. | 136 | * Perform establish/resign SET PGID on a single path. |
281 | */ | 137 | */ |
282 | static int __ccw_device_do_nop(struct ccw_device *cdev) | 138 | static void spid_do(struct ccw_device *cdev) |
283 | { | 139 | { |
284 | struct subchannel *sch; | 140 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
285 | struct ccw1 *ccw; | 141 | struct ccw_request *req = &cdev->private->req; |
286 | int ret; | 142 | u8 fn; |
287 | 143 | ||
288 | sch = to_subchannel(cdev->dev.parent); | 144 | /* Use next available path that is not already in correct state. */ |
289 | 145 | req->lpm = lpm_adjust(req->lpm, cdev->private->pgid_todo_mask); | |
290 | /* Setup nop channel program. */ | 146 | if (!req->lpm) |
291 | ccw = cdev->private->iccws; | 147 | goto out_nopath; |
292 | ccw->cmd_code = CCW_CMD_NOOP; | 148 | /* Channel program setup. */ |
293 | ccw->cda = 0; | 149 | if (req->lpm & sch->opm) |
294 | ccw->count = 0; | 150 | fn = SPID_FUNC_ESTABLISH; |
295 | ccw->flags = CCW_FLAG_SLI; | 151 | else |
296 | 152 | fn = SPID_FUNC_RESIGN; | |
297 | /* Reset device status. */ | 153 | if (cdev->private->flags.mpath) |
298 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | 154 | fn |= SPID_FUNC_MULTI_PATH; |
299 | 155 | spid_build_cp(cdev, fn); | |
300 | /* Try multiple times. */ | 156 | ccw_request_start(cdev); |
301 | ret = -EACCES; | 157 | return; |
302 | if (cdev->private->iretry > 0) { | 158 | |
303 | cdev->private->iretry--; | 159 | out_nopath: |
304 | /* Reset internal retry indication. */ | 160 | verify_done(cdev, sch->vpm ? 0 : -EACCES); |
305 | cdev->private->flags.intretry = 0; | ||
306 | ret = cio_start (sch, cdev->private->iccws, | ||
307 | cdev->private->imask); | ||
308 | /* We expect an interrupt in case of success or busy | ||
309 | * indication. */ | ||
310 | if ((ret == 0) || (ret == -EBUSY)) | ||
311 | return ret; | ||
312 | } | ||
313 | /* nop command failed on this path. */ | ||
314 | CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel " | ||
315 | "0.%x.%04x, lpm %02X, became 'not operational'\n", | ||
316 | cdev->private->dev_id.devno, sch->schid.ssid, | ||
317 | sch->schid.sch_no, cdev->private->imask); | ||
318 | return ret; | ||
319 | } | 161 | } |
320 | 162 | ||
163 | static void verify_start(struct ccw_device *cdev); | ||
321 | 164 | ||
322 | /* | 165 | /* |
323 | * Called from interrupt context to check if a valid answer | 166 | * Process SET PGID request result for a single path. |
324 | * to Set Path Group ID was received. | ||
325 | */ | 167 | */ |
326 | static int | 168 | static void spid_callback(struct ccw_device *cdev, void *data, int rc) |
327 | __ccw_device_check_pgid(struct ccw_device *cdev) | ||
328 | { | 169 | { |
329 | struct subchannel *sch; | 170 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
330 | struct irb *irb; | 171 | struct ccw_request *req = &cdev->private->req; |
331 | 172 | ||
332 | sch = to_subchannel(cdev->dev.parent); | 173 | switch (rc) { |
333 | irb = &cdev->private->irb; | 174 | case 0: |
334 | if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { | 175 | sch->vpm |= req->lpm & sch->opm; |
335 | /* Retry Set PGID if requested. */ | 176 | break; |
336 | if (cdev->private->flags.intretry) { | 177 | case -EACCES: |
337 | cdev->private->flags.intretry = 0; | 178 | break; |
338 | return -EAGAIN; | 179 | case -EOPNOTSUPP: |
180 | if (cdev->private->flags.mpath) { | ||
181 | /* Try without multipathing. */ | ||
182 | cdev->private->flags.mpath = 0; | ||
183 | goto out_restart; | ||
339 | } | 184 | } |
340 | return -ETIME; | 185 | /* Try without pathgrouping. */ |
341 | } | 186 | cdev->private->flags.pgroup = 0; |
342 | if (irb->esw.esw0.erw.cons) { | 187 | goto out_restart; |
343 | if (irb->ecw[0] & SNS0_CMD_REJECT) | 188 | default: |
344 | return -EOPNOTSUPP; | 189 | goto err; |
345 | /* Hmm, whatever happened, try again. */ | ||
346 | CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, " | ||
347 | "cnt %02d, " | ||
348 | "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n", | ||
349 | cdev->private->dev_id.ssid, | ||
350 | cdev->private->dev_id.devno, | ||
351 | irb->esw.esw0.erw.scnt, | ||
352 | irb->ecw[0], irb->ecw[1], | ||
353 | irb->ecw[2], irb->ecw[3], | ||
354 | irb->ecw[4], irb->ecw[5], | ||
355 | irb->ecw[6], irb->ecw[7]); | ||
356 | return -EAGAIN; | ||
357 | } | 190 | } |
358 | if (irb->scsw.cmd.cc == 3) { | 191 | req->lpm >>= 1; |
359 | CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x," | 192 | spid_do(cdev); |
360 | " lpm %02X, became 'not operational'\n", | 193 | return; |
361 | cdev->private->dev_id.devno, sch->schid.ssid, | 194 | |
362 | sch->schid.sch_no, cdev->private->imask); | 195 | out_restart: |
363 | return -EACCES; | 196 | verify_start(cdev); |
364 | } | 197 | return; |
365 | return 0; | 198 | err: |
199 | verify_done(cdev, rc); | ||
200 | } | ||
201 | |||
202 | static void spid_start(struct ccw_device *cdev) | ||
203 | { | ||
204 | struct ccw_request *req = &cdev->private->req; | ||
205 | |||
206 | /* Initialize request data. */ | ||
207 | memset(req, 0, sizeof(*req)); | ||
208 | req->timeout = PGID_TIMEOUT; | ||
209 | req->maxretries = PGID_RETRIES; | ||
210 | req->lpm = 0x80; | ||
211 | req->callback = spid_callback; | ||
212 | spid_do(cdev); | ||
213 | } | ||
214 | |||
215 | static int pgid_cmp(struct pgid *p1, struct pgid *p2) | ||
216 | { | ||
217 | return memcmp((char *) p1 + 1, (char *) p2 + 1, | ||
218 | sizeof(struct pgid) - 1); | ||
366 | } | 219 | } |
367 | 220 | ||
368 | /* | 221 | /* |
369 | * Called from interrupt context to check the path status after a nop has | 222 | * Determine pathgroup state from PGID data. |
370 | * been send. | ||
371 | */ | 223 | */ |
372 | static int __ccw_device_check_nop(struct ccw_device *cdev) | 224 | static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, |
225 | int *mismatch, int *reserved, int *reset) | ||
373 | { | 226 | { |
374 | struct subchannel *sch; | 227 | struct pgid *pgid = &cdev->private->pgid[0]; |
375 | struct irb *irb; | 228 | struct pgid *first = NULL; |
376 | 229 | int lpm; | |
377 | sch = to_subchannel(cdev->dev.parent); | 230 | int i; |
378 | irb = &cdev->private->irb; | 231 | |
379 | if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { | 232 | *mismatch = 0; |
380 | /* Retry NOP if requested. */ | 233 | *reserved = 0; |
381 | if (cdev->private->flags.intretry) { | 234 | *reset = 0; |
382 | cdev->private->flags.intretry = 0; | 235 | for (i = 0, lpm = 0x80; i < 8; i++, pgid++, lpm >>= 1) { |
383 | return -EAGAIN; | 236 | if ((cdev->private->pgid_valid_mask & lpm) == 0) |
237 | continue; | ||
238 | if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) | ||
239 | *reserved = 1; | ||
240 | if (pgid->inf.ps.state1 == SNID_STATE1_RESET) { | ||
241 | /* A PGID was reset. */ | ||
242 | *reset = 1; | ||
243 | continue; | ||
384 | } | 244 | } |
385 | return -ETIME; | 245 | if (!first) { |
386 | } | 246 | first = pgid; |
387 | if (irb->scsw.cmd.cc == 3) { | 247 | continue; |
388 | CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x," | 248 | } |
389 | " lpm %02X, became 'not operational'\n", | 249 | if (pgid_cmp(pgid, first) != 0) |
390 | cdev->private->dev_id.devno, sch->schid.ssid, | 250 | *mismatch = 1; |
391 | sch->schid.sch_no, cdev->private->imask); | ||
392 | return -EACCES; | ||
393 | } | 251 | } |
394 | return 0; | 252 | if (!first) |
253 | first = &channel_subsystems[0]->global_pgid; | ||
254 | *p = first; | ||
395 | } | 255 | } |
396 | 256 | ||
397 | static void | 257 | static u8 pgid_to_donepm(struct ccw_device *cdev) |
398 | __ccw_device_verify_start(struct ccw_device *cdev) | ||
399 | { | 258 | { |
400 | struct subchannel *sch; | 259 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
401 | __u8 func; | 260 | struct pgid *pgid; |
402 | int ret; | 261 | int i; |
403 | 262 | int lpm; | |
404 | sch = to_subchannel(cdev->dev.parent); | 263 | u8 donepm = 0; |
405 | /* Repeat for all paths. */ | 264 | |
406 | for (; cdev->private->imask; cdev->private->imask >>= 1, | 265 | /* Set bits for paths which are already in the target state. */ |
407 | cdev->private->iretry = 5) { | 266 | for (i = 0; i < 8; i++) { |
408 | if ((cdev->private->imask & sch->schib.pmcw.pam) == 0) | 267 | lpm = 0x80 >> i; |
409 | /* Path not available, try next. */ | 268 | if ((cdev->private->pgid_valid_mask & lpm) == 0) |
410 | continue; | 269 | continue; |
411 | if (cdev->private->options.pgroup) { | 270 | pgid = &cdev->private->pgid[i]; |
412 | if (sch->opm & cdev->private->imask) | 271 | if (sch->opm & lpm) { |
413 | func = SPID_FUNC_ESTABLISH; | 272 | if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED) |
414 | else | 273 | continue; |
415 | func = SPID_FUNC_RESIGN; | 274 | } else { |
416 | ret = __ccw_device_do_pgid(cdev, func); | 275 | if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED) |
417 | } else | 276 | continue; |
418 | ret = __ccw_device_do_nop(cdev); | 277 | } |
419 | /* We expect an interrupt in case of success or busy | 278 | if (cdev->private->flags.mpath) { |
420 | * indication. */ | 279 | if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH) |
421 | if (ret == 0 || ret == -EBUSY) | 280 | continue; |
422 | return; | 281 | } else { |
423 | /* Permanent path failure, try next. */ | 282 | if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH) |
283 | continue; | ||
284 | } | ||
285 | donepm |= lpm; | ||
424 | } | 286 | } |
425 | /* Done with all paths. */ | 287 | |
426 | ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV); | 288 | return donepm; |
427 | } | 289 | } |
428 | 290 | ||
429 | /* | 291 | static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) |
430 | * Got interrupt for Set Path Group ID. | ||
431 | */ | ||
432 | void | ||
433 | ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) | ||
434 | { | 292 | { |
435 | struct subchannel *sch; | 293 | int i; |
436 | struct irb *irb; | ||
437 | int ret; | ||
438 | 294 | ||
439 | irb = (struct irb *) __LC_IRB; | 295 | for (i = 0; i < 8; i++) |
296 | memcpy(&cdev->private->pgid[i], pgid, sizeof(struct pgid)); | ||
297 | } | ||
440 | 298 | ||
441 | if (irb->scsw.cmd.stctl == | 299 | /* |
442 | (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { | 300 | * Process SENSE PGID data and report result. |
443 | if (__ccw_device_should_retry(&irb->scsw)) | 301 | */ |
444 | __ccw_device_verify_start(cdev); | 302 | static void snid_done(struct ccw_device *cdev, int rc) |
445 | return; | 303 | { |
304 | struct ccw_dev_id *id = &cdev->private->dev_id; | ||
305 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
306 | struct pgid *pgid; | ||
307 | int mismatch = 0; | ||
308 | int reserved = 0; | ||
309 | int reset = 0; | ||
310 | u8 donepm; | ||
311 | |||
312 | if (rc) | ||
313 | goto out; | ||
314 | pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset); | ||
315 | if (reserved) | ||
316 | rc = -EUSERS; | ||
317 | else if (mismatch) | ||
318 | rc = -EOPNOTSUPP; | ||
319 | else { | ||
320 | donepm = pgid_to_donepm(cdev); | ||
321 | sch->vpm = donepm & sch->opm; | ||
322 | cdev->private->pgid_todo_mask &= ~donepm; | ||
323 | pgid_fill(cdev, pgid); | ||
446 | } | 324 | } |
447 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) | 325 | out: |
448 | return; | 326 | CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " |
449 | sch = to_subchannel(cdev->dev.parent); | 327 | "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid, |
450 | if (cdev->private->options.pgroup) | 328 | id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm, |
451 | ret = __ccw_device_check_pgid(cdev); | 329 | cdev->private->pgid_todo_mask, mismatch, reserved, reset); |
452 | else | 330 | switch (rc) { |
453 | ret = __ccw_device_check_nop(cdev); | ||
454 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | ||
455 | |||
456 | switch (ret) { | ||
457 | /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ | ||
458 | case 0: | 331 | case 0: |
459 | /* Path verification ccw finished successfully, update lpm. */ | 332 | /* Anything left to do? */ |
460 | sch->vpm |= sch->opm & cdev->private->imask; | 333 | if (cdev->private->pgid_todo_mask == 0) { |
461 | /* Go on with next path. */ | 334 | verify_done(cdev, sch->vpm == 0 ? -EACCES : 0); |
462 | cdev->private->imask >>= 1; | 335 | return; |
463 | cdev->private->iretry = 5; | 336 | } |
464 | __ccw_device_verify_start(cdev); | 337 | /* Perform path-grouping. */ |
338 | spid_start(cdev); | ||
465 | break; | 339 | break; |
466 | case -EOPNOTSUPP: | 340 | case -EOPNOTSUPP: |
467 | /* | 341 | /* Path-grouping not supported. */ |
468 | * One of those strange devices which claim to be able | 342 | cdev->private->flags.pgroup = 0; |
469 | * to do multipathing but not for Set Path Group ID. | 343 | cdev->private->flags.mpath = 0; |
470 | */ | 344 | verify_start(cdev); |
471 | if (cdev->private->flags.pgid_single) | ||
472 | cdev->private->options.pgroup = 0; | ||
473 | else | ||
474 | cdev->private->flags.pgid_single = 1; | ||
475 | /* Retry */ | ||
476 | sch->vpm = 0; | ||
477 | cdev->private->imask = 0x80; | ||
478 | cdev->private->iretry = 5; | ||
479 | /* fall through. */ | ||
480 | case -EAGAIN: /* Try again. */ | ||
481 | __ccw_device_verify_start(cdev); | ||
482 | break; | ||
483 | case -ETIME: /* Set path group id stopped by timeout. */ | ||
484 | ccw_device_verify_done(cdev, -ETIME); | ||
485 | break; | ||
486 | case -EACCES: /* channel is not operational. */ | ||
487 | cdev->private->imask >>= 1; | ||
488 | cdev->private->iretry = 5; | ||
489 | __ccw_device_verify_start(cdev); | ||
490 | break; | 345 | break; |
346 | default: | ||
347 | verify_done(cdev, rc); | ||
491 | } | 348 | } |
492 | } | 349 | } |
493 | 350 | ||
494 | void | 351 | /* |
495 | ccw_device_verify_start(struct ccw_device *cdev) | 352 | * Create channel program to perform a SENSE PGID on a single path. |
353 | */ | ||
354 | static void snid_build_cp(struct ccw_device *cdev) | ||
355 | { | ||
356 | struct ccw_request *req = &cdev->private->req; | ||
357 | struct ccw1 *cp = cdev->private->iccws; | ||
358 | int i = 8 - ffs(req->lpm); | ||
359 | |||
360 | /* Channel program setup. */ | ||
361 | cp->cmd_code = CCW_CMD_SENSE_PGID; | ||
362 | cp->cda = (u32) (addr_t) &cdev->private->pgid[i]; | ||
363 | cp->count = sizeof(struct pgid); | ||
364 | cp->flags = CCW_FLAG_SLI; | ||
365 | req->cp = cp; | ||
366 | } | ||
367 | |||
368 | /* | ||
369 | * Perform SENSE PGID on a single path. | ||
370 | */ | ||
371 | static void snid_do(struct ccw_device *cdev) | ||
496 | { | 372 | { |
497 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | 373 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
374 | struct ccw_request *req = &cdev->private->req; | ||
375 | |||
376 | /* Adjust lpm if paths are not set in pam. */ | ||
377 | req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); | ||
378 | if (!req->lpm) | ||
379 | goto out_nopath; | ||
380 | snid_build_cp(cdev); | ||
381 | ccw_request_start(cdev); | ||
382 | return; | ||
383 | |||
384 | out_nopath: | ||
385 | snid_done(cdev, cdev->private->pgid_valid_mask ? 0 : -EACCES); | ||
386 | } | ||
498 | 387 | ||
499 | cdev->private->flags.pgid_single = 0; | 388 | /* |
500 | cdev->private->imask = 0x80; | 389 | * Process SENSE PGID request result for single path. |
501 | cdev->private->iretry = 5; | 390 | */ |
391 | static void snid_callback(struct ccw_device *cdev, void *data, int rc) | ||
392 | { | ||
393 | struct ccw_request *req = &cdev->private->req; | ||
394 | |||
395 | if (rc == 0) | ||
396 | cdev->private->pgid_valid_mask |= req->lpm; | ||
397 | else if (rc != -EACCES) | ||
398 | goto err; | ||
399 | req->lpm >>= 1; | ||
400 | snid_do(cdev); | ||
401 | return; | ||
402 | |||
403 | err: | ||
404 | snid_done(cdev, rc); | ||
405 | } | ||
502 | 406 | ||
503 | /* Start with empty vpm. */ | 407 | /* |
504 | sch->vpm = 0; | 408 | * Perform path verification. |
409 | */ | ||
410 | static void verify_start(struct ccw_device *cdev) | ||
411 | { | ||
412 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
413 | struct ccw_request *req = &cdev->private->req; | ||
414 | struct ccw_dev_id *devid = &cdev->private->dev_id; | ||
505 | 415 | ||
506 | /* Get current pam. */ | 416 | sch->vpm = 0; |
507 | if (cio_update_schib(sch)) { | 417 | sch->lpm = sch->schib.pmcw.pam; |
508 | ccw_device_verify_done(cdev, -ENODEV); | 418 | /* Initialize request data. */ |
509 | return; | 419 | memset(req, 0, sizeof(*req)); |
420 | req->timeout = PGID_TIMEOUT; | ||
421 | req->maxretries = PGID_RETRIES; | ||
422 | req->lpm = 0x80; | ||
423 | if (cdev->private->flags.pgroup) { | ||
424 | CIO_TRACE_EVENT(4, "snid"); | ||
425 | CIO_HEX_EVENT(4, devid, sizeof(*devid)); | ||
426 | req->callback = snid_callback; | ||
427 | snid_do(cdev); | ||
428 | } else { | ||
429 | CIO_TRACE_EVENT(4, "nop"); | ||
430 | CIO_HEX_EVENT(4, devid, sizeof(*devid)); | ||
431 | req->filter = nop_filter; | ||
432 | req->callback = nop_callback; | ||
433 | nop_do(cdev); | ||
510 | } | 434 | } |
511 | /* After 60s path verification is considered to have failed. */ | ||
512 | ccw_device_set_timeout(cdev, 60*HZ); | ||
513 | __ccw_device_verify_start(cdev); | ||
514 | } | 435 | } |
515 | 436 | ||
516 | static void | 437 | /** |
517 | __ccw_device_disband_start(struct ccw_device *cdev) | 438 | * ccw_device_verify_start - perform path verification |
439 | * @cdev: ccw device | ||
440 | * | ||
441 | * Perform an I/O on each available channel path to @cdev to determine which | ||
442 | * paths are operational. The resulting path mask is stored in sch->vpm. | ||
443 | * If device options specify pathgrouping, establish a pathgroup for the | ||
444 | * operational paths. When finished, call ccw_device_verify_done with a | ||
445 | * return code specifying the result. | ||
446 | */ | ||
447 | void ccw_device_verify_start(struct ccw_device *cdev) | ||
518 | { | 448 | { |
519 | struct subchannel *sch; | 449 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
520 | int ret; | 450 | |
521 | 451 | CIO_TRACE_EVENT(4, "vrfy"); | |
522 | sch = to_subchannel(cdev->dev.parent); | 452 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); |
523 | while (cdev->private->imask != 0) { | 453 | /* Initialize PGID data. */ |
524 | if (sch->lpm & cdev->private->imask) { | 454 | memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); |
525 | ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND); | 455 | cdev->private->pgid_valid_mask = 0; |
526 | if (ret == 0) | 456 | cdev->private->pgid_todo_mask = sch->schib.pmcw.pam; |
527 | return; | 457 | /* |
528 | } | 458 | * Initialize pathgroup and multipath state with target values. |
529 | cdev->private->iretry = 5; | 459 | * They may change in the course of path verification. |
530 | cdev->private->imask >>= 1; | 460 | */ |
531 | } | 461 | cdev->private->flags.pgroup = cdev->private->options.pgroup; |
532 | ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV); | 462 | cdev->private->flags.mpath = cdev->private->options.mpath; |
463 | cdev->private->flags.doverify = 0; | ||
464 | verify_start(cdev); | ||
533 | } | 465 | } |
534 | 466 | ||
535 | /* | 467 | /* |
536 | * Got interrupt for Unset Path Group ID. | 468 | * Process disband SET PGID request result. |
537 | */ | 469 | */ |
538 | void | 470 | static void disband_callback(struct ccw_device *cdev, void *data, int rc) |
539 | ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event) | ||
540 | { | 471 | { |
541 | struct subchannel *sch; | 472 | struct subchannel *sch = to_subchannel(cdev->dev.parent); |
542 | struct irb *irb; | 473 | struct ccw_dev_id *id = &cdev->private->dev_id; |
543 | int ret; | 474 | |
475 | if (rc) | ||
476 | goto out; | ||
477 | /* Ensure consistent multipathing state at device and channel. */ | ||
478 | cdev->private->flags.mpath = 0; | ||
479 | if (sch->config.mp) { | ||
480 | sch->config.mp = 0; | ||
481 | rc = cio_commit_config(sch); | ||
482 | } | ||
483 | out: | ||
484 | CIO_MSG_EVENT(0, "disb: device 0.%x.%04x: rc=%d\n", id->ssid, id->devno, | ||
485 | rc); | ||
486 | ccw_device_disband_done(cdev, rc); | ||
487 | } | ||
544 | 488 | ||
545 | irb = (struct irb *) __LC_IRB; | 489 | /** |
490 | * ccw_device_disband_start - disband pathgroup | ||
491 | * @cdev: ccw device | ||
492 | * | ||
493 | * Execute a SET PGID channel program on @cdev to disband a previously | ||
494 | * established pathgroup. When finished, call ccw_device_disband_done with | ||
495 | * a return code specifying the result. | ||
496 | */ | ||
497 | void ccw_device_disband_start(struct ccw_device *cdev) | ||
498 | { | ||
499 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
500 | struct ccw_request *req = &cdev->private->req; | ||
501 | u8 fn; | ||
502 | |||
503 | CIO_TRACE_EVENT(4, "disb"); | ||
504 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); | ||
505 | /* Request setup. */ | ||
506 | memset(req, 0, sizeof(*req)); | ||
507 | req->timeout = PGID_TIMEOUT; | ||
508 | req->maxretries = PGID_RETRIES; | ||
509 | req->lpm = sch->schib.pmcw.pam & sch->opm; | ||
510 | req->callback = disband_callback; | ||
511 | fn = SPID_FUNC_DISBAND; | ||
512 | if (cdev->private->flags.mpath) | ||
513 | fn |= SPID_FUNC_MULTI_PATH; | ||
514 | spid_build_cp(cdev, fn); | ||
515 | ccw_request_start(cdev); | ||
516 | } | ||
546 | 517 | ||
547 | if (irb->scsw.cmd.stctl == | 518 | static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2) |
548 | (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { | 519 | { |
549 | if (__ccw_device_should_retry(&irb->scsw)) | 520 | struct ccw_request *req = &cdev->private->req; |
550 | __ccw_device_disband_start(cdev); | 521 | struct ccw1 *cp = cdev->private->iccws; |
551 | return; | 522 | |
552 | } | 523 | cp[0].cmd_code = CCW_CMD_STLCK; |
553 | if (ccw_device_accumulate_and_sense(cdev, irb) != 0) | 524 | cp[0].cda = (u32) (addr_t) buf1; |
554 | return; | 525 | cp[0].count = 32; |
555 | sch = to_subchannel(cdev->dev.parent); | 526 | cp[0].flags = CCW_FLAG_CC; |
556 | ret = __ccw_device_check_pgid(cdev); | 527 | cp[1].cmd_code = CCW_CMD_RELEASE; |
557 | memset(&cdev->private->irb, 0, sizeof(struct irb)); | 528 | cp[1].cda = (u32) (addr_t) buf2; |
558 | switch (ret) { | 529 | cp[1].count = 32; |
559 | /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ | 530 | cp[1].flags = 0; |
560 | case 0: /* disband successful. */ | 531 | req->cp = cp; |
561 | ccw_device_disband_done(cdev, ret); | ||
562 | break; | ||
563 | case -EOPNOTSUPP: | ||
564 | /* | ||
565 | * One of those strange devices which claim to be able | ||
566 | * to do multipathing but not for Unset Path Group ID. | ||
567 | */ | ||
568 | cdev->private->flags.pgid_single = 1; | ||
569 | /* fall through. */ | ||
570 | case -EAGAIN: /* Try again. */ | ||
571 | __ccw_device_disband_start(cdev); | ||
572 | break; | ||
573 | case -ETIME: /* Set path group id stopped by timeout. */ | ||
574 | ccw_device_disband_done(cdev, -ETIME); | ||
575 | break; | ||
576 | case -EACCES: /* channel is not operational. */ | ||
577 | cdev->private->imask >>= 1; | ||
578 | cdev->private->iretry = 5; | ||
579 | __ccw_device_disband_start(cdev); | ||
580 | break; | ||
581 | } | ||
582 | } | 532 | } |
583 | 533 | ||
584 | void | 534 | static void stlck_callback(struct ccw_device *cdev, void *data, int rc) |
585 | ccw_device_disband_start(struct ccw_device *cdev) | ||
586 | { | 535 | { |
587 | /* After 60s disbanding is considered to have failed. */ | 536 | ccw_device_stlck_done(cdev, data, rc); |
588 | ccw_device_set_timeout(cdev, 60*HZ); | 537 | } |
589 | 538 | ||
590 | cdev->private->flags.pgid_single = 0; | 539 | /** |
591 | cdev->private->iretry = 5; | 540 | * ccw_device_stlck_start - perform unconditional release |
592 | cdev->private->imask = 0x80; | 541 | * @cdev: ccw device |
593 | __ccw_device_disband_start(cdev); | 542 | * @data: data pointer to be passed to ccw_device_stlck_done |
543 | * @buf1: data pointer used in channel program | ||
544 | * @buf2: data pointer used in channel program | ||
545 | * | ||
546 | * Execute a channel program on @cdev to release an existing PGID reservation. | ||
547 | * When finished, call ccw_device_stlck_done with a return code specifying the | ||
548 | * result. | ||
549 | */ | ||
550 | void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1, | ||
551 | void *buf2) | ||
552 | { | ||
553 | struct subchannel *sch = to_subchannel(cdev->dev.parent); | ||
554 | struct ccw_request *req = &cdev->private->req; | ||
555 | |||
556 | CIO_TRACE_EVENT(4, "stlck"); | ||
557 | CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); | ||
558 | /* Request setup. */ | ||
559 | memset(req, 0, sizeof(*req)); | ||
560 | req->timeout = PGID_TIMEOUT; | ||
561 | req->maxretries = PGID_RETRIES; | ||
562 | req->lpm = sch->schib.pmcw.pam & sch->opm; | ||
563 | req->data = data; | ||
564 | req->callback = stlck_callback; | ||
565 | stlck_build_cp(cdev, buf1, buf2); | ||
566 | ccw_request_start(cdev); | ||
594 | } | 567 | } |
568 | |||