Commit bf18140d authored by Peter Oberparleiter's avatar Peter Oberparleiter Committed by Vasily Gorbik

s390/vmur: generate uevent on unsolicited device end

When a traditional channel-attached device transitions from not-ready to
ready state, an unsolicited DEVICE END I/O interrupt is raised. This
happens for example when a new file arrives in the z/VM virtual reader
device.

Change the Linux kernel to generate a change uevent when such an
interrupt occurs for any online unit record devices supported by the
vmur driver. This can be useful to automatically trigger processing of
files as they arrive in the reader device.

A sample udev rule for running a program when this event occurs looks as
follows:

  ENV{DRIVER}=="vmur", ACTION=="change", ENV{EVENT}=="unsol_de", \
    RUN{program}="/path/to/program"

The rule can be tested using the following steps:

1. Set reader device online (assuming default reader device number 000c)

   $ chzdev -ea 0.0.000c

2. Force a ready-state transition using z/VM's READY CP command

   $ vmcp ready 000c
Suggested-by: default avatarAlan Altmark <Alan_Altmark@us.ibm.com>
Reviewed-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Reviewed-by: default avatarSven Schnelle <svens@linux.ibm.com>
Signed-off-by: default avatarPeter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent f3e59ff3
...@@ -15,12 +15,14 @@ ...@@ -15,12 +15,14 @@
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kobject.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/cio.h> #include <asm/cio.h>
#include <asm/ccwdev.h> #include <asm/ccwdev.h>
#include <asm/debug.h> #include <asm/debug.h>
#include <asm/diag.h> #include <asm/diag.h>
#include <asm/scsw.h>
#include "vmur.h" #include "vmur.h"
...@@ -78,6 +80,8 @@ static struct ccw_driver ur_driver = { ...@@ -78,6 +80,8 @@ static struct ccw_driver ur_driver = {
static DEFINE_MUTEX(vmur_mutex); static DEFINE_MUTEX(vmur_mutex);
static void ur_uevent(struct work_struct *ws);
/* /*
* Allocation, freeing, getting and putting of urdev structures * Allocation, freeing, getting and putting of urdev structures
* *
...@@ -108,6 +112,7 @@ static struct urdev *urdev_alloc(struct ccw_device *cdev) ...@@ -108,6 +112,7 @@ static struct urdev *urdev_alloc(struct ccw_device *cdev)
ccw_device_get_id(cdev, &urd->dev_id); ccw_device_get_id(cdev, &urd->dev_id);
mutex_init(&urd->io_mutex); mutex_init(&urd->io_mutex);
init_waitqueue_head(&urd->wait); init_waitqueue_head(&urd->wait);
INIT_WORK(&urd->uevent_work, ur_uevent);
spin_lock_init(&urd->open_lock); spin_lock_init(&urd->open_lock);
refcount_set(&urd->ref_count, 1); refcount_set(&urd->ref_count, 1);
urd->cdev = cdev; urd->cdev = cdev;
...@@ -275,6 +280,18 @@ static int do_ur_io(struct urdev *urd, struct ccw1 *cpa) ...@@ -275,6 +280,18 @@ static int do_ur_io(struct urdev *urd, struct ccw1 *cpa)
return rc; return rc;
} }
static void ur_uevent(struct work_struct *ws)
{
struct urdev *urd = container_of(ws, struct urdev, uevent_work);
char *envp[] = {
"EVENT=unsol_de", /* Unsolicited device-end interrupt */
NULL
};
kobject_uevent_env(&urd->cdev->dev.kobj, KOBJ_CHANGE, envp);
urdev_put(urd);
}
/* /*
* ur interrupt handler, called from the ccw_device layer * ur interrupt handler, called from the ccw_device layer
*/ */
...@@ -288,11 +305,21 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -288,11 +305,21 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat, intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
irb->scsw.cmd.count); irb->scsw.cmd.count);
} }
urd = dev_get_drvdata(&cdev->dev);
if (!intparm) { if (!intparm) {
TRACE("ur_int_handler: unsolicited interrupt\n"); TRACE("ur_int_handler: unsolicited interrupt\n");
if (scsw_dstat(&irb->scsw) & DEV_STAT_DEV_END) {
/*
* Userspace might be interested in a transition to
* device-ready state.
*/
urdev_get(urd);
schedule_work(&urd->uevent_work);
}
return; return;
} }
urd = dev_get_drvdata(&cdev->dev);
/* On special conditions irb is an error pointer */ /* On special conditions irb is an error pointer */
if (IS_ERR(irb)) if (IS_ERR(irb))
urd->io_request_rc = PTR_ERR(irb); urd->io_request_rc = PTR_ERR(irb);
...@@ -927,6 +954,10 @@ static int ur_set_offline_force(struct ccw_device *cdev, int force) ...@@ -927,6 +954,10 @@ static int ur_set_offline_force(struct ccw_device *cdev, int force)
rc = -EBUSY; rc = -EBUSY;
goto fail_urdev_put; goto fail_urdev_put;
} }
if (cancel_work_sync(&urd->uevent_work)) {
/* Work not run yet - need to release reference here */
urdev_put(urd);
}
device_destroy(vmur_class, urd->char_device->dev); device_destroy(vmur_class, urd->char_device->dev);
cdev_del(urd->char_device); cdev_del(urd->char_device);
urd->char_device = NULL; urd->char_device = NULL;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#define _VMUR_H_ #define _VMUR_H_
#include <linux/refcount.h> #include <linux/refcount.h>
#include <linux/workqueue.h>
#define DEV_CLASS_UR_I 0x20 /* diag210 unit record input device class */ #define DEV_CLASS_UR_I 0x20 /* diag210 unit record input device class */
#define DEV_CLASS_UR_O 0x10 /* diag210 unit record output device class */ #define DEV_CLASS_UR_O 0x10 /* diag210 unit record output device class */
...@@ -76,6 +77,7 @@ struct urdev { ...@@ -76,6 +77,7 @@ struct urdev {
wait_queue_head_t wait; /* wait queue to serialize open */ wait_queue_head_t wait; /* wait queue to serialize open */
int open_flag; /* "urdev is open" flag */ int open_flag; /* "urdev is open" flag */
spinlock_t open_lock; /* serialize critical sections */ spinlock_t open_lock; /* serialize critical sections */
struct work_struct uevent_work; /* work to send uevent */
}; };
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment