Skip to content

Commit

Permalink
[S390] zcrypt: Use of Thin Interrupts
Browse files Browse the repository at this point in the history
When the machine supports AP adapter interrupts polling will be
switched off at module initialization and the driver will work in
interrupt mode.

Signed-off-by: Felix Beck <felix.beck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
  • Loading branch information
Felix Beck authored and Martin Schwidefsky committed Dec 25, 2008
1 parent 320c04c commit cb17a63
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 4 deletions.
1 change: 1 addition & 0 deletions arch/s390/include/asm/isc.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#define CHSC_SCH_ISC 7 /* CHSC subchannels */
/* Adapter interrupts. */
#define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */
#define AP_ISC 6 /* adjunct processor (crypto) devices */

/* Functions for registration of I/O interruption subclasses */
void isc_register(unsigned int isc);
Expand Down
174 changes: 171 additions & 3 deletions drivers/s390/crypto/ap_bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
* Felix Beck <felix.beck@de.ibm.com>
*
* Adjunct processor bus.
*
Expand Down Expand Up @@ -34,6 +35,10 @@
#include <linux/mutex.h>
#include <asm/s390_rdev.h>
#include <asm/reset.h>
#include <asm/airq.h>
#include <asm/atomic.h>
#include <asm/system.h>
#include <asm/isc.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>

Expand All @@ -46,6 +51,7 @@ static enum hrtimer_restart ap_poll_timeout(struct hrtimer *);
static int ap_poll_thread_start(void);
static void ap_poll_thread_stop(void);
static void ap_request_timeout(unsigned long);
static inline void ap_schedule_poll_timer(void);

/*
* Module description.
Expand Down Expand Up @@ -80,18 +86,28 @@ static int ap_config_time = AP_CONFIG_TIME;
static DECLARE_WORK(ap_config_work, ap_scan_bus);

/*
* Tasklet & timer for AP request polling.
* Tasklet & timer for AP request polling and interrupts
*/
static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);
static atomic_t ap_poll_requests = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
static struct task_struct *ap_poll_kthread = NULL;
static DEFINE_MUTEX(ap_poll_thread_mutex);
static void *ap_interrupt_indicator;
static struct hrtimer ap_poll_timer;
/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
* If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/
static unsigned long long poll_timeout = 250000;

/**
* ap_using_interrupts() - Returns non-zero if interrupt support is
* available.
*/
static inline int ap_using_interrupts(void)
{
return ap_interrupt_indicator != NULL;
}

/**
* ap_intructions_available() - Test if AP instructions are available.
*
Expand All @@ -112,6 +128,23 @@ static inline int ap_instructions_available(void)
return reg1;
}

/**
* ap_interrupts_available(): Test if AP interrupts are available.
*
* Returns 1 if AP interrupts are available.
*/
static int ap_interrupts_available(void)
{
unsigned long long facility_bits[2];

if (stfle(facility_bits, 2) <= 1)
return 0;
if (!(facility_bits[0] & (1ULL << 61)) ||
!(facility_bits[1] & (1ULL << 62)))
return 0;
return 1;
}

/**
* ap_test_queue(): Test adjunct processor queue.
* @qid: The AP queue number
Expand Down Expand Up @@ -152,6 +185,80 @@ static inline struct ap_queue_status ap_reset_queue(ap_qid_t qid)
return reg1;
}

#ifdef CONFIG_64BIT
/**
* ap_queue_interruption_control(): Enable interruption for a specific AP.
* @qid: The AP queue number
* @ind: The notification indicator byte
*
* Returns AP queue status.
*/
static inline struct ap_queue_status
ap_queue_interruption_control(ap_qid_t qid, void *ind)
{
register unsigned long reg0 asm ("0") = qid | 0x03000000UL;
register unsigned long reg1_in asm ("1") = 0x0000800000000000UL | AP_ISC;
register struct ap_queue_status reg1_out asm ("1");
register void *reg2 asm ("2") = ind;
asm volatile(
".long 0xb2af0000" /* PQAP(RAPQ) */
: "+d" (reg0), "+d" (reg1_in), "=d" (reg1_out), "+d" (reg2)
:
: "cc" );
return reg1_out;
}
#endif

/**
* ap_queue_enable_interruption(): Enable interruption on an AP.
* @qid: The AP queue number
* @ind: the notification indicator byte
*
* Enables interruption on AP queue via ap_queue_interruption_control(). Based
* on the return value it waits a while and tests the AP queue if interrupts
* have been switched on using ap_test_queue().
*/
static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
{
#ifdef CONFIG_64BIT
struct ap_queue_status status;
int t_depth, t_device_type, rc, i;

rc = -EBUSY;
status = ap_queue_interruption_control(qid, ind);

for (i = 0; i < AP_MAX_RESET; i++) {
switch (status.response_code) {
case AP_RESPONSE_NORMAL:
if (status.int_enabled)
return 0;
break;
case AP_RESPONSE_RESET_IN_PROGRESS:
case AP_RESPONSE_BUSY:
break;
case AP_RESPONSE_Q_NOT_AVAIL:
case AP_RESPONSE_DECONFIGURED:
case AP_RESPONSE_CHECKSTOPPED:
case AP_RESPONSE_INVALID_ADDRESS:
return -ENODEV;
case AP_RESPONSE_OTHERWISE_CHANGED:
if (status.int_enabled)
return 0;
break;
default:
break;
}
if (i < AP_MAX_RESET - 1) {
udelay(5);
status = ap_test_queue(qid, &t_depth, &t_device_type);
}
}
return rc;
#else
return -EINVAL;
#endif
}

/**
* __ap_send(): Send message to adjunct processor queue.
* @qid: The AP queue number
Expand Down Expand Up @@ -295,6 +402,11 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type)
case AP_RESPONSE_CHECKSTOPPED:
rc = -ENODEV;
break;
case AP_RESPONSE_INVALID_ADDRESS:
rc = -ENODEV;
break;
case AP_RESPONSE_OTHERWISE_CHANGED:
break;
case AP_RESPONSE_BUSY:
break;
default:
Expand Down Expand Up @@ -345,6 +457,15 @@ static int ap_init_queue(ap_qid_t qid)
status = ap_test_queue(qid, &dummy, &dummy);
}
}
if (rc == 0 && ap_using_interrupts()) {
rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator);
/* If interruption mode is supported by the machine,
* but an AP can not be enabled for interruption then
* the AP will be discarded. */
if (rc)
pr_err("Registering adapter interrupts for "
"AP %d failed\n", AP_QID_DEVICE(qid));
}
return rc;
}

Expand Down Expand Up @@ -599,6 +720,14 @@ static ssize_t ap_config_time_show(struct bus_type *bus, char *buf)
return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time);
}

static ssize_t ap_interrupts_show(struct bus_type *bus, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n",
ap_using_interrupts() ? 1 : 0);
}

static BUS_ATTR(ap_interrupts, 0444, ap_interrupts_show, NULL);

static ssize_t ap_config_time_store(struct bus_type *bus,
const char *buf, size_t count)
{
Expand Down Expand Up @@ -653,7 +782,8 @@ static ssize_t poll_timeout_store(struct bus_type *bus, const char *buf,
ktime_t hr_time;

/* 120 seconds = maximum poll interval */
if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || time > 120000000000)
if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 ||
time > 120000000000ULL)
return -EINVAL;
poll_timeout = time;
hr_time = ktime_set(0, poll_timeout);
Expand All @@ -672,6 +802,7 @@ static struct bus_attribute *const ap_bus_attrs[] = {
&bus_attr_ap_domain,
&bus_attr_config_time,
&bus_attr_poll_thread,
&bus_attr_ap_interrupts,
&bus_attr_poll_timeout,
NULL,
};
Expand Down Expand Up @@ -814,6 +945,11 @@ static int ap_probe_device_type(struct ap_device *ap_dev)
return rc;
}

static void ap_interrupt_handler(void *unused1, void *unused2)
{
tasklet_schedule(&ap_tasklet);
}

/**
* __ap_scan_bus(): Scan the AP bus.
* @dev: Pointer to device
Expand Down Expand Up @@ -928,6 +1064,8 @@ ap_config_timeout(unsigned long ptr)
*/
static inline void ap_schedule_poll_timer(void)
{
if (ap_using_interrupts())
return;
if (hrtimer_is_queued(&ap_poll_timer))
return;
hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout),
Expand Down Expand Up @@ -1207,6 +1345,12 @@ static void ap_poll_all(unsigned long dummy)
unsigned long flags;
struct ap_device *ap_dev;

/* Reset the indicator if interrupts are used. Thus new interrupts can
* be received. Doing it in the beginning of the tasklet is therefor
* important that no requests on any AP get lost.
*/
if (ap_using_interrupts())
xchg((u8 *)ap_interrupt_indicator, 0);
do {
flags = 0;
spin_lock(&ap_device_lock);
Expand Down Expand Up @@ -1268,6 +1412,8 @@ static int ap_poll_thread_start(void)
{
int rc;

if (ap_using_interrupts())
return 0;
mutex_lock(&ap_poll_thread_mutex);
if (!ap_poll_kthread) {
ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
Expand Down Expand Up @@ -1301,8 +1447,12 @@ static void ap_request_timeout(unsigned long data)
{
struct ap_device *ap_dev = (struct ap_device *) data;

if (ap_dev->reset == AP_RESET_ARMED)
if (ap_dev->reset == AP_RESET_ARMED) {
ap_dev->reset = AP_RESET_DO;

if (ap_using_interrupts())
tasklet_schedule(&ap_tasklet);
}
}

static void ap_reset_domain(void)
Expand Down Expand Up @@ -1345,6 +1495,16 @@ int __init ap_module_init(void)
printk(KERN_WARNING "AP instructions not installed.\n");
return -ENODEV;
}
if (ap_interrupts_available()) {
isc_register(AP_ISC);
ap_interrupt_indicator = s390_register_adapter_interrupt(
&ap_interrupt_handler, NULL, AP_ISC);
if (IS_ERR(ap_interrupt_indicator)) {
ap_interrupt_indicator = NULL;
isc_unregister(AP_ISC);
}
}

register_reset_call(&ap_reset_call);

/* Create /sys/bus/ap. */
Expand Down Expand Up @@ -1408,6 +1568,10 @@ int __init ap_module_init(void)
bus_unregister(&ap_bus_type);
out:
unregister_reset_call(&ap_reset_call);
if (ap_using_interrupts()) {
s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC);
isc_unregister(AP_ISC);
}
return rc;
}

Expand Down Expand Up @@ -1443,6 +1607,10 @@ void ap_module_exit(void)
bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
bus_unregister(&ap_bus_type);
unregister_reset_call(&ap_reset_call);
if (ap_using_interrupts()) {
s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC);
isc_unregister(AP_ISC);
}
}

#ifndef CONFIG_ZCRYPT_MONOLITHIC
Expand Down
6 changes: 5 additions & 1 deletion drivers/s390/crypto/ap_bus.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
* Felix Beck <felix.beck@de.ibm.com>
*
* Adjunct processor bus header file.
*
Expand Down Expand Up @@ -67,7 +68,8 @@ struct ap_queue_status {
unsigned int queue_empty : 1;
unsigned int replies_waiting : 1;
unsigned int queue_full : 1;
unsigned int pad1 : 5;
unsigned int pad1 : 4;
unsigned int int_enabled : 1;
unsigned int response_code : 8;
unsigned int pad2 : 16;
};
Expand All @@ -78,6 +80,8 @@ struct ap_queue_status {
#define AP_RESPONSE_DECONFIGURED 0x03
#define AP_RESPONSE_CHECKSTOPPED 0x04
#define AP_RESPONSE_BUSY 0x05
#define AP_RESPONSE_INVALID_ADDRESS 0x06
#define AP_RESPONSE_OTHERWISE_CHANGED 0x07
#define AP_RESPONSE_Q_FULL 0x10
#define AP_RESPONSE_NO_PENDING_REPLY 0x10
#define AP_RESPONSE_INDEX_TOO_BIG 0x11
Expand Down

0 comments on commit cb17a63

Please sign in to comment.