Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 585b954e authored by Sebastian Ott's avatar Sebastian Ott Committed by Martin Schwidefsky
Browse files

[S390] cio: notify drivers of channel path events



This patch adds a notification mechanism to inform ccw drivers
about changes to channel paths, which occured while the device
is online.

Signed-off-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent eb4f5d93
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -91,6 +91,16 @@ struct ccw_device {
	void (*handler) (struct ccw_device *, unsigned long, struct irb *);
};

/*
 * Possible events used by the path_event notifier.
 */
#define PE_NONE				0x0
#define PE_PATH_GONE			0x1 /* A path is no longer available. */
#define PE_PATH_AVAILABLE		0x2 /* A path has become available and
					       was successfully verified. */
#define PE_PATHGROUP_ESTABLISHED	0x4 /* A pathgroup was reset and had
					       to be established again. */

/*
 * Possible CIO actions triggered by the unit check handler.
 */
@@ -109,6 +119,7 @@ enum uc_todo {
 * @set_online: called when setting device online
 * @set_offline: called when setting device offline
 * @notify: notify driver of device state changes
 * @path_event: notify driver of channel path events
 * @shutdown: called at device shutdown
 * @prepare: prepare for pm state transition
 * @complete: undo work done in @prepare
@@ -127,6 +138,7 @@ struct ccw_driver {
	int (*set_online) (struct ccw_device *);
	int (*set_offline) (struct ccw_device *);
	int (*notify) (struct ccw_device *, int);
	void (*path_event) (struct ccw_device *, int *);
	void (*shutdown) (struct ccw_device *);
	int (*prepare) (struct ccw_device *);
	void (*complete) (struct ccw_device *);
+9 −0
Original line number Diff line number Diff line
@@ -1147,6 +1147,7 @@ err:
static int io_subchannel_chp_event(struct subchannel *sch,
				   struct chp_link *link, int event)
{
	struct ccw_device *cdev = sch_get_cdev(sch);
	int mask;

	mask = chp_ssd_get_mask(&sch->ssd_info, link);
@@ -1156,22 +1157,30 @@ static int io_subchannel_chp_event(struct subchannel *sch,
	case CHP_VARY_OFF:
		sch->opm &= ~mask;
		sch->lpm &= ~mask;
		if (cdev)
			cdev->private->path_gone_mask |= mask;
		io_subchannel_terminate_path(sch, mask);
		break;
	case CHP_VARY_ON:
		sch->opm |= mask;
		sch->lpm |= mask;
		if (cdev)
			cdev->private->path_new_mask |= mask;
		io_subchannel_verify(sch);
		break;
	case CHP_OFFLINE:
		if (cio_update_schib(sch))
			return -ENODEV;
		if (cdev)
			cdev->private->path_gone_mask |= mask;
		io_subchannel_terminate_path(sch, mask);
		break;
	case CHP_ONLINE:
		if (cio_update_schib(sch))
			return -ENODEV;
		sch->lpm |= mask & sch->opm;
		if (cdev)
			cdev->private->path_new_mask |= mask;
		io_subchannel_verify(sch);
		break;
	}
+32 −0
Original line number Diff line number Diff line
@@ -349,9 +349,13 @@ out:

static void ccw_device_oper_notify(struct ccw_device *cdev)
{
	struct subchannel *sch = to_subchannel(cdev->dev.parent);

	if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) {
		/* Reenable channel measurements, if needed. */
		ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
		/* Save indication for new paths. */
		cdev->private->path_new_mask = sch->vpm;
		return;
	}
	/* Driver doesn't want device back. */
@@ -462,6 +466,32 @@ static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e)
	}
}

static void ccw_device_report_path_events(struct ccw_device *cdev)
{
	struct subchannel *sch = to_subchannel(cdev->dev.parent);
	int path_event[8];
	int chp, mask;

	for (chp = 0, mask = 0x80; chp < 8; chp++, mask >>= 1) {
		path_event[chp] = PE_NONE;
		if (mask & cdev->private->path_gone_mask & ~(sch->vpm))
			path_event[chp] |= PE_PATH_GONE;
		if (mask & cdev->private->path_new_mask & sch->vpm)
			path_event[chp] |= PE_PATH_AVAILABLE;
		if (mask & cdev->private->pgid_reset_mask & sch->vpm)
			path_event[chp] |= PE_PATHGROUP_ESTABLISHED;
	}
	if (cdev->online && cdev->drv->path_event)
		cdev->drv->path_event(cdev, path_event);
}

static void ccw_device_reset_path_events(struct ccw_device *cdev)
{
	cdev->private->path_gone_mask = 0;
	cdev->private->path_new_mask = 0;
	cdev->private->pgid_reset_mask = 0;
}

void
ccw_device_verify_done(struct ccw_device *cdev, int err)
{
@@ -498,6 +528,7 @@ callback:
					      &cdev->private->irb);
			memset(&cdev->private->irb, 0, sizeof(struct irb));
		}
		ccw_device_report_path_events(cdev);
		break;
	case -ETIME:
	case -EUSERS:
@@ -516,6 +547,7 @@ callback:
		ccw_device_done(cdev, DEV_STATE_NOT_OPER);
		break;
	}
	ccw_device_reset_path_events(cdev);
}

/*
+17 −6
Original line number Diff line number Diff line
@@ -213,6 +213,17 @@ static void spid_start(struct ccw_device *cdev)
	spid_do(cdev);
}

static int pgid_is_reset(struct pgid *p)
{
	char *c;

	for (c = (char *)p + 1; c < (char *)(p + 1); c++) {
		if (*c != 0)
			return 0;
	}
	return 1;
}

static int pgid_cmp(struct pgid *p1, struct pgid *p2)
{
	return memcmp((char *) p1 + 1, (char *) p2 + 1,
@@ -223,7 +234,7 @@ static int pgid_cmp(struct pgid *p1, struct pgid *p2)
 * Determine pathgroup state from PGID data.
 */
static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
			 int *mismatch, int *reserved, int *reset)
			 int *mismatch, int *reserved, u8 *reset)
{
	struct pgid *pgid = &cdev->private->pgid[0];
	struct pgid *first = NULL;
@@ -238,9 +249,8 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
			continue;
		if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE)
			*reserved = 1;
		if (pgid->inf.ps.state1 == SNID_STATE1_RESET) {
			/* A PGID was reset. */
			*reset = 1;
		if (pgid_is_reset(pgid)) {
			*reset |= lpm;
			continue;
		}
		if (!first) {
@@ -307,7 +317,7 @@ static void snid_done(struct ccw_device *cdev, int rc)
	struct pgid *pgid;
	int mismatch = 0;
	int reserved = 0;
	int reset = 0;
	u8 reset = 0;
	u8 donepm;

	if (rc)
@@ -321,11 +331,12 @@ static void snid_done(struct ccw_device *cdev, int rc)
		donepm = pgid_to_donepm(cdev);
		sch->vpm = donepm & sch->opm;
		cdev->private->pgid_todo_mask &= ~donepm;
		cdev->private->pgid_reset_mask |= reset;
		pgid_fill(cdev, pgid);
	}
out:
	CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
		      "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid,
		      "todo=%02x mism=%d rsvd=%d reset=%02x\n", id->ssid,
		      id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
		      cdev->private->pgid_todo_mask, mismatch, reserved, reset);
	switch (rc) {
+5 −2
Original line number Diff line number Diff line
@@ -153,6 +153,9 @@ struct ccw_device_private {
	int iretry;
	u8 pgid_valid_mask;	/* mask of valid PGIDs */
	u8 pgid_todo_mask;	/* mask of PGIDs to be adjusted */
	u8 pgid_reset_mask;	/* mask of PGIDs which were reset */
	u8 path_gone_mask;	/* mask of paths, that became unavailable */
	u8 path_new_mask;	/* mask of paths, that became available */
	struct {
		unsigned int fast:1;	/* post with "channel end" */
		unsigned int repall:1;	/* report every interrupt status */