What context does a Linux kernel timer function runs in? - timer

When a timer created with the add_timer API expires and the function assigned at the timer structure runs, in what context does it run? Is it interrupt context or some kernel process context?

It is of course in interrupt context, more precisely, in softirq context, see below (kernel/timer.c):
static inline void __run_timers(struct tvec_base *base)
{
struct timer_list *timer;
spin_lock_irq(&base->lock);
while (time_after_eq(jiffies, base->timer_jiffies)) {
struct list_head work_list;
struct list_head *head = &work_list;
int index = base->timer_jiffies & TVR_MASK;
/*
* Cascade timers:
*/
if (!index &&
(!cascade(base, &base->tv2, INDEX(0))) &&
(!cascade(base, &base->tv3, INDEX(1))) &&
!cascade(base, &base->tv4, INDEX(2)))
cascade(base, &base->tv5, INDEX(3));
++base->timer_jiffies;
list_replace_init(base->tv1.vec + index, &work_list);
while (!list_empty(head)) {
void (*fn)(unsigned long);
unsigned long data;
bool irqsafe;
timer = list_first_entry(head, struct timer_list,entry);
fn = timer->function;
data = timer->data;
irqsafe = tbase_get_irqsafe(timer->base);
timer_stats_account_timer(timer);
base->running_timer = timer;
detach_expired_timer(timer, base);
if (irqsafe) {
spin_unlock(&base->lock);
call_timer_fn(timer, fn, data); // <=========== HERE
spin_lock(&base->lock);
} else {
spin_unlock_irq(&base->lock);
call_timer_fn(timer, fn, data); // <============ HERE
spin_lock_irq(&base->lock);
}
}
}
base->running_timer = NULL;
spin_unlock_irq(&base->lock);
}
/*
* This function runs timers and the timer-tq in bottom half context.
*/
static void run_timer_softirq(struct softirq_action *h)
{
struct tvec_base *base = __this_cpu_read(tvec_bases);
hrtimer_run_pending();
if (time_after_eq(jiffies, base->timer_jiffies))
__run_timers(base);
}
void __init init_timers(void)
{
int err;
/* ensure there are enough low bits for flags in timer->base pointer */
BUILD_BUG_ON(__alignof__(struct tvec_base) & TIMER_FLAG_MASK);
err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
(void *)(long)smp_processor_id());
init_timer_stats();
BUG_ON(err != NOTIFY_OK);
register_cpu_notifier(&timers_nb);
open_softirq(TIMER_SOFTIRQ, run_timer_softirq); // <============= HERE
}

Related

Correct way to pass a char array to a callback function in C

I'm dynamically repeatedly creating a char[] of fixed length 32 and pass it to function that stores the pointer in a struct and passes it later on to a callback function.
Unforntunately I don't know the correct way to allocate the memory for this string data.
This is the function, where the nonceHex is created in a loop
static int polling_thread(struct pt *pt, uint16_t ticks)
{
static uint16_t timer;
timer += ticks;
PT_BEGIN(pt);
while (1)
{
uint8_t nonce[NONCE_LEN] = {};
char nonceHex[2 * NONCE_LEN] = "";
RNG(nonce, NONCE_LEN);
hex_encode(nonce, sizeof(nonce), nonceHex);
...
struct mg_connection *c = mg_connect_http(mgos_get_mgr(), http_cb, nonceHex, url, (const char *)sEtagHeader, NULL);
...
timer = 0;
PT_WAIT_UNTIL(pt, timer >= 500);
}
PT_END(pt);
}
And this is the callback function where the char[] is passed by the framework (mongoose os) as cb_arg
static void http_cb(struct mg_connection *nc, int ev, void *evd, void *cb_arg)
{
...
case MG_EV_HTTP_REPLY:
{
const char *nonceHex = (const char *)cb_arg;
LOG(LL_DEBUG, ("http_cb nonce: %s", nonceHex));
LOG(LL_DEBUG, ("http_cb nc->user_data: %s", (const char *)nc->user_data));
}
...
}
Unfortunately when I run this code I don't received the nonceHex but something that looks like unitialized char[]
http_cb nonce: Pa�?`B�?�a�?���?�c�?
http_cb nc->user_data: Pa�?`B�?�a�?���?�c�?
Do I need to dynamically malloc the char[]? If I use a static char[] it works, but it's not correct as nonceHex is overwritten by subsequent loop cycles and so cb_arg in the callback function contains invalid data.
Can anyone tell me how this would be programmed correctly?
#######################################################
Changed my function to
static int polling_thread(struct pt *pt, uint16_t ticks)
{
static uint16_t timer;
timer += ticks;
PT_BEGIN(pt);
while (1)
{
...
uint8_t nonce[NONCE_LEN] = {};
char *nonceHex = malloc(2 * NONCE_LEN + 1);
RNG(nonce, NONCE_LEN);
hex_encode(nonce, sizeof(nonce), nonceHex);
*(nonceHex+2 * NONCE_LEN) = '\0';
struct mg_connection *c = mg_connect_http(mgos_get_mgr(), http_cb, nonceHex, url, (const char *)sEtagHeader, NULL);
...
timer = 0;
PT_WAIT_UNTIL(pt, timer >= 500);
}
PT_END(pt);
}
Added call to free in callback function
static void http_cb(struct mg_connection *nc, int ev, void *evd, void *cb_arg)
{
...
case MG_EV_HTTP_REPLY:
{
char *nonceHex = (char *)cb_arg;
LOG(LL_DEBUG, ("http_cb nonce: %s", nonceHex));
LOG(LL_DEBUG, ("http_cb nc->user_data: %s", (const char *)nc->user_data));
free(nonceHex);
}
...
}
This results in a panic
Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.
Can you see my fault?

Esp32-(xQueueGenericReceive)- assert failed

I have been working on a project in which the analog values are sampled at a particular frequency and stored in an array. Then the value will be sent to user application ESP32 using BLE. But I got stuck in this error.
/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/queue.c:1443
(xQueueGenericReceive)- assert failed! abort() was called at PC
0x4008e1d5 on core 1
Backtrace: 0x40091b38:0x3ffe0b20 0x40091d69:0x3ffe0b40
0x4008e1d5:0x3ffe0b60 0x400d1a2d:0x3ffe0ba0 0x4008e525:0x3ffe0be0
Rebooting... ets Jun 8 2016 00:22:57
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) configsip: 0,
SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1 load:0x3fff0018,len:4 load:0x3fff001c,len:1044
load:0x40078000,len:8896 load:0x40080400,len:5816 entry 0x400806ac
I am Using Esp32arduino and FreeRTOS for programming. The error is in the semaphore from the interrupt but I couldn't be able to find out exact solution. Please help me out guys.
#include <ArduinoJson.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#if CONFIG_FREERTOS_UNICORE
static const BaseType_t app_cpu = 0;
#else
static const BaseType_t app_cpu = 1;
#endif
//ADC Related Global Variables
static const uint16_t timer_divider = 80;
static const uint64_t timer_max_count = 1000;
static const int adc_pin = A0;
static const int BUF_SIZE = 1000;
static int buf[BUF_SIZE];
int Buff_Len = 0;
static int Read = 0;
static int Write = 0;
static int count = 0;
static float avg = 0;
int i = 0;
int BLE_flag = 0;
String cmd;
static hw_timer_t *timer = NULL;
static uint16_t val;
static int count1 = 0;
static SemaphoreHandle_t bin_sem = NULL;
static SemaphoreHandle_t bin_sem2 = NULL;
static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
//ADC Related Global Variables
//BLE Global Variable
char Reading[4];
BLEServer *pServer = NULL;
BLECharacteristic *pTxCharacteristic;
bool deviceConnected = false;
bool oldDeviceConnected = false;
//Declaration BLE necessary Classes
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
class MyServerCallbacks:public BLEServerCallbacks
{
void onConnect (BLEServer * pServer)
{
deviceConnected = true;
};
void onDisconnect (BLEServer * pServer)
{
deviceConnected = false;
}
};
//BLE Global Variables
//Task Section
void IRAM_ATTR onTimer ()
{
//sampling
xSemaphoreGiveFromISR (bin_sem2, &task_woken);
if (task_woken)
{
portYIELD_FROM_ISR ();
}
}
void move_to_Queue (void *parameters)
{
while (1)
{
xSemaphoreTake (bin_sem2, portMAX_DELAY);
if (Buff_Len == BUF_SIZE || count1 > 2000)
{
Serial.println ("Buffer is full");
xSemaphoreGive (bin_sem);
}
else
{
// storing the instantaneous sample value to buffer
}
}
}
void BLE_Task (void *parameters)
{
while (1) {
xSemaphoreTake (bin_sem, portMAX_DELAY);
Serial.println ("BLE");
// sending the data\lu
delay (10); // bluetooth stack will go into congestion, if too many packets are sent
}
}
Serial.println ();
}
}
void setup ()
{
// put your setup code here, to run once:
Serial.begin (115200);
vTaskDelay (1000 / portTICK_PERIOD_MS);
//BLE Declarations
BLEDevice::init ("UART Service");
pServer = BLEDevice::createServer ();
pServer->setCallbacks (new MyServerCallbacks ());
BLEService *pService = pServer->createService (SERVICE_UUID);
pTxCharacteristic = pService->createCharacteristic (CHARACTERISTIC_UUID_TX,
BLECharacteristic::
PROPERTY_NOTIFY);
pTxCharacteristic->addDescriptor (new BLE2902 ());
pService->start ();
pServer->getAdvertising ()->start ();
Serial.println ("Waiting a client connection to notify...");
//BLE Declaration
//ADC Semaphore and Timer Declarations
bin_sem = xSemaphoreCreateBinary ();
bin_sem2 = xSemaphoreCreateBinary ();
if (bin_sem == NULL || bin_sem2 == NULL)
{
Serial.println ("Could not create semaphore");
ESP.restart ();
}
xTaskCreatePinnedToCore (move_to_Queue,
"move_to_Queue", 1024, NULL, 2, NULL, app_cpu);
xTaskCreatePinnedToCore (BLE_Task,
"BLE_Task", 2048, NULL, 2, NULL, app_cpu);
timer = timerBegin (0, timer_divider, true);
// Provide ISR to timer (timer, function, edge)
timerAttachInterrupt (timer, &onTimer, true);
// At what count should ISR trigger (timer, count, autoreload)
timerAlarmWrite (timer, timer_max_count, true);
// Allow ISR to trigger
timerAlarmEnable (timer);
vTaskDelete (NULL);
}
void loop ()
{
// put your main code here, to run repeatedly:
}
`
Whole code: https://pastebin.com/K8ppkG28
Thanks in advance guys

Linux kernel windows-like event implementation

We need to implement Windows-like kernel event for Linux. These functions are intended to behave as corresponding KeInitializeEvent, KeSetEvent, KeResetEvent, KePulseEvent, and KeWaitForSingleObject from Windows kernel. Synchronization event is called auto reset here, and Notification event is called manual reset. Here is the code:
Event.h
#define WAIT_FOREVER -1
#define event_init(event, manual_reset, initial_state) __event_init(event, manual_reset, initial_state)
#define event_set(event) __event_set(event)
#define event_reset(event) __event_reset(event)
#define event_pulse(event) __event_pulse(event)
#define event_wait(event, ms_timeout) __event_wait(event, (ms_timeout == WAIT_FOREVER) ? (WAIT_FOREVER) : ((ms_timeout * HZ) / 1000))
typedef struct _wait_t
{
atomic_t b;
wait_queue_head_t q;
struct list_head list;
} wait_t;
typedef struct _event_t
{
struct list_head Wait;
bool AutoReset;
bool State;
} event_t;
void __event_init_lib(void);
void __event_init(event_t *event, bool manual_reset, bool initial_state);
bool __event_set(event_t *event);
bool __event_reset(event_t *event);
bool __event_pulse(event_t *event);
status_t __event_wait(event_t *event, time_t timeout);
Event.c
wait_t g_stor[100];
spinlock_t g_lock;
void __event_init_lib(void)
{
wait_t *ptr;
for (int i = 0; i < ARRAY_SIZE(g_stor); ++i)
{
ptr = &g_stor[i];
atomic_set(&ptr->b, 2);
init_waitqueue_head(&ptr->q);
INIT_LIST_HEAD(&ptr->list);
}
spin_lock_init(&g_lock);
}
void __event_init(event_t *event, bool manual_reset, bool initial_state)
{
INIT_LIST_HEAD(&event->Wait);
event->State = initial_state;
event->AutoReset = !manual_reset;
}
status_t __event_wait(event_t *event, time_t timeout)
{
bool b;
wait_t *ptr;
status_t status;
spin_lock(&g_lock);
if (event->State)
{
if (event->AutoReset) event->State = false;
spin_unlock(&g_lock);
return s_success;
}
for (int i = 0; i < ARRAY_SIZE(g_stor); ++i)
{
ptr = &g_stor[i];
if (atomic_cmpxchg(&ptr->b, 2, 0) == 2) break;
}
list_add_tail(&ptr->list, &event->Wait); // note: we need to insert in the end of the list
spin_unlock(&g_lock);
if (timeout == WAIT_FOREVER) wait_event(ptr->q, b = (atomic_cmpxchg(&ptr->b, 1, 2) == 1));
else wait_event_timeout(ptr->q, b = (atomic_cmpxchg(&ptr->b, 1, 2) == 1), timeout);
if (b) status = s_success;
else status = s_timeout;
return status;
}
bool __event_set(event_t *event)
{
bool PrevState;
struct list_head *entry;
wait_t *Wait;
//if (!event->AutoReset && event->State) return true;
spin_lock(&g_lock);
PrevState = event->State;
event->State = true;
if (!PrevState && !list_empty(&event->Wait)) // check if we became signaled right now
// and we have waiters
{
if (event->AutoReset)
{
entry = event->Wait.next;
Wait = container_of(entry, wait_t, list);
atomic_set(&Wait->b, 1);
wake_up(&(Wait->q));
event->State = false;
list_del(entry);
}
else
{
entry = event->Wait.next;
while (entry != &event->Wait)
{
Wait = container_of(entry, wait_t, list);
atomic_set(&Wait->b, 1);
wake_up(&(Wait->q));
entry = entry->next;
list_del(entry->prev);
}
}
}
spin_unlock(&g_lock);
return PrevState;
}
bool __event_reset(event_t *event)
{
bool PrevState;
spin_lock(&g_lock);
PrevState = event->State;
event->State = false;
spin_unlock(&g_lock);
return PrevState;
}
bool __event_pulse(event_t *event)
{
bool PrevState;
struct list_head *entry;
wait_t *Wait;
spin_lock(&g_lock);
PrevState = event->State;
if (!PrevState && !list_empty(&event->Wait)) // check if we became signaled right now
// and we have waiters
{
if (event->AutoReset)
{
entry = event->Wait.next;
Wait = container_of(entry, wait_t, list);
atomic_set(&Wait->b, 1);
wake_up(&(Wait->q));
list_del(entry);
}
else
{
entry = event->Wait.next;
while (entry != &event->Wait)
{
Wait = container_of(entry, wait_t, list);
atomic_set(&Wait->b, 1);
wake_up(&(Wait->q));
entry = entry->next;
list_del(entry->prev);
}
}
}
event->State = false;
spin_unlock(&g_lock);
return PrevState;
}
I think each waiting thread needs its own condition variable, because if we have one condition variable and set it to true, new waiter may arrive and pass through wait_event unintentionally without even falling to sleep. So we need to maintain list of condition variables, hence to wake up correct thread we also need multiple wait queues. Also ReactOS source suggest that event maintains the list of waiters.
Since we can't use thread local storage variables in kernel mode (at least in no easy way) I decided to implement array of wait blocks. When we need to insert waiter in the list we loop this array in search for free wait block. This leads me to believe that we need to use single global lock as ReactOS does (dispatcher lock), not separate lock for each event object.
We need event object for our video camera driver ported from Windows. Everything seems to work fine, however frames per second sometimes drops from 14 fps to 10 (and image flickers). It led me to believe that there is something wrong with the implementation of event.
If you have some suggestions, please share. Thank you.

kprobe, function scheduling - processor lockup - Linux kernel

I've a function I wrote in order to run a given function on all processors. It works perfectly well in all cases except the following case:
When I try to use it within a kprobe that I registered.
Here's some code:
static DEFINE_MUTEX(entryMutex);
static struct kretprobe my_kprobe = {
.entry_handler = (kprobe_opcode_t *) NULL,
.handler = (kprobe_opcode_t *) process_entry_callback,
.maxactive = 1000,
.data_size = 0
};
static int driver_init(void)
{
my_kprobe.kp.addr = (kprobe_opcode_t*)kallsyms_lookup_name("sys_execve");
if ((ret = register_kretprobe(&my_kprobe)) < 0)
return -1;
return 0;
}
void foo(void* nothing)
{
printk("In foo\n");
}
static int process_entry_callback(struct kretprobe_instance* instance, struct pt_regs* regs)
{
mutex_lock(&entryMutex);
for(int i = 0; i < 4; ++i) // assumes there are 4 processors
run_func(foo, NULL, i);
mutex_unlock(&entryMutex);
return 0;
}
void run_func_wrap(struct function_data* data)
{
data->func(data->context);
wake_up_process(data->waiting_task);
*(data->condition) = TRUE;
}
void run_func(SCHEDULED_FUNC func, void *context, int processor)
{
struct function_data data;
struct task_struct* th;
BOOLEAN condition = FALSE;
wait_queue_head_t queue;
init_waitqueue_head(&queue);
data.func = func;
data.waiting_task = current;
data.context = context;
data.condition = &condition;
th = kthread_create(sched_func_wrap, &data, "th");
kthread_bind(th, processor);
wake_up_process(th);
wait_event(queue, condition);
}
F
After the call to 'run_func' in process_entry_callback I can no longer run any programs. Every time I start a new program it just stuck. After a while I get 'processor lockup' warning in the system log.
I suspect that it has something to do with the IRQ levels.
Any suggestions ?
EDIT:
It also happens when using the following function:
smp_call_function_single
which can be found in smp.c # the Linux kernel source code.
instead of my function:
run_func

i2c device driver. System down when try to send data

I trying to write i2c device driver and all is gone fine:
device detected successfully
device in sysfs class with value attribute created too
then I init a hrtimer and start it
I am trying to send data from store function of attribute and it's work fine
but when I trying to send data from hrtimer callback function my system going down. Here is some code (i was remove incudes, defines and non-important code):
led-indicator.h
struct timer_job {
ktime_t period;
struct hrtimer timer;
...
};
struct ledindicator {
struct i2c_client *client;
struct device *device;
struct timer_job job;
int value;
};
led-indicator.c
include "led-indicator.h"
static struct ledindicator *li;
// device attribute [value] store function
static ssize_t value_store(struct device *device, struct device_attribute *attr, const char *buffer, size_t count)
{
int val;
kstrtoint(buffer, 10, &val);
li->value = val;
i2c_smbus_write_byte_data(li->client, 0xBA, 0x02); // this is work fine
LOG("New value is %d \n", li->value);
return count;
}
//timer callback, fire each timer_job.period
static enum hrtimer_restart send_next(struct hrtimer * tim) {
long start = jiffies;
struct timer_job *job = container_of(tim, struct timer_job, timer);
i2c_smbus_write_byte_data(li->client, 0xBA, 0x02); // this cause system fault
hrtimer_forward_now(tim, job->period);
return HRTIMER_RESTART;
}
//when device is detected I am create sysfs device and init timer.
enter code here//Also I am crate led_indicator structure and save i2c_client pointer for future use
static int li_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
li = kmalloc(sizeof(*li), GFP_KERNEL);
li->client = client;
li->value = 0;
...
// here creating device and attribute for it
...
hrtimer_init(&(li->job.timer), CLOCK_MONOTONIC, HRTIMER_MODE_REL);
li->job.timer.function = send_next;
li->job.period = ktime_set(1, 0); //1 sec
hrtimer_start(&(li->job.timer), li->job.period, HRTIMER_MODE_REL);
return ret;
}
What is wrong?

Resources