blob: 3a7ade2831b3d86f1b2aacb70d57d88dea96b5d0 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/*
*
* (C) COPYRIGHT 2014, 2017, 2020-2021 ARM Limited. All rights reserved.
*
* This program is free software and is provided to you under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation, and any use by you of this program is subject to the terms
* of such GNU license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you can access it online at
* http://www.gnu.org/licenses/gpl-2.0.html.
*
*/
/* Kernel UTF result management functions */
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/printk.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/err.h>
#include <kutf/kutf_suite.h>
#include <kutf/kutf_resultset.h>
/* Lock to protect all result structures */
static DEFINE_SPINLOCK(kutf_result_lock);
struct kutf_result_set *kutf_create_result_set(void)
{
struct kutf_result_set *set;
set = kmalloc(sizeof(*set), GFP_KERNEL);
if (!set) {
pr_err("Failed to allocate resultset");
goto fail_alloc;
}
INIT_LIST_HEAD(&set->results);
init_waitqueue_head(&set->waitq);
set->flags = 0;
return set;
fail_alloc:
return NULL;
}
int kutf_add_result(struct kutf_context *context,
enum kutf_result_status status,
const char *message)
{
struct kutf_mempool *mempool = &context->fixture_pool;
struct kutf_result_set *set = context->result_set;
/* Create the new result */
struct kutf_result *new_result;
BUG_ON(set == NULL);
new_result = kutf_mempool_alloc(mempool, sizeof(*new_result));
if (!new_result) {
pr_err("Result allocation failed\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&new_result->node);
new_result->status = status;
new_result->message = message;
spin_lock(&kutf_result_lock);
list_add_tail(&new_result->node, &set->results);
spin_unlock(&kutf_result_lock);
wake_up(&set->waitq);
return 0;
}
void kutf_destroy_result_set(struct kutf_result_set *set)
{
if (!list_empty(&set->results))
pr_err("%s: Unread results from test\n", __func__);
kfree(set);
}
static bool kutf_has_result(struct kutf_result_set *set)
{
bool has_result;
spin_lock(&kutf_result_lock);
if (set->flags & KUTF_RESULT_SET_WAITING_FOR_INPUT)
/* Pretend there are results if waiting for input */
has_result = true;
else
has_result = !list_empty(&set->results);
spin_unlock(&kutf_result_lock);
return has_result;
}
struct kutf_result *kutf_remove_result(struct kutf_result_set *set)
{
struct kutf_result *result = NULL;
int ret;
do {
ret = wait_event_interruptible(set->waitq,
kutf_has_result(set));
if (ret)
return ERR_PTR(ret);
spin_lock(&kutf_result_lock);
if (!list_empty(&set->results)) {
result = list_first_entry(&set->results,
struct kutf_result,
node);
list_del(&result->node);
} else if (set->flags & KUTF_RESULT_SET_WAITING_FOR_INPUT) {
/* Return a fake result */
static struct kutf_result waiting = {
.status = KUTF_RESULT_USERDATA_WAIT
};
result = &waiting;
}
/* If result == NULL then there was a race with the event
* being removed between the check in kutf_has_result and
* the lock being obtained. In this case we retry
*/
spin_unlock(&kutf_result_lock);
} while (result == NULL);
return result;
}
void kutf_set_waiting_for_input(struct kutf_result_set *set)
{
spin_lock(&kutf_result_lock);
set->flags |= KUTF_RESULT_SET_WAITING_FOR_INPUT;
spin_unlock(&kutf_result_lock);
wake_up(&set->waitq);
}
void kutf_clear_waiting_for_input(struct kutf_result_set *set)
{
spin_lock(&kutf_result_lock);
set->flags &= ~KUTF_RESULT_SET_WAITING_FOR_INPUT;
spin_unlock(&kutf_result_lock);
}