// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note /* * * (C) COPYRIGHT 2014-2015, 2018-2023 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. * */ #include #include #include "backend/gpu/mali_kbase_model_linux.h" static struct kbase_error_atom *error_track_list; #ifdef CONFIG_MALI_ERROR_INJECT_RANDOM /** Kernel 6.1.0 has dropped prandom_u32(), use get_random_u32() */ #if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) #define prandom_u32 get_random_u32 #endif /*following error probability are set quite high in order to stress the driver*/ static unsigned int error_probability = 50; /* to be set between 0 and 100 */ /* probability to have multiple error give that there is an error */ static unsigned int multiple_error_probability = 50; /* all the error conditions supported by the model */ #define TOTAL_FAULTS 27 /* maximum number of levels in the MMU translation table tree */ #define MAX_MMU_TABLE_LEVEL 4 /* worst case scenario is <1 MMU fault + 1 job fault + 2 GPU faults> */ #define MAX_CONCURRENT_FAULTS 3 /** * gpu_generate_error - Generate GPU error */ static void gpu_generate_error(void) { unsigned int errors_num = 0; /*is there at least one error? */ if ((prandom_u32() % 100) < error_probability) { /* pick up a faulty mmu address space */ hw_error_status.faulty_mmu_as = prandom_u32() % NUM_MMU_AS; /* pick up an mmu table level */ hw_error_status.mmu_table_level = 1 + (prandom_u32() % MAX_MMU_TABLE_LEVEL); hw_error_status.errors_mask = (u32)(1 << (prandom_u32() % TOTAL_FAULTS)); /*is there also one or more errors? */ if ((prandom_u32() % 100) < multiple_error_probability) { errors_num = 1 + (prandom_u32() % (MAX_CONCURRENT_FAULTS - 1)); while (errors_num-- > 0) { u32 temp_mask; temp_mask = (u32)(1 << (prandom_u32() % TOTAL_FAULTS)); /* below we check that no bit of the same error * type is set again in the error mask */ if ((temp_mask & IS_A_JOB_ERROR) && (hw_error_status.errors_mask & IS_A_JOB_ERROR)) { errors_num++; continue; } if ((temp_mask & IS_A_MMU_ERROR) && (hw_error_status.errors_mask & IS_A_MMU_ERROR)) { errors_num++; continue; } if ((temp_mask & IS_A_GPU_ERROR) && (hw_error_status.errors_mask & IS_A_GPU_ERROR)) { errors_num++; continue; } /* this error mask is already set */ if ((hw_error_status.errors_mask | temp_mask) == hw_error_status.errors_mask) { errors_num++; continue; } hw_error_status.errors_mask |= temp_mask; } } } } #endif int job_atom_inject_error(struct kbase_error_params *params) { struct kbase_error_atom *new_elem; KBASE_DEBUG_ASSERT(params); new_elem = kzalloc(sizeof(*new_elem), GFP_KERNEL); if (!new_elem) { model_error_log(KBASE_CORE, "\njob_atom_inject_error: kzalloc failed for new_elem\n"); return -ENOMEM; } new_elem->params.jc = params->jc; new_elem->params.errors_mask = params->errors_mask; new_elem->params.mmu_table_level = params->mmu_table_level; new_elem->params.faulty_mmu_as = params->faulty_mmu_as; /*circular list below */ if (error_track_list == NULL) { /*no elements */ error_track_list = new_elem; new_elem->next = error_track_list; } else { struct kbase_error_atom *walker = error_track_list; while (walker->next != error_track_list) walker = walker->next; new_elem->next = error_track_list; walker->next = new_elem; } return 0; } void midgard_set_error(u32 job_slot) { #ifdef CONFIG_MALI_ERROR_INJECT_RANDOM gpu_generate_error(); #else struct kbase_error_atom *walker, *auxiliar; if (error_track_list != NULL) { walker = error_track_list->next; auxiliar = error_track_list; do { if (walker->params.jc == hw_error_status.current_jc) { /* found a faulty atom matching with the * current one */ hw_error_status.errors_mask = walker->params.errors_mask; hw_error_status.mmu_table_level = walker->params.mmu_table_level; hw_error_status.faulty_mmu_as = walker->params.faulty_mmu_as; hw_error_status.current_job_slot = job_slot; if (walker->next == walker) { /* only one element */ kfree(error_track_list); error_track_list = NULL; } else { auxiliar->next = walker->next; if (walker == error_track_list) error_track_list = walker->next; kfree(walker); } break; } auxiliar = walker; walker = walker->next; } while (auxiliar->next != error_track_list); } #endif /* CONFIG_MALI_ERROR_INJECT_RANDOM */ }