Linux kernel windows-like event implementation - c

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.

Related

Keeping track of all threads in a thread pool

I am looking at using the Windows Threading API and the issue it seems to have is you cannot keep track of when all the threads are completed. You can keep track of when the work item has been completed, assuming you kept track of each one. From my research there is no direct way to query the thread pool to see if the work items submitted has all be completed.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
VOID CALLBACK MyWorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK Work) {
DWORD threadId = GetCurrentThreadId();
BOOL bRet = FALSE;
printf("%d thread\n", threadId);
return;
}
int main() {
TP_CALLBACK_ENVIRON CallBackEnviron;
PTP_POOL pool = NULL;
PTP_CLEANUP_GROUP cleanupgroup = NULL;
PTP_WORK_CALLBACK workcallback = MyWorkCallback;
PTP_TIMER timer = NULL;
PTP_WORK work = NULL;
InitializeThreadpoolEnvironment(&CallBackEnviron);
pool = CreateThreadpool(NULL);
SetThreadpoolThreadMaximum(pool, 1);
SetThreadpoolThreadMinimum(pool, 3);
SetThreadpoolCallbackPool(&CallBackEnviron, pool);
for (int i = 0; i < 10; ++i) {
work = CreateThreadpoolWork(workcallback, NULL, &CallBackEnviron);
SubmitThreadpoolWork(work);
WaitForThreadpoolWorkCallbacks(work, FALSE); // This waits for the work item to get completed.
}
return 1;
}
Here is a simple example. What happens is on the WaitForThreadpoolWorkCallbacks I am able to wait on that specific work item. Which is no problem if I am doing a few things. However, if I am traversing a directory and have thousands of files that I need to have work done on them, I don't want to keep track of each individual work item. Is it possible to query the Thread Pool queue to see if anything is left for processing? Or to find out if any of the threads are still working?
you need keep track of active tasks ( like pendcnt in comment) +1. but this must not be global variable, but member in some struct. and pass pointer to this struct to work item. increment this counter before call SubmitThreadpoolWork and decrement from callback, before exit. but you also need and event - set this event in signal state, when counter became 0. and wait on event from main thread. if your code inside dll, which can be unloaded - you need also reference dll, before SubmitThreadpoolWork and FreeLibraryWhenCallbackReturns from callback. also important that counter value - was 1 (not 0) ininitally - so this is count_of_active_cb + 1, and decrement it before begin wait (if not do this - counter can became 0 early - for instance first callback exit before you activate second)
class Task
{
HANDLE _hEvent = 0;
ULONG _dwThreadId = 0;
LONG _dwRefCount = 1;
public:
~Task()
{
if (_hEvent) CloseHandle(_hEvent);
}
ULONG Init()
{
if (HANDLE hEvent = CreateEvent(0, 0, 0, 0))
{
_hEvent = hEvent;
return NOERROR;
}
return GetLastError();
}
void AddTask()
{
InterlockedIncrementNoFence(&_dwRefCount);
}
void EndTask()
{
if (!InterlockedDecrement(&_dwRefCount))
{
if (_dwThreadId != GetCurrentThreadId())
{
if (!SetEvent(_hEvent)) __debugbreak();
}
}
}
void Wait()
{
_dwThreadId = GetCurrentThreadId();
EndTask();
if (_dwRefCount && WaitForSingleObject(_hEvent, INFINITE) != WAIT_OBJECT_0) __debugbreak();
}
};
VOID CALLBACK MyWorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK /*Work*/)
{
// need only if your code in dll which can be unloaded
FreeLibraryWhenCallbackReturns(Instance, (HMODULE)&__ImageBase);
WCHAR sz[32];
swprintf_s(sz, _countof(sz), L"[%x] thread", GetCurrentThreadId());
MessageBoxW(0, 0, sz, MB_ICONINFORMATION);
reinterpret_cast<Task*>(Parameter)->EndTask();
}
void CbDemo()
{
Task task;
if (task.Init() == NOERROR)
{
ULONG n = 2;
do
{
if (PTP_WORK pwk = CreateThreadpoolWork(MyWorkCallback, &task, 0))
{
HMODULE hmod;
// need only if your code in dll which can be unloaded
if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (PWSTR)&__ImageBase, &hmod))
{
task.AddTask();
SubmitThreadpoolWork(pwk);
}
CloseThreadpoolWork(pwk);
}
} while (--n);
MessageBoxW(0, 0, L"Main Thread", MB_ICONWARNING);
task.Wait();
__nop();
}
}

How to efficiently find last key and value in GTree

I need to develop a set of functions to extend glib2 GTree with:
find first element
find last
find nearest (floor, ceil, greatest less than, least greater than)
Finding first is easy. You simply stop the g_tree_foreach() calback after first. But how to find the last element without traversing the whole tree?
I thought I could use g_tree_search() with a callback that keeps returning a positive value until found, but how do I know I'm currently on the last element?
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <glib.h>
static
gint compare_int(gconstpointer p1, gconstpointer p2) {
int i1 = GPOINTER_TO_INT(p1);
int i2 = GPOINTER_TO_INT(p2);
//printf("%d %d\n", i1, i2);
return i1 == i2 ? 0 : i1 > i2 ? 1 : -1;
}
static
gboolean traverse(gpointer key, gpointer value, gpointer data) {
//int ikey = GPOINTER_TO_INT(key);
const char *sval = (const char *)value;
printf("%s\n", sval);
return FALSE;
}
static
gint find_last(gconstpointer p, gpointer user_data) {
return 1;
}
static inline const char *NULS(const char *s) {
return s ? s : "NULL";
}
int main(int argc, char *argv[]) {
GTree *tree = g_tree_new(compare_int);
g_tree_insert(tree, GINT_TO_POINTER(10), "ten");
g_tree_insert(tree, GINT_TO_POINTER(-99), "minus ninety-nine");
g_tree_insert(tree, GINT_TO_POINTER(8), "eight");
g_tree_foreach(tree, traverse, NULL);
printf("=======\n%s\n", NULS((const char*)g_tree_search(tree, (GCompareFunc)find_last, NULL)));
return 0;
}
I didn't want to fully implement my own tree, because I wanted to perform advanced search on GTree instances received from 3rd-party code.
Instead I thought that Glib authors would hardly change their internal structures these days and that I could use their fields directly.
The result is the extended version of the internal function g_tree_find_node() from gtree.c. I added two parameters to control whether I want first, last or nearest node. The algorithm for nearest nodes differs from java's TreeMap, because our node doesn't have a pointer to its parent. Full code with the unit test is here: gtreeex.c.
typedef enum {
FIND_EXACT = 0,
FIND_FLOOR = 0x2,
FIND_CEIL = 0x20,
FIND_LOWER = (FIND_FLOOR + 1),
FIND_HIGHER = (FIND_CEIL + 1)
} find_mode;
static GTreeNode *
g_tree_find_node_ex (GTree *tree,
gconstpointer key,
GCompareDataFunc key_compare,
find_mode mode
)
{
GTreeNode *node;
gint cmp;
GTreeNode *last_lesser_node = NULL;
GTreeNode *last_greater_node = NULL;
node = tree->root;
if (!node)
return NULL;
while (1)
{
cmp = key_compare (key, node->key, tree->key_compare_data);
if (cmp == 0) {
if (mode == FIND_LOWER) {
cmp = -1;
} else if (mode == FIND_HIGHER) {
cmp = 1;
} else {
return node;
}
}
if (cmp < 0)
{
if (!node->left_child) {
if ( (mode & FIND_FLOOR) ) {
return last_lesser_node; /* can be null */
}
if ( (mode & FIND_CEIL) ) {
return node;
}
return NULL;
}
last_greater_node = node;
node = node->left;
}
else
{
if (!node->right_child) {
if ( (mode & FIND_CEIL) ) {
return last_greater_node; /* can be null */
}
if ( (mode & FIND_FLOOR) ) {
return node;
}
return NULL;
}
last_lesser_node = node;
node = node->right;
}
}
}
For better performance it's possible to use preprocessor macros instead of the two new parameters, replace if with #if and include the bits header multiple times.

Linux DMA: Using the DMAengine for scatter-gather transactions

I try to use the DMAengine API from a custom kernel driver to perform a scatter-gather operation. I have a contiguous memory region as source and I want to copy its data in several distributed buffers through a scatterlist structure. The DMA controller is the PL330 one that supports the DMAengine API (see PL330 DMA controller).
My test code is the following:
In my driver header file (test_driver.h):
#ifndef __TEST_DRIVER_H__
#define __TEST_DRIVER_H__
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/of_dma.h>
#define SG_ENTRIES 3
#define BUF_SIZE 16
#define DEV_BUF 0x10000000
struct dma_block {
void * data;
int size;
};
struct dma_private_info {
struct sg_table sgt;
struct dma_block * blocks;
int nblocks;
int dma_started;
struct dma_chan * dma_chan;
struct dma_slave_config dma_config;
struct dma_async_tx_descriptor * dma_desc;
dma_cookie_t cookie;
};
struct test_platform_device {
struct platform_device * pdev;
struct dma_private_info dma_priv;
};
#define _get_devp(tdev) (&((tdev)->pdev->dev))
#define _get_dmapip(tdev) (&((tdev)->dma_priv))
int dma_stop(struct test_platform_device * tdev);
int dma_start(struct test_platform_device * tdev);
int dma_start_block(struct test_platform_device * tdev);
int dma_init(struct test_platform_device * tdev);
int dma_exit(struct test_platform_device * tdev);
#endif
In my source that contains the dma functions (dma_functions.c):
#include <linux/slab.h>
#include "test_driver.h"
#define BARE_RAM_BASE 0x10000000
#define BARE_RAM_SIZE 0x10000000
struct ram_bare {
uint32_t * __iomem map;
uint32_t base;
uint32_t size;
};
static void dma_sg_check(struct test_platform_device * tdev)
{
struct dma_private_info * dma_priv = _get_dmapip(tdev);
struct device * dev = _get_devp(tdev);
uint32_t * buf;
unsigned int bufsize;
int nwords;
int nbytes_word = sizeof(uint32_t);
int nblocks;
struct ram_bare ramb;
uint32_t * p;
int i;
int j;
ramb.map = ioremap(BARE_RAM_BASE,BARE_RAM_SIZE);
ramb.base = BARE_RAM_BASE;
ramb.size = BARE_RAM_SIZE;
dev_info(dev,"nblocks: %d \n",dma_priv->nblocks);
p = ramb.map;
nblocks = dma_priv->nblocks;
for( i = 0 ; i < nblocks ; i++ ) {
buf = (uint32_t *) dma_priv->blocks[i].data;
bufsize = dma_priv->blocks[i].size;
nwords = dma_priv->blocks[i].size/nbytes_word;
dev_info(dev,"block[%d],size %d: ",i,bufsize);
for ( j = 0 ; j < nwords; j++, p++) {
dev_info(dev,"DMA: 0x%x, RAM: 0x%x",buf[j],ioread32(p));
}
}
iounmap(ramb.map);
}
static int dma_sg_exit(struct test_platform_device * tdev)
{
struct dma_private_info * dma_priv = _get_dmapip(tdev);
int ret = 0;
int i;
for( i = 0 ; i < dma_priv->nblocks ; i++ ) {
kfree(dma_priv->blocks[i].data);
}
kfree(dma_priv->blocks);
sg_free_table(&(dma_priv->sgt));
return ret;
}
int dma_stop(struct test_platform_device * tdev)
{
struct dma_private_info * dma_priv = _get_dmapip(tdev);
struct device * dev = _get_devp(tdev);
int ret = 0;
dma_unmap_sg(dev,dma_priv->sgt.sgl,\
dma_priv->sgt.nents, DMA_FROM_DEVICE);
dma_sg_exit(tdev);
dma_priv->dma_started = 0;
return ret;
}
static void dma_callback(void * param)
{
enum dma_status dma_stat;
struct test_platform_device * tdev = (struct test_platform_device *) param;
struct dma_private_info * dma_priv = _get_dmapip(tdev);
struct device * dev = _get_devp(tdev);
dev_info(dev,"Checking the DMA state....\n");
dma_stat = dma_async_is_tx_complete(dma_priv->dma_chan,\
dma_priv->cookie, NULL, NULL);
if(dma_stat == DMA_COMPLETE) {
dev_info(dev,"DMA complete! \n");
dma_sg_check(tdev);
dma_stop(tdev);
} else if (unlikely(dma_stat == DMA_ERROR)) {
dev_info(dev,"DMA error! \n");
dma_stop(tdev);
}
}
static void dma_busy_loop(struct test_platform_device * tdev)
{
struct dma_private_info * dma_priv = _get_dmapip(tdev);
struct device * dev = _get_devp(tdev);
enum dma_status status;
int status_change = -1;
do {
status = dma_async_is_tx_complete(dma_priv->dma_chan, dma_priv->cookie, NULL, NULL);
switch(status) {
case DMA_COMPLETE:
if(status_change != 0)
dev_info(dev,"DMA status: COMPLETE\n");
status_change = 0;
break;
case DMA_PAUSED:
if (status_change != 1)
dev_info(dev,"DMA status: PAUSED\n");
status_change = 1;
break;
case DMA_IN_PROGRESS:
if(status_change != 2)
dev_info(dev,"DMA status: IN PROGRESS\n");
status_change = 2;
break;
case DMA_ERROR:
if (status_change != 3)
dev_info(dev,"DMA status: ERROR\n");
status_change = 3;
break;
default:
dev_info(dev,"DMA status: UNKNOWN\n");
status_change = -1;
break;
}
} while(status != DMA_COMPLETE);
dev_info(dev,"DMA transaction completed! \n");
}
static int dma_sg_init(struct test_platform_device * tdev)
{
struct dma_private_info * dma_priv = _get_dmapip(tdev);
struct scatterlist *sg;
int ret = 0;
int i;
ret = sg_alloc_table(&(dma_priv->sgt), SG_ENTRIES, GFP_ATOMIC);
if(ret)
goto out_mem2;
dma_priv->nblocks = SG_ENTRIES;
dma_priv->blocks = (struct dma_block *) kmalloc(dma_priv->nblocks\
*sizeof(struct dma_block), GFP_ATOMIC);
if(dma_priv->blocks == NULL)
goto out_mem1;
for( i = 0 ; i < dma_priv->nblocks ; i++ ) {
dma_priv->blocks[i].size = BUF_SIZE;
dma_priv->blocks[i].data = kmalloc(dma_priv->blocks[i].size, GFP_ATOMIC);
if(dma_priv->blocks[i].data == NULL)
goto out_mem3;
}
for_each_sg(dma_priv->sgt.sgl, sg, dma_priv->sgt.nents, i)
sg_set_buf(sg,dma_priv->blocks[i].data,dma_priv->blocks[i].size);
return ret;
out_mem3:
i--;
while(i >= 0)
kfree(dma_priv->blocks[i].data);
kfree(dma_priv->blocks);
out_mem2:
sg_free_table(&(dma_priv->sgt));
out_mem1:
ret = -ENOMEM;
return ret;
}
static int _dma_start(struct test_platform_device * tdev,int block)
{
struct dma_private_info * dma_priv = _get_dmapip(tdev);
struct device * dev = _get_devp(tdev);
int ret = 0;
int sglen;
/* Step 1: Allocate and initialize the SG list */
dma_sg_init(tdev);
/* Step 2: Map the SG list */
sglen = dma_map_sg(dev,dma_priv->sgt.sgl,\
dma_priv->sgt.nents, DMA_FROM_DEVICE);
if(! sglen)
goto out2;
/* Step 3: Configure the DMA */
(dma_priv->dma_config).direction = DMA_DEV_TO_MEM;
(dma_priv->dma_config).src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
(dma_priv->dma_config).src_maxburst = 1;
(dma_priv->dma_config).src_addr = (dma_addr_t) DEV_BUF;
dmaengine_slave_config(dma_priv->dma_chan, \
&(dma_priv->dma_config));
/* Step 4: Prepare the SG descriptor */
dma_priv->dma_desc = dmaengine_prep_slave_sg(dma_priv->dma_chan, \
dma_priv->sgt.sgl, dma_priv->sgt.nents, DMA_DEV_TO_MEM, \
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (dma_priv->dma_desc == NULL) {
dev_err(dev,"DMA could not assign a descriptor! \n");
goto out1;
}
/* Step 5: Set the callback method */
(dma_priv->dma_desc)->callback = dma_callback;
(dma_priv->dma_desc)->callback_param = (void *) tdev;
/* Step 6: Put the DMA descriptor in the queue */
dma_priv->cookie = dmaengine_submit(dma_priv->dma_desc);
/* Step 7: Fires the DMA transaction */
dma_async_issue_pending(dma_priv->dma_chan);
dma_priv->dma_started = 1;
if(block)
dma_busy_loop(tdev);
return ret;
out1:
dma_stop(tdev);
out2:
ret = -1;
return ret;
}
int dma_start(struct test_platform_device * tdev) {
return _dma_start(tdev,0);
}
int dma_start_block(struct test_platform_device * tdev) {
return _dma_start(tdev,1);
}
int dma_init(struct test_platform_device * tdev)
{
int ret = 0;
struct dma_private_info * dma_priv = _get_dmapip(tdev);
struct device * dev = _get_devp(tdev);
dma_priv->dma_chan = dma_request_slave_channel(dev, \
"dma_chan0");
if (dma_priv->dma_chan == NULL) {
dev_err(dev,"DMA channel busy! \n");
ret = -1;
}
dma_priv->dma_started = 0;
return ret;
}
int dma_exit(struct test_platform_device * tdev)
{
int ret = 0;
struct dma_private_info * dma_priv = _get_dmapip(tdev);
if(dma_priv->dma_started) {
dmaengine_terminate_all(dma_priv->dma_chan);
dma_stop(tdev);
dma_priv->dma_started = 0;
}
if(dma_priv->dma_chan != NULL)
dma_release_channel(dma_priv->dma_chan);
return ret;
}
In my driver source file (test_driver.c):
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include "test_driver.h"
static int dma_block=0;
module_param_named(dma_block, dma_block, int, 0444);
static struct test_platform_device tdev;
static struct of_device_id test_of_match[] = {
{ .compatible = "custom,test-driver-1.0", },
{}
};
static int test_probe(struct platform_device *op)
{
int ret = 0;
struct device * dev = &(op->dev);
const struct of_device_id *match = of_match_device(test_of_match, &op->dev);
if (!match)
return -EINVAL;
tdev.pdev = op;
dma_init(&tdev);
if(dma_block)
ret = dma_start_block(&tdev);
else
ret = dma_start(&tdev);
if(ret) {
dev_err(dev,"Error to start DMA transaction! \n");
} else {
dev_info(dev,"DMA OK! \n");
}
return ret;
}
static int test_remove(struct platform_device *op)
{
dma_exit(&tdev);
return 0;
}
static struct platform_driver test_platform_driver = {
.probe = test_probe,
.remove = test_remove,
.driver = {
.name = "test-driver",
.owner = THIS_MODULE,
.of_match_table = test_of_match,
},
};
static int test_init(void)
{
platform_driver_register(&test_platform_driver);
return 0;
}
static void test_exit(void)
{
platform_driver_unregister(&test_platform_driver);
}
module_init(test_init);
module_exit(test_exit);
MODULE_AUTHOR("klyone");
MODULE_DESCRIPTION("DMA SG test module");
MODULE_LICENSE("GPL");
However, the DMA never calls my callback function and I do not have any idea why it happens. Maybe, I am misunderstanding something...
Could anyone help me?
Thanks in advance.
Caveat: I don't have a definitive solution for you, but merely some observations and suggestions on how to debug this [based on many years of experience writing/debugging linux device drivers].
I presume you believe the callback is not being done because you don't get any printk messages. But, the callback is the only place that has them. But, is the printk level set high enough to see the messages? I'd add a dev_info to your module init, to prove it prints as expected.
Also, you [probably] won't get a callback if dma_start doesn't work as expected, so I'd add some dev_info calls there, too (e.g. before and after the call in step 7). I also notice that not all calls in dma_start check error returns [may be fine or void return, just mentioning in case you missed one]
At this point, it should be noted that there are really two questions here: (1) Did your DMA request start successfully [and complete]? (2) Did you get a callback?
So, I'd split off some code from dma_complete into (e.g.) dma_test_done. The latter does the same checking but only prints the "complete" message. You can call this in a poll mode to verify DMA completion.
So, if you [eventually] get a completion, then the problem reduces to why you didn't get the callback. If, however, you don't [even] get a completion, that's an even more fundamental problem.
This reminds me. You didn't show any code that calls dma_start or how you wait for the completion. I presume that if your callback were working, it would issue a wakeup of some sort that the base level would wait on. Or, the callback would do the request deallocate/cleanup (i.e. more code you'd write)
At step 7, you're calling dma_async_issue_pending, which should call pl330_issue_pending. pl330_issue_pending will call pl330_tasklet.
pl330_tasklet is a tasklet function, but it can also be called directly [to kick off DMA when there are no active requests].
pl330_tasklet will loop on its "work" queue and move any completed items to its "completed" queue. It then tries to start new requests. It then loops on its completed queue and issues the callbacks.
pl330_tasklet grabs the callback pointer, but if it's null it is silently ignored. You've set a callback, but it might be good to verify that where you set the callback is the same place [or propagates to] the place where pl330_tasklet will fetch it from.
When you make the call, everything may be busy, so there are no completed requests, no room to start a new request, so nothing to complete. In that case, pl330_tasklet will be called again later.
So, when dma_async_issue_pending returns, nothing may have happened yet. This is quite probable for your case.
pl330_tasklet tries to start new DMA by calling fill_queue. It will check that a descriptor is not [already] busy by looking at status != BUSY. So, you may wish to verify that yours has the correct value. Otherwise, you'd never get a callback [or even any DMA start].
Then, fill_queue will try to start the request via pl330_submit_req. But, that can return an error (e.g. queue already full), so, again, things are deferred.
For reference, notice the following comment at the top of pl330_submit_req:
Submit a list of xfers after which the client wants notification.
Client is not notified after each xfer unit, just once after all
xfer units are done or some error occurs.
What I'd do is start hacking up pl330.c and add debug messages and cross-checking. If your system is such that pl330 is servicing many other requests, you might limit the debug messages by checking that the device's private data pointer matches yours.
In particular, you'd like to get a message when your request actually gets started, so you could add a debug message to the end of pl330_submit_req
Then, adding messages within pl330_tasklet for requests will help, too.
Those are two good starting points. But, don't be afraid to add more printk calls as needed. You may be surprised by what gets called [or doesn't get called] or in what order.
UPDATE:
If I install the kernel module with the blocking behaviour, everything is initialized well. However, the dma_busy_loop function shows that the DMA descriptor is always IN PROGESS and the DMA transaction never completes. For this reason, the callback function is not executed. What could be happening?
Did a little more research. Cookies are just sequence numbers that increment. For example, if you issue a request that gets broken up into [say] 10 separate scatter/gather operations [descriptors], each one gets a unique cookie value. The cookie return value is the latest/last of the bunch (e.g. 10).
When you're calling (1) dma_async_is_tx_complete, (2) it calls chan->device->device_tx_status, (3) which is pl330_tx_status, (4) which calls dma_cookie_status
Side note/tip: When I was tracking this down, I just kept flipping back and forth between dmaengine.h and pl330.c. It was like: Look at (1), it calls (2). Where is that set? In pl330.c, I presume. So, I grepped for the string and got the name of pl330's function (i.e. (3)). So, I go there, and see that it does (4). So ... Back to dmaengine.h ...
However, when you make the outer call, you're ignoring [setting to NULL] the last two arguments. These can be useful because they return the "last" and "used" cookies. So, even if you don't get full completion, these values could change and show partial progress.
One of them should eventually be >= to the "return" cookie value. (i.e.) The entire operation should be complete. So, this will help differentiate what may be happening.
Also, note that in dmaengine.h, right below dma_async_is_tx_complete, there is dma_async_is_complete. This function is what decides whether to return DMA_COMPLETE or DMA_IN_PROGRESS, based on the cookie value you pass and the "last" and "used" cookie values. It's passive, and not used in the code path [AFAICT], but it does show how to calculate completion yourself.

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

What context does a Linux kernel timer function runs in?

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
}

Resources