device_pgid.c 15.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
/*
 * drivers/s390/cio/device_pgid.c
 *
 *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
 *			 IBM Corporation
6
 *    Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *		 Martin Schwidefsky (schwidefsky@de.ibm.com)
 *
 * Path Group ID functions.
 */

#include <linux/module.h>
#include <linux/init.h>

#include <asm/ccwdev.h>
#include <asm/cio.h>
#include <asm/delay.h>
#include <asm/lowcore.h>

#include "cio.h"
#include "cio_debug.h"
#include "css.h"
#include "device.h"
24
#include "ioasm.h"
25
#include "io_sch.h"
L
Linus Torvalds 已提交
26

27 28 29 30
/*
 * Helper function called from interrupt context to decide whether an
 * operation should be tried again.
 */
31
static int __ccw_device_should_retry(union scsw *scsw)
32 33
{
	/* CC is only valid if start function bit is set. */
34
	if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1)
35 36
		return 1;
	/* No more activity. For sense and set PGID we stubbornly try again. */
37
	if (!scsw->cmd.actl)
38 39 40 41
		return 1;
	return 0;
}

L
Linus Torvalds 已提交
42 43 44 45 46 47 48 49 50 51
/*
 * Start Sense Path Group ID helper function. Used in ccw_device_recog
 * and ccw_device_sense_pgid.
 */
static int
__ccw_device_sense_pgid_start(struct ccw_device *cdev)
{
	struct subchannel *sch;
	struct ccw1 *ccw;
	int ret;
52
	int i;
L
Linus Torvalds 已提交
53 54

	sch = to_subchannel(cdev->dev.parent);
55 56 57 58 59
	/* Return if we already checked on all paths. */
	if (cdev->private->imask == 0)
		return (sch->lpm == 0) ? -ENODEV : -EACCES;
	i = 8 - ffs(cdev->private->imask);

L
Linus Torvalds 已提交
60 61 62 63 64 65 66 67 68 69 70 71
	/* Setup sense path group id channel program. */
	ccw = cdev->private->iccws;
	ccw->cmd_code = CCW_CMD_SENSE_PGID;
	ccw->count = sizeof (struct pgid);
	ccw->flags = CCW_FLAG_SLI;

	/* Reset device status. */
	memset(&cdev->private->irb, 0, sizeof(struct irb));
	/* Try on every path. */
	ret = -ENODEV;
	while (cdev->private->imask != 0) {
		/* Try every path multiple times. */
72
		ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
L
Linus Torvalds 已提交
73 74
		if (cdev->private->iretry > 0) {
			cdev->private->iretry--;
75 76
			/* Reset internal retry indication. */
			cdev->private->flags.intretry = 0;
L
Linus Torvalds 已提交
77 78 79 80 81
			ret = cio_start (sch, cdev->private->iccws, 
					 cdev->private->imask);
			/* ret is 0, -EBUSY, -EACCES or -ENODEV */
			if (ret != -EACCES)
				return ret;
82
			CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel "
83
				      "0.%x.%04x, lpm %02X, became 'not "
L
Linus Torvalds 已提交
84
				      "operational'\n",
85 86
				      cdev->private->dev_id.devno,
				      sch->schid.ssid,
87
				      sch->schid.sch_no, cdev->private->imask);
L
Linus Torvalds 已提交
88 89 90 91

		}
		cdev->private->imask >>= 1;
		cdev->private->iretry = 5;
92
		i++;
L
Linus Torvalds 已提交
93
	}
94

L
Linus Torvalds 已提交
95 96 97 98 99 100 101 102
	return ret;
}

void
ccw_device_sense_pgid_start(struct ccw_device *cdev)
{
	int ret;

103 104 105
	/* Set a timeout of 60s */
	ccw_device_set_timeout(cdev, 60*HZ);

L
Linus Torvalds 已提交
106 107 108
	cdev->private->state = DEV_STATE_SENSE_PGID;
	cdev->private->imask = 0x80;
	cdev->private->iretry = 5;
109
	memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
L
Linus Torvalds 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122 123
	ret = __ccw_device_sense_pgid_start(cdev);
	if (ret && ret != -EBUSY)
		ccw_device_sense_pgid_done(cdev, ret);
}

/*
 * Called from interrupt context to check if a valid answer
 * to Sense Path Group ID was received.
 */
static int
__ccw_device_check_sense_pgid(struct ccw_device *cdev)
{
	struct subchannel *sch;
	struct irb *irb;
124
	int i;
L
Linus Torvalds 已提交
125 126 127

	sch = to_subchannel(cdev->dev.parent);
	irb = &cdev->private->irb;
128
	if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
129 130 131 132 133
		/* Retry Sense PGID if requested. */
		if (cdev->private->flags.intretry) {
			cdev->private->flags.intretry = 0;
			return -EAGAIN;
		}
L
Linus Torvalds 已提交
134
		return -ETIME;
135
	}
L
Linus Torvalds 已提交
136 137 138 139 140 141 142 143 144
	if (irb->esw.esw0.erw.cons &&
	    (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) {
		/*
		 * If the device doesn't support the Sense Path Group ID
		 *  command further retries wouldn't help ...
		 */
		return -EOPNOTSUPP;
	}
	if (irb->esw.esw0.erw.cons) {
145
		CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, "
L
Linus Torvalds 已提交
146 147
			      "lpum %02X, cnt %02d, sns : "
			      "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
148 149
			      cdev->private->dev_id.ssid,
			      cdev->private->dev_id.devno,
L
Linus Torvalds 已提交
150 151 152 153 154 155 156 157
			      irb->esw.esw0.sublog.lpum,
			      irb->esw.esw0.erw.scnt,
			      irb->ecw[0], irb->ecw[1],
			      irb->ecw[2], irb->ecw[3],
			      irb->ecw[4], irb->ecw[5],
			      irb->ecw[6], irb->ecw[7]);
		return -EAGAIN;
	}
158
	if (irb->scsw.cmd.cc == 3) {
159 160
		u8 lpm;

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);
L
Linus Torvalds 已提交
166 167
		return -EACCES;
	}
168 169
	i = 8 - ffs(cdev->private->imask);
	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 "
L
Linus Torvalds 已提交
171
			      "is reserved by someone else\n",
172
			      cdev->private->dev_id.devno, sch->schid.ssid,
173
			      sch->schid.sch_no);
L
Linus Torvalds 已提交
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
		return -EUSERS;
	}
	return 0;
}

/*
 * Got interrupt for Sense Path Group ID.
 */
void
ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
{
	struct subchannel *sch;
	struct irb *irb;
	int ret;

	irb = (struct irb *) __LC_IRB;
190

191
	if (irb->scsw.cmd.stctl ==
L
Linus Torvalds 已提交
192
	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
193
		if (__ccw_device_should_retry(&irb->scsw)) {
L
Linus Torvalds 已提交
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
			ret = __ccw_device_sense_pgid_start(cdev);
			if (ret && ret != -EBUSY)
				ccw_device_sense_pgid_done(cdev, ret);
		}
		return;
	}
	if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
		return;
	sch = to_subchannel(cdev->dev.parent);
	ret = __ccw_device_check_sense_pgid(cdev);
	memset(&cdev->private->irb, 0, sizeof(struct irb));
	switch (ret) {
	/* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
	case -EOPNOTSUPP:	/* Sense Path Group ID not supported */
		ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
		break;
	case -ETIME:		/* Sense path group id stopped by timeout. */
		ccw_device_sense_pgid_done(cdev, -ETIME);
		break;
	case -EACCES:		/* channel is not operational. */
		sch->lpm &= ~cdev->private->imask;
215 216
		/* Fall through. */
	case 0:			/* Sense Path Group ID successful. */
L
Linus Torvalds 已提交
217 218 219 220 221 222
		cdev->private->imask >>= 1;
		cdev->private->iretry = 5;
		/* Fall through. */
	case -EAGAIN:		/* Try again. */
		ret = __ccw_device_sense_pgid_start(cdev);
		if (ret != 0 && ret != -EBUSY)
223
			ccw_device_sense_pgid_done(cdev, ret);
L
Linus Torvalds 已提交
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
		break;
	case -EUSERS:		/* device is reserved for someone else. */
		ccw_device_sense_pgid_done(cdev, -EUSERS);
		break;
	}
}

/*
 * Path Group ID helper function.
 */
static int
__ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
{
	struct subchannel *sch;
	struct ccw1 *ccw;
	int ret;

	sch = to_subchannel(cdev->dev.parent);

	/* Setup sense path group id channel program. */
244
	cdev->private->pgid[0].inf.fc = func;
L
Linus Torvalds 已提交
245
	ccw = cdev->private->iccws;
246
	if (cdev->private->flags.pgid_single)
247
		cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
248 249
	else
		cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
L
Linus Torvalds 已提交
250
	ccw->cmd_code = CCW_CMD_SET_PGID;
251
	ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
L
Linus Torvalds 已提交
252 253 254 255 256 257 258
	ccw->count = sizeof (struct pgid);
	ccw->flags = CCW_FLAG_SLI;

	/* Reset device status. */
	memset(&cdev->private->irb, 0, sizeof(struct irb));

	/* Try multiple times. */
259
	ret = -EACCES;
L
Linus Torvalds 已提交
260 261
	if (cdev->private->iretry > 0) {
		cdev->private->iretry--;
262 263
		/* Reset internal retry indication. */
		cdev->private->flags.intretry = 0;
L
Linus Torvalds 已提交
264 265
		ret = cio_start (sch, cdev->private->iccws,
				 cdev->private->imask);
266 267 268
		/* We expect an interrupt in case of success or busy
		 * indication. */
		if ((ret == 0) || (ret == -EBUSY))
L
Linus Torvalds 已提交
269 270
			return ret;
	}
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);
L
Linus Torvalds 已提交
276 277 278
	return ret;
}

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
/*
 * Helper function to send a nop ccw down a path.
 */
static int __ccw_device_do_nop(struct ccw_device *cdev)
{
	struct subchannel *sch;
	struct ccw1 *ccw;
	int ret;

	sch = to_subchannel(cdev->dev.parent);

	/* Setup nop channel program. */
	ccw = cdev->private->iccws;
	ccw->cmd_code = CCW_CMD_NOOP;
	ccw->cda = 0;
	ccw->count = 0;
	ccw->flags = CCW_FLAG_SLI;

	/* Reset device status. */
	memset(&cdev->private->irb, 0, sizeof(struct irb));

	/* Try multiple times. */
301
	ret = -EACCES;
302 303
	if (cdev->private->iretry > 0) {
		cdev->private->iretry--;
304 305
		/* Reset internal retry indication. */
		cdev->private->flags.intretry = 0;
306 307
		ret = cio_start (sch, cdev->private->iccws,
				 cdev->private->imask);
308 309 310
		/* We expect an interrupt in case of success or busy
		 * indication. */
		if ((ret == 0) || (ret == -EBUSY))
311 312
			return ret;
	}
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 318 319 320 321
		      sch->schid.sch_no, cdev->private->imask);
	return ret;
}


L
Linus Torvalds 已提交
322 323 324 325 326 327 328 329 330 331 332 333
/*
 * Called from interrupt context to check if a valid answer
 * to Set Path Group ID was received.
 */
static int
__ccw_device_check_pgid(struct ccw_device *cdev)
{
	struct subchannel *sch;
	struct irb *irb;

	sch = to_subchannel(cdev->dev.parent);
	irb = &cdev->private->irb;
334
	if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
335 336 337 338 339
		/* Retry Set PGID if requested. */
		if (cdev->private->flags.intretry) {
			cdev->private->flags.intretry = 0;
			return -EAGAIN;
		}
L
Linus Torvalds 已提交
340
		return -ETIME;
341
	}
L
Linus Torvalds 已提交
342 343 344 345
	if (irb->esw.esw0.erw.cons) {
		if (irb->ecw[0] & SNS0_CMD_REJECT)
			return -EOPNOTSUPP;
		/* Hmm, whatever happened, try again. */
346 347
		CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, "
			      "cnt %02d, "
L
Linus Torvalds 已提交
348
			      "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
349 350 351
			      cdev->private->dev_id.ssid,
			      cdev->private->dev_id.devno,
			      irb->esw.esw0.erw.scnt,
L
Linus Torvalds 已提交
352 353 354 355 356 357
			      irb->ecw[0], irb->ecw[1],
			      irb->ecw[2], irb->ecw[3],
			      irb->ecw[4], irb->ecw[5],
			      irb->ecw[6], irb->ecw[7]);
		return -EAGAIN;
	}
358
	if (irb->scsw.cmd.cc == 3) {
359
		CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x,"
360
			      " lpm %02X, became 'not operational'\n",
361
			      cdev->private->dev_id.devno, sch->schid.ssid,
362
			      sch->schid.sch_no, cdev->private->imask);
L
Linus Torvalds 已提交
363 364 365 366 367
		return -EACCES;
	}
	return 0;
}

368 369 370 371 372 373 374 375 376 377 378
/*
 * Called from interrupt context to check the path status after a nop has
 * been send.
 */
static int __ccw_device_check_nop(struct ccw_device *cdev)
{
	struct subchannel *sch;
	struct irb *irb;

	sch = to_subchannel(cdev->dev.parent);
	irb = &cdev->private->irb;
379
	if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
380 381 382 383 384
		/* Retry NOP if requested. */
		if (cdev->private->flags.intretry) {
			cdev->private->flags.intretry = 0;
			return -EAGAIN;
		}
385
		return -ETIME;
386
	}
387
	if (irb->scsw.cmd.cc == 3) {
388
		CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x,"
389
			      " lpm %02X, became 'not operational'\n",
390
			      cdev->private->dev_id.devno, sch->schid.ssid,
391 392 393 394 395 396
			      sch->schid.sch_no, cdev->private->imask);
		return -EACCES;
	}
	return 0;
}

L
Linus Torvalds 已提交
397 398 399 400
static void
__ccw_device_verify_start(struct ccw_device *cdev)
{
	struct subchannel *sch;
401
	__u8 func;
L
Linus Torvalds 已提交
402 403 404
	int ret;

	sch = to_subchannel(cdev->dev.parent);
405 406 407 408 409 410
	/* Repeat for all paths. */
	for (; cdev->private->imask; cdev->private->imask >>= 1,
				     cdev->private->iretry = 5) {
		if ((cdev->private->imask & sch->schib.pmcw.pam) == 0)
			/* Path not available, try next. */
			continue;
411
		if (cdev->private->options.pgroup) {
412 413 414 415
			if (sch->opm & cdev->private->imask)
				func = SPID_FUNC_ESTABLISH;
			else
				func = SPID_FUNC_RESIGN;
416 417 418
			ret = __ccw_device_do_pgid(cdev, func);
		} else
			ret = __ccw_device_do_nop(cdev);
419 420
		/* We expect an interrupt in case of success or busy
		 * indication. */
L
Linus Torvalds 已提交
421 422
		if (ret == 0 || ret == -EBUSY)
			return;
423
		/* Permanent path failure, try next. */
L
Linus Torvalds 已提交
424
	}
425 426
	/* Done with all paths. */
	ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV);
L
Linus Torvalds 已提交
427 428 429 430 431 432 433 434 435 436 437 438 439
}
		
/*
 * Got interrupt for Set Path Group ID.
 */
void
ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
{
	struct subchannel *sch;
	struct irb *irb;
	int ret;

	irb = (struct irb *) __LC_IRB;
440

441
	if (irb->scsw.cmd.stctl ==
L
Linus Torvalds 已提交
442
	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
443
		if (__ccw_device_should_retry(&irb->scsw))
L
Linus Torvalds 已提交
444 445 446 447 448 449
			__ccw_device_verify_start(cdev);
		return;
	}
	if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
		return;
	sch = to_subchannel(cdev->dev.parent);
450 451 452 453
	if (cdev->private->options.pgroup)
		ret = __ccw_device_check_pgid(cdev);
	else
		ret = __ccw_device_check_nop(cdev);
L
Linus Torvalds 已提交
454
	memset(&cdev->private->irb, 0, sizeof(struct irb));
455

L
Linus Torvalds 已提交
456 457 458
	switch (ret) {
	/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
	case 0:
459 460 461 462
		/* Path verification ccw finished successfully, update lpm. */
		sch->vpm |= sch->opm & cdev->private->imask;
		/* Go on with next path. */
		cdev->private->imask >>= 1;
L
Linus Torvalds 已提交
463 464 465 466 467 468 469 470
		cdev->private->iretry = 5;
		__ccw_device_verify_start(cdev);
		break;
	case -EOPNOTSUPP:
		/*
		 * One of those strange devices which claim to be able
		 * to do multipathing but not for Set Path Group ID.
		 */
471 472 473 474
		if (cdev->private->flags.pgid_single)
			cdev->private->options.pgroup = 0;
		else
			cdev->private->flags.pgid_single = 1;
475 476 477 478
		/* Retry */
		sch->vpm = 0;
		cdev->private->imask = 0x80;
		cdev->private->iretry = 5;
L
Linus Torvalds 已提交
479 480 481 482 483 484 485 486
		/* fall through. */
	case -EAGAIN:		/* Try again. */
		__ccw_device_verify_start(cdev);
		break;
	case -ETIME:		/* Set path group id stopped by timeout. */
		ccw_device_verify_done(cdev, -ETIME);
		break;
	case -EACCES:		/* channel is not operational. */
487
		cdev->private->imask >>= 1;
L
Linus Torvalds 已提交
488 489 490 491 492 493 494 495 496
		cdev->private->iretry = 5;
		__ccw_device_verify_start(cdev);
		break;
	}
}

void
ccw_device_verify_start(struct ccw_device *cdev)
{
497 498
	struct subchannel *sch = to_subchannel(cdev->dev.parent);

L
Linus Torvalds 已提交
499
	cdev->private->flags.pgid_single = 0;
500
	cdev->private->imask = 0x80;
L
Linus Torvalds 已提交
501
	cdev->private->iretry = 5;
502 503 504 505 506

	/* Start with empty vpm. */
	sch->vpm = 0;

	/* Get current pam. */
507
	if (stsch(sch->schid, &sch->schib)) {
508 509 510
		ccw_device_verify_done(cdev, -ENODEV);
		return;
	}
511 512
	/* After 60s path verification is considered to have failed. */
	ccw_device_set_timeout(cdev, 60*HZ);
L
Linus Torvalds 已提交
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
	__ccw_device_verify_start(cdev);
}

static void
__ccw_device_disband_start(struct ccw_device *cdev)
{
	struct subchannel *sch;
	int ret;

	sch = to_subchannel(cdev->dev.parent);
	while (cdev->private->imask != 0) {
		if (sch->lpm & cdev->private->imask) {
			ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND);
			if (ret == 0)
				return;
		}
		cdev->private->iretry = 5;
		cdev->private->imask >>= 1;
	}
532
	ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
L
Linus Torvalds 已提交
533 534 535 536 537 538 539 540 541 542 543 544 545
}

/*
 * Got interrupt for Unset Path Group ID.
 */
void
ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
{
	struct subchannel *sch;
	struct irb *irb;
	int ret;

	irb = (struct irb *) __LC_IRB;
546

547
	if (irb->scsw.cmd.stctl ==
L
Linus Torvalds 已提交
548
	    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
549
		if (__ccw_device_should_retry(&irb->scsw))
L
Linus Torvalds 已提交
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
			__ccw_device_disband_start(cdev);
		return;
	}
	if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
		return;
	sch = to_subchannel(cdev->dev.parent);
	ret = __ccw_device_check_pgid(cdev);
	memset(&cdev->private->irb, 0, sizeof(struct irb));
	switch (ret) {
	/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
	case 0:			/* disband successful. */
		ccw_device_disband_done(cdev, ret);
		break;
	case -EOPNOTSUPP:
		/*
		 * One of those strange devices which claim to be able
		 * to do multipathing but not for Unset Path Group ID.
		 */
		cdev->private->flags.pgid_single = 1;
		/* fall through. */
	case -EAGAIN:		/* Try again. */
		__ccw_device_disband_start(cdev);
		break;
	case -ETIME:		/* Set path group id stopped by timeout. */
		ccw_device_disband_done(cdev, -ETIME);
		break;
	case -EACCES:		/* channel is not operational. */
		cdev->private->imask >>= 1;
		cdev->private->iretry = 5;
		__ccw_device_disband_start(cdev);
		break;
	}
}

void
ccw_device_disband_start(struct ccw_device *cdev)
{
587 588 589
	/* After 60s disbanding is considered to have failed. */
	ccw_device_set_timeout(cdev, 60*HZ);

L
Linus Torvalds 已提交
590 591 592 593 594
	cdev->private->flags.pgid_single = 0;
	cdev->private->iretry = 5;
	cdev->private->imask = 0x80;
	__ccw_device_disband_start(cdev);
}