How do I make sure insmod fails on error? - linux-kernel

I developed a peripheral driver for Linux. The .probe function performs the usual error checks like memory allocation failures, and also attempts to communicate with the hardware and in any type of error, deallocates any memory and returns an error code like -ENOMEM or -EIO.
The problem is, although the module probe function return -EIO when the hardware is unreachable, I still see the module is listed in lsmod output. Is it possible to make sure an insmod completely fails when there is a problem during initialization?
Here is my current probe function. All device specific functions return an appropriate error code on failure, usually -EIO.
static int mlx90399_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err;
struct mlx90399_data *data;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
dev_err(&client->dev, "Memory allocation fails\n");
err = -ENOMEM;
goto exit;
}
i2c_set_clientdata(client, data);
data->client = client;
mutex_init(&data->lock);
data->mode = MLX90399_MODE_OFF;
err = mlx90399_reset(client);
if (err < 0)
goto exit;
msleep(1); /* nominal 0.6ms from reset */
err = mlx90399_write_register_defaults(client);
if (err < 0)
goto exit;
err = mlx90399_update_scale(client);
if (err < 0)
goto exit;
data->indio_dev = iio_allocate_device(0);
if (data->indio_dev == NULL) {
err = -ENOMEM;
goto exit;
}
data->indio_dev->dev.parent = &client->dev;
data->indio_dev->info = &mlx90399_info;
data->indio_dev->dev_data = (void *)(data);
data->indio_dev->modes = INDIO_DIRECT_MODE;
mlx90399_setup_irq(client);
err = iio_device_register(data->indio_dev);
if(err < 0)
goto exit;
return 0;
exit:
kfree(data);
return err;
}

See the comment in __driver_attach():
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
To make the module initialization fail, unregister the driver and return an error code from your init function.

Note that there isn't necessarily a 1:1 relationship between a module and a device. One module may be used for several devices. With the use of device trees, for example, a device tree may declare several on-board UARTs, all using one serial device kernel module. The module's probe function would be called several times, once for each device. Just because one probe call fails, that doesn't necessarily mean the module should be unloaded.

Related

Couldn't link kernel provided function to my user defined kernel module

I'm writing a kernel module that works with page table entries, in order to implement a new page replacement policy. I want to get a list of page table entries owned by a specific process. So far I have retrieved a linked list of vma structures which basically carry information about pages owned by a process. There is a function called walk_page_vma which takes the pointer to the vma and gives back the page tables, it has been defined in mm/pagewalk.c also declared in linux/mm.h. Therefore, I have included linux/mm.h in my code.
process_pid = -1;
struct task_struct* task_list;
size_t process_counter = 0;
for_each_process(task_list) {
if (strcmp(task_list->comm, process_name) == 0){
process_pid = task_list->pid;
pr_info("found %s pid = %d \n", process_name, process_pid);
struct vm_area_struct *mmap = task_list->mm->mmap;
while(mmap != NULL){
struct mm_walk walk;
int res = walk_page_vma(mmap, &walk);
if (res == 0) {
printk("walked successfully\n");
} else {
printk("failed to walk!\n");
}
mmap = mmap->vm_next;
}
// break;
}
// pr_info("== %s [%d]\n", task_list->comm, task_list->pid);
++process_counter;
}
if (process_pid){
// pr_info("found %s pid = %d \n", process_name, process_pid);
} else {
pr_info("couldn't find %s pid. exiting! \n", process_name);
}
// printk(KERN_INFO "== Number of process: %zu\n", process_counter);
At building time, it throws a warning saying
WARNING: "walk_page_vma" [/home/myusername/Projects/ProjectModule/my_module.ko] undefined!
and is unable to load it when calling insmode.
walk_page_vma is not exported and so it can't be used in a dynamically loadable module.
You must export it by patching the kernel (be aware that such a change will be refused by upstream devs) or compile your code as "built-in".

DeviceIoControl error 1 incorrect function

I have created a device in kernel space and the access it in user space using CreateFile I am able to send ioctl to the driver and they are executed properly. The don't know how to trace what happens after WdfRequestComplete and upon return I end with error 1 (invalid function). Before this is flagged as dup please note there is a difference with this in that I write my driver ioctl and in that I am using synch io not asynch.
In user space:
fd = CreateFile(dev_path,
(FILE_GENERIC_READ | FILE_GENERIC_WRITE),
(FILE_SHARE_READ | FILE_SHARE_WRITE),
NULL, OPEN_EXISTING, 0, NULL);
// ... error checking code here
DeviceIoControl(fd, // device handler
VIRTQC_CMD_MMAP, // command to send
&inputBuffer,
inputBufferLength,
&outputBuffer,
outputBufferLength,
&returnLength,
(LPOVERLAPPED)NULL); // overlapped structure not needed using sync io
and in Kernel space
status = WdfRequestRetrieveInputBuffer(Request, InputBufferLength, &inputBuffer, NULL);
if (!NT_SUCCESS(status))
{
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
return;
}
inputVirtArg = (VirtioQCArg*)inputBuffer;
status = WdfRequestRetrieveOutputBuffer(Request, OutputBufferLength, &outputBuffer, NULL);
if (!NT_SUCCESS(status))
{
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
return;
}
outputVirtArg = (VirtioQCArg*)outputBuffer;
switch (IoControlCode)
{
case VIRTQC_CMD_MMAP:
if (PsGetCurrentThread() == irp->Tail.Overlay.Thread)
{
status = CreateAndMapMemory(device, &(inputVirtArg), &(outputVirtArg));
outputVirtArg->flag = (!NT_SUCCESS(status)) ? 0 : 1;
}
else
status = STATUS_UNSUCCESSFUL;
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
WdfRequestComplete(Request, status);
Update 1:
I have tried WdfRequestCompleteWithInformation(Request, status, OutputBufferLength); but same result.
Also, I notice that the address of inputBuffer and outputBuffer are the same.
Update 2:
I tried doing
temp = ExAllocatePoolWithTag(
NonPagedPool,
PAGE_SIZE,
MEMORY_TAG
);
// other code to
RtlCopyMemory((VirtioQCArg*)outputBuffer, temp, OutputBufferLength);
still get error 1
I had defined my ioctl cmds as enums in my linux driver (which works fine) and when implementing the driver in windows I used the same enum definition.
enum
{
// Module & Execution control (driver API)
VIRTIQC_SET = 200,
VIRTIQC_UNSET,
// more cmds.....
}
In windows defining control codes take a bit more. As explained here the CTL_CODE macro should be used to define new IOCTL control codes.
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
In my case I ended up with defining this:
#define VIRTQC_MAP CTL_CODE(FILE_DEVICE_NETWORK, 0xC8, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define VIRTQC_UNMAP CTL_CODE(FILE_DEVICE_NETWORK, 0xC9, METHOD_OUT_DIRECT, FILE_READ_DATA)
I know the function code less than 0x800 are reserved for MS but the device on my host requires this codes so I'm just providing what is being asked.

How do cdev and its associated file operation work?

Actually working on a PCI driver.
I have two PCIe cards with same device ID and vendor ID.
So to make a difference, I assign these two cards with two different MINOR numbers.
//request for device numbers
error = alloc_chrdev_region(&devt, 0, cards_found, DEVICE_NAME);
if (error == 0)
{
major = MAJOR(devt);
printk(KERN_INFO "(drv_init): MAJOR number is %d\n", major);
printk(KERN_INFO "(drv_init): MINOR number range from 0 to %d\n", cards_found-1);
cdevs = cdev_alloc();
cdevs->owner = THIS_MODULE;
cdev_init(cdevs, fops);
for(i=0;i<cards_found,i++)
{
devt = MKDEV(major, i);
error = cdev_add(cdevs, devt, 1);
if (error == 0)
{
printk(KERN_INFO "(drv_init): cdev_add success for minor number: %d", i);
}
else
{
printk(KERN_ALERT "(drv_init): cdev_add failed for minor number: %d,error code: %d, exit driver\n", i, error);
devt = MKDEV(major, 0);
unregister_chrdev_region(devt, cards_found);
pci_unregister_driver(&my_pci_driver);
return(error);
}
}
} `
I'm doing this because all docs I found on internet suggests that one MINOR number for one device.
But I can't understand how could OS know which card is targeted when I do a fops_open, since the fops is bundled to all devices.
Need your help, Thx everyone.
PS: fops = file operations
Signature of .open operation is
int open(struct inode* inode, struct file* file)
Minor number of device opened can be obtained via
iminor(inode)
Other file operations also may obtain device number using file->f_inode as inode.
Alternatively, .open may store some device-specific data in file->f_private, and other operations may access them that way.

How to remove dynamically assigned major number from /proc/devices?

In my kernel driver project I register with a dynamic major number by calling
register_chrdev(0, "xxxxx", &xxxxx);
and unregistered my module with
unregister_chrdev(0. "xxxxx");
When I load my driver with insmod, I received dynamic major number, for example 243, and, after rmmod, success removing module.
But, after removing the module /proc/devices still shows the major number (243).
How do I get removing my driver to also remove its major number from the list in /proc/devices?
When you call register_chrdev() with 0 as the first argument to request the assignment of a dynamic major number, the return value will be the assigned major number, which you should save.
Then when you call unregister_chrdev() you should pass the saved major number as an argument, rather than the 0 you were. Also make sure that the device name argument matches. And be aware that this function returns a result, which you can check for status/failure - in the latter case you definitely want to printk() a message so that you know that your code has not accomplished its goal.
You can see a complete example at http://www.tldp.org/LDP/lkmpg/2.6/html/x569.html with the key parts being:
static int Major; /* Major number assigned to our device driver */
int init_module(void)
{
Major = register_chrdev(0, DEVICE_NAME, &fops);
if (Major < 0) {
printk(KERN_ALERT "Registering char device failed with %d\n", Major);
return Major;
}
return SUCCESS;
}
void cleanup_module(void)
{
int ret = unregister_chrdev(Major, DEVICE_NAME);
if (ret < 0)
printk(KERN_ALERT "Error in unregister_chrdev: %d\n", ret);
}
Also be aware that this method of registering a device is considered outdated - you might want to research the newer method.

I/O to device from kernel module fails with EFAULT

I have created block device in kernel module. When some I/O happens I read/write all data from/to another existing device (let's say /dev/sdb).
It opens OK, but read/write operations return 14 error(EFAULT,Bad Address). After some research I found that I need map address to user space(probably buffer or filp variables), but copy_to_user function does not help. Also I looked to mmap() and remap_pfn_range() functions, but I can not get how to use them in my code, especially where to get correct vm_area_struct structure. All examples that I found, used char devices and file_operations structure, not block device.
Any hints? Thanks for help.
Here is my code for reading:
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
filp = filp_open("/dev/sdb", O_RDONLY | O_DIRECT | O_SYNC, 00644);
if(IS_ERR(filp))
{
set_fs(old_fs);
int err = PTR_ERR(filp);
printk(KERN_ALERT"Can not open file - %d", err);
return;
}
else
{
bytesRead = vfs_read(filp, buffer, nbytes, &offset); //It gives 14 error
filp_close(filp, NULL);
}
set_fs(old_fs);
I found a better way for I/O to block device from kernel module. I have used bio structure for that. Hope this information save somebody from headache.
1) So, if you want to redirect I/O from your block device to existing block device, you have to use own make_request function. For that you should use blk_alloc_queue function to create queue for your block device like this:
device->queue = blk_alloc_queue(GFP_KERNEL);
blk_queue_make_request(device->queue, own_make_request);
Than into own_make_request function change bi_bdev member into bio structure to device in which you redirecting I/O and call generic_make_request function:
bio->bi_bdev = device_in_which_redirect;
generic_make_request(bio);
More information here at 16 chapter. If link is broken by some cause, here is name of the book - "Linux Device Drivers, Third Edition"
2) If you want read or write your own data to existing block device from kernel module you should use submit_bio function.
Code for writing into specific sector(you need to implement writeComplete function also):
void writePage(struct block_device *device,
sector_t sector, int size, struct page *page)
{
struct bio *bio = bio_alloc(GFP_NOIO, 1);
bio->bi_bdev = vnode->blkDevice;
bio->bi_sector = sector;
bio_add_page(bio, page, size, 0);
bio->bi_end_io = writeComplete;
submit_bio(WRITE_FLUSH_FUA, bio);
}
Code for reading from specific sector(you need to implement readComplete function also):
int readPage(struct block_device *device, sector_t sector, int size,
struct page *page)
{
int ret;
struct completion event;
struct bio *bio = bio_alloc(GFP_NOIO, 1);
bio->bi_bdev = device;
bio->bi_sector = sector;
bio_add_page(bio, page, size, 0);
init_completion(&event);
bio->bi_private = &event;
bio->bi_end_io = readComplete;
submit_bio(READ | REQ_SYNC, bio);
wait_for_completion(&event);
ret = test_bit(BIO_UPTODATE, &bio->bi_flags);
bio_put(bio);
return ret;
}
page can be allocated with alloc_page(GFP_KERNEL). Also for changing data in page use page_address(page). It returns void* so you can interpret that pointer as whatever you want.

Resources