| // 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); |
| } |