@@ -131,6 +131,33 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err,
zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdires2");
}
+static void zfcp_qdio_irq_tasklet(struct tasklet_struct *tasklet)
+{
+ struct zfcp_qdio *qdio = from_tasklet(qdio, tasklet, irq_tasklet);
+ struct ccw_device *cdev = qdio->adapter->ccw_device;
+ unsigned int start, error;
+ int completed;
+
+ /* Check the Response Queue, and kick off the Request Queue tasklet: */
+ completed = qdio_get_next_buffers(cdev, 0, &start, &error);
+ if (completed < 0)
+ return;
+ if (completed > 0)
+ zfcp_qdio_int_resp(cdev, error, 0, start, completed,
+ (unsigned long) qdio);
+
+ if (qdio_start_irq(cdev))
+ /* More work pending: */
+ tasklet_schedule(&qdio->irq_tasklet);
+}
+
+static void zfcp_qdio_poll(struct ccw_device *cdev, unsigned long data)
+{
+ struct zfcp_qdio *qdio = (struct zfcp_qdio *) data;
+
+ tasklet_schedule(&qdio->irq_tasklet);
+}
+
static struct qdio_buffer_element *
zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req)
{
@@ -332,6 +359,8 @@ void zfcp_qdio_close(struct zfcp_qdio *qdio)
wake_up(&qdio->req_q_wq);
+ tasklet_disable(&qdio->irq_tasklet);
+ qdio_stop_irq(adapter->ccw_device);
qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR);
/* cleanup used outbound sbals */
@@ -387,6 +416,7 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio)
init_data.no_output_qs = 1;
init_data.input_handler = zfcp_qdio_int_resp;
init_data.output_handler = zfcp_qdio_int_req;
+ init_data.irq_poll = zfcp_qdio_poll;
init_data.int_parm = (unsigned long) qdio;
init_data.input_sbal_addr_array = input_sbals;
init_data.output_sbal_addr_array = output_sbals;
@@ -433,6 +463,11 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio)
atomic_set(&qdio->req_q_free, QDIO_MAX_BUFFERS_PER_Q);
atomic_or(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status);
+ /* Enable processing for QDIO interrupts: */
+ tasklet_enable(&qdio->irq_tasklet);
+ /* This results in a qdio_start_irq(): */
+ tasklet_schedule(&qdio->irq_tasklet);
+
zfcp_qdio_shost_update(adapter, qdio);
return 0;
@@ -450,6 +485,8 @@ void zfcp_qdio_destroy(struct zfcp_qdio *qdio)
if (!qdio)
return;
+ tasklet_kill(&qdio->irq_tasklet);
+
if (qdio->adapter->ccw_device)
qdio_free(qdio->adapter->ccw_device);
@@ -475,6 +512,8 @@ int zfcp_qdio_setup(struct zfcp_adapter *adapter)
spin_lock_init(&qdio->req_q_lock);
spin_lock_init(&qdio->stat_lock);
+ tasklet_setup(&qdio->irq_tasklet, zfcp_qdio_irq_tasklet);
+ tasklet_disable(&qdio->irq_tasklet);
adapter->qdio = qdio;
return 0;
@@ -10,6 +10,7 @@
#ifndef ZFCP_QDIO_H
#define ZFCP_QDIO_H
+#include <linux/interrupt.h>
#include <asm/qdio.h>
#define ZFCP_QDIO_SBALE_LEN PAGE_SIZE
@@ -44,6 +45,7 @@ struct zfcp_qdio {
u64 req_q_util;
atomic_t req_q_full;
wait_queue_head_t req_q_wq;
+ struct tasklet_struct irq_tasklet;
struct zfcp_adapter *adapter;
u16 max_sbale_per_sbal;
u16 max_sbale_per_req;