#include <sys/types.h>
#include <sys/sunddi.h>
#include <sys/callb.h>
#include <sys/ksynch.h>
#include <sys/systm.h>
#include <sys/taskq_impl.h>
#include <sys/crypto/api.h>
#include <sys/crypto/sched_impl.h>
kmutex_t cbuf_list_lock;
kcondvar_t cbuf_list_cv;
static kcf_cbuf_elem_t *cbuf_list_head;
static kcf_cbuf_elem_t *cbuf_list_tail;
crypto_bc_t
crypto_bufcall_alloc(void)
{
kcf_cbuf_elem_t *cbufp;
cbufp = kmem_zalloc(sizeof (kcf_cbuf_elem_t), KM_SLEEP);
mutex_init(&cbufp->kc_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&cbufp->kc_cv, NULL, CV_DEFAULT, NULL);
cbufp->kc_state = CBUF_FREE;
return (cbufp);
}
int
crypto_bufcall_free(crypto_bc_t bc)
{
kcf_cbuf_elem_t *cbufp = (kcf_cbuf_elem_t *)bc;
mutex_enter(&cbufp->kc_lock);
if (cbufp->kc_state != CBUF_FREE) {
mutex_exit(&cbufp->kc_lock);
return (CRYPTO_BUSY);
}
mutex_exit(&cbufp->kc_lock);
mutex_destroy(&cbufp->kc_lock);
cv_destroy(&cbufp->kc_cv);
kmem_free(cbufp, sizeof (kcf_cbuf_elem_t));
return (CRYPTO_SUCCESS);
}
int
crypto_bufcall(crypto_bc_t bc, void (*func)(void *arg), void *arg)
{
kcf_cbuf_elem_t *cbufp;
cbufp = (kcf_cbuf_elem_t *)bc;
if (cbufp == NULL || func == NULL) {
return (CRYPTO_ARGUMENTS_BAD);
}
mutex_enter(&cbuf_list_lock);
mutex_enter(&cbufp->kc_lock);
if (cbufp->kc_state != CBUF_FREE) {
mutex_exit(&cbufp->kc_lock);
mutex_exit(&cbuf_list_lock);
return (CRYPTO_BUSY);
}
cbufp->kc_state = CBUF_WAITING;
cbufp->kc_func = func;
cbufp->kc_arg = arg;
cbufp->kc_prev = cbufp->kc_next = NULL;
if (cbuf_list_head == NULL) {
cbuf_list_head = cbuf_list_tail = cbufp;
} else {
cbuf_list_tail->kc_next = cbufp;
cbufp->kc_prev = cbuf_list_tail;
cbuf_list_tail = cbufp;
}
cv_signal(&cbuf_list_cv);
mutex_exit(&cbufp->kc_lock);
mutex_exit(&cbuf_list_lock);
return (CRYPTO_SUCCESS);
}
int
crypto_unbufcall(crypto_bc_t bc)
{
kcf_cbuf_elem_t *cbufp = (kcf_cbuf_elem_t *)bc;
mutex_enter(&cbuf_list_lock);
mutex_enter(&cbufp->kc_lock);
if (cbufp->kc_state == CBUF_WAITING) {
kcf_cbuf_elem_t *nextp = cbufp->kc_next;
kcf_cbuf_elem_t *prevp = cbufp->kc_prev;
if (nextp != NULL)
nextp->kc_prev = prevp;
else
cbuf_list_tail = prevp;
if (prevp != NULL)
prevp->kc_next = nextp;
else
cbuf_list_head = nextp;
cbufp->kc_state = CBUF_FREE;
} else if (cbufp->kc_state == CBUF_RUNNING) {
mutex_exit(&cbuf_list_lock);
while (cbufp->kc_state == CBUF_RUNNING)
cv_wait(&cbufp->kc_cv, &cbufp->kc_lock);
mutex_exit(&cbufp->kc_lock);
return (CRYPTO_SUCCESS);
}
mutex_exit(&cbufp->kc_lock);
mutex_exit(&cbuf_list_lock);
return (CRYPTO_SUCCESS);
}
#define KCF_GSWQ_AVAIL (gswq->gs_maxjobs - gswq->gs_njobs)
#define GSWQ_MINFREE 3
static void
kcf_run_cbufcalls(void)
{
kcf_cbuf_elem_t *cbufp;
int count;
mutex_enter(&cbuf_list_lock);
count = KCF_GSWQ_AVAIL;
while ((cbufp = cbuf_list_head) != NULL) {
if (GSWQ_MINFREE <= count) {
count -= GSWQ_MINFREE;
mutex_enter(&cbufp->kc_lock);
cbuf_list_head = cbufp->kc_next;
cbufp->kc_state = CBUF_RUNNING;
mutex_exit(&cbufp->kc_lock);
mutex_exit(&cbuf_list_lock);
(*cbufp->kc_func)(cbufp->kc_arg);
mutex_enter(&cbufp->kc_lock);
cbufp->kc_state = CBUF_FREE;
cv_broadcast(&cbufp->kc_cv);
mutex_exit(&cbufp->kc_lock);
mutex_enter(&cbuf_list_lock);
} else {
break;
}
}
if (cbuf_list_head == NULL)
cbuf_list_tail = NULL;
mutex_exit(&cbuf_list_lock);
}
void
crypto_bufcall_service(void)
{
callb_cpr_t cprinfo;
CALLB_CPR_INIT(&cprinfo, &cbuf_list_lock, callb_generic_cpr,
"crypto_bufcall_service");
mutex_enter(&cbuf_list_lock);
for (;;) {
if (cbuf_list_head != NULL && KCF_GSWQ_AVAIL >= GSWQ_MINFREE) {
mutex_exit(&cbuf_list_lock);
kcf_run_cbufcalls();
mutex_enter(&cbuf_list_lock);
}
if (cbuf_list_head != NULL) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
mutex_exit(&cbuf_list_lock);
delay(30 * drv_usectohz(1000000));
mutex_enter(&cbuf_list_lock);
CALLB_CPR_SAFE_END(&cprinfo, &cbuf_list_lock);
}
if (cbuf_list_head == NULL) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(&cbuf_list_cv, &cbuf_list_lock);
CALLB_CPR_SAFE_END(&cprinfo, &cbuf_list_lock);
}
}
}