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

Commit 40ff4cc0 authored by Sebastian Ott's avatar Sebastian Ott Committed by Martin Schwidefsky
Browse files

s390: add scm notification



Detect an scm change notification in store event information.
Update affected scm devices and notify their drivers.

Signed-off-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 1d1c8f78
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ struct scm_driver {
	struct device_driver drv;
	int (*probe) (struct scm_device *scmdev);
	int (*remove) (struct scm_device *scmdev);
	void (*notify) (struct scm_device *scmdev);
	void (*handler) (struct scm_device *scmdev, void *data, int error);
};

+17 −0
Original line number Diff line number Diff line
@@ -398,6 +398,20 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
	}
}

static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area)
{
	int ret;

	CIO_CRW_EVENT(4, "chsc: scm change notification\n");
	if (sei_area->rs != 7)
		return;

	ret = scm_update_information();
	if (ret)
		CIO_CRW_EVENT(0, "chsc: updating change notification"
			      " failed (rc=%d).\n", ret);
}

static void chsc_process_sei(struct chsc_sei_area *sei_area)
{
	/* Check if we might have lost some information. */
@@ -419,6 +433,9 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
	case 8: /* channel-path-configuration notification */
		chsc_process_sei_chp_config(sei_area);
		break;
	case 12: /* scm change notification */
		chsc_process_sei_scm_change(sei_area);
		break;
	default: /* other stuff */
		CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
			      sei_area->cc);
+7 −0
Original line number Diff line number Diff line
@@ -154,4 +154,11 @@ struct chsc_scm_info {

int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);

#ifdef CONFIG_SCM_BUS
int scm_update_information(void);
#else /* CONFIG_SCM_BUS */
#define scm_update_information() 0
#endif /* CONFIG_SCM_BUS */


#endif
+49 −1
Original line number Diff line number Diff line
@@ -196,6 +196,47 @@ static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
	spin_lock_init(&scmdev->lock);
}

/*
 * Check for state-changes, notify the driver and userspace.
 */
static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
{
	struct scm_driver *scmdrv;
	bool changed;

	device_lock(&scmdev->dev);
	changed = scmdev->attrs.rank != sale->rank ||
		  scmdev->attrs.oper_state != sale->op_state;
	scmdev->attrs.rank = sale->rank;
	scmdev->attrs.oper_state = sale->op_state;
	if (!scmdev->dev.driver)
		goto out;
	scmdrv = to_scm_drv(scmdev->dev.driver);
	if (changed && scmdrv->notify)
		scmdrv->notify(scmdev);
out:
	device_unlock(&scmdev->dev);
	if (changed)
		kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE);
}

static int check_address(struct device *dev, void *data)
{
	struct scm_device *scmdev = to_scm_dev(dev);
	struct sale *sale = data;

	return scmdev->address == sale->sa;
}

static struct scm_device *scmdev_find(struct sale *sale)
{
	struct device *dev;

	dev = bus_find_device(&scm_bus_type, NULL, sale, check_address);

	return dev ? to_scm_dev(dev) : NULL;
}

static int scm_add(struct chsc_scm_info *scm_info, size_t num)
{
	struct sale *sale, *scmal = scm_info->scmal;
@@ -203,6 +244,13 @@ static int scm_add(struct chsc_scm_info *scm_info, size_t num)
	int ret;

	for (sale = scmal; sale < scmal + num; sale++) {
		scmdev = scmdev_find(sale);
		if (scmdev) {
			scmdev_update(scmdev, sale);
			/* Release reference from scm_find(). */
			put_device(&scmdev->dev);
			continue;
		}
		scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
		if (!scmdev)
			return -ENODEV;
@@ -218,7 +266,7 @@ static int scm_add(struct chsc_scm_info *scm_info, size_t num)
	return 0;
}

static int scm_update_information(void)
int scm_update_information(void)
{
	struct chsc_scm_info *scm_info;
	u64 token = 0;