0

I am trying to build a kernel module (stress-test tool for a hardware) for a Linux 3.10.45 on x64. So far it seemed to work fine, until adding a mutex.

I added mutex using and the functions mutex_init, mutex_lock, mutex_unlock and mutex_destroy.

Building the module yielded no errors or warnings, but when loading with 'insmod', there are error messages in dmesg:

[76603.744551] tryBlk: Unknown symbol mutex_lock_nested (err 0)
[76603.744574] tryBlk: Unknown symbol mutex_destroy (err 0)

I found a hint that with 'Unknown symbol', it sometimes helps to add the MODULE_LICENSE("GPL v2") line.

No difference.

Looking at linux/mutex.h, I found that the mutex_lock will only define to mutex_lock_nested if the symbol CONFIG_DEBUG_LOCK_ALLOC is defined. Checking this, it seems to be defined in my .config . (Cannot remember touching it. It is basically just a kernel from kernel.org, built).

Is there a problem with this? Do I need to manually add something else to my module to make it build with this debug-feature?

Tried to change include files and sequence. No difference.

System is running Debian-7 'Wheezy' x64, with kernel changed to a 3.10.45 .

The file using the mutex:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include "ring.h"




struct RingBuf
{
    unsigned char *buffer;
    unsigned int size;
    unsigned int inp,outp;
    struct mutex mtx;
};



static int _bytesavail(struct RingBuf *self);
static int _spaceavail(struct RingBuf *self);


struct RingBuf *RingBuf_init(unsigned int size)
{
  struct RingBuf *self;
    if( size<16 )size=16;
    if( size>0x10000000u )return 0;
    if( size & (size-1) )
    {
      unsigned int ns;
        // is not a power of 2.
        size = size<<1;
        while(1)
        {
            ns=size&(size-1);
            if(!ns)break;
            size=ns;
        }
    }
    self = (struct RingBuf*)vmalloc(sizeof(*self)+size);
    memset( self , 0 , sizeof(*self) );
    self->buffer = (unsigned char*)(self+1);
    self->size = size;
    self->inp = 0;
    self->outp = 0;
    mutex_init( &(self->mtx) );
    return self;
}

void RingBuf_uninit(struct RingBuf *self)
{
    if(!self)return;
    mutex_lock( &(self->mtx) );
    mutex_destroy( &(self->mtx) );
    memset( self , 0xFE , sizeof(*self) );
    vfree(self);
}

int RingBuf_add(struct RingBuf *self,const void *data,int num)
{
  int cpy;
    if(num<=0)return 0;
    mutex_lock( &(self->mtx) );
    // check amount to copy
    cpy = _spaceavail(self);
    if(cpy>num)cpy=num;
    // one part or split
    if( self->inp+cpy <= self->size )
    {
        // one chunk
        memcpy( self->buffer+self->inp , data , cpy );
    }else{
      int p1 = (self->size-self->inp);
        // wrapped
        memcpy( self->buffer+self->inp , data , p1 );
        memcpy( self->buffer , ((const unsigned char*)data)+p1 , cpy-p1 );
    }
    self->inp = (self->inp+cpy) & (self->size-1) ;
    mutex_unlock( &(self->mtx) );
    return cpy;
}

int RingBuf_get(struct RingBuf *self,void *data,int num)
{
  int cpy;
    if(num<=0)return 0;
    mutex_lock( &(self->mtx) );
    // check amount to copy
    cpy = _bytesavail(self);
    if(cpy>num)cpy=num;
    // one part or split
    if( self->outp+cpy <= self->size )
    {
        // one chunk
        memcpy( data , self->buffer+self->outp , cpy );
    }else{
      int p1 = (self->size-self->outp);
        // wrapped
        memcpy( data , self->buffer+self->outp , p1 );
        memcpy( ((unsigned char*)data)+p1 , self->buffer , cpy-p1 );
    }
    self->outp = (self->outp+cpy) & (self->size-1) ;
    mutex_unlock( &(self->mtx) );
    return cpy;
}

int RingBuf_get_user(struct RingBuf *self,void __user *data,int num)
{
  int cpy;
  int ret;
    if(num<=0)return 0;
    mutex_lock( &(self->mtx) );
    // check amount to copy
    cpy = _bytesavail(self);
    if(cpy>num)cpy=num;
    // one part or split
    if( self->outp+cpy <= self->size )
    {
        // one chunk
        ret = copy_to_user( data , self->buffer+self->outp , cpy );
    }else{
      int p1 = (self->size-self->outp);
        // wrapped
        ret = copy_to_user( data , self->buffer+self->outp , p1 );
        if(!ret)
            ret = copy_to_user( ((unsigned char*)data)+p1 , self->buffer , cpy-p1 );
    }
    if(ret)return -1;
    self->outp = (self->outp+cpy) & (self->size-1) ;
    mutex_unlock( &(self->mtx) );
    return cpy;
}

int RingBuf_numBytes(struct RingBuf *self)
{
  int result;
    mutex_lock( &(self->mtx) );
    result = _bytesavail(self);
    mutex_unlock( &(self->mtx) );
    return result;
}

static int _bytesavail(struct RingBuf *self)
{
    return (self->inp-self->outp)&(self->size-1);
}

static int _spaceavail(struct RingBuf *self)
{
    return (self->outp-self->inp-1)&(self->size-1);
}

1 Answers1

0

I just found that it somehow depends on how I am building the module.

The mutex stuff is in another source-file (the one listed above). When I cut/paste it into the first main-source of the module, it works.

So it is a problem of how to create a module spanning several source files.

My (NOT CORRECTLY WORKING) Makefile is:

obj-m += tryBlk.o
tryBlk-objs := ring.o

It builds without warnings, but the kernel-symbols in the 'ring.o' file are not correctly found.

===============================================

I also solved the other problem now. To share, the makefile was bad.

Here the better makefile:

obj-m := tryBlk.o
tryBlk-objs := tryBlk_main.o ring.o

I also needed to rename the main part from 'tryBlk.c' to 'tryBlk_main.c'

  • As far as I can tell, your kernel module was thinking it was getting built for a kernel with CONFIG_DEBUG_LOCK_ALLOC, while your kernel didn't have that. This is a debug feature, as far as I know - we only have it enabled in our debug kernel configuration, where we want to find bugs in kernel drivers. Obviously, debugging mutex problems is going to slow things down in the kernel. – fencekicker Oct 09 '20 at 09:20