s390: add scm notification
Detect an scm change notification in store event information. Update affected scm devices and notify their drivers. Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
1d1c8f78be
commit
40ff4cc066
|
@ -102,6 +102,7 @@ struct scm_driver {
|
||||||
struct device_driver drv;
|
struct device_driver drv;
|
||||||
int (*probe) (struct scm_device *scmdev);
|
int (*probe) (struct scm_device *scmdev);
|
||||||
int (*remove) (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);
|
void (*handler) (struct scm_device *scmdev, void *data, int error);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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)
|
static void chsc_process_sei(struct chsc_sei_area *sei_area)
|
||||||
{
|
{
|
||||||
/* Check if we might have lost some information. */
|
/* 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 */
|
case 8: /* channel-path-configuration notification */
|
||||||
chsc_process_sei_chp_config(sei_area);
|
chsc_process_sei_chp_config(sei_area);
|
||||||
break;
|
break;
|
||||||
|
case 12: /* scm change notification */
|
||||||
|
chsc_process_sei_scm_change(sei_area);
|
||||||
|
break;
|
||||||
default: /* other stuff */
|
default: /* other stuff */
|
||||||
CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
|
CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
|
||||||
sei_area->cc);
|
sei_area->cc);
|
||||||
|
|
|
@ -154,4 +154,11 @@ struct chsc_scm_info {
|
||||||
|
|
||||||
int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);
|
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
|
#endif
|
||||||
|
|
|
@ -196,6 +196,47 @@ static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
|
||||||
spin_lock_init(&scmdev->lock);
|
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)
|
static int scm_add(struct chsc_scm_info *scm_info, size_t num)
|
||||||
{
|
{
|
||||||
struct sale *sale, *scmal = scm_info->scmal;
|
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;
|
int ret;
|
||||||
|
|
||||||
for (sale = scmal; sale < scmal + num; sale++) {
|
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);
|
scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
|
||||||
if (!scmdev)
|
if (!scmdev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -218,7 +266,7 @@ static int scm_add(struct chsc_scm_info *scm_info, size_t num)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int scm_update_information(void)
|
int scm_update_information(void)
|
||||||
{
|
{
|
||||||
struct chsc_scm_info *scm_info;
|
struct chsc_scm_info *scm_info;
|
||||||
u64 token = 0;
|
u64 token = 0;
|
||||||
|
|
Loading…
Reference in New Issue