concept
When the kernel processes interrupt requests, it is required to process as many interrupts as possible in unit time, that is, the system requires the throughput of processing interrupts to be as large as possible. This requires that the interrupt handler should be as short and concise as possible, and there should be no time-consuming operations. However, most interrupt handlers are very complex and difficult to complete in a short time. In order to improve the responsiveness and concurrency of the system, it is necessary to solve the problems of short time requirements and large workload of balancing interrupt handler. SylixOS divides interrupt processing into two stages, namely, the top half and the bottom half:
- The top half is generally an emergency hardware operation, which generally includes reading the interrupt status in the register, clearing the interrupt flag, and hanging the bottom half processing program in the execution queue of the bottom half;
- The bottom half performs most of the time-consuming operations and can be interrupted by new interrupts.
If the interrupt processing program is simple enough, it does not need to be divided into top half and bottom half, but can be completed directly in the top half. The mechanism of the bottom half of SylixOS implementation is interrupt delay work queue (InterDefer).
Interface
Interface | explain |
---|---|
PLW_JOB_QUEUE API_InterDeferGet(ULONG ulCPUId); | Obtain the interrupt delay queue of the corresponding CPU |
ULONG API_InterDeferJobAdd(PLW_JOB_QUEUE pjobq, VOIDFUNCPTR pfunc, PVOID pvArg); | Add a task to the interrupt delay processing queue |
ULONG API_InterDeferJobDelete(PLW_JOB_QUEUE pjobq, BOOL bMatchArg, VOIDFUNCPTR pfunc, PVOID pvArg); | Delete task from interrupt delay processing queue |
realization
The interrupt delay work queue is implemented based on the kernel work queue, and an execution thread with priority 0 (the highest priority) is created. For multi-core processors, you can choose that each core has its own independent work queue, or you can use the single work queue mode like a single core.
/********************************************************************************************************* ** ** China software open source organization ** ** Embedded real-time operating system ** ** SylixOS(TM) LW : long wing ** ** Copyright All Rights Reserved ** **--------------File information-------------------------------------------------------------------------------- ** ** File name: interdefer c ** ** Created by: Han Hui (Han Hui) ** ** Document creation date: May 9, 2016 ** ** Description: interrupt delay queue processing *********************************************************************************************************/ #define __SYLIXOS_KERNEL #include "../SylixOS/kernel/include/k_kernel.h" /********************************************************************************************************* Crop configuration *********************************************************************************************************/ #if LW_CFG_ISR_DEFER_EN > 0 /********************************************************************************************************* Der ISR queue per CPU *********************************************************************************************************/ #if (LW_CFG_SMP_EN > 0) && (LW_CFG_ISR_DEFER_PER_CPU > 0) static LW_JOB_QUEUE _K_jobqIsrDefer[LW_CFG_MAX_PROCESSORS]; static LW_JOB_MSG _K_jobmsgIsrDefer[LW_CFG_MAX_PROCESSORS][LW_CFG_ISR_DEFER_SIZE]; #else static LW_JOB_QUEUE _K_jobqIsrDefer[1]; static LW_JOB_MSG _K_jobmsgIsrDefer[LW_CFG_ISR_DEFER_SIZE]; #endif /* LW_CFG_SMP_EN > 0 */ /********************************************************************************************************* ** Function name:__ interDeferTask ** Function Description: get interrupt stack usage ** Input: ulcpuid CPU number ** pstFreeByteSize Free stack size (can be LW_NULL) ** pstUsedByteSize Use stack size (can be LW_NULL) ** Output: ** Global variables: ** Calling module: *********************************************************************************************************/ static PVOID _interDeferTask (PVOID pvArg) { PLW_JOB_QUEUE pjobq = (PLW_JOB_QUEUE)pvArg; for (;;) { _jobQueueExec(pjobq, LW_OPTION_WAIT_INFINITE); } return (LW_NULL); } /********************************************************************************************************* ** Function name:_ interDeferInit ** Function Description: initialize interrupt delay processing ** Input: NONE ** Output: NONE ** Global variables: ** Calling module: *********************************************************************************************************/ VOID _interDeferInit (VOID) { CHAR cDefer[LW_CFG_OBJECT_NAME_SIZE] = "t_isrdefer"; LW_CLASS_THREADATTR threadattr; LW_OBJECT_HANDLE ulId; #if (LW_CFG_SMP_EN > 0) && (LW_CFG_ISR_DEFER_PER_CPU > 0) INT i; LW_CLASS_CPUSET cpuset; LW_CPU_ZERO(&cpuset); API_ThreadAttrBuild(&threadattr, LW_CFG_THREAD_DEFER_STK_SIZE, LW_CFG_ISR_DEFER_PRIO, (LW_OPTION_THREAD_STK_CHK | LW_OPTION_THREAD_SAFE | LW_OPTION_OBJECT_GLOBAL | LW_OPTION_THREAD_DETACHED | LW_OPTION_THREAD_AFFINITY_ALWAYS), LW_NULL); LW_CPU_FOREACH (i) { if (_jobQueueInit(&_K_jobqIsrDefer[i], &_K_jobmsgIsrDefer[i][0], LW_CFG_ISR_DEFER_SIZE, LW_FALSE)) { _DebugHandle(__ERRORMESSAGE_LEVEL, "can not create ISR defer queue.\r\n"); return; } lib_itoa(i, &cDefer[10], 10); API_ThreadAttrSetArg(&threadattr, &_K_jobqIsrDefer[i]); ulId = API_ThreadInit(cDefer, _interDeferTask, &threadattr, LW_NULL); if (ulId == LW_OBJECT_HANDLE_INVALID) { _DebugHandle(__ERRORMESSAGE_LEVEL, "can not create ISR defer task.\r\n"); return; } LW_CPU_SET(i, &cpuset); API_ThreadSetAffinity(ulId, sizeof(LW_CLASS_CPUSET), &cpuset); /* Lock to specified CPU */ LW_CPU_CLR(i, &cpuset); API_ThreadStart(ulId); } #else if (_jobQueueInit(&_K_jobqIsrDefer[0], &_K_jobmsgIsrDefer[0], LW_CFG_ISR_DEFER_SIZE, LW_FALSE)) { _DebugHandle(__ERRORMESSAGE_LEVEL, "can not create ISR defer queue.\r\n"); return; } API_ThreadAttrBuild(&threadattr, LW_CFG_THREAD_DEFER_STK_SIZE, LW_CFG_ISR_DEFER_PRIO, (LW_OPTION_THREAD_STK_CHK | LW_OPTION_THREAD_SAFE | LW_OPTION_OBJECT_GLOBAL | LW_OPTION_THREAD_DETACHED | LW_OPTION_THREAD_AFFINITY_ALWAYS), &_K_jobqIsrDefer[0]); ulId = API_ThreadInit(cDefer, _interDeferTask, &threadattr, LW_NULL); if (ulId == LW_OBJECT_HANDLE_INVALID) { _DebugHandle(__ERRORMESSAGE_LEVEL, "can not create ISR defer task.\r\n"); return; } API_ThreadStart(ulId); #endif /* LW_CFG_SMP_EN > 0 */ } /* LW_CFG_ISR_DEFER_PER_CPU */ /********************************************************************************************************* ** Function name: API_InterDeferGet ** Function Description: obtain the interrupt delay queue of the corresponding CPU ** Input: ulcpuid CPU number ** Output: interrupt delay queue ** Global variables: ** Calling module: API function *********************************************************************************************************/ LW_API PLW_JOB_QUEUE API_InterDeferGet (ULONG ulCPUId) { if (ulCPUId >= LW_NCPUS) { _ErrorHandle(ERANGE); return (LW_NULL); } #if (LW_CFG_SMP_EN > 0) && (LW_CFG_ISR_DEFER_PER_CPU > 0) return (&_K_jobqIsrDefer[ulCPUId]); #else return (&_K_jobqIsrDefer[0]); #endif /* LW_CFG_SMP_EN > 0 */ } /* LW_CFG_ISR_DEFER_PER_CPU */ /********************************************************************************************************* ** Function name: API_InterDeferJobAdd ** Function Description: add a task to the interrupt delay processing queue ** Input: pjobq queue ** pfunc Processing function ** pvArg Processing parameters ** Output: ERROR CODE ** Global variables: ** Calling module: API function *********************************************************************************************************/ LW_API ULONG API_InterDeferJobAdd (PLW_JOB_QUEUE pjobq, VOIDFUNCPTR pfunc, PVOID pvArg) { if (!pjobq) { _ErrorHandle(EINVAL); return (EINVAL); } return (_jobQueueAdd(pjobq, pfunc, pvArg, LW_NULL, LW_NULL, LW_NULL, LW_NULL, LW_NULL)); } /********************************************************************************************************* ** Function name: API_InterDeferJobDelete ** Function Description: delete task from interrupt delay processing queue ** Input: pjobq queue ** bMatchArg Judge whether to match parameters ** pfunc Processing function ** pvArg Processing parameters ** Output: NONE ** Global variables: ** Calling module: API function *********************************************************************************************************/ LW_API ULONG API_InterDeferJobDelete (PLW_JOB_QUEUE pjobq, BOOL bMatchArg, VOIDFUNCPTR pfunc, PVOID pvArg) { if (!pjobq) { _ErrorHandle(EINVAL); return (EINVAL); } _jobQueueDel(pjobq, (bMatchArg) ? 1 : 0, pfunc, pvArg, LW_NULL, LW_NULL, LW_NULL, LW_NULL, LW_NULL); return (ERROR_NONE); } #endif /* LW_CFG_ISR_DEFER_EN > 0 */ /********************************************************************************************************* END *********************************************************************************************************/
usage
The following code shows the use of the bottom half of the SylixOS interrupt in driver development. When the kernel module is loaded, it creates a SylixOS work queue without delayed execution function and a thread. The thread is used to set the interrupt processing function of a GPIO and enable the interrupt. When the corresponding key interrupt is generated, the interrupt processing function will clear the interrupt, Insert the time-consuming operation into the work queue. When uninstalling the kernel module, clear the relevant settings of the GPIO and delete the work queue.
#define __SYLIXOS_STDIO #define __SYLIXOS_KERNEL #include <SylixOS.h> #include <module.h> #define KEY_NUM 36 PVOID _G_pvWorkQueue; static INT _G_iIrqNum; static VOID __workHandler(VOID) { printk("work handler function start.\n"); API_TimeSSleep(5); printk("work handler function stop.\n"); } static irqreturn_t GpioIsr (INT iGpioNum, ULONG ulVector) { API_GpioClearIrq(iGpioNum); API_WorkQueueInsert(_G_pvWorkQueue, 0, __workHandler, LW_NULL, LW_NULL, LW_NULL, LW_NULL, LW_NULL, LW_NULL); return (ERROR_NONE); } static PVOID __keyThread (PVOID pvArg) { INT iError; iError = API_GpioRequestOne(KEY_NUM, LW_GPIOF_IN, "KEY"); if (iError != ERROR_NONE) { printk("failed to request gpio %d!\n", KEY_NUM); return (NULL); } _G_iIrqNum = API_GpioSetupIrq(KEY_NUM, LW_FALSE, 0); if (_G_iIrqNum == PX_ERROR) { printk("failed to setup gpio %d irq!\n", KEY_NUM); return (NULL); } iError = API_InterVectorConnect((ULONG)_G_iIrqNum, (PINT_SVR_ROUTINE)GpioIsr, (PVOID)KEY_NUM, "GpioIsr"); if (iError != ERROR_NONE) { printk("failed to connect GpioIsr!\n"); return (NULL); } API_InterVectorEnable(_G_iIrqNum); return (NULL); } void module_init (void) { LW_CLASS_THREADATTR threadattr; printk("interrupt_module init!\n"); API_ThreadAttrBuild(&threadattr, 4 * LW_CFG_KB_SIZE, LW_PRIO_NORMAL, LW_OPTION_THREAD_STK_CHK, LW_NULL); _G_pvWorkQueue = API_WorkQueueCreate("t_workqueue", 10, FALSE, 0, &threadattr); if (_G_pvWorkQueue == LW_NULL) { printk("WorkQueue create failed.\n"); return; } API_ThreadCreate("t_key", (PTHREAD_START_ROUTINE)__keyThread, LW_NULL, LW_NULL); } void module_exit (void) { API_InterVectorDisconnect((ULONG)_G_iIrqNum, (PINT_SVR_ROUTINE)GpioIsr, (PVOID)KEY_NUM); API_GpioFree(KEY_NUM); API_WorkQueueDelete(_G_pvWorkQueue); printk("interrupt_module exit!\n"); }
Load the module under the SylixOS Shell, and then press the specified key to trigger the interrupt:
#insmod ./interrupt.ko interrupt_module init! module interrupt.ko register ok, handle: 0x13338f0 work handler function start. work handler function stop.
Uninstall the module under the SylixOS Shell:
#rmmod interrupt.ko interrupt_module exit! module /lib/modules/interrupt.ko unregister ok.