static void ap_poll_timeout(unsigned long);
static int ap_poll_thread_start(void);
static void ap_poll_thread_stop(void);
+static void ap_request_timeout(unsigned long);
/**
* Module description.
case AP_RESPONSE_NORMAL:
return 0;
case AP_RESPONSE_Q_FULL:
+ case AP_RESPONSE_RESET_IN_PROGRESS:
return -EBUSY;
default: /* Device is gone. */
return -ENODEV;
if (status.queue_empty)
return -ENOENT;
return -EBUSY;
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ return -EBUSY;
default:
return -ENODEV;
}
i = AP_MAX_RESET; /* return with -ENODEV */
break;
case AP_RESPONSE_RESET_IN_PROGRESS:
+ rc = -EBUSY;
case AP_RESPONSE_BUSY:
default:
break;
}
- if (rc != -ENODEV)
+ if (rc != -ENODEV && rc != -EBUSY)
break;
if (i < AP_MAX_RESET - 1) {
udelay(5);
return rc;
}
+/**
+ * Arm request timeout if a AP device was idle and a new request is submitted.
+ */
+static void ap_increase_queue_count(struct ap_device *ap_dev)
+{
+ int timeout = ap_dev->drv->request_timeout;
+
+ ap_dev->queue_count++;
+ if (ap_dev->queue_count == 1) {
+ mod_timer(&ap_dev->timeout, jiffies + timeout);
+ ap_dev->reset = AP_RESET_ARMED;
+ }
+}
+
+/**
+ * AP device is still alive, re-schedule request timeout if there are still
+ * pending requests.
+ */
+static void ap_decrease_queue_count(struct ap_device *ap_dev)
+{
+ int timeout = ap_dev->drv->request_timeout;
+
+ ap_dev->queue_count--;
+ if (ap_dev->queue_count > 0)
+ mod_timer(&ap_dev->timeout, jiffies + timeout);
+ else
+ /**
+ * The timeout timer should to be disabled now - since
+ * del_timer_sync() is very expensive, we just tell via the
+ * reset flag to ignore the pending timeout timer.
+ */
+ ap_dev->reset = AP_RESET_IGNORE;
+}
+
/**
* AP device related attributes.
*/
struct ap_driver *ap_drv = ap_dev->drv;
ap_flush_queue(ap_dev);
+ del_timer_sync(&ap_dev->timeout);
if (ap_drv->remove)
ap_drv->remove(ap_dev);
spin_lock_bh(&ap_device_lock);
__ap_scan_bus);
rc = ap_query_queue(qid, &queue_depth, &device_type);
if (dev) {
+ if (rc == -EBUSY) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(AP_RESET_TIMEOUT);
+ rc = ap_query_queue(qid, &queue_depth,
+ &device_type);
+ }
ap_dev = to_ap_dev(dev);
spin_lock_bh(&ap_dev->lock);
if (rc || ap_dev->unregistered) {
spin_unlock_bh(&ap_dev->lock);
- put_device(dev);
device_unregister(dev);
+ put_device(dev);
continue;
- } else
- spin_unlock_bh(&ap_dev->lock);
- }
- if (dev) {
+ }
+ spin_unlock_bh(&ap_dev->lock);
put_device(dev);
continue;
}
INIT_LIST_HEAD(&ap_dev->pendingq);
INIT_LIST_HEAD(&ap_dev->requestq);
INIT_LIST_HEAD(&ap_dev->list);
+ setup_timer(&ap_dev->timeout, ap_request_timeout,
+ (unsigned long) ap_dev);
if (device_type == 0)
ap_probe_device_type(ap_dev);
else
switch (status.response_code) {
case AP_RESPONSE_NORMAL:
atomic_dec(&ap_poll_requests);
- ap_dev->queue_count--;
+ ap_decrease_queue_count(ap_dev);
list_for_each_entry(ap_msg, &ap_dev->pendingq, list) {
if (ap_msg->psmid != ap_dev->reply->psmid)
continue;
switch (status.response_code) {
case AP_RESPONSE_NORMAL:
atomic_inc(&ap_poll_requests);
- ap_dev->queue_count++;
+ ap_increase_queue_count(ap_dev);
list_move_tail(&ap_msg->list, &ap_dev->pendingq);
ap_dev->requestq_count--;
ap_dev->pendingq_count++;
*flags |= 2;
break;
case AP_RESPONSE_Q_FULL:
+ case AP_RESPONSE_RESET_IN_PROGRESS:
*flags |= 2;
break;
case AP_RESPONSE_MESSAGE_TOO_BIG:
list_add_tail(&ap_msg->list, &ap_dev->pendingq);
atomic_inc(&ap_poll_requests);
ap_dev->pendingq_count++;
- ap_dev->queue_count++;
+ ap_increase_queue_count(ap_dev);
ap_dev->total_request_count++;
break;
case AP_RESPONSE_Q_FULL:
+ case AP_RESPONSE_RESET_IN_PROGRESS:
list_add_tail(&ap_msg->list, &ap_dev->requestq);
ap_dev->requestq_count++;
ap_dev->total_request_count++;
tasklet_schedule(&ap_tasklet);
}
+/**
+ * Reset a not responding AP device and move all requests from the
+ * pending queue to the request queue.
+ */
+static void ap_reset(struct ap_device *ap_dev)
+{
+ int rc;
+
+ ap_dev->reset = AP_RESET_IGNORE;
+ atomic_sub(ap_dev->queue_count, &ap_poll_requests);
+ ap_dev->queue_count = 0;
+ list_splice_init(&ap_dev->pendingq, &ap_dev->requestq);
+ ap_dev->requestq_count += ap_dev->pendingq_count;
+ ap_dev->pendingq_count = 0;
+ rc = ap_init_queue(ap_dev->qid);
+ if (rc == -ENODEV)
+ ap_dev->unregistered = 1;
+}
+
/**
* Poll all AP devices on the bus in a round robin fashion. Continue
* polling until bit 2^0 of the control flags is not set. If bit 2^1
if (!ap_dev->unregistered) {
if (ap_poll_queue(ap_dev, flags))
ap_dev->unregistered = 1;
+ if (ap_dev->reset == AP_RESET_DO)
+ ap_reset(ap_dev);
}
spin_unlock(&ap_dev->lock);
return 0;
mutex_unlock(&ap_poll_thread_mutex);
}
+/**
+ * Handling of request timeouts
+ */
+static void ap_request_timeout(unsigned long data)
+{
+ struct ap_device *ap_dev = (struct ap_device *) data;
+
+ if (ap_dev->reset == AP_RESET_ARMED)
+ ap_dev->reset = AP_RESET_DO;
+}
+
static void ap_reset_domain(void)
{
int i;
#define AP_DEVICES 64 /* Number of AP devices. */
#define AP_DOMAINS 16 /* Number of AP domains. */
#define AP_MAX_RESET 90 /* Maximum number of resets. */
+#define AP_RESET_TIMEOUT (HZ/2) /* Time in ticks for reset timeouts. */
#define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */
#define AP_POLL_TIME 1 /* Time in ticks between receive polls. */
#define AP_DEVICE_TYPE_CEX2A 6
#define AP_DEVICE_TYPE_CEX2C 7
+/**
+ * AP reset flag states
+ */
+#define AP_RESET_IGNORE 0 /* request timeout will be ignored */
+#define AP_RESET_ARMED 1 /* request timeout timer is active */
+#define AP_RESET_DO 2 /* AP reset required */
+
struct ap_device;
struct ap_message;
/* receive is called from tasklet context */
void (*receive)(struct ap_device *, struct ap_message *,
struct ap_message *);
+ int request_timeout; /* request timeout in jiffies */
};
#define to_ap_drv(x) container_of((x), struct ap_driver, driver)
int queue_depth; /* AP queue depth.*/
int device_type; /* AP device type. */
int unregistered; /* marks AP device as unregistered */
+ struct timer_list timeout; /* Timer for request timeouts. */
+ int reset; /* Reset required after req. timeout. */
int queue_count; /* # messages currently on AP queue. */
.remove = zcrypt_cex2a_remove,
.receive = zcrypt_cex2a_receive,
.ids = zcrypt_cex2a_ids,
+ .request_timeout = CEX2A_CLEANUP_TIME,
};
/**
goto out_free;
init_completion(&work);
ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible_timeout(
- &work, CEX2A_CLEANUP_TIME);
- if (rc > 0)
+ rc = wait_for_completion_interruptible(&work);
+ if (rc == 0)
rc = convert_response(zdev, &ap_msg, mex->outputdata,
mex->outputdatalength);
- else {
- /* Signal pending or message timed out. */
+ else
+ /* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
- if (rc == 0)
- /* Message timed out. */
- rc = -ETIME;
- }
out_free:
kfree(ap_msg.message);
return rc;
goto out_free;
init_completion(&work);
ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible_timeout(
- &work, CEX2A_CLEANUP_TIME);
- if (rc > 0)
+ rc = wait_for_completion_interruptible(&work);
+ if (rc == 0)
rc = convert_response(zdev, &ap_msg, crt->outputdata,
crt->outputdatalength);
- else {
- /* Signal pending or message timed out. */
+ else
+ /* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
- if (rc == 0)
- /* Message timed out. */
- rc = -ETIME;
- }
out_free:
kfree(ap_msg.message);
return rc;
.remove = zcrypt_pcica_remove,
.receive = zcrypt_pcica_receive,
.ids = zcrypt_pcica_ids,
+ .request_timeout = PCICA_CLEANUP_TIME,
};
/**
goto out_free;
init_completion(&work);
ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible_timeout(
- &work, PCICA_CLEANUP_TIME);
- if (rc > 0)
+ rc = wait_for_completion_interruptible(&work);
+ if (rc == 0)
rc = convert_response(zdev, &ap_msg, mex->outputdata,
mex->outputdatalength);
- else {
- /* Signal pending or message timed out. */
+ else
+ /* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
- if (rc == 0)
- /* Message timed out. */
- rc = -ETIME;
- }
out_free:
kfree(ap_msg.message);
return rc;
goto out_free;
init_completion(&work);
ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible_timeout(
- &work, PCICA_CLEANUP_TIME);
- if (rc > 0)
+ rc = wait_for_completion_interruptible(&work);
+ if (rc == 0)
rc = convert_response(zdev, &ap_msg, crt->outputdata,
crt->outputdatalength);
- else {
- /* Signal pending or message timed out. */
+ else
+ /* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
- if (rc == 0)
- /* Message timed out. */
- rc = -ETIME;
- }
out_free:
kfree(ap_msg.message);
return rc;
.remove = zcrypt_pcicc_remove,
.receive = zcrypt_pcicc_receive,
.ids = zcrypt_pcicc_ids,
+ .request_timeout = PCICC_CLEANUP_TIME,
};
/**
goto out_free;
init_completion(&work);
ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible_timeout(
- &work, PCICC_CLEANUP_TIME);
- if (rc > 0)
+ rc = wait_for_completion_interruptible(&work);
+ if (rc == 0)
rc = convert_response(zdev, &ap_msg, mex->outputdata,
mex->outputdatalength);
- else {
- /* Signal pending or message timed out. */
+ else
+ /* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
- if (rc == 0)
- /* Message timed out. */
- rc = -ETIME;
- }
out_free:
free_page((unsigned long) ap_msg.message);
return rc;
goto out_free;
init_completion(&work);
ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible_timeout(
- &work, PCICC_CLEANUP_TIME);
- if (rc > 0)
+ rc = wait_for_completion_interruptible(&work);
+ if (rc == 0)
rc = convert_response(zdev, &ap_msg, crt->outputdata,
crt->outputdatalength);
- else {
- /* Signal pending or message timed out. */
+ else
+ /* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
- if (rc == 0)
- /* Message timed out. */
- rc = -ETIME;
- }
out_free:
free_page((unsigned long) ap_msg.message);
return rc;
.remove = zcrypt_pcixcc_remove,
.receive = zcrypt_pcixcc_receive,
.ids = zcrypt_pcixcc_ids,
+ .request_timeout = PCIXCC_CLEANUP_TIME,
};
/**
goto out_free;
init_completion(&resp_type.work);
ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible_timeout(
- &resp_type.work, PCIXCC_CLEANUP_TIME);
- if (rc > 0)
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
rc = convert_response_ica(zdev, &ap_msg, mex->outputdata,
mex->outputdatalength);
- else {
- /* Signal pending or message timed out. */
+ else
+ /* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
- if (rc == 0)
- /* Message timed out. */
- rc = -ETIME;
- }
out_free:
free_page((unsigned long) ap_msg.message);
return rc;
goto out_free;
init_completion(&resp_type.work);
ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible_timeout(
- &resp_type.work, PCIXCC_CLEANUP_TIME);
- if (rc > 0)
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
rc = convert_response_ica(zdev, &ap_msg, crt->outputdata,
crt->outputdatalength);
- else {
- /* Signal pending or message timed out. */
+ else
+ /* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
- if (rc == 0)
- /* Message timed out. */
- rc = -ETIME;
- }
out_free:
free_page((unsigned long) ap_msg.message);
return rc;
goto out_free;
init_completion(&resp_type.work);
ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible_timeout(
- &resp_type.work, PCIXCC_CLEANUP_TIME);
- if (rc > 0)
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
- else {
- /* Signal pending or message timed out. */
+ else
+ /* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
- if (rc == 0)
- /* Message timed out. */
- rc = -ETIME;
- }
out_free:
memset(ap_msg.message, 0x0, ap_msg.length);
kfree(ap_msg.message);