simple block device - request processing in threads
Andras Pal
apal at szofi.net
Fri Mar 3 05:12:33 EST 2017
Dear All,
with some experience in low-level C programming, I started to develop a
kernel module which allows I2C slave EEPROM-alike devices (EEPROMs, FRAMs,
BBRAMs, etc) to be accessed as block devices. Since the i/o transactions are
relatively slow (say, 40k/sec), i'm using kernel threads. However, it seems
that while the read (rq_data_dir(req)) == 0) operations are fine, write
operations (rq_data_dir != 0) seems to be lost. For instance, if I do `dd
bs=512 count=32 of=/dev/i2cblk0`, only the first 8 sectors are fetched by
blk_fetch_request(). I copy the critical/relevant/interesting parts after this
message. The code is based on drivers/block/nbd.c.
In addition - according to printk()-based debugging - after each write request,
the kernel starts to access sectors in the order of 0, 24, 8, 56, 120, 64, 32
(with 8 sectors == 1 page per request). Maybe it seeks for some filesystems...?
Can this feature be turned off (like setting
disk->queue->backing_dev_info.ra_pages to zero in order to turn off
readaheaad)?
thanks in advance, cheers, Andras
/*****************************************************************************/
static int i2cblk_process_io_request(struct i2cblk_device *device,struct request *req)
{
int ret;
/* i2cblk_transfer(): this function calls i2c_master_send/recv(), etc. */
ret=i2cblk_transfer(device, blk_rq_pos(req), blk_rq_cur_sectors(req),
bio_data(req->bio),
rq_data_dir(req));
return(ret);
}
static void i2cblk_request(struct request_queue *q)
{
struct request *req;
while ( (req=blk_fetch_request(q)) != NULL )
{ struct i2cblk_device *device;
if ( req->cmd_type != REQ_TYPE_FS )
{ __blk_end_request_all(req, -EIO);
continue;
}
/* spin_unlock_irq(q->queue_lock); */
device=req->rq_disk->private_data;
spin_lock_irq(&device->active_lock);
list_add_tail(&req->queuelist,&device->active_queue);
spin_unlock_irq(&device->active_lock);
wake_up(&device->active_wq);
/* spin_lock_irq(q->queue_lock); */
};
}
static void i2cblk_end_request(struct request *req)
{
int error;
struct request_queue *q = req->q;
unsigned long flags;
error=(req->errors ? -EIO : 0);
spin_lock_irqsave(q->queue_lock, flags);
__blk_end_request_all(req, error);
spin_unlock_irqrestore(q->queue_lock, flags);
}
static void i2cblk_handle_request(struct i2cblk_device *device,struct request *req)
{
if ( i2cblk_process_io_request(device,req) )
req->errors++;
i2cblk_end_request(req);
return;
}
static int i2cblk_thread(void *data)
{
struct i2cblk_device *device = data;
struct request *req;
while ( ! kthread_should_stop() )
{
wait_event_interruptible(device->active_wq,kthread_should_stop() || (!list_empty(&device->active_queue)));
if ( list_empty(&device->active_queue) )
continue;
spin_lock_irq(&device->active_lock);
req = list_entry(device->active_queue.next,struct request,queuelist);
list_del_init(&req->queuelist);
spin_unlock_irq(&device->active_lock);
i2cblk_handle_request(device,req);
};
return(0);
}
More information about the Kernelnewbies
mailing list