I am implementing a timeout for some parameters in my kernel module.
So I am using struct timer_list and Associated API's to implement a 12 sec timer.
So as mentioned in the IBM developer Works guide for kernel timers I use:
struct timer_list my_timer;
init_timer_on_stack(&my_timer);
void tick_timer()
{
my_timer.expires = jiffies + delay * HZ; //delay is global variable int = 12.
my_timer.data=0;
my_timer.function = my_timer_func;
add_timer(&my_timer);
}
So each time my timer expires I do my work in my_timer_func and call tick_timer again to reset the timer.
Next, I would like to implement the delay as a sysctl entry.
But the change should immediately call the tick_timer function and reset the timer with new delay. SO how can I detect this change and remove any current timer or reset it.
Should there be any kernel thread to detect the change in delay
Kernel has no mechanism for detect changes in variables. Instead, you should perform corresponded actions before/after your code changes your variable.
When you add sysctl entry, you also set handler for it(ctl_table->proc_handler). This handler defines actions, which are executed when read/write method for entry is called. Standard proc_do* functions only set/get value of variable, so you should define your handler. Something like this:
int my_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
// Call standard helper..
int res = proc_dointvec(table, write, buffer, lenp, ppos);
if(write && !res) {
// Additional actions on successfull write.
}
return res;
}
Modification of the timer's timeout can be performed using mod_timer function.
Related
We have a driver that schedules work using a timer (using add_timer). Whenever the work results in a change, an application should be notified of this change.
Currently this is done by providing a sysfs entry from the driver, that blocks a read operation until there is a change in the data.
(Please see the relevant code from the driver and the application in the code block below.)
I have inspected the source of most functions related to this in the linux kernel (4.14.98), and I did not notice any obvious problems in dev_attr_show, sysfs_kf_seq_show,__vfs_read and vfs_read.
However in seq_read the mutex file->private_data->lock is held for the duration of the read:
https://elixir.bootlin.com/linux/v4.14.98/source/fs/seq_file.c#L165
Will this pose a problem?
Are there any other (potential?) problems that I should be aware of?
Please note that:
The data will change at least once per second, usually way faster
The application should respond as soon as possible to a change in the data
This runs in a controlled (embedded) environment
// driver.c
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int xxx_block_c = 0;
// driver calls this to notify the application that something changed (from a timer, `timer_list`)
void xxx_Persist(void)
{
xxx_block_c = 1;
wake_up_interruptible(&wq);
}
// Sysfs entry that blocks until there is a change in the data.
static ssize_t xxx_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
wait_event_interruptible(wq, xxx_block_c != 0);
xxx_block_c = 0;
/* Remainder of the implementation */
}
//application.cpp
std::ifstream xxx;
xxx.open("xxx", std::ios::binary | std::ios::in);
while (true)
{
xxx.clear();
xxx.seekg(0, std::ios_base::beg);
xxx.read(data, size);
/* Do something with data */
}
I'm trying to set up a hardware interrupt handler in protected mode, using djgpp-2 for compiling in dosbox-0.74. Here's the smallest code possible (timer interrupt), I guess:
#include <dpmi.h>
#include <go32.h>
#include <stdio.h>
unsigned int counter = 0;
void handler(void) {
++counter;
}
void endHandler(void) {}
int main(void) {
_go32_dpmi_seginfo oldInfo, newInfo;
_go32_dpmi_lock_data(&counter, sizeof(counter));
_go32_dpmi_lock_code(handler, endHandler - handler);
_go32_dpmi_get_protected_mode_interrupt_vector(8, &oldInfo);
newInfo.pm_offset = (int) handler;
newInfo.pm_selector = _go32_my_cs();
_go32_dpmi_allocate_iret_wrapper(&newInfo);
_go32_dpmi_set_protected_mode_interrupt_vector(8, &newInfo);
while (counter < 3) {
printf("%u\n", counter);
}
_go32_dpmi_set_protected_mode_interrupt_vector(8, &oldInfo);
_go32_dpmi_free_iret_wrapper(&newInfo);
return 0;
}
Note that I'm not chaining my handler but replacing it. The counter won't increase beyond 1 (therefore never stopping the main loop) making me guess that the handler doesn't return correctly or is called only once. Chaining on the other hand works fine (remove the wrapper-lines and replace set_protected_mode with chain_protected_mode).
Am I missing a line?
You need to chain the old interrupt handler, like in the example Jonathon Reinhart linked to in the documentation, as the old handler will tell the interrupt controller to stop asserting the interrupt. It will also have the added benefit of keeping the BIOS clock ticking, so it doesn't lose a few seconds each time you run the program. Otherwise when your interrupt handler returns the CPU will immediately call the handler again and your program will get stuck in an infinite loop.
Also there's no guarantee that GCC will place endHandler after handler. I'd recommend just simply locking both the page handler starts on and the next page in case it straddles a page:
_go32_dpmi_lock_code((void *) handler, 4096);
Note the cast is required here, as there's no automatic conversion from pointer to a function types to pointer to void.
I am writing a kernel module in which I need to trigger a function on a periodic basis. The function will access a queue and process its elements. The number of elements in the queue is dynamic and so the processing time.
In the following code, I have added 1ms sleep to represent the processing time. I am getting this error : [116588.117966] BUG: scheduling while atomic: systemd-journal/408/0x00010000. If my understanding is correct, this happens since I try to sleep 1ms when the expiry time of the hr_timer is just 1us. I can increase this expiry time but the processing time of the queue can be sometimes more than seconds, sometimes in hours even. Please help me to achieve this.
unsigned long timer_interval_ns = 1e3;
static struct hrtimer hr_timer;
enum hrtimer_restart timer_callback( struct hrtimer *timer_for_restart )
{
uint64_t rawtime;
struct timespec curtime;
ktime_t currtime , interval;
/ * My Operations would take ~ 1ms, so adding 1ms for simplicity* /
msleep(1);
currtime = ktime_get();
interval = ktime_set(0,timer_interval_ns);
hrtimer_forward(timer_for_restart, currtime, interval);
return HRTIMER_RESTART;
}
static int __init timer_init(void) {
ktime_t ktime = ktime_set( 0, timer_interval_ns );
hrtimer_init( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
hr_timer.function = &timer_callback;
hrtimer_start( &hr_timer, ktime, HRTIMER_MODE_REL );
return 0;
}
BUG: scheduling while atomic
This message means that while you are in an atomic context you tried to schedule some other task.
To make it easy (so, not perfect and orthodox explanation): if a function is running in an atomic context, this function cannot stop its execution and call the scheduler (a.k.a. sleeping).
When you call msleep(1) you are actually asking the kernel to schedule some other task because for 1 millisecond you do not have anything to do and you ask the kernel to use this time to do something useful. But this is not allowed in an atomic context. Functions running in an atomic context must finish their execution without any interruption.
Another example of function that will sleep and you may have the temptation to use is kmalloc. If you need it in an atomic context then use the GFP_ATOMIC flag in order to have an atomic allocation that does not sleep (schedule).
Another example of atomic context is the interrupt handler function.
Another problem that you may have with msleep(1) is that is not guaranteed that it will sleep 1 millisecond. It it too short to be guaranteed. Indeed, it is suggested to use a sleeping time greater or equal than 20 milliseconds. If you need a shorter sleeping time, then use delay functions.
Read The following links:
https://en.wikipedia.org/wiki/Linearizability
https://lwn.net/Articles/274695/
https://www.kernel.org/doc/Documentation/timers/timers-howto.txt
Is there a way to adjust an hrtimer's parameters (specifically I want to adjust min_delta_ns) from within a kernel module?
I'm writing a kernel module that has some outputs driven by an hrtimer. Here's a rough outline of the basic code:
#include <linux/hrtimer.h>
#include <linux/sched.h>
#define MAXRUNS 300000
#define PERIOD_IN_NS 100000
static struct hrtimer hr_timer;
static ktime_t ktime_period_ns;
static volatile int runcount = 0;
static int some_function(parameters) {
ktime_period_ns= ktime_set( 0, PERIOD_IN_NS );
hrtimer_init ( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
htimer.function = timer_callback;
hrtimer_start( &hr_timer, ktime_period_ns, HRTIMER_MODE_REL );
return 0;
}
static enum hrtimer_restart timer_callback(struct hrtimer *timer)
{
runcount++;
if (runcount < MAXRUNS) {
// do stuff
hrtimer_forward_now(&hr_timer, ktime_period_ns);
return HRTIMER_RESTART;
} else {
runcount = 0;
return HRTIMER_NORESTART;
}
}
When I run it with PERIOD_IN_NS of 100,000 or greater everything works great. However, if I drop that value to say, 50,000, the period of my clamps at around 90,000 (ish) and output becomes unpredictable.
I ran cat /proc/timer_list to get the details of my timers and here are the details what I believe is the relevant timer:
Tick Device: mode: 1
Per CPU device: 0
Clock Event Device: mxc_timer1
max_delta_ns: 1431655752223
min_delta_ns: 85000
mult: 6442451
shift: 31
mode: 3
next_event: 13571723000000 nsecs
set_next_event: v2_set_next_event
set_mode: mxc_set_mode
event_handler: hrtimer_interrupt
retries: 0
From what I've read about how hrtimer works, that min_delta_ns of 85000 means that I can't run interrupts with a period any smaller than 85,000 nanoseconds. I'd like to try to decrease that value to see if I can get my code to cycle any faster without detrimental effects to the system (I'm running this on Raspberry-Pi-like dev board called the HummingBoard).
It looks like this clock is being initially configured in my specific architecture's version of time.c (line 180), but I can't figure out how to access and modify the values outside of that context in my custom kernel module.
Is there a way to adjust the values of an hrtimer's parameters from within my kernel module?
The min_delta_ns value describes a property of the hardware device.
Even if you were able to change this value (which you cannot), the timer event would not actually arrive any faster.
I need to be able to start multiple timers simultaneously and know specifically if a timer has stopped or is still going.
#define RESEND_TIMEOUT 5
void timerCreate();
void timer_start(timer_t * timer, uint32 timeout);
bool timer_complete(timer_t * timer);
int main() {
timer_t resend_timer = timerCreate();
timer_start(&resend_timer, RESEND_TIMEOUT);
while(1) {
if (timer_complete(&resend_timer))
break;
}
}
void timer_start(timer_t * timer, uint32_t timeout)
{
printf("timer starting\n");
struct itimerspec it_val;
it_val.it_value.tv_sec = timeout;
it_val.it_value.tv_nsec = 0;
// timer expires once
it_val.it_interval.tv_sec = 0;
it_val.it_interval.tv_nsec = 0;
if (timer_settime(*timer, 0, &it_val, NULL) == -1) {
errExit("Could not set timeout");
}
}
// return true if timer ended
bool timer_complete(timer_t * timer)
{
if(timer_getoverrun(*timer) == 0)
return false;
else
return true;
}
I never break out of the loop. Why can't I get the overrun of the timer (it always returns 0, which means the timer has not passed its expiration)? Yet when I add a signal handler, I know that the timer expires.
I want to try timer_gettime(timer_t timerid, struct itimerspec *curr_value) inside of my timer_complete function to see if the remaining time is 0, but how can I pass the curr_value argument without having a global variable?
Last but not least, I have tried with the TIMER_ABSTIME flag when arming the timer with timer_settime. From the manpage of int timer_settime(timer_t timerid, int flags,
const struct itimerspec *new_value,
struct itimerspec * old_value):
By default, the initial expiration time specified in
new_value->it_value is interpreted relative to the current time on
the timer's clock at the time of the call. This can be modified by
specifying TIMER_ABSTIME in flags, in which case new_value->it_value
is interpreted as an absolute value as measured on the timer's clock;
that is, the timer will expire when the clock value reaches the value
specified by new_value->it_value. If the specified absolute time has
already passed, then the timer expires immediately, and the overrun
count (see timer_getoverrun(2)) will be set correctly.
I never break out of the loop. Why can't I get the overrun of the timer (it always returns 0, which means the timer has not passed its expiration)?
No, it means you had no overruns.
The OS is not going to queue timer signals even if you specify realtime signals. Overun tells you how many signals would have been queued if the wood chuck didn't chuck signals.
So consider you set a timer to go off once every second. But for some reason you didn't handle the signal. Say you had it blocked for 5 seconds. The overrun count is going to be 4 - the signal you will/are processing and the 4 you missed.
In your case you set a one-time timer to go off after "timeout" seconds. The signal was delivered. There will be no more signals hence overrun is always going to be 0, as it should be.