[S390] ap/zcrypt: Suspend/Resume ap bus and zcrypt
Add Suspend/Resume support to ap bus and zcrypt. All enhancements are done in the ap bus. No changes in the crypto card specific part are necessary. Signed-off-by: Felix Beck <felix.beck@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Родитель
6618241b47
Коммит
772f54720a
|
@ -54,6 +54,12 @@ static int ap_poll_thread_start(void);
|
||||||
static void ap_poll_thread_stop(void);
|
static void ap_poll_thread_stop(void);
|
||||||
static void ap_request_timeout(unsigned long);
|
static void ap_request_timeout(unsigned long);
|
||||||
static inline void ap_schedule_poll_timer(void);
|
static inline void ap_schedule_poll_timer(void);
|
||||||
|
static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags);
|
||||||
|
static int ap_device_remove(struct device *dev);
|
||||||
|
static int ap_device_probe(struct device *dev);
|
||||||
|
static void ap_interrupt_handler(void *unused1, void *unused2);
|
||||||
|
static void ap_reset(struct ap_device *ap_dev);
|
||||||
|
static void ap_config_timeout(unsigned long ptr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Module description.
|
* Module description.
|
||||||
|
@ -101,6 +107,10 @@ static struct hrtimer ap_poll_timer;
|
||||||
* If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/
|
* If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/
|
||||||
static unsigned long long poll_timeout = 250000;
|
static unsigned long long poll_timeout = 250000;
|
||||||
|
|
||||||
|
/* Suspend flag */
|
||||||
|
static int ap_suspend_flag;
|
||||||
|
static struct bus_type ap_bus_type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ap_using_interrupts() - Returns non-zero if interrupt support is
|
* ap_using_interrupts() - Returns non-zero if interrupt support is
|
||||||
* available.
|
* available.
|
||||||
|
@ -617,10 +627,79 @@ static int ap_uevent (struct device *dev, struct kobj_uevent_env *env)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ap_bus_suspend(struct device *dev, pm_message_t state)
|
||||||
|
{
|
||||||
|
struct ap_device *ap_dev = to_ap_dev(dev);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (!ap_suspend_flag) {
|
||||||
|
ap_suspend_flag = 1;
|
||||||
|
|
||||||
|
/* Disable scanning for devices, thus we do not want to scan
|
||||||
|
* for them after removing.
|
||||||
|
*/
|
||||||
|
del_timer_sync(&ap_config_timer);
|
||||||
|
if (ap_work_queue != NULL) {
|
||||||
|
destroy_workqueue(ap_work_queue);
|
||||||
|
ap_work_queue = NULL;
|
||||||
|
}
|
||||||
|
tasklet_disable(&ap_tasklet);
|
||||||
|
}
|
||||||
|
/* Poll on the device until all requests are finished. */
|
||||||
|
do {
|
||||||
|
flags = 0;
|
||||||
|
__ap_poll_device(ap_dev, &flags);
|
||||||
|
} while ((flags & 1) || (flags & 2));
|
||||||
|
|
||||||
|
ap_device_remove(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ap_bus_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
struct ap_device *ap_dev = to_ap_dev(dev);
|
||||||
|
|
||||||
|
if (ap_suspend_flag) {
|
||||||
|
ap_suspend_flag = 0;
|
||||||
|
if (!ap_interrupts_available())
|
||||||
|
ap_interrupt_indicator = NULL;
|
||||||
|
ap_device_probe(dev);
|
||||||
|
ap_reset(ap_dev);
|
||||||
|
setup_timer(&ap_dev->timeout, ap_request_timeout,
|
||||||
|
(unsigned long) ap_dev);
|
||||||
|
ap_scan_bus(NULL);
|
||||||
|
init_timer(&ap_config_timer);
|
||||||
|
ap_config_timer.function = ap_config_timeout;
|
||||||
|
ap_config_timer.data = 0;
|
||||||
|
ap_config_timer.expires = jiffies + ap_config_time * HZ;
|
||||||
|
add_timer(&ap_config_timer);
|
||||||
|
ap_work_queue = create_singlethread_workqueue("kapwork");
|
||||||
|
if (!ap_work_queue)
|
||||||
|
return -ENOMEM;
|
||||||
|
tasklet_enable(&ap_tasklet);
|
||||||
|
if (!ap_using_interrupts())
|
||||||
|
ap_schedule_poll_timer();
|
||||||
|
else
|
||||||
|
tasklet_schedule(&ap_tasklet);
|
||||||
|
if (ap_thread_flag)
|
||||||
|
rc = ap_poll_thread_start();
|
||||||
|
} else {
|
||||||
|
ap_device_probe(dev);
|
||||||
|
ap_reset(ap_dev);
|
||||||
|
setup_timer(&ap_dev->timeout, ap_request_timeout,
|
||||||
|
(unsigned long) ap_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static struct bus_type ap_bus_type = {
|
static struct bus_type ap_bus_type = {
|
||||||
.name = "ap",
|
.name = "ap",
|
||||||
.match = &ap_bus_match,
|
.match = &ap_bus_match,
|
||||||
.uevent = &ap_uevent,
|
.uevent = &ap_uevent,
|
||||||
|
.suspend = ap_bus_suspend,
|
||||||
|
.resume = ap_bus_resume
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ap_device_probe(struct device *dev)
|
static int ap_device_probe(struct device *dev)
|
||||||
|
@ -1066,7 +1145,7 @@ ap_config_timeout(unsigned long ptr)
|
||||||
*/
|
*/
|
||||||
static inline void ap_schedule_poll_timer(void)
|
static inline void ap_schedule_poll_timer(void)
|
||||||
{
|
{
|
||||||
if (ap_using_interrupts())
|
if (ap_using_interrupts() || ap_suspend_flag)
|
||||||
return;
|
return;
|
||||||
if (hrtimer_is_queued(&ap_poll_timer))
|
if (hrtimer_is_queued(&ap_poll_timer))
|
||||||
return;
|
return;
|
||||||
|
@ -1384,6 +1463,8 @@ static int ap_poll_thread(void *data)
|
||||||
|
|
||||||
set_user_nice(current, 19);
|
set_user_nice(current, 19);
|
||||||
while (1) {
|
while (1) {
|
||||||
|
if (ap_suspend_flag)
|
||||||
|
return 0;
|
||||||
if (need_resched()) {
|
if (need_resched()) {
|
||||||
schedule();
|
schedule();
|
||||||
continue;
|
continue;
|
||||||
|
@ -1414,7 +1495,7 @@ static int ap_poll_thread_start(void)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (ap_using_interrupts())
|
if (ap_using_interrupts() || ap_suspend_flag)
|
||||||
return 0;
|
return 0;
|
||||||
mutex_lock(&ap_poll_thread_mutex);
|
mutex_lock(&ap_poll_thread_mutex);
|
||||||
if (!ap_poll_kthread) {
|
if (!ap_poll_kthread) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче