189 lines
5.4 KiB
C
189 lines
5.4 KiB
C
|
#include "sst_internal.h"
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/kthread.h>
|
||
|
#include <linux/random.h>
|
||
|
#include <linux/slab.h>
|
||
|
|
||
|
static sst_info_t *cur_sst_info = NULL;
|
||
|
static char an_answer[] = "You shall ask a question!\n";
|
||
|
|
||
|
char *sst_questions[SST_MAX_QUESTIONS] = {
|
||
|
"What is the answer to the Ultimate Question of Life, The Universe, and Everything?",
|
||
|
"What do you get if you multiply six by nine?"
|
||
|
};
|
||
|
EXPORT_SYMBOL(sst_questions);
|
||
|
|
||
|
char *sst_answers[SST_MAX_ANSWERS] = {
|
||
|
"42!\n",
|
||
|
"Six by nine. Forty two.\n",
|
||
|
"Flash!, a-ah. Savior of the Universe. Flash!, a-ah.\n"
|
||
|
};
|
||
|
EXPORT_SYMBOL(sst_answers);
|
||
|
|
||
|
sst_info_t* get_sst_info(void) {
|
||
|
return cur_sst_info;
|
||
|
}
|
||
|
EXPORT_SYMBOL(get_sst_info);
|
||
|
|
||
|
static int control_thread_work(void *data) {
|
||
|
sst_info_t *info = (sst_info_t*)data;
|
||
|
int err, i;
|
||
|
#if 0
|
||
|
int sleep = 0;
|
||
|
#endif
|
||
|
size_t len_answer = 0, len_question = 0;
|
||
|
char *question = NULL, *answer = NULL, *my_answer = NULL;
|
||
|
|
||
|
sst_debug("Using data at 0x%lx\n", (uintptr_t)info);
|
||
|
while(1) {
|
||
|
wait_event_interruptible(info->wait, !is_empty(&info->questions));
|
||
|
if (kthread_should_stop()) {
|
||
|
break;
|
||
|
}
|
||
|
err = sst_consume_question(info, &question);
|
||
|
if (err) {
|
||
|
pr_err("Questions was empty!\n");
|
||
|
continue;
|
||
|
} else if (question == NULL) {
|
||
|
pr_err("Question is NULL although ret value is 0.\n");
|
||
|
continue;
|
||
|
} else {
|
||
|
printk("Received msg at 0x%lx\n", (uintptr_t)question);
|
||
|
sst_debug("Received msg '%s' at 0x%lx\n", question, (uintptr_t)question);
|
||
|
}
|
||
|
len_question = strlen(question);
|
||
|
if (len_question > 2 && (question[len_question - 1] == '?' ||
|
||
|
(question[len_question - 1] == '\n' && question[len_question - 2] == '?'))) {
|
||
|
my_answer = sst_answers[0];
|
||
|
for (i = 0; i < SST_MAX_QUESTIONS; i++) {
|
||
|
if (strcmp(question, sst_questions[i]) == 0) {
|
||
|
my_answer = sst_answers[i];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
my_answer = an_answer;
|
||
|
}
|
||
|
len_answer = strlen(my_answer) + 1;
|
||
|
answer = kmalloc(len_answer, GFP_KERNEL);
|
||
|
if (!answer) {
|
||
|
pr_err("Cannot allocate memory for answer!\n");
|
||
|
kfree(question);
|
||
|
continue;
|
||
|
}
|
||
|
snprintf(answer, len_answer, "%s", my_answer);
|
||
|
if (sst_produce_answer(info, answer)) {
|
||
|
pr_err("Answers is full!\n");
|
||
|
kfree(answer);
|
||
|
} else {
|
||
|
sst_debug("The universe has an answer at 0x%lx for you!\n", (uintptr_t)answer);
|
||
|
}
|
||
|
#if 0
|
||
|
sleep = get_random_u8() % 90;
|
||
|
sst_debug("Randomly sleeping for %d secs. ZzzzZZzz\n", sleep);
|
||
|
ssleep(sleep);
|
||
|
#endif
|
||
|
kfree(question);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int sst_init(void) {
|
||
|
cur_sst_info = kmalloc(sizeof(*cur_sst_info), GFP_KERNEL);
|
||
|
if (!cur_sst_info) {
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
spin_lock_init(&cur_sst_info->lock_questions);
|
||
|
spin_lock_init(&cur_sst_info->lock_answers);
|
||
|
sema_init(&cur_sst_info->answers_rdy, 0);
|
||
|
init_waitqueue_head(&cur_sst_info->wait);
|
||
|
init_bbuffer(&cur_sst_info->answers);
|
||
|
init_bbuffer(&cur_sst_info->questions);
|
||
|
sst_debug("Allocated private data for the universe at [0x%lx,0x%lx]\n", (uintptr_t)cur_sst_info, (uintptr_t)((uintptr_t)cur_sst_info + sizeof(*cur_sst_info)));
|
||
|
cur_sst_info->thread = kthread_create(control_thread_work, get_sst_info(), "sst-worker-%d", 1);
|
||
|
if (IS_ERR(cur_sst_info->thread)) {
|
||
|
kfree(cur_sst_info);
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
wake_up_process(cur_sst_info->thread);
|
||
|
sst_debug("Created and started control thread (%d)\n", cur_sst_info->thread->pid);
|
||
|
return 0;
|
||
|
}
|
||
|
EXPORT_SYMBOL(sst_init);
|
||
|
|
||
|
void sst_destroy(void) {
|
||
|
sst_debug("Waiting for control thread (%d) to terminate...\n", cur_sst_info->thread->pid);
|
||
|
kthread_stop(cur_sst_info->thread);
|
||
|
sst_debug("Control thread (%d) terminated.\n", cur_sst_info->thread->pid);
|
||
|
kfree(cur_sst_info);
|
||
|
sst_debug("Freed private data for the universe at 0x%lx\n", (uintptr_t)cur_sst_info);
|
||
|
};
|
||
|
EXPORT_SYMBOL(sst_destroy);
|
||
|
|
||
|
int sst_produce_question(sst_info_t *info, char *value) {
|
||
|
int err = 0;
|
||
|
|
||
|
if (!info) {
|
||
|
return -1;
|
||
|
}
|
||
|
sst_debug("%s:info=%lx, questions=%lx\n", __func__, (uintptr_t)cur_sst_info, (uintptr_t)&info->questions);
|
||
|
spin_lock_bh(&info->lock_questions);
|
||
|
err = produce(&info->questions, value);
|
||
|
spin_unlock_bh(&info->lock_questions);
|
||
|
if (!err) {
|
||
|
wake_up_interruptible(&info->wait);
|
||
|
}
|
||
|
return err;
|
||
|
}
|
||
|
EXPORT_SYMBOL(sst_produce_question);
|
||
|
|
||
|
int sst_produce_answer(sst_info_t *info, char *value) {
|
||
|
int err = 0;
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (!info) {
|
||
|
return -1;
|
||
|
}
|
||
|
sst_debug("%s:info=%lx, answers=%lx\n", __func__, (uintptr_t)cur_sst_info, (uintptr_t)&info->answers);
|
||
|
spin_lock_irqsave(&info->lock_answers, flags);
|
||
|
err = produce(&info->answers, value);
|
||
|
spin_unlock_irqrestore(&info->lock_answers, flags);
|
||
|
up(&info->answers_rdy);
|
||
|
return err;
|
||
|
}
|
||
|
EXPORT_SYMBOL(sst_produce_answer);
|
||
|
|
||
|
int sst_consume_question(sst_info_t *info, char **value) {
|
||
|
int err = 0;
|
||
|
|
||
|
if (!info) {
|
||
|
return -1;
|
||
|
}
|
||
|
sst_debug("%s:info=%lx, questions=%lx\n", __func__, (uintptr_t)cur_sst_info, (uintptr_t)&info->questions);
|
||
|
spin_lock_bh(&info->lock_questions);
|
||
|
err = consume(&info->questions, value);
|
||
|
spin_unlock_bh(&info->lock_questions);
|
||
|
return err;
|
||
|
}
|
||
|
EXPORT_SYMBOL(sst_consume_question);
|
||
|
|
||
|
int sst_consume_answer(sst_info_t *info, char **value) {
|
||
|
int err = 0;
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (!info) {
|
||
|
return -1;
|
||
|
}
|
||
|
sst_debug("%s:info=%lx, answers=%lx\n", __func__, (uintptr_t)cur_sst_info, (uintptr_t)&info->answers);
|
||
|
if (down_trylock(&info->answers_rdy)) {
|
||
|
sst_debug("Sry. No answer for you!\n");
|
||
|
return -2;
|
||
|
}
|
||
|
spin_lock_irqsave(&info->lock_answers, flags);
|
||
|
err = consume(&info->answers, value);
|
||
|
spin_unlock_irqrestore(&info->lock_answers, flags);
|
||
|
return err;
|
||
|
}
|
||
|
EXPORT_SYMBOL(sst_consume_answer);
|