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