martes, 19 de abril de 2011

Updated minibd driver for linux (sbull.c)

Hi!

I'm embarking myself in a journey to write a block driver for linux (raid5 recovery stuff, anyone? Doesn't ring a bell? Look around this blog).

My firsts steps have involved trying to figure out how linux's block layer api works... so I looked for the simplest example I could find.... and it was in the sbull.c of the minibd drive. It's a dull ram-based block driver where the basics for the API are set in place for people to see how a driver is created.

I started working with it but it was written for older versions of the kernel and the block layer API has received an overhaul that made the sbull.c driver as I got it unusable so I sat down to modify the driver to make it work while trying to figure out how things work... I hopefully did both... but even if I didn't get to understand how the API works, I did make the driver work. How well am I getting the API? Guess will have to wait for my real driver to come out to know for sure.

Here it goes. First, the Makefile so you don't have to reinvent the wheel:
obj-m := sbull.o

PWD := $(shell pwd)
KDIR := /lib/modules/$(shell uname -r)/build

all:
        make -C $(KDIR) M=$(PWD) modules
clean:
        make -C $(KDIR) M=$(PWD) clean


That's it... and now the driver (sbull.c):

/*
* Mini-block driver.
*
* this code is based on one example from LWN.NET: http://lwn.net/Articles/31513/
*
* Copyright 2003 Eklektix, Inc.  Redistributable under the terms
* of the GNU GPL.
*
* Update to new block layer api by Edmundo Carmona
* works on linux 2.6.38.3 tested on UML.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include


MODULE_LICENSE("Dual BSD/GPL");

/*
* some values that could have been configurable... but aren't (at least,
* not in the revision of the driver).
*/
#define KERNEL_SECTOR_SIZE 512
#define MAJOR_NUM 240 /* a free major number, according to devices.txt */
#define MINIBD_MINORS 16 /* number of possible partitions */
#define N_SECTORS 1024 /* number of sectors in our block device */
#define MINIBLOCK_SECTOR_SIZE 512 /* sector size */

/*
* request queue of our mini-block device.
*/
static struct request_queue *miniblock_queue;

/*
* Internal representation of our device.
*/
static struct miniblock_device {
        unsigned long size;
        spinlock_t lock;
        u8 *data;
        struct gendisk *gd;
};

static struct miniblock_device *Miniblock = NULL;

/*
* Real handling in our device of IO Requests.
*/
static void miniblock_transfer(struct miniblock_device *dev, struct request * req)
{
    unsigned long offset = blk_rq_pos(req) * MINIBLOCK_SECTOR_SIZE;
    unsigned long nbytes = blk_rq_cur_bytes(req);

    if ((offset + nbytes) > dev->size) {
        printk (KERN_NOTICE "minibd: working past the end of the block device (%ld %ld)\n", offset, nbytes);
        return;
    }
   
    if (rq_data_dir(req) == WRITE) {
        memcpy(dev->data + offset, req->buffer, nbytes);
    } else {
        memcpy(req->buffer, dev->data + offset, nbytes);
    }
}

/*
* driver handler of requests.
*/
static void miniblock_request(struct request_queue *q)
{
    struct request *req;

    req = blk_fetch_request(q);
    while (req != NULL) {
   
        if (req->cmd_type != REQ_TYPE_FS) {
            printk (KERN_NOTICE "Skip non-CMD request\n");
            blk_end_request_all(req, -EIO);
            continue;
        }
       
        miniblock_transfer(Miniblock, req);
   
    // if there are no more requests, _do not call_ blk_fetch_request
        if (!blk_end_request_cur(req, 0)) {
        req = blk_fetch_request(q);
    }
    }
}

/*
* custom Ioctls for our device.
*/
int miniblock_ioctl (struct inode *inode, struct file *filp,
                unsigned int cmd, unsigned long arg)
{
        long size;
        struct hd_geometry geo;

        switch(cmd) {
            case HDIO_GETGEO:
                size = Miniblock->size*(MINIBLOCK_SECTOR_SIZE/KERNEL_SECTOR_SIZE);
                geo.cylinders = (size & ~0x3f) >> 6;
                geo.heads = 4;
                geo.sectors = 16;
                geo.start = 4;
                if (copy_to_user((void *) arg, &geo, sizeof(geo)))
                        return -EFAULT;
                return 0;
    }

    return -ENOTTY;
}

/*
* operations associated for our device.
*/
static struct block_device_operations miniblock_ops = {
    .owner          = THIS_MODULE,
    .ioctl          = miniblock_ioctl
};

/*
* Module init.
*/
static int __init miniblock_init(void)
{
        static int ret;

        ret = register_blkdev(MAJOR_NUM, "minibd");
        if (ret < 0) {
                printk(KERN_WARNING "minibd: error assigning major number\n");
                return -EBUSY;
        }

        printk(KERN_DEBUG "minibd: Successfully registeres driver\n");

        Miniblock = kmalloc(sizeof(struct miniblock_device), GFP_KERNEL);
        if (Miniblock == NULL) {
                printk(KERN_WARNING "minidb: error assigning memory with kmalloc\n");
                goto out_unregister;
        }

        /*
        * Initializing our device.
        */
        memset(Miniblock, 0, sizeof(struct miniblock_device));
        Miniblock->size = N_SECTORS*MINIBLOCK_SECTOR_SIZE;
        Miniblock->data = vmalloc(Miniblock->size);
        if (Miniblock->data == NULL) {
                printk(KERN_WARNING "minidb: error assigning memory with vmalloc\n");
                kfree(Miniblock);
                goto out_unregister;
        }
        spin_lock_init(&Miniblock->lock);

        /*
        * Create our request queue (one block per device).
        */
        miniblock_queue = blk_init_queue(miniblock_request, &Miniblock->lock);
        if (miniblock_queue == NULL) {
                printk(KERN_WARNING "minibd: error on blk_init_queue\n");
                goto out_free;
        }

        blk_queue_logical_block_size(miniblock_queue, MINIBLOCK_SECTOR_SIZE);

        /*
        * Fill out our gendisk structure
        */
        Miniblock->gd = alloc_disk(MINIBD_MINORS);
        if (!Miniblock->gd) {
                printk(KERN_WARNING "minibd: error on alloc_disk\n");
                goto out_free;
        }

        Miniblock->gd->major = MAJOR_NUM;
        Miniblock->gd->first_minor = 0;
        Miniblock->gd->fops = &miniblock_ops;
        Miniblock->gd->private_data = Miniblock;
        snprintf(Miniblock->gd->disk_name, 10, "%s", "minibd0");
        set_capacity(Miniblock->gd, N_SECTORS*(MINIBLOCK_SECTOR_SIZE/KERNEL_SECTOR_SIZE));
        Miniblock->gd->queue = miniblock_queue;

        add_disk(Miniblock->gd);

        return 0;

out_free:
        vfree(Miniblock->data);
        kfree(Miniblock);
out_unregister:
        unregister_blkdev(MAJOR_NUM, "minibd");

        return -ENOMEM;
}

/*
* module removal
*/
static void __exit miniblock_exit(void)
{
        del_gendisk(Miniblock->gd);
        put_disk(Miniblock->gd);

        unregister_blkdev(MAJOR_NUM, "minibd");
        blk_cleanup_queue(miniblock_queue);

        vfree(Miniblock->data);
        kfree(Miniblock);

        printk(KERN_DEBUG "minibd: driver removed successfully\n");
}

module_init(miniblock_init);
module_exit(miniblock_exit);

lunes, 18 de abril de 2011

My journey updating to natty

Updating to a new version of ubuntu always has its glitches.... I hate doing clean installs (this is not Windows after all, right? So we should be able to do updates while working on the box) so I always do dist-upgrades in order to move to a new release of ubuntu (and don't try to change my mind... I'm stubborn and won't do it).

In my case, I wanted to update because I was having a small glitch with my intel graphics related to refreshing of the screen on my Samsung N150P plus netbook. Sometimes I had to switch to a different virtual desktop and come back in order to force the refresh of the whole screen to see things right... and I was tired of it.

I'm using kubuntu, anyway... so here's the journey so far.

First, going to /etc/apt/ and edit sources.list in order to change all "maverick"s in the file for "natty" (as a matter of fact, this is not what I do but the results are exactly that).

Then, performing the actual upgrade (you are on your own if you choose to do it this way, so no whining is accepted if something breaks):

sudo apt-get update
sudo apt-get dist-upgrade -y --force-yes -f

Give it time to download the stuff it has to download (about 800+ MBs for me), then answer a few question for a few packages, then the upgrade is finished (by the way... I normally had to run the dist-upgade a couple of times in order to get all packages installed... there was always a glitch... but not this time... it was flawless).

And here is where things get tricky. I reboot my computer and it fails to boot with the natty kernel with a kernel panic. Damn... it's never going to give itself away easily, is it? Aparently it doesn't understand where the root partition is. So I start booting with the maverick kernel that's still installed. Fortunately it does boot and I'm able to start kde normally..... except that graphics are a MESS (in order to keep the article PG-13). I had to disable desktop effects in order to have something usable. Ugh! And I thought natty would get rid of the refresh problem.

So I file a bug to complain about it (come on, guys! Natty is almost out there and the graphics on my netbook are awful!)... and when I'm about to finish the submission process, it hits me like a truck: old kernel. :-S I better start the new kernel to see if the graphics are better there. I file the bug anywhere with a comment at the end.

And then a new chapter of education begings. After a few attempts of trying to solve the problem by hand, I end up running update-grub to see what happens... and then I see that there's an error when trying to generate a new grub.cfg file:

$ sudo update-grub
Generating grub.cfg ...
Found linux image: /boot/vmlinuz-2.6.38-8-generic
Found initrd image: /boot/initrd.img-2.6.38-8-generic
Found linux image: /boot/vmlinuz-2.6.35-28-generic
Found initrd image: /boot/initrd.img-2.6.35-28-generic
Found memtest86+ image: /boot/memtest86+.bin
error: syntax error.
error: Incorrect command.
error: syntax error.
error: line no: 143
Syntax errors are detected in generated GRUB config file.
Ensure that there are no errors in /etc/default/grub
and /etc/grub.d/* files or please file a bug report with
/boot/grub/grub.cfg.new file attached.
done

What do we have in the new broken grub.cfg file (grub.cfg.new) around line 143?

submenu "Xen 3.3" {
}

Well, well.... who knows why this is... but this xen stuff is going out. So I chmod -x /etc/grub.d/20_linux_xen and try again:

$ sudo update-grub
Generating grub.cfg ...
Found linux image: /boot/vmlinuz-2.6.38-8-generic
Found initrd image: /boot/initrd.img-2.6.38-8-generic
Found linux image: /boot/vmlinuz-2.6.35-28-generic
Found initrd image: /boot/initrd.img-2.6.35-28-generic
Found memtest86+ image: /boot/memtest86+.bin
done

Much better now.... Now I'm able to reboot with the natty kernel and we are fine. And let me tell you that graphics are much better now... I have noticed a couple of things so far (I have to restart squid3 after I reboot in order to make it work, when the screensaver starts, sometimes it kills the whole kde session) but I'll look around to see if updates arrive that solve this problems or if there are easy fixes for them (besides not using them, of course).

jueves, 14 de abril de 2011

Internet connection through (GSM) USB Modem with wvdial

Hi!

Just tonight I'm having problems to start my internet connection with the NetworkManager on kubuntu maverick. The connection has been working like a charm these days so I'd think it's some kind of hiccups.

I had taken the time to install wvdial on my box hoping I wasn't going to need it... normally console based applications are much more verbose on the kind of problems they are facing to do things so they are a very good fallback method when you have problems and so given the current situation, I'll give wvdial a shot.

So, here's the recipe for it. First, set up a simple text file with the configuration you will be using.. it depends on the carrier you are using. I have COMCEL (in colombia) and so this is the content of the file:

[Dialer Defaults]
        Modem = /dev/ttyUSB0
        Phone = *99#
        Username = COMCELWEB
        Password = COMCELWEB

Phone, username and password depend on your carrier but it's fairly simple, right? Say the file is called comcel.txt, then call wvdial supplying this file name as the config file:

$ wvdial -C comcel.txt
A while later, you will be connected.... this is the output for me:

--> WvDial: Internet dialer version 1.60
--> Cannot get information for serial port.
--> Initializing modem.
--> Sending: ATZ
OK
--> Modem initialized.
--> Sending: ATDT*99#
--> Waiting for carrier.
ATDT*99#
CONNECT
--> Carrier detected.  Waiting for prompt.
--> Don't know what to do!  Starting pppd and hoping for the best.
--> Starting pppd at Thu Apr 14 21:46:29 2011
--> Pid of pppd: 2594
--> Using interface ppp0
--> local  IP address x.x.x.x
--> remote IP address y.y.y.y
--> primary   DNS address z.z.z.z
--> secondary DNS address w.w.w.w

And that's how I made this post. Perhaps I could set up a little DB with the configuration in different carriers. Do you use wvdial to set up internet connection through a usb modem (through GSM)? Want to give it up? I'll update this post.