#include "sst_internal.h" #include #include #include #include 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);