/*
* bsg.c - block layer implementation of the sg v3 interface
*
* Copyright (C) 2004 Jens Axboe <axboe@suse.de> SUSE Labs
* Copyright (C) 2004 Peter M. Jones <pjones@redhat.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License version 2. See the file "COPYING" in the main directory of this
* archive for more details.
*
*/
/*
* TODO
* - Should this get merged, block/scsi_ioctl.c will be migrated into
* this file. To keep maintenance down, it's easier to have them
* seperated right now.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/blkdev.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/percpu.h>
#include <linux/uio.h>
#include <linux/bsg.h>
#include <scsi/scsi.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/sg.h>
static char bsg_version[] = "block layer sg (bsg) 0.4";
struct bsg_command;
struct bsg_device {
struct gendisk *disk;
request_queue_t *queue;
spinlock_t lock;
struct list_head busy_list;
struct list_head done_list;
struct hlist_node dev_list;
atomic_t ref_count;
int minor;
int queued_cmds;
int done_cmds;
unsigned long *cmd_bitmap;
struct bsg_command *cmd_map;
wait_queue_head_t wq_done;
wait_queue_head_t wq_free;
char name[BDEVNAME_SIZE];
int max_queue;
unsigned long flags;
};
enum {
BSG_F_BLOCK = 1,
BSG_F_WRITE_PERM = 2,
};
/*
* command allocation bitmap defines
*/
#define BSG_CMDS_PAGE_ORDER (1)
#define BSG_CMDS_PER_LONG (sizeof(unsigned long) * 8)
#define BSG_CMDS_MASK (BSG_CMDS_PER_LONG - 1)
#define BSG_CMDS_BYTES (PAGE_SIZE * (1 << BSG_CMDS_PAGE_ORDER))
#define BSG_CMDS (BSG_CMDS_BYTES / sizeof(struct bsg_command))
#undef BSG_DEBUG
#ifdef BSG_DEBUG
#define dprintk(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ##args)
#else
#define dprintk(fmt, args...)
#endif
#define list_entry_bc(entry) list_entry((entry), struct bsg_command, list)
/*
* just for testing
*/
#define BSG_MAJOR (240)
static DEFINE_MUTEX(bsg_mutex);
static int bsg_device_nr;
#define BSG_LIST_SIZE (8)
#define bsg_list_idx(minor) ((minor) & (BSG_LIST_SIZE - 1))
static struct hlist_head bsg_device_list[BSG_LIST_SIZE];
static struct class *bsg_class;
static LIST_HEAD(bsg_class_list);
/*
* our internal command type
*/
struct bsg_command {
struct bsg_device *bd;
struct list_head list;
struct request *rq;
struct bio *bio;
int err;
struct sg_io_v4 hdr;
struct sg_io_v4 __user *uhdr;
char sense[SCSI_SENSE_BUFFERSIZE];
};
static void bsg_free_command(struct bsg_command *bc)
{
struct bsg_device *bd = bc->bd;
unsigned long bitnr = bc - bd->cmd_map;
unsigned long flags;
dprintk("%s: command bit offset %lu\n", bd->name, bitnr);
spin_lock_irqsave(&bd->lock, flags);
bd->queued_cmds--;
__clear_bit(bitnr, bd->cmd_bitmap);
spin_unlock_irqrestore(&bd->lock, flags);
wake_up(&bd->wq_free);
}
static struct bsg_command *__bsg_alloc_command(struct bsg_device *bd)
{
struct bsg_command *bc = NULL;
unsigned long *map;
int free_nr;
spin_lock_irq(&bd->lock);
if (bd->queued_cmds >= bd->max_queue)
goto out;
for (free_nr = 0, map = bd->cmd_bitmap; *map == ~0UL; map++)
free_nr += BSG_CMDS_PER_LONG;
BUG_ON(*map == ~0UL);
bd->queued_cmds++;
free_nr += ffz(*map);
__set_bit(free_nr, bd->cmd_bitmap);
spin_unlock_irq(&bd->lock);
bc = bd->cmd_map + free_nr;
memset(bc, 0, sizeof(*bc));
bc->bd = bd;
INIT_LIST_HEAD(&bc->list);
dprintk("%s: returning free cmd %p (bit %d)\n", bd->name, bc, free_nr);
return bc;
out:
dprintk("%s: failed (depth %d)\n", bd->name, bd->queued_cmds);
spin_unlock_irq(&bd->lock);
return bc;
}
static inline void
bsg_del_done_cmd(struct bsg_device *bd, struct bsg_command *bc)
{
bd->done_cmds--;
list_del(&bc->list);
}
static inline void
bsg_add_done_cmd(struct