scheduling tasks in linux kernel module everyday at a user provided time - c

I am writing a linux kernel module which schedules a task using schedule_delayed_work at a particular time which in turn send a signal to a user space program to do some task.
What I did is manually given the time in milliseconds (say 5000ms) and changed it to jiffies using "msec to jiffies" function and tested it and worked.
My use case is that the user will give a time (say 5 pm) and the module has to schedule it to send the signal everyday at 5 pm to the user program. I am totally confused in how to calculate the milliseconds from the user given time for everyday basis.
I used workqueue to create a queue and then the task to accomplish and doing the scheduling.
My kernel module:
static void wq_handler_function(struct work_struct *work);
static unsigned long delay;
static struct workqueue_struct *my_wq; // my workqueue
static DECLARE_DELAYED_WORK(my_work, wq_handler_function); //my work/task
static void wq_handler_function(struct work_struct *work)
{
printk(KERN_DEBUG "handler function called\n");
if(my_wq)
{
/*Do some work like sending signal to user space*/
schedule_delayed_work(&my_work, delay); /*reschedule after the first scheduled time finished*/
}
}
int sig_init_module(void)
{
printk(KERN_DEBUG "signal module initiated\n");
delay = msecs_to_jiffies(5000); //Manually given 5000ms (5 sec) for scheuling
if(!my_wq)
my_wq = create_workqueue("my_queue");
if(my_wq)
{
schedule_delayed_work(&my_work, delay); /*schedule for the first time while module initiates*/
}
return 0;
}
void sig_cleanup_module(void)
{
flush_scheduled_work();
cancel_delayed_work_sync(&my_work);
flush_workqueue(my_wq);
destroy_workqueue(my_wq);
printk(KERN_DEBUG "signal module removed\n");
}
module_init(sig_init_module);
module_exit(sig_cleanup_module);
Kindly help me to have a solution for this. Thanks in advance!!!.

I don't understand why kernel modification would be desirable or necessary. If you want something periodically done (e.g. log rotation), add it to cron. Another option would be to use timerfd.

use mktime() function in kernel code which takes the wall time as arguments and directly returns the jiffies value.
For info about mktime, see this http://www.makelinux.net/ldd3/chp-7-sect-2

Related

How to resync time from NTP server in esp-idf?

I use an ESP32 with esp-idf. I need correct time, therefore i'm trying resync time with NTP server.
I use this example.[1]: https://github.com/espressif/esp-idf/tree/master/examples/protocols/sntp
When i call again the obtain_time() method the device is rebooting.
What do i wrong? I didn't find anything which help.
I (2259) initialise_wifi: Setting WiFi configuration SSID OpenWrt
I (2359) syncTime: I'm runing :)
I (2369) getTimeNow: Time is not set yet. Connecting to WiFi and getting time over NTP.
I (2389) initialize_sntp: Initializing SNTP
I (2389) obtain_time: Waiting for system time to be set... (1/10)
...
I (18389) obtain_time: Waiting for system time to be set... (9/10)
-----The time is correct, but when i'm trying resync with NTP
I (20639) getTimeNow: Time is not set yet. Connecting to WiFi and getting time over NTP.
I (20639) initialize_sntp: Initializing SNTP
assertion "Operating mode must not be set while SNTP client is running" failed: file "/home/lenovov510/esp/esp-idf/components/lwip/lwip/src/apps/sntp/sntp.c", line 600, function: sntp_s
etoperatingmode
abort() was called at PC 0x400d2c6b on core 1
ELF file SHA256: 145d1f5e047670ed10c462ae090b3e64db1c5aa158a9988417a513b2ee801051
Backtrace: 0x4008623c:0x3ffc7e00 0x40086489:0x3ffc7e20 0x400d2c6b:0x3ffc7e40 0x4011e251:0x3ffc7e70 0x400d28b4:0x3ffc7e90 0x400d28c7:0x3ffc7eb0 0x400d2aff:0x3ffc7f10 0x400d2bcd:0x3ffc7fa0
0x4008b569:0x3ffc7fc0
Rebooting...
There is my methods:
This give back the timestamp.
void getDateTime(char *dateTime)
{
char *TAG = "getDateTime";
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
char strftime_buf[64];
setenv("TZ", "GTM-2", 1);
tzset();
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
sprintf(dateTime, "20%d-%d-%d+%d:%d:%d", timeinfo.tm_year - 100, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
}
This method trying to update time.
void syncTime()
{
char *TAG = "syncTime";
obtain_time();
}
static void obtain_time(void)
{
static const char *TAG = "obtain_time";
initialize_sntp();
time_t now = 0;
struct tm timeinfo = {0};
int retry = 0;
const int retry_count = 10;
while (retry!=retry_count)// timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count )
{
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
vTaskDelay(2000 / portTICK_PERIOD_MS);
time(&now);
localtime_r(&now, &timeinfo);
}
}
//----
static void initialize_sntp(void)
{
static const char *TAG = "initialize_sntp";
ESP_LOGI(TAG, "Initializing SNTP");
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "pool.ntp.org");
sntp_init();
...
//Update the timeInSec and Datettime variable
void updateTimeVariables(void *pvParameter)
{
char *TAG = "updateTimeVariables";
while (1 == 1)
{
getDateTime(dateTime);
timeInSec = getTimeNow();
vTaskDelay(500 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
//Sync NTP server.
void updateTime(void *pvParameter)
{
char *TAG = "updateTime";
while (1 == 1)
{
syncTime();
vTaskDelay(10000 / portTICK_PERIOD_MS);//1800000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
...
xTaskCreate(&updateTime, "updateTime", 4000, NULL, 6, NULL);
xTaskCreate(&updateTimeVariables, "updateTimeVariables", 4000, NULL, 0, NULL);
Looks like you are trying to initialize sntp every time you update time.
Watch for second line of obtain_time function:
static const char *TAG = "obtain_time";
initialize_sntp(); // <<<< THIS ONE.
time_t now = 0;
struct tm timeinfo = {0};
//.....
You have to change you code in a way initialize_sntp would be called only once.
To solve this problem, you need to do a couple of things.
1.) Modify the sntp.c file in users/[username]/.platformio/packages/framework-espidf/components/lwip/lwip/src/apps/sntp/sntp.c and the users/[username]/.platformio/packages/framework-espidf/components/lwip/lwip/include/lwip/apps/sntp.h files and change the following:
a.) in the sntp.c file - change the static void sntp_request(void *arg) to "
void sntp_request(void *arg) to make the function available to other modules. It's somewhere around line 490 in the source file. Also, at line number 160, remove the word "static" to prevent compiler errors.
b.) In the sntp.h header file, add the statement void sntp_request(void *) to make the function prototype available to your code.
Below is my code with modifications to allow calling sntp_request() as needed. I call mine every 30 minutes or so, but you could wait a longer time, maybe one a day, would be sufficient to keep the clock reasonably stable.
bool sntp_1st_init = true; // 1st init call allowed
static void obtain_time(void)
{
if(sntp_1st_init) // doing this again?
{
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "north-america.pool.ntp.org");
ESP_LOGI(TAG, "Initializing SNTP");
sntp_1st_init = false; // don't call again
sntp_init(); // init and set time
}
else
{
ESP_LOGI(TAG, "Syncing System Time");
sntp_request(NULL); // sync time again
}
// wait for System time to be set by monitoring Date changes
int retry = 0;
const int retry_count = 15;
while(timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count)
{
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)",
retry, retry_count);
vTaskDelay((1 * ONEsec) / portTICK_PERIOD_MS);
time(&now);
localtime_r(&now, &timeinfo);
}
}
The bool "sntp_1st_init" is set true on program startup and set false after 1st sntp init takes place. The call to "sntp_setoperatingmode(SNTP_OPMODE_POLL)" can only be performed one time, so must be placed in the sntp_1st_init code section.
I have confirmed that these changes work by altering the system time to something other than the correct time and observing that the time gets corrected as expected.
The original authors limited the functionality of the sntp code by making the sntp_request() function static, preventing the user from making additional sntp corrections to the computer clock time. Even the best oscillator drifts over time, and if your going to bother to use sntp, you might as well allow for clock drift corrections as well.
Hope this helps.
Jerry
JWM Engineering Group
Luckily the sntp_stop() function doesn't remove previous settings (including servers) so you can use this:
sntp_stop();
sntp_init();
On the first run the sync takes ~30s and subsequent runs ~500ms.
I place this in a FreeRTOS task:
#include "esp_sntp.h"
#include "freertos/task.h"
void update(void* pvParameters) {
while (true) {
sntp_stop();
sntp_init();
vTaskDelay(pdMS_TO_TICKS(60 * 60 * 1000));
}
}
void setup(void) {
// Add your SNTP setup code here
xTaskCreate(update, "NtpUpdate", 2048, NULL, tskIDLE_PRIORITY,
&updateHandle);
}
Through issue 4386 the SNTP documentation has been updated with the following:
An application with this initialization code will periodically
synchronize the time. The time synchronization period is determined by
CONFIG_LWIP_SNTP_UPDATE_DELAY (default value is one hour). To modify
the variable, set CONFIG_LWIP_SNTP_UPDATE_DELAY in project
configuration.
All you need is to use the below code in your application:
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "pool.ntp.org");
sntp_init();

Linux Kernel - How to match a jprobe to kretprobe?

I am writing a kernel module to monitor a few syscalls wanting to return the function arguments to user-land (via netlink socket) if the call was successful.
jprobe.kp.symbol_name = "rename";
jprobe.entry = rename_handler;
kretprobe.kp.symbol_name = "rename";
kretprobe.handler = rename_ret_handler;
static rename_obj_t _g_cur_rename = NULL;
static void _rename_handler(const char *oldpath, const char *newpath)
{
_g_cur_rename = create_rename(oldpath, newpath);
jprobe_return();
}
static void _rename_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
/* Send only if successful */
if (regs_return_value(regs) == 0) {
add_send_queue(_g_cur_rename);
}
return 0;
}
I worry that another rename syscall may preempt[1] the current one after the jprobe and I will send incorrect return codes and arguments.
jprobe: rename(a, b)
jprobe rename(c, d)
kretprobe
kretprobe
Edit: This article[2] states that interrupts are disabled during a kprobe handler. But does that mean that interrupts are disable throughout the whole chain (jprobe -> kprobe -> kretprobe) or just for that single kprobe?
https://unix.stackexchange.com/questions/186355/few-questions-about-system-calls-and-kernel-modules-kernel-services-in-parallel
https://lwn.net/Articles/132196/
Interrupts are disabled for each jprobe call: not for the entire sequence.
How many calls are you expecting in the time it will take the application to process them? There are different approaches depending on how fast you expect the calls to come in. The simplest method, if you are only expecting maybe a few hundred calls before you can process them and you will dedicate the static memory to the purpose, is to implement a static array of rename_obj_t objects in memory and then use atomic_add from the kernel asm includes to point to the next entry (mod the size of your array).
This way you are returning a unique static reference each time, so long as the counter doesn't wrap around before you process the returned values. atomic_add is guaranteed to have the correct memory barriers in place so you don't have to worry about things like cache coherency.

Alternative to blocking code

Attempting to use mbed OS scheduler for a small project.
As mbed os is Asynchronous I need to avoid blocking code.
However the library for my wireless receiver uses a blocking line of:
while (!(wireless.isRxData()));
Is there an alternative way to do this that won't block all the code until a message is received?
static void listen(void) {
wireless.quickRxSetup(channel, addr1);
sprintf(ackData,"Ack data \r\n");
wireless.acknowledgeData(ackData, strlen(ackData), 1);
while (!(wireless.isRxData()));
len = wireless.getRxData(msg);
}
static void motor(void) {
pc.printf("Motor\n");
m.speed(1);
n.speed(1);
led1 = 1;
wait(0.5);
m.speed(0);
n.speed(0);
}
static void sendData() {
wireless.quickTxSetup(channel, addr1);
strcpy(accelData, "Robot");
wireless.transmitData(accelData ,strlen(accelData));
}
void app_start(int, char**) {
minar::Scheduler::postCallback(listen).period(minar::milliseconds(500)).tolerance(minar::milliseconds(1000));
minar::Scheduler::postCallback(motor).period(minar::milliseconds(500));
minar::Scheduler::postCallback(sendData).period(minar::milliseconds(500)).delay(minar::milliseconds(3000));
}
You should remove the while (!(wireless.isRxData())); loop in your listen function. Replace it with:
if (wireless.isRxData()) {
len = wireless.getRxData(msg);
// Process data
}
Then, you can process your data in that if statement, or you can call postCallback on another function that will do your processing.
Instead of looping until data is available, you'll want to poll for data. If RX data is not available, exit the function and set a timer to go off after a short interval. When the timer goes off, check for data again. Repeat until data is available. I'm not familiar with your OS so I can't offer any specific code. This may be as simple as adding a short "sleep" call inside the while loop, or may involve creating another callback from the scheduler.

Run userspace process from tasklet

Hello I am trying to run userspace process using call_usermodehelper_exec from tasklet that is created in kernel module for ARM board (from Olimex) with Allwinner SOC.
my code is prety simple:
#include <linux/kernel.h>
#include <linux/module.h>
char my_tasklet_data[]="tasklet executed";
static int umh_test(void)
{
struct subprocess_info *sub_info;
char *argv[] = { "/usr/bin/logger", "help!", NULL };
static char *envp[] = {
"HOME=/",
"TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };
sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC);
if (sub_info == NULL) return -ENOMEM;
return call_usermodehelper_exec(sub_info, UMH_WAIT_PROC);
}
void my_tasklet_function(unsigned long data)
{
printk(KERN_INFO "%s\n", (char *)data);
umh_test();
return;
}
DECLARE_TASKLET(my_tasklet, my_tasklet_function,
(unsigned long) &my_tasklet_data);
static int __init my_tasklet_init(void)
{
tasklet_schedule(&my_tasklet);
return 0;
}
module_init(my_tasklet_init);
static void __exit my_tasklet_cleanup(void)
{
tasklet_kill(&my_tasklet);
}
module_exit(my_tasklet_cleanup);
after insmod tasklet.ko tasklet is executed and more over /usr/bin/logger is called, but I got also nasty kernel error like this:
[ 969.327698] tasklet executed
<3>BUG: scheduling while atomic: ksoftirqd/0/3/0x00000102
[ 969.335769] BUG: scheduling while atomic: ksoftirqd/0/3/0x00000102
<d>Modules linked in:[ 969.343861] Modules linked in:root#a20-olimex:~#tasklet(O) tasklet(O) disp_ump disp_ump mali_drm mali_drm cpufreq_powersave cpufreq_powersave cpufreq_stats cpufreq_stats drm drm cpufreq_userspace cpufreq_userspace cpufreq_conservative cpufreq_conservative mali mali g_ether g_ether pwm_sunxi pwm_sunxi sun4i_csi0 sun4i_csi0 videobuf_dma_contig videobuf_dma_contig videobuf_core videobuf_core gt2005 gt2005 sun4i_keyboard sun4i_keyboard ledtrig_heartbeat ledtrig_heartbeat leds_sunxi leds_sunxi led_class led_class sunxi_emac sunxi_emac sunxi_gmac sunxi_gmac sunxi_cedar_mod sunxi_cedar_mod 8192cu 8192cu ump ump lcd lcd [last unloaded: tasklet] [last unloaded: tasklet]
[<c0015058>] (unwind_backtrace+0x0/0x134) from [<c058455c>] (__schedule+0x744/0x7d0)
[ 969.410844] [<c0015058>] (unwind_backtrace+0x0/0x134) from [<c058455c>] (__schedule+0x744/0x7d0)
[<c058455c>] (__schedule+0x744/0x7d0) from [<c0582a20>] (schedule_timeout+0x1b8/0x220)
[ 969.427299] [<c058455c>] (__schedule+0x744/0x7d0) from [<c0582a20>] (schedule_timeout+0x1b8/0x220)
[<c0582a20>] (schedule_timeout+0x1b8/0x220) from [<c0583c04>] (wait_for_common+0xe4/0x138)
[ 969.444267] [<c0582a20>] (schedule_timeout+0x1b8/0x220) from [<c0583c04>] (wait_for_common+0xe4/0x138)
[<c0583c04>] (wait_for_common+0xe4/0x138) from [<c0049dc0>] (call_usermodehelper_exec+0x140/0x154)
[ 969.462267] [<c0583c04>] (wait_for_common+0xe4/0x138) from [<c0049dc0>] (call_usermodehelper_exec+0x140/0x154)
[<c0049dc0>] (call_usermodehelper_exec+0x140/0x154) from [<bf1c6050>] (my_tasklet_function+0x50/0x58 [tasklet])
[ 969.482096] [<c0049dc0>] (call_usermodehelper_exec+0x140/0x154) from [<bf1c6050>] (my_tasklet_function+0x50/0x58 [tasklet])
[<bf1c6050>] (my_tasklet_function+0x50/0x58 [tasklet]) from [<c003c800>] (tasklet_action+0x98/0x134)
[ 969.502110] [<bf1c6050>] (my_tasklet_function+0x50/0x58 [tasklet]) from [<c003c800>] (tasklet_action+0x98/0x134)
[<c003c800>] (tasklet_action+0x98/0x134) from [<c003c9d4>] (__do_softirq+0xd4/0x168)
[ 969.519768] [<c003c800>] (tasklet_action+0x98/0x134) from [<c003c9d4>] (__do_softirq+0xd4/0x168)
[<c003c9d4>] (__do_softirq+0xd4/0x168) from [<c003cb58>] (run_ksoftirqd+0xf0/0x1bc)
[ 969.535943] [<c003c9d4>] (__do_softirq+0xd4/0x168) from [<c003cb58>] (run_ksoftirqd+0xf0/0x1bc)
[<c003cb58>] (run_ksoftirqd+0xf0/0x1bc) from [<c00513a4>] (kthread+0x90/0x94)
[ 969.551513] [<c003cb58>] (run_ksoftirqd+0xf0/0x1bc) from [<c00513a4>] (kthread+0x90/0x94)
[<c00513a4>] (kthread+0x90/0x94) from [<c000f4b8>] (kernel_thread_exit+0x0/0x8)
[ 969.566739] [<c00513a4>] (kthread+0x90/0x94) from [<c000f4b8>] (kernel_thread_exit+0x0/0x8)
I try find similar problems because I am learning driver-development but all I have found was problem related to, lets call it kernel broken builds.
I use own build of kernel, and I don't exclude that can be kernel related, but if someone is new with some topic he make sure that code has not any reprehensible error.
So any help will be appreciated
The comment on call_usermodehelper_exec says in part:
* #wait: wait for the application to finish and return status.
* when UMH_NO_WAIT don't wait at all, but you get no useful error back
* when the program couldn't be exec'ed. This makes it safe to call
* from interrupt context.
Calling from a tasklet is, in effect, an interrupt context so you cannot wait there. That is, you cannot block, because there is no task context with which to block. (This is what "scheduling while atomic" is telling you.)
You'll either need to work with a process context part, or you'll need to use UMH_NO_WAIT instead of UMH_WAIT_PROC. For example, you could have
made the usermode helper call with UMH_WAIT_PROC from my_tasklet_init because there you have a task context.
Tasklet is executed in soft-irq context, so cannot sleep (call schedule function).
Usermode helper, from the other side, can sleep(like any other userspace code).
That's why you get BUG: scheduling while atomic message.

Problem with Array of Queues in FreeRTOS

I am building a FreeRTOS application. I created a module which registers a freeRTOS queue handle from another module and when an interrupt in this module module occurs, it sends a message to all the registered queues. But it seems I am able to send the message from the queue but not able to receive it at the other module.
Here is my code.
remote module:-
CanRxMsg RxMessage;
can_rx0_queue = xQueueCreate( 10, sizeof(CanRxMsg) ); // can_rx0_queue is globally defined
// Register my queue with can module
if (registerRxQueueWithCAN(can_rx0_queue) == -1)
{
TurnLedRed();
}
while(1)
{
if(can_rx0_queue){
while( xQueueReceive( can_rx0_queue, ( void * ) &RxMessage, portMAX_DELAY))
{
}
.....
Here is the registration module
#define MAX_NUMBER_OF_RX_QUEUES 2
//xQueueHandle rxQueueStore[MAX_NUMBER_OF_RX_QUEUES];
typedef struct QUEUE_REGISTRY_ITEM
{
// signed char *pcQueueName;
xQueueHandle xHandle;
} xQueueRegistryItem;
xQueueRegistryItem rxQueueStore[MAX_NUMBER_OF_RX_QUEUES];
int numberOfQueuesRegistered;
#define cError -1
#define cSuccess 0
void processInterrupt()
{
for(int i=0; i < numberOfQueuesRegistered; i++)
{
if(xQueueSendFromISR(rxQueueStore[i].xHandle,(void *) &RxMessage,&tmp) != pdTRUE)
TurnLedRed();
if(tmp)resched_needed = pdTRUE;
}
portEND_SWITCHING_ISR(resched_needed);
}
int registerRxQueueWithCAN(xQueueHandle myQueue)
{
if(numberOfQueuesRegistered == MAX_NUMBER_OF_RX_QUEUES)
{
// Over Flow of registerations
TurnLedRed();
return cError;
}else
{
rxQueueStore[numberOfQueuesRegistered].xHandle = myQueue;
numberOfQueuesRegistered++;
}
return cSuccess;
}
Few points:-
xQuehandle is typdefed to "void *"
The code works if remove the registration thing and just do with directly pointer of queue in xQueueSendFromISR if I take the pointer by extern.
Any advice or information required?
At first glance I cannot see anything obviously wrong. The problem might be outside of the code you have shown, like how is can_rx0_queue declared, how is the interrupt entered, which port are you using, etc.
There is a FreeRTOS support forum, linked to from the FreeRTOS home page http://www.FreeRTOS.org
Regards.
I think Richard is right. The problem could be issues that are not within your code that you have posted here.
Are you calling any form of suspension on the receiving Task that is waiting on the Queue? When you invoke a vTaskSuspend() on a Task that is blocked waiting on a Queue, the Task that is suspended will be moved to the pxSuspendedTaskList and it will "forget" that it is waiting on an Event Queue because the pvContainer of xEventListItem in that Task will be set to NULL.
You might want to check if your receiving Task is ever suspended while waiting on a Queue. Hope that helped. Cheers!
Your shared memory should at least be declared volatile:
volatile xQueueRegistryItem rxQueueStore[MAX_NUMBER_OF_RX_QUEUES] ;
volatile int numberOfQueuesRegistered ;
otherwise the compiler may optimise out read or writes to these because it has no concept of different threads of execution (between the ISR and the main thread).
Also I recall that some PIC C runtime start-up options do not apply zero-initialisation of static data in order to minimise start-up time, if you are using such a start-up, you should explicitly initialise numberOfQueuesRegistered. I would suggest that to do so would be a good idea in any case.
It is not clear from your code that RxMessage in the ISR is not the same as RxMessage in the 'remote module'; they should not be shared, since that would allow the ISR to potentially modify the data while the receiving thread was processing it. If they could be shared, there would ne no reason to have a queue in the first place, since shared memory and a semaphore would suffice.
As a side-note, there is never any need to cast a pointer to void*, and you should generally avoid doing so, since it will prevent the compiler from issuing an error if you were to pass something other than a pointer. The whole point of a void* is rather that it can accept any pointer type.

Resources