I'm writing a simple character device driver in Linux. But I've encountered some issues. I've added timer but it's incorrect. I need to 1) execute timer one; 2) execute timer n times each 30 seconds apart and stop; 3) execute timer continuously until user sends a STOP command to the driver. And also the user process have to get back the data (snap shot) from device through command IO interface at the end of every timer interrupt completion. I can use jiffies, tasklets, interrupts but I have no idea how to implement them in this driver.
Then about the sysinfo(). I've added the code of sysinfo() to the user application but it has to be in the kernel application code and then send back the sysinfo() to the user application process and show in the terminal.
Here are my kernel and user application codes respectively.
Kernel:
/**
* #file diasdd.c
* #date 16 May 2017
* #version 0.1
* #brief This module maps to /dev/diasdd and
* comes with a helper C program that can be run in Linux user space to communicate with
* this the LKM.
*/
#include <linux/init.h> // Macros used to mark up functions e.g. __init __exit
#include <linux/module.h> // Core header for loading LKMs into the kernel
#include <linux/device.h> // Header to support the kernel Driver Model
#include <linux/kernel.h> // Contains types, macros, functions for the kernel
#include <linux/fs.h> // Header for the Linux file system support
#include <asm/uaccess.h> // Required for the copy to user function
#include <linux/timer.h>
#define DEVICE_NAME "diasdd" ///< The device will appear at /dev/diasdd using this value
#define CLASS_NAME "dd" ///< The device class -- this is a character device driver
MODULE_LICENSE("GPL"); ///< The license type -- this affects available functionality
MODULE_DESCRIPTION("A simple Linux char driver"); ///< The description -- see modinfo
MODULE_VERSION("0.1"); ///< A version number to inform users
static int majorNumber; ///< Stores the device number -- determined automatically
static char message[256] = {0}; ///< Memory for the string that is passed from user space
static short size_of_message; ///< Used to remember the size of the string stored
static int numberOpens = 0; ///< Counts the number of times the device is opened
static struct class* diasddClass = NULL; ///< The device-driver class struct pointer
static struct device* diasddDevice = NULL; ///< The device-driver device struct pointer
// The prototype functions for the character driver -- must come before the struct definition
static int dev_open(struct inode *, struct file *);
static int dev_release(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char *, size_t, loff_t *);
static ssize_t dev_write(struct file *, const char *, size_t, loff_t *);
static struct timer_list my_timer;
void my_timer_callback( unsigned long data )
{
printk( "DiasDD: my_timer_callback called (%ld).\n", jiffies );
}
/** #brief Devices are represented as file structure in the kernel. The file_operations structure from
* /linux/fs.h lists the callback functions that we wish to associated with our file operations
* using a C99 syntax structure. char devices usually implement open, read, write and release calls
*/
static struct file_operations fops =
{
.open = dev_open,
.read = dev_read,
.write = dev_write,
.release = dev_release,
};
/** #brief The LKM initialization function
* The static keyword restricts the visibility of the function to within this C file. The __init
* macro means that for a built-in driver (not a LKM) the function is only used at initialization
* time and that it can be discarded and its memory freed up after that point.
* #return returns 0 if successful
*/
static int __init diasdd_init(void){
// Timer starts here
int ret;
printk("DiasDD: Timer module installing\n");
// my_timer.function, my_timer.data
setup_timer( &my_timer, my_timer_callback, 0 );
printk( "DiasDD: Starting timer to fire in 300ms (%ld)\n", jiffies );
ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(300) );
if (ret) printk("Error in mod_timer\n");
printk(KERN_INFO "DiasDD: Initializing the DiasDD LKM\n");
// Try to dynamically allocate a major number for the device -- more difficult but worth it
majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
if (majorNumber<0){
printk(KERN_ALERT "DiasDD failed to register a major number\n");
return majorNumber;
printk(KERN_INFO "DiasDD: registered correctly with major number %d\n", majorNumber);
}
// Register the device class
diasddClass = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(diasddClass)){ // Check for error and clean up if there is
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to register device class\n");
return PTR_ERR(diasddClass); // Correct way to return an error on a pointer
}
printk(KERN_INFO "DiasDD: device class registered correctly\n");
// Register the device driver
diasddDevice = device_create(diasddClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
if (IS_ERR(diasddDevice)){ // Clean up if there is an error
class_destroy(diasddClass); // Repeated code but the alternative is goto statements
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device\n");
return PTR_ERR(diasddDevice);
}
printk(KERN_INFO "DiasDD: device class created correctly\n"); // Made it! device was initialized
return 0;
}
/** #brief The LKM cleanup function
* Similar to the initialization function, it is static. The __exit macro notifies that if this
* code is used for a built-in driver (not a LKM) that this function is not required.
*/
static void __exit diasdd_exit(void){
// Timer ends here
int ret;
ret = del_timer( &my_timer );
if (ret) printk("DiasDD: The timer is still in use...\n");
printk("DiasDD: Timer module uninstalling\n");
device_destroy(diasddClass, MKDEV(majorNumber, 0)); // remove the device
class_unregister(diasddClass); // unregister the device class
class_destroy(diasddClass); // remove the device class
unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
printk(KERN_INFO "DiasDD: Goodbye from the LKM!\n");
}
/** #brief The device open function that is called each time the device is opened
* This will only increment the numberOpens counter in this case.
* #param inodep A pointer to an inode object (defined in linux/fs.h)
* #param filep A pointer to a file object (defined in linux/fs.h)
*/
static int dev_open(struct inode *inodep, struct file *filep){
numberOpens++;
printk(KERN_INFO "DiasDD: Device has been opened %d time(s)\n", numberOpens);
return 0;
}
/** #brief This function is called whenever device is being read from user space i.e. data is
* being sent from the device to the user. In this case it uses the copy_to_user() function to
* send the buffer string to the user and captures any errors.
* #param filep A pointer to a file object (defined in linux/fs.h)
* #param buffer The pointer to the buffer to which this function writes the data
* #param len The length of the b
* #param offset The offset if required
*/
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
int error_count = 0;
// copy_to_user has the format ( * to, *from, size) and returns 0 on success
error_count = copy_to_user(buffer, message, size_of_message);
if (error_count==0){ // if true then have success
printk(KERN_INFO "DiasDD: Sent %d characters to the user\n", size_of_message);
return (size_of_message=0); // clear the position to the start and return 0
}
else {
printk(KERN_INFO "DiasDD: Failed to send %d characters to the user\n", error_count);
return -EFAULT; // Failed -- return a bad address message (i.e. -14)
}
}
/** #brief This function is called whenever the device is being written to from user space i.e.
* data is sent to the device from the user. The data is copied to the message[] array in this
* LKM using the sprintf() function along with the length of the string.
* #param filep A pointer to a file object
* #param buffer The buffer to that contains the string to write to the device
* #param len The length of the array of data that is being passed in the const char buffer
* #param offset The offset if required
*/
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
sprintf(message, "%s(%zu letters)", buffer, len); // appending received string with its length
size_of_message = strlen(message); // store the length of the stored message
printk(KERN_INFO "DiasDD: Received %zu characters from the user\n", len);
printk(KERN_INFO "DiasDD: Received message: %s\n", buffer);
return len;
}
/** #brief The device release function that is called whenever the device is closed/released by
* the user space program
* #param inodep A pointer to an inode object (defined in linux/fs.h)
* #param filep A pointer to a file object (defined in linux/fs.h)
*/
static int dev_release(struct inode *inodep, struct file *filep){
printk(KERN_INFO "DiasDD: Device successfully closed\n");
return 0;
}
/** #brief A module must use the module_init() module_exit() macros from linux/init.h, which
* identify the initialization function at insertion time and the cleanup function (as
* listed above)
*/
module_init(diasdd_init);
module_exit(diasdd_exit);
User Process:
/**
* #file testdiasdd.c
* #date 16 May 2017
* #version 0.1
* #brief A Linux user space program that communicates with the diasdd.c LKM. It passes a
* string to the LKM and reads the response from the LKM. For this example to work the device
* must be called /dev/diasdd.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/sysinfo.h> // sysinfo
#include <unistd.h> // sysconf
#define BUFFER_LENGTH 256 ///< The buffer length (crude but fine)
static char receive[BUFFER_LENGTH]; ///< The receive buffer from the LKM
int main(){
int ret, fd;
char stringToSend[BUFFER_LENGTH];
printf("Starting device test code example...\n");
fd = open("/dev/diasdd", O_RDWR); // Open the device with read/write access
if (fd < 0){
perror("Failed to open the device...");
return errno;
}
printf("Type in a short string to send to the kernel module:\n");
scanf("%[^\n]%*c", stringToSend); // Read in a string (with spaces)
printf("Writing message to the device [%s].\n", stringToSend);
ret = write(fd, stringToSend, strlen(stringToSend)); // Send the string to the LKM
if (ret < 0){
perror("Failed to write the message to the device.");
return errno;
}
printf("Press ENTER to read back from the device...\n");
getchar();
printf("Reading from the device...\n");
ret = read(fd, receive, BUFFER_LENGTH); // Read the response from the LKM
if (ret < 0){
perror("Failed to read the message from the device.");
return errno;
}
printf("The received message is: [%s]\n", receive);
printf("End of the program\n");
struct sysinfo info;
if (sysinfo(&info) != 0)
printf("\n");
printf("Current system info:\n");
printf("Uptime: %ld:%ld:%ld\n", info.uptime/3600, info.uptime%3600/60, info.uptime%60);
printf("Total RAM: %ld MB\n", info.totalram/1024/1024);
printf("Free RAM: %ld MB\n", (info.totalram-info.freeram)/1024/1024);
printf("Shared RAM: %ld MB\n", info.sharedram/1024/1024);
printf("Memory used by buffers: %ld MB\n", info.bufferram/1024/1024);
printf("Free swap space size: %ld bytes\n", info.freeswap);
printf("Process count: %d\n", info.procs);
return 0;
}
This is my Makefile:
obj-m := diasdd.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
$(CC) testdiasdd.c -o test
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
rm test
Related
In the case of linux kernel device drivers there is the file_operations struct, or fops struct, which allows the driver to define handlers for various file operations.
My question is about the .release fop handler.
I know the release handler will only be called when the last file descriptor (fd) for the file object is closed (or munmapped). This is done when fput is called on the file and the file->f_count reaches 0.
However - I am unclear on if other file operations can be running simultaneously in a another thread when release is entered.
For example:
could 1 thread of a process be inside the ioctl handler for the file (or fd), while another thread of the same process is inside of the release handler?
Can release be a factor in race conditions for the file object?
could 1 thread of a process be inside the ioctl handler for the file (or fd), while another thread of the same process is inside of the release handler?
No. The release entry point is called when the reference counter on the
file entry is 0. ioctl() increments the reference counter on the file. So, the release entry point will not be called while an ioctl() is on tracks.
Foreword
The source code discussed below is:
GLIBC 2.31
Linux 5.4
GLIBC's pthread management
The GLIBC's pthread_create() actually involves a clone() system call with
the following flags:
CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
According to the manual of clone(), the CLONE_FILES flag makes the threads of a process
share the same file descriptor table. Any file descriptor created by
one thread is also valid in the other threads. Similarly, if one thread closes a file descriptor, or changes its associated flags (using the fcntl() F_SETFD operation), the other threads are also affected.
clone() on the kernel side
When clone() is passed CLONE_FILES, the files_struct is not duplicated but a reference counter is incremented. As a consequence, the task structures of both threads point on the same files_struct (files field):
. The task structure is defined in include/linux/sched.h:
struct task_struct {
[...]
/* Open file information: */
struct files_struct *files; /// <==== Table of open files shared between thread
[...]
. In kernel/fork.c, the clone() service calls copy_files() to increment the reference counter on the files_struct
static int copy_files(unsigned long clone_flags, struct task_struct *tsk)
{
struct files_struct *oldf, *newf;
int error = 0;
/*
* A background process may not have any files ...
*/
oldf = current->files;
if (!oldf)
goto out;
if (clone_flags & CLONE_FILES) {
atomic_inc(&oldf->count); // <==== Ref counter incremented: files_struct is shared
goto out;
}
newf = dup_fd(oldf, &error);
if (!newf)
goto out;
tsk->files = newf;
error = 0;
out:
return error;
}
. The files_struct is defined in include/linux/fdtable.h:
/*
* Open file table structure
*/
struct files_struct {
/*
* read mostly part
*/
atomic_t count; // <==== Reference counter
bool resize_in_progress;
wait_queue_head_t resize_wait;
struct fdtable __rcu *fdt;
struct fdtable fdtab;
/*
* written part on a separate cache line in SMP
*/
spinlock_t file_lock ____cacheline_aligned_in_smp;
unsigned int next_fd;
unsigned long close_on_exec_init[1];
unsigned long open_fds_init[1];
unsigned long full_fds_bits_init[1];
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
ioctl() operation
ioctl() system call is defined fs/ioctl.c. It calls fdget() first to increment the reference counter on the file entry, do the requested operation and then call fdput()
int ksys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
int error;
struct fd f = fdget(fd);
if (!f.file)
return -EBADF;
error = security_file_ioctl(f.file, cmd, arg);
if (!error)
error = do_vfs_ioctl(f.file, fd, cmd, arg);
fdput(f);
return error;
}
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{
return ksys_ioctl(fd, cmd, arg);
}
The file entry is defined in include/linux/fs.h. Its reference counter is the f_count field:
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
/*
* Protects f_ep_links, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
enum rw_hint f_write_hint;
atomic_long_t f_count; // <===== Reference counter
unsigned int f_flags;
[...]
} __randomize_layout
__attribute__((aligned(4)));
Example
Here is a simple device driver into which the file operations merely display a message when they are triggered. The ioctl() entry makes the caller sleep 5 seconds:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/delay.h>
MODULE_LICENSE("GPL");
#define DEVICE_NAME "device"
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static long int device_ioctl(struct file *, unsigned int, unsigned long);
static int device_flush(struct file *, fl_owner_t);
static const struct file_operations fops = {
.owner = THIS_MODULE,
.read = device_read,
.write = device_write,
.unlocked_ioctl = device_ioctl,
.open = device_open,
.flush = device_flush,
.release = device_release
};
struct cdev *device_cdev;
dev_t deviceNumbers;
static int __init init(void)
{
// This returns the major number chosen dynamically in deviceNumbers
int ret = alloc_chrdev_region(&deviceNumbers, 0, 1, DEVICE_NAME);
if (ret < 0) {
printk(KERN_ALERT "Error registering: %d\n", ret);
return -1;
}
device_cdev = cdev_alloc();
cdev_init(device_cdev, &fops);
ret = cdev_add(device_cdev, deviceNumbers, 1);
printk(KERN_INFO "Device initialized (major number is %d)\n", MAJOR(deviceNumbers));
return 0;
}
static void __exit cleanup(void)
{
unregister_chrdev_region(deviceNumbers, 1);
cdev_del(device_cdev);
printk(KERN_INFO "Device unloaded\n");
}
static int device_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device open\n");
return 0;
}
static int device_flush(struct file *file, fl_owner_t id)
{
printk(KERN_INFO "Device flush\n");
return 0;
}
static int device_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "Device released\n");
return 0;
}
static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
printk(KERN_INFO "Device write\n");
return len;
}
static ssize_t device_read(struct file *filp, char *buff, size_t len, loff_t * off)
{
printk(KERN_INFO "Device read\n");
return 0;
}
static long int device_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
{
printk(KERN_INFO "Device ioctl enter\n");
msleep_interruptible(5000);
printk(KERN_INFO "Device ioctl out\n");
return 0;
}
module_init(init);
module_exit(cleanup);
Here is a user space program which involves the main thread and a secondary one. The main thread opens the above device and waits for the secondary thread to start (barrier) before closing the device after 1 second. Meanwhile, the secondary thread calls ioctl() on the above device which makes it sleep 5 seconds. Then it calls ioctl() a second time before exiting.
The expected behavior is to make the main thread close the device file while the secondary thread is running the ioctl().
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
static int dev_fd;
static pthread_barrier_t barrier;
void *entry(void *arg)
{
int rc;
printf("Thread running...\n");
// Rendez-vous with main thread
pthread_barrier_wait(&barrier);
rc = ioctl(dev_fd, 0);
printf("rc = %d, errno = %d\n", rc, errno);
rc = ioctl(dev_fd, 0);
printf("rc = %d, errno = %d\n", rc, errno);
return NULL;
}
int main(void)
{
pthread_t tid;
dev_fd = open("/dev/device", O_RDWR);
pthread_barrier_init(&barrier, NULL, 2);
pthread_create(&tid,NULL, entry, NULL);
pthread_barrier_wait(&barrier);
sleep(1);
close(dev_fd);
pthread_join(tid,NULL);
return 0;
}
Installation of the kernel module:
$ sudo insmod ./device.ko
$ dmesg
[13270.589766] Device initialized (major number is 237)
$ sudo mknod /dev/device c 237 0
$ sudo chmod 666 /dev/device
$ ls -l /dev/device
crw-rw-rw- 1 root root 237, 0 janv. 27 10:55 /dev/device
The execution of the program shows that the first ioctl() makes the thread wait 5 seconds. But the second returns in error with EBADF (9) because meanwhile the device file has been closed by the main thread:
$ gcc p1.c -lpthread
$ ./a.out
Thread running...
rc = 0, errno = 0
rc = -1, errno = 9
In the kernel log, we can see that the close() in the main thread merely triggered a flush() operation on the device while the first ioctl() was on tracks in the secondary thread. Then, once the first ioctl() returned, the internals of the kernel freed the file entry (reference counter dropped to 0) and so, the second ioctl() did not reach the device as the file descriptor no longer referenced an opened file. Hence, the EBADF error on the second call:
[13270.589766] Device initialized (major number is 237)
[13656.862951] Device open <==== Open() in the main thread
[13656.863315] Device ioctl enter <==== 1st ioctl() in secondary thread
[13657.863523] Device flush <==== 1 s later, flush() = close() in the main thread
[13661.941238] Device ioctl out <==== 5 s later, the 1st ioctl() returns
[13661.941244] Device released <==== The file is released because the reference counter reached 0
I developed a kernel module which allocates some kernel memory and remaps it to physical registers of an FPGA when user software opens the device, it also writes in a hardware register which triggers an interruption which is finally used by the probing functions of the kernel to detect the IRQ number at module init, which is 61 in my case. (I got to this point reading the excellent LDD3 book from O'Reilly, but since I'm a newbie in the kernel world I have some trouble getting my driver working well)
Thereby, I am accessing hardware registers from the kernel itself and from the user space using a small soft that I named "regedit". To access the registers from the kernel I used ioremap and I wrote the mmap function to allow regedit to access the registers from the user space using remap_pfn_range.
My first problem is that I suppose there is a better way than separately calling ioremap and remap_pfn_range to do the same thing, but I don't know how to allocate memory, remap it, access it from the kernel and by the same time provide it to the user space.
My second problem is that when I install the module, I see that my driver is able to read and write the registers using ioremap because I successfully detect the IRQ number by triggering an interrupt (by writing the register at offset 0), and when opening the device, my irq handler routine is called and successfully acknowledges the interrupt by writing 0 in the register. But, because there is a but, when I try to read the same register using my soft regedit, I get a bus error.
My guess is that only three registers are physically wired (offsets 0, 4 and 8) and maybe when I think I'm reading a single 32 bits register, the kernel is in fact reading a larger buffer (PAGE_SIZE aligned I presume) and accesses a forbidden area. (To prove that the problem is from my driver I used /dev/mem in my regedit soft, and it's working fine)
I am using a linux kernel 3.12 on a Xilinx Zynq ZC702 board using a processor ARM Cortex A9.
Here is the code of my driver :
Driver Header File
#ifndef DRIVER_H_
#define DRIVER_H_
/* --------------------------------------------------------------
* External References
* ------------------------------------------------------------*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/moduleparam.h>
/* --------------------------------------------------------------
* Application Includes
* ------------------------------------------------------------*/
/* --------------------------------------------------------------
* Constants Definition
* ------------------------------------------------------------*/
#define MODULE_NAME "mydriver"
#define DEFAULT_MAJOR_NUMBER 0 // If zero, major number will be automatically allocated
#define DEFAULT_MINOR_NUMBER 0
#define NB_DEVICES 1 // Number of devices to register
/*
* Hardware defines
*/
#define NB_PAGES 256 // Number of pages of the memory mapping
#define REG_IRQ 0x43C00000 // IRQ register address
/*
* Modules params
*/
static unsigned int irq_param = 0;
/*
* Kernel module information
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AwaX");
MODULE_VERSION("0.1");
MODULE_ALIAS(MODULE_NAME);
MODULE_DESCRIPTION("Kernel module which handles the hardware interrupts and process them");
module_param(irq_param, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(irq_param, "The IRQ line number to be used");
/* --------------------------------------------------------------
* Macros Definition
* ------------------------------------------------------------*/
#define LOG(kernLvl, str, tag) printk(kernLvl "%-6.6s %s() : "str"\n", tag, (char*) __func__);
#define LOGA(kernLvl, str, tag,...) printk(kernLvl "%-6.6s %s() : "str"\n", tag, (char*) __func__, __VA_ARGS__);
#define LOG_TRACE(str) LOG(KERN_DEBUG, str, "KTRACE")
#define LOG_DEBUG(str) LOG(KERN_INFO, str, "KDEBUG")
#define LOG_INFO(str) LOG(KERN_NOTICE, str, "KINFO")
#define LOG_IT(str) LOG(KERN_NOTICE, str, "IT")
#define LOG_WARN(str) LOG(KERN_WARNING, str, "KWARN")
#define LOG_ERROR(str) LOG(KERN_ERR, str, "KERROR")
#define LOG_FATAL(str) LOG(KERN_ALERT, str, "KFATAL")
#define LOG_TRACE_(str,...) LOGA(KERN_DEBUG, str, "KTRACE", __VA_ARGS__)
#define LOG_DEBUG_(str,...) LOGA(KERN_INFO, str, "KDEBUG", __VA_ARGS__)
#define LOG_INFO_(str,...) LOGA(KERN_NOTICE, str, "KINFO", __VA_ARGS__)
#define LOG_IT_(str,...) LOGA(KERN_NOTICE, str, "IT", __VA_ARGS__)
#define LOG_WARN_(str,...) LOGA(KERN_WARNING, str, "KWARN", __VA_ARGS__)
#define LOG_ERROR_(str,...) LOGA(KERN_ERR, str, "KERROR", __VA_ARGS__)
#define LOG_FATAL_(str,...) LOGA(KERN_ALERT, str, "KFATAL", __VA_ARGS__)
/* --------------------------------------------------------------
* Types Definition
* ------------------------------------------------------------*/
/*
* Internal data structure
*/
typedef struct _module_data {
int major; // Major device number
int minor; // Minor device number
dev_t mmap_dev; // Holds device numbers (major and minor)
struct cdev mmap_cdev; // Kernel internal struct representing the device
int *vmalloc_area; // Pointer to the vmalloc'd area - always page aligned
int *kmalloc_area; // Pointer to the kmalloc'd area, rounded up to a page boundary
void *kmalloc_ptr; // Original pointer for kmalloc'd area as returned by kmalloc
// Mapping
volatile int *map_area; // Base address of the registers kernel memory
volatile void *io_area; // Base address of the registers i/o physical memory
// Interrupts
unsigned int irq; // Interrupt number
} module_data;
/* --------------------------------------------------------------
* Functions Definition
* ------------------------------------------------------------*/
#endif /* DRIVER_H_ */
Driver Source File
/* --------------------------------------------------------------
* External References
* ------------------------------------------------------------*/
#include "driver.h"
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/memory.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm-generic/errno-base.h>
/* --------------------------------------------------------------
* Application Includes
* ------------------------------------------------------------*/
/* --------------------------------------------------------------
* Static data
* ------------------------------------------------------------*/
// Internal data
static module_data *_module;
/* --------------------------------------------------------------
* Local Functions Definition
* ------------------------------------------------------------*/
/*
* Module functions
*/
static int __init my_module_init (void);
static void __exit my_module_cleanup (void);
static int my_module_open (struct inode *inode, struct file *filp);
static int my_module_release (struct inode *inode, struct file *filp);
static int my_module_mmap (struct file *filp, struct vm_area_struct *vma);
/*
* Static functions
*/
static void my_vma_open (struct vm_area_struct *vma);
static void my_vma_close (struct vm_area_struct *vma);
static irqreturn_t my_irq_handler (int irq, void *dev_id, struct pt_regs *regs);
static int my_allocate_device (void);
static int my_register_device (void);
static unsigned int my_probe_irq (void);
static int my_mmap_kmem (struct file *filp, struct vm_area_struct *vma);
static int my_mmap_vmem (struct file *filp, struct vm_area_struct *vma);
/*
* Specifies the functions associated with the device operations.
*/
static struct file_operations _module_fops = {
.owner = THIS_MODULE,
.open = my_module_open,
.release = my_module_release,
.mmap = my_module_mmap,
};
/*
* Specifies the functions associated with the remap operations.
*/
static struct vm_operations_struct _module_vmops = {
.open = my_vma_open,
.close = my_vma_close,
};
/* --------------------------------------------------------------
* Functions Implementation
* ------------------------------------------------------------*/
/*****************************************************************************
* Initialization function of the module which allocates the major and minor
* numbers and registers the device to /proc/devices. The creation of the
* device in /dev must be done by an external script.
*
* #return
* SUCCESS : 0
* FAILURE : Negative error code.
*****************************************************************************/
static int __init my_module_init (void) {
unsigned int irqprobe = 0;
int err = 0;
int i = 0;
LOG_INFO_("Initializing module %s", MODULE_NAME);
LOG_INFO_("Module param : irq_param = %u", irq_param);
/*
* Init internal data
*/
_module = kmalloc(sizeof(module_data), GFP_KERNEL);
memset(_module, 0, sizeof(module_data));
if (_module == NULL) {
goto out;
}
_module->major = DEFAULT_MAJOR_NUMBER;
_module->minor = DEFAULT_MINOR_NUMBER;
_module->map_area = NULL;
_module->io_area = NULL;
_module->irq = irq_param;
/*
* Allocate kmalloc memory
*/
_module->kmalloc_ptr = kmalloc((NB_PAGES + 2) * PAGE_SIZE, GFP_KERNEL);
if (_module->kmalloc_ptr == NULL) {
err = -ENOMEM;
goto out_kfree;
}
// Round it up to the page bondary
_module->kmalloc_area = (int *) ((((unsigned long) _module->kmalloc_ptr) + PAGE_SIZE - 1) & PAGE_MASK);
// Use the kernel memory to access registers from the module
_module->map_area = _module->kmalloc_area;
/*
* Allocate vmalloc memory
*/
_module->vmalloc_area = (int *) vmalloc(NB_PAGES * PAGE_SIZE);
if (_module->vmalloc_area == NULL) {
err = -ENOMEM;
goto out_vfree;
}
/*
* Remap physical addresses
*/
_module->io_area = ioremap(REG_IRQ, NB_PAGES * PAGE_SIZE);
if (_module->io_area == NULL) {
LOG_ERROR_("Physical memory remapping failed (base_addr=%#x, size=%#lx)", REG_IRQ, NB_PAGES * PAGE_SIZE);
goto out_iofree;
}
/*
* Allocates the device numbers
*/
err = my_allocate_device();
if (err) {
LOG_ERROR_("Device allocation failed with code : %d", err);
goto out_unalloc_region;
}
// If no IRQ number has been specified
if (_module->irq <= 0) {
// Probes for an IRQ line number
LOG_INFO("Probing IRQ number...");
irqprobe = my_probe_irq();
if (irqprobe == 0) { // Probe failed
LOG_ERROR("IRQ probing failed : cannot find IRQ number");
} else if (irqprobe < 0) { // Probe error
LOG_ERROR_("IRQ probing failed with error code : %d", err);
} else {
// If an irq number is found
LOG_INFO_("IRQ number detected : %u", irqprobe);
_module->irq = irqprobe;
}
} else { // If an irq number has been specified via a module parameter
LOG_INFO_("IRQ number param specified : irq=%u", _module->irq);
}
// Registers the device making it live immediately
err = my_register_device();
if (err) {
LOG_ERROR_("Device register failed with code : %d", err);
goto out_unregister;
}
LOG_INFO_("Module %s initialized successfully !", MODULE_NAME);
return 0;
/*
* Error fallbacks
*/
out_unregister:
LOG_DEBUG_("Deallocating chrdev for %s", MODULE_NAME);
cdev_del(&_module->mmap_cdev);
// Unreserve the pages
LOG_DEBUG("Unreserving memory pages");
for (i = 0; i < NB_PAGES * PAGE_SIZE; i += PAGE_SIZE) {
SetPageReserved(vmalloc_to_page((void *) (((unsigned long) _module->vmalloc_area) + i)));
SetPageReserved(virt_to_page(((unsigned long )_module->kmalloc_area) + i));
}
out_unalloc_region:
LOG_DEBUG_("Unregistering device %s", MODULE_NAME);
unregister_chrdev_region(_module->mmap_dev, NB_DEVICES);
out_iofree:
iounmap(_module->io_area);
out_vfree:
vfree(_module->vmalloc_area);
out_kfree:
kfree(_module->kmalloc_ptr);
out:
return err;
}
/*****************************************************************************
* Cleanup function of the module which unregisters the major number and
* removes the created device from the system.
*
* #return
* void
*****************************************************************************/
static void __exit my_module_cleanup (void) {
LOG_INFO_("Cleaning up module %s", MODULE_NAME);
// Unregisters a range of device numbers.
unregister_chrdev_region(_module->mmap_dev, NB_DEVICES);
LOG_INFO_("Unregistered device %s", MODULE_NAME);
// Free kernel memory
vfree(_module->vmalloc_area);
kfree(_module->kmalloc_ptr);
kfree(_module);
// Remove the cdev from the system, possibly freeing the structure itself
cdev_del(&_module->mmap_cdev);
LOG_INFO_("Deallocated chrdev for %s", MODULE_NAME);
}
/*****************************************************************************
* Open function of the module which makes this module accessible from the
* user space.
*
* #return
* SUCCESS : 0
* FAILURE : Negative error code.
*****************************************************************************/
static int my_module_open (struct inode *inode, struct file *filp) {
int err = 0;
// If an interrupt line has been requested
if (_module->irq > 0) {
// Registers the interrupt handler to the kernel
err = request_irq(_module->irq, (irq_handler_t) my_irq_handler, 0, MODULE_NAME, _module);
if (err) {
LOG_ERROR_("%s : Cannot get assigned irq %d, request_irq() failed, code=%d", MODULE_NAME, _module->irq, err);
_module->irq = -1;
return err;
} else {
LOG_INFO_("IRQ number %u assigned successfully to module %s", _module->irq, MODULE_NAME);
}
} else {
LOG_ERROR("Invalid IRQ number : the device will not see the hardware interrupts");
}
LOG_INFO_("%s opened successfully", MODULE_NAME);
return 0;
}
/*****************************************************************************
* Close function of the module which releases the use of this module from
* the user space.
*
* #return
* SUCCESS : 0
* FAILURE : Negative error code.
*****************************************************************************/
static int my_module_release (struct inode *inode, struct file *filp) {
LOG_INFO_("%s closing...", MODULE_NAME);
// Removes the interrupt handler from kernel
LOG_INFO_("Releasing irq number %u", _module->irq);
free_irq(_module->irq, _module);
LOG_INFO_("%s closed", MODULE_NAME);
return 0;
}
/*****************************************************************************
* Creates a new mapping in the virtual address space of the calling process.
* It makes possible for a user process to access physical memory in the
* kernel space.
*
* #param filp
* The file or device.
* #param vma
* The virtual memory area into which the page range is being
* mapped.
* #return
* SUCCESS : 0
* FAILURE : Negative error code.
*****************************************************************************/
static int my_module_mmap (struct file *filp, struct vm_area_struct *vma) {
unsigned long start = vma->vm_start; // Virtual address where remapping begins
unsigned long end = vma->vm_end;
unsigned long length = end - start;
unsigned long maxLength = NB_PAGES * PAGE_SIZE;
unsigned long pgoff = vma->vm_pgoff; // PFN of the physAddr to which vAddr is mapped
// Checks length - do not allow larger mappings than the number of pages allocated
if (length > maxLength) {
LOG_ERROR_("Specified virtual memory area is too big : 0x%lx , 0x%lx", length, maxLength);
return -EIO;
}
// At offset 0
if (pgoff == 0) {
// we map the vmalloc'd area
LOG_DEBUG_("Allocating virtual memory, start=%#lx, length=%#lx, pgoff=%#lx", start, length, pgoff);
return my_mmap_vmem(filp, vma);
} else {
// we map the kmalloc'd area
LOG_DEBUG_("Allocating kernel memory, start=%#lx, length=%#lx, pgoff=%#lx", start, length, pgoff);
return my_mmap_kmem(filp, vma);
}
return -EIO;
}
static void my_vma_open (struct vm_area_struct* vma) {
LOG_INFO_("%s VMA open, virtAddr=%#lx, physAddr=%#lx", MODULE_NAME, vma->vm_start, vma->vm_pgoff << PAGE_SHIFT);
}
static void my_vma_close (struct vm_area_struct* vma) {
LOG_INFO_("%s VMA close", MODULE_NAME);
}
/*****************************************************************************
* Interrupt handler which :
* - Reads the registers to know the source of the IT,
* - Clears the IT and wakes up the handler task.
*
* #param irq
* The interrupt number requested.
* #param dev_id
* Pointer to the device structure passed to the function
* request_irq() containing internal data (used when the driver
* manages several instances of the same device).
* #param regs
* Used for debug. Holds a snapshot of the processor's context
* before the processor entered interrupted code.
* #return
* SUCCESS : 0
* FAILURE : Negative error code.
*****************************************************************************/
static irqreturn_t my_irq_handler (int irq, void *dev_id, struct pt_regs *regs) {
module_data *moduleData = dev_id;
LOG_IT_("%s interrupted, interrupt number = %d", MODULE_NAME, irq);
if (moduleData != NULL) {
// Resets irq
iowrite32(0x0, _module->io_area);
wmb();
} else {
LOG_ERROR("Device structure is NULL");
}
return IRQ_HANDLED;
}
/*****************************************************************************
* Allocates major and minor numbers for this device.
*
* #return
* SUCCESS : 0
* FAILURE : Negative error code.
*****************************************************************************/
static int my_allocate_device (void) {
int err = 0;
// If a non zero major number is specified
if (_module->major) {
// Updates the dev structure used as input for register_chrdev_region
_module->mmap_dev = MKDEV(_module->major, _module->minor);
// Registers a range of device numbers.
err = register_chrdev_region(_module->mmap_dev, NB_DEVICES, MODULE_NAME);
} else { // If major number is zero, then allocates it dynamically
// Allocates a range of char device numbers chosen dynamically
err = alloc_chrdev_region(&_module->mmap_dev, _module->minor, NB_DEVICES, MODULE_NAME);
_module->major = MAJOR(_module->mmap_dev);
}
// Checks result
if (err) {
LOG_ERROR_("cannot get major number %d", _module->major);
return err;
} else {
LOG_INFO_("Registered device %s : major=%d, minor=%d", MODULE_NAME, _module->major, _module->minor);
}
// Initializes cdev and file operations
cdev_init(&_module->mmap_cdev, &_module_fops);
_module->mmap_cdev.owner = THIS_MODULE;
_module->mmap_cdev.ops = &_module_fops;
return 0;
}
/*****************************************************************************
* Registers the device, makes it live immediately, therefore all
* initialization routines must be done before calling this function.
*
* #return
* SUCCESS : 0
* FAILURE : Negative error code.
*****************************************************************************/
static int my_register_device (void) {
int err = 0;
// Adds the device to the system making it live immediately
err = cdev_add(&_module->mmap_cdev, _module->mmap_dev, NB_DEVICES);
if (err) {
LOG_ERROR("Could not allocate chrdev");
return err;
} else {
LOG_INFO_("Allocated chrdev for %s", MODULE_NAME);
}
return 0;
}
/*****************************************************************************
* This function uses the probe functions of the kernel to find the irq
* number of the hardware device.
*
* #return
* SUCCESS : 0
* FAILURE : Negative error code.
*****************************************************************************/
static unsigned int my_probe_irq (void) {
unsigned int irq = -1;
int count = 0;
do {
volatile unsigned long mask;
// Reset the interrupts
iowrite32(0x0, _module->io_area);
wmb(); // Memory barrier
// Start kernel probing
mask = probe_irq_on();
// Trigger all interrupts
iowrite32(0xffffffff, _module->io_area);
wmb(); // Memory barrier
// Wait for it
ndelay(1000);
// Try to find which interrupt occurred
irq = probe_irq_off(mask);
} while (irq < 0 && count++ < 5);
return irq;
}
/*****************************************************************************
* Helper function, mmap's the vmalloc'd area which is not physically
* contiguous.
*
* #return
* SUCCESS : 0
* FAILURE : Negative error code.
*****************************************************************************/
static int my_mmap_vmem (struct file *filp, struct vm_area_struct *vma) {
unsigned long start = vma->vm_start;
unsigned long end = vma->vm_end;
unsigned long pfn = 0;
long length = end - start;
int ret = 0;
// Check length - do not allow larger mappings than the number of pages allocated
if (length > NB_PAGES * PAGE_SIZE) {
LOG_ERROR_("Specified length (%lu) is larger than the number of pages allocated", length);
return -EIO;
}
// Loop over all pages, map it page individually
while (length > 0) {
pfn = vmalloc_to_pfn(_module->vmalloc_area);
ret = remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED);
if (ret < 0) {
LOG_ERROR_("remap_pfn_range() failed with error %d, addr=%#lx, offset=%#lx", ret, start, pfn);
return ret;
}
start += PAGE_SIZE;
_module->vmalloc_area += PAGE_SIZE;
length -= PAGE_SIZE;
}
vma->vm_ops = &_module_vmops; // Specifies open_vma() and close_vma() functions
my_vma_open(vma); // Calls explicitely open_vma() as its not done by calling mmap()
return 0;
}
/*****************************************************************************
* Helper function, mmap's the kmalloc'd area which is physically contiguous.
*
* #return
* SUCCESS : 0
* FAILURE : Negative error code.
*****************************************************************************/
static int my_mmap_kmem (struct file *filp, struct vm_area_struct *vma) {
unsigned long start = vma->vm_start;
unsigned long end = vma->vm_end;
long length = end - start;
int ret = 0;
// Check length - do not allow larger mappings than the number of pages allocated
if (length > NB_PAGES * PAGE_SIZE) {
LOG_ERROR_("Specified length (%lu) is larger than the number of pages allocated", length);
return -EIO;
}
// Map the whole physically contiguous area in one piece
//pfn = virt_to_phys((void *) _module->kmalloc_area) >> PAGE_SHIFT;
ret = remap_pfn_range(vma, start, vma->vm_pgoff, length, vma->vm_page_prot);
if (ret < 0) {
return ret;
}
vma->vm_ops = &_module_vmops; // Specifies open_vma() and close_vma() functions
my_vma_open(vma); // Calls explicitely open_vma() as its not done by calling mmap()
return 0;
}
/*
* MANDATORY
*
* Used by kernel to load this module and specifies its entry points.
*/
module_init(my_module_init);
module_exit(my_module_cleanup);
I am writing a character device that will be given a processes pid and return the parent process id, start time, and number of siblings. I am using this site: http://tldp.org/LDP/lkmpg/2.6/html/x892.html#AEN972 as a guide.
I would like to add an argument into the read call of the character driver but it is not letting me do that. My code looks like this:
/*
* chardev.c - Create an input/output character device
*/
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
#include <linux/fs.h>
#include <asm/uaccess.h> /* for get_user and put_user */
#include "chardev.h"
#define SUCCESS 0
#define DEVICE_NAME "char_dev"
#define BUF_LEN 80
struct procinfo {
pid_t pid;
pid_t ppid;
struct timespec start_time;
int num_sib;
};
/*
* Is the device open right now? Used to prevent
* concurent access into the same device
*/
static int Device_Open = 0;
/*
* The message the device will give when asked
*/
static char Message[BUF_LEN];
/*
* How far did the process reading the message get?
* Useful if the message is larger than the size of the
* buffer we get to fill in device_read.
*/
static char *Message_Ptr;
/*
* This is called whenever a process attempts to open the device file
*/
static int device_open(struct inode *inode, struct file *file)
{
#ifdef DEBUG
printk(KERN_INFO "device_open(%p)\n", file);
#endif
/*
* We don't want to talk to two processes at the same time
*/
if (Device_Open)
return -EBUSY;
Device_Open++;
/*
* Initialize the message
*/
Message_Ptr = Message;
try_module_get(THIS_MODULE);
return SUCCESS;
}
static int device_release(struct inode *inode, struct file *file)
{
#ifdef DEBUG
printk(KERN_INFO "device_release(%p,%p)\n", inode, file);
#endif
/*
* We're now ready for our next caller
*/
Device_Open--;
module_put(THIS_MODULE);
return SUCCESS;
}
/*
* This function is called whenever a process which has already opened the
* device file attempts to read from it.
*/
static ssize_t device_read(struct file *file, /* see include/linux/fs.h */
char __user * buffer, /* buffer to be
* filled with data */
pid_t pid,
size_t length, /* length of the buffer */
loff_t * offset)
{
/*
* Number of bytes actually written to the buffer
*/
int bytes_read = 0;
#ifdef DEBUG
printk(KERN_INFO "device_read(%p,%p,%d)\n", file, buffer, length);
#endif
/*
* If we're at the end of the message, return 0
* (which signifies end of file)
*/
if (*Message_Ptr == 0)
return 0;
/*
* Actually put the data into the buffer
*/
while (length && *Message_Ptr) {
/*
* Because the buffer is in the user data segment,
* not the kernel data segment, assignment wouldn't
* work. Instead, we have to use put_user which
* copies data from the kernel data segment to the
* user data segment.
*/
put_user(*(Message_Ptr++), buffer++);
length--;
bytes_read++;
}
#ifdef DEBUG
printk(KERN_INFO "Read %d bytes, %d left\n", bytes_read, length);
#endif
/*
* Read functions are supposed to return the number
* of bytes actually inserted into the buffer
*/
return bytes_read;
}
/*
* This function is called when somebody tries to
* write into our device file.
*/
static ssize_t
device_write(struct file *file,
const char __user * buffer, size_t length, loff_t * offset)
{
int i;
#ifdef DEBUG
printk(KERN_INFO "device_write(%p,%s,%d)", file, buffer, length);
#endif
for (i = 0; i < length && i < BUF_LEN; i++)
get_user(Message[i], buffer + i);
Message_Ptr = Message;
/*
* Again, return the number of input characters used
*/
return i;
}
/*
* This function is called whenever a process tries to do an ioctl on our
* device file. We get two extra parameters (additional to the inode and file
* structures, which all device functions get): the number of the ioctl called
* and the parameter given to the ioctl function.
*
* If the ioctl is write or read/write (meaning output is returned to the
* calling process), the ioctl call returns the output of this function.
*
*/
long device_ioctl(struct file *file, /* see include/linux/fs.h */
unsigned int ioctl_num, /* number and param for ioctl */
unsigned long ioctl_param)
{
int i;
char *temp;
char ch;
/*
* Switch according to the ioctl called
*/
switch (ioctl_num) {
case IOCTL_SET_MSG:
/*
* Receive a pointer to a message (in user space) and set that
* to be the device's message. Get the parameter given to
* ioctl by the process.
*/
temp = (char *)ioctl_param;
/*
* Find the length of the message
*/
get_user(ch, temp);
for (i = 0; ch && i < BUF_LEN; i++, temp++)
get_user(ch, temp);
device_write(file, (char *)ioctl_param, i, 0);
break;
case IOCTL_GET_MSG:
/*
* Give the current message to the calling process -
* the parameter we got is a pointer, fill it.
*/
i = device_read(file, (char *)ioctl_param, 99, 0);
/*
* Put a zero at the end of the buffer, so it will be
* properly terminated
*/
put_user('\0', (char *)ioctl_param + i);
break;
case IOCTL_GET_NTH_BYTE:
/*
* This ioctl is both input (ioctl_param) and
* output (the return value of this function)
*/
return Message[ioctl_param];
break;
}
return SUCCESS;
}
/* Module Declarations */
/*
* This structure will hold the functions to be called
* when a process does something to the device we
* created. Since a pointer to this structure is kept in
* the devices table, it can't be local to
* init_module. NULL is for unimplemented functions.
*/
struct file_operations Fops = {
.read = device_read,
.write = device_write,
.unlocked_ioctl = device_ioctl,
.open = device_open,
.release = device_release, /* a.k.a. close */
};
/*
* Initialize the module - Register the character device
*/
int init_module()
{
int ret_val;
/*
* Register the character device (atleast try)
*/
ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops);
/*
* Negative values signify an error
*/
if (ret_val < 0) {
printk(KERN_ALERT "%s failed with %d\n",
"Sorry, registering the character device ", ret_val);
return ret_val;
}
printk(KERN_INFO "%s The major device number is %d.\n",
"Registeration is a success", MAJOR_NUM);
printk(KERN_INFO "If you want to talk to the device driver,\n");
printk(KERN_INFO "you'll have to create a device file. \n");
printk(KERN_INFO "We suggest you use:\n");
printk(KERN_INFO "mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM);
printk(KERN_INFO "The device file name is important, because\n");
printk(KERN_INFO "the ioctl program assumes that's the\n");
printk(KERN_INFO "file you'll use.\n");
return 0;
}
/*
* Cleanup - unregister the appropriate file from /proc
*/
void cleanup_module()
{
unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
#if 0
int ret;
/*
* Unregister the device
*/
ret = unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
/*
* If there's an error, report it
*/
if (ret < 0)
printk(KERN_ALERT "Error: unregister_chrdev: %d\n", ret);
#endif
}
I am calculating a timestamp in the kernel and later I want to transfer the timestamp from kernel to the user space. So I am using procfs for communication between kernel and user. I am using the procfile_read function for sending the data from kernel as shown below.
I modified and calculated the timestamp of the kernel code as shown below.
This code is at the network device driver level.
int netif_rx(struct sk_buff *skb)
{
__net_timestamp(skb);//I modify the code in kernel to get the timestamp and store in buffer
// or I can use : skb->tstamp = ktime_get_real(); //to get the timestamp
}
/**
* procfs2.c - create a "file" in /proc
*
*/
#include <linux/module.h> /* Specifically, a module */
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
#include <asm/uaccess.h> /* for copy_from_user */
#define PROCFS_MAX_SIZE 1024
#define PROCFS_NAME "buffer1k"
/**
* This structure hold information about the /proc file
*
*/
static struct proc_dir_entry *Our_Proc_File;
/**
* The buffer used to store character for this module
*
*/
static char procfs_buffer[PROCFS_MAX_SIZE];
/**
* The size of the buffer
*
*/
static unsigned long procfs_buffer_size = 0;
/**
* This function is called then the /proc file is read
*
*/
int
procfile_read(char *buffer,
char **buffer_location,
off_t offset, int buffer_length, int *eof, void *data)
{
int ret;
printk(KERN_INFO "procfile_read (/proc/%s) called\n", PROCFS_NAME);
if (offset > 0) {
/* we have finished to read, return 0 */
ret = 0;
} else {
/* fill the buffer, return the buffer size */
memcpy(buffer, procfs_buffer, procfs_buffer_size);
ret = procfs_buffer_size;
}
return ret;
}
/**
*This function is called when the module is loaded
*
*/
int init_module()
{
/* create the /proc file */
Our_Proc_File = create_proc_entry(PROCFS_NAME, 0644, NULL);
if (Our_Proc_File == NULL) {
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
PROCFS_NAME);
return -ENOMEM;
}
Our_Proc_File->read_proc = procfile_read;
Our_Proc_File->owner = THIS_MODULE;
Our_Proc_File->mode = S_IFREG | S_IRUGO;
Our_Proc_File->uid = 0;
Our_Proc_File->gid = 0;
Our_Proc_File->size = 37;
printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME);
return 0; /* everything is ok */
}
/**
*This function is called when the module is unloaded
*
*/
void cleanup_module()
{
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME);
}
The module initialization establishes a procfs entry with create_proc_entry(). The functions procfile_write and procfile_read are initialized to handle writes and reads on this entry. The module's cleanup_module() function, called when the module is unloaded, removes the procfs entry by calling cleanup_module().
My question is how to add the timestamp calculated into the procfile_read function and send it to the user space?
Crude and simple: set a global variable in your module with the timestamp and use snprintf to copy it into your profs_buffer. In fact, you might do snprintf directly into the provided buffer and loose the procfs_buffer all together.
There are multiple problems with this approach (it's not atomic, for one) but if you just want a crude debug or logging interface it works.
I am learning about the proc and loadable kernel module (LKM) for reading data from the kernel to the user space. I asked for some info regarding procfs in another post.
Could someone please tell me what lkm and procfs are, and where I can write the code for lkm and proc (location in kernel source code)?
You don't write code for an LKM inside kernel source (though it's possible, it's not recommended unless you're working on what will become a normally distributed module). You instead create your own directory and provide your code.
The functions you write to provide procfs interfaces is just code that is part of your LKM source.
http://linux.die.net/lkmpg/x769.html has a simple example using procfs, reproduced here:
/**
* procfs2.c - create a "file" in /proc
*
*/
#include <linux/module.h> /* Specifically, a module */
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
#include <asm/uaccess.h> /* for copy_from_user */
#define PROCFS_MAX_SIZE 1024
#define PROCFS_NAME "buffer1k"
/**
* This structure hold information about the /proc file
*
*/
static struct proc_dir_entry *Our_Proc_File;
/**
* The buffer used to store character for this module
*
*/
static char procfs_buffer[PROCFS_MAX_SIZE];
/**
* The size of the buffer
*
*/
static unsigned long procfs_buffer_size = 0;
/**
* This function is called then the /proc file is read
*
*/
int
procfile_read(char *buffer,
char **buffer_location,
off_t offset, int buffer_length, int *eof, void *data)
{
int ret;
printk(KERN_INFO "procfile_read (/proc/%s) called\n", PROCFS_NAME);
if (offset > 0) {
/* we have finished to read, return 0 */
ret = 0;
} else {
/* fill the buffer, return the buffer size */
memcpy(buffer, procfs_buffer, procfs_buffer_size);
ret = procfs_buffer_size;
}
return ret;
}
/**
* This function is called with the /proc file is written
*
*/
int procfile_write(struct file *file, const char *buffer, unsigned long count,
void *data)
{
/* get buffer size */
procfs_buffer_size = count;
if (procfs_buffer_size > PROCFS_MAX_SIZE ) {
procfs_buffer_size = PROCFS_MAX_SIZE;
}
/* write data to the buffer */
if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size) ) {
return -EFAULT;
}
return procfs_buffer_size;
}
/**
*This function is called when the module is loaded
*
*/
int init_module()
{
/* create the /proc file */
Our_Proc_File = create_proc_entry(PROCFS_NAME, 0644, NULL);
if (Our_Proc_File == NULL) {
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
PROCFS_NAME);
return -ENOMEM;
}
Our_Proc_File->read_proc = procfile_read;
Our_Proc_File->write_proc = procfile_write;
Our_Proc_File->owner = THIS_MODULE;
Our_Proc_File->mode = S_IFREG | S_IRUGO;
Our_Proc_File->uid = 0;
Our_Proc_File->gid = 0;
Our_Proc_File->size = 37;
printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME);
return 0; /* everything is ok */
}
/**
*This function is called when the module is unloaded
*
*/
void cleanup_module()
{
remove_proc_entry(PROCFS_NAME, &proc_root);
printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME);
}
The module initialization establishes a procfs entry with create_proc_entry(). The functions procfile_write and procfile_read are initialized to handle writes and reads on this entry. The module's cleanup_module() function, called when the module is unloaded, removes the procfs entry by calling cleanup_module().
You'll find a tutorial for building kernel modules at http://www.cyberciti.biz/tips/compiling-linux-kernel-module.html. The summary of that is:
1) Ensure you have kernel source installed in /usr/src.
2) Create a makefile that looks like:
obj-m = procfs2.o
KVERSION = $(shell uname -r)
all:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
3) build the module with the command make
4) load the module into memory with the command insmod procfs2.ko (do this as the root user)
Not listed in the tutorial is: if your module has problems, expect to reboot. Crashes in kernel modules will often take down your system.