a885c8c431
HDIO_GETGEO is implemented in most block drivers, and all of them have to duplicate the code to copy the structure to userspace, as well as getting the start sector. This patch moves that to common code [1] and adds a ->getgeo method to fill out the raw kernel hd_geometry structure. For many drivers this means ->ioctl can go away now. [1] the s390 block drivers are odd in this respect. xpram sets ->start to 4 always which seems more than odd, and the dasd driver shifts the start offset around, probably because of it's non-standard sector size. Signed-off-by: Christoph Hellwig <hch@lst.de> Cc: Jens Axboe <axboe@suse.de> Cc: <mike.miller@hp.com> Cc: Jeff Dike <jdike@addtoit.com> Cc: Paolo Giarrusso <blaisorblade@yahoo.it> Cc: Bartlomiej Zolnierkiewicz <B.Zolnierkiewicz@elka.pw.edu.pl> Cc: Neil Brown <neilb@cse.unsw.edu.au> Cc: Markus Lidel <Markus.Lidel@shadowconnect.com> Cc: Russell King <rmk@arm.linux.org.uk> Cc: David Woodhouse <dwmw2@infradead.org> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com> Cc: James Bottomley <James.Bottomley@steeleye.com> Signed-off-by: Adrian Bunk <bunk@stusta.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
270 lines
6.3 KiB
C
270 lines
6.3 KiB
C
/* Copyright (c) 2004 Coraid, Inc. See COPYING for GPL terms. */
|
|
/*
|
|
* aoeblk.c
|
|
* block device routines
|
|
*/
|
|
|
|
#include <linux/hdreg.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/genhd.h>
|
|
#include <linux/netdevice.h>
|
|
#include "aoe.h"
|
|
|
|
static kmem_cache_t *buf_pool_cache;
|
|
|
|
/* add attributes for our block devices in sysfs */
|
|
static ssize_t aoedisk_show_state(struct gendisk * disk, char *page)
|
|
{
|
|
struct aoedev *d = disk->private_data;
|
|
|
|
return snprintf(page, PAGE_SIZE,
|
|
"%s%s\n",
|
|
(d->flags & DEVFL_UP) ? "up" : "down",
|
|
(d->flags & DEVFL_CLOSEWAIT) ? ",closewait" : "");
|
|
}
|
|
static ssize_t aoedisk_show_mac(struct gendisk * disk, char *page)
|
|
{
|
|
struct aoedev *d = disk->private_data;
|
|
|
|
return snprintf(page, PAGE_SIZE, "%012llx\n",
|
|
(unsigned long long)mac_addr(d->addr));
|
|
}
|
|
static ssize_t aoedisk_show_netif(struct gendisk * disk, char *page)
|
|
{
|
|
struct aoedev *d = disk->private_data;
|
|
|
|
return snprintf(page, PAGE_SIZE, "%s\n", d->ifp->name);
|
|
}
|
|
/* firmware version */
|
|
static ssize_t aoedisk_show_fwver(struct gendisk * disk, char *page)
|
|
{
|
|
struct aoedev *d = disk->private_data;
|
|
|
|
return snprintf(page, PAGE_SIZE, "0x%04x\n", (unsigned int) d->fw_ver);
|
|
}
|
|
|
|
static struct disk_attribute disk_attr_state = {
|
|
.attr = {.name = "state", .mode = S_IRUGO },
|
|
.show = aoedisk_show_state
|
|
};
|
|
static struct disk_attribute disk_attr_mac = {
|
|
.attr = {.name = "mac", .mode = S_IRUGO },
|
|
.show = aoedisk_show_mac
|
|
};
|
|
static struct disk_attribute disk_attr_netif = {
|
|
.attr = {.name = "netif", .mode = S_IRUGO },
|
|
.show = aoedisk_show_netif
|
|
};
|
|
static struct disk_attribute disk_attr_fwver = {
|
|
.attr = {.name = "firmware-version", .mode = S_IRUGO },
|
|
.show = aoedisk_show_fwver
|
|
};
|
|
|
|
static void
|
|
aoedisk_add_sysfs(struct aoedev *d)
|
|
{
|
|
sysfs_create_file(&d->gd->kobj, &disk_attr_state.attr);
|
|
sysfs_create_file(&d->gd->kobj, &disk_attr_mac.attr);
|
|
sysfs_create_file(&d->gd->kobj, &disk_attr_netif.attr);
|
|
sysfs_create_file(&d->gd->kobj, &disk_attr_fwver.attr);
|
|
}
|
|
void
|
|
aoedisk_rm_sysfs(struct aoedev *d)
|
|
{
|
|
sysfs_remove_link(&d->gd->kobj, "state");
|
|
sysfs_remove_link(&d->gd->kobj, "mac");
|
|
sysfs_remove_link(&d->gd->kobj, "netif");
|
|
sysfs_remove_link(&d->gd->kobj, "firmware-version");
|
|
}
|
|
|
|
static int
|
|
aoeblk_open(struct inode *inode, struct file *filp)
|
|
{
|
|
struct aoedev *d;
|
|
ulong flags;
|
|
|
|
d = inode->i_bdev->bd_disk->private_data;
|
|
|
|
spin_lock_irqsave(&d->lock, flags);
|
|
if (d->flags & DEVFL_UP) {
|
|
d->nopen++;
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
return 0;
|
|
}
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int
|
|
aoeblk_release(struct inode *inode, struct file *filp)
|
|
{
|
|
struct aoedev *d;
|
|
ulong flags;
|
|
|
|
d = inode->i_bdev->bd_disk->private_data;
|
|
|
|
spin_lock_irqsave(&d->lock, flags);
|
|
|
|
if (--d->nopen == 0 && (d->flags & DEVFL_CLOSEWAIT)) {
|
|
d->flags &= ~DEVFL_CLOSEWAIT;
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
aoecmd_cfg(d->aoemajor, d->aoeminor);
|
|
return 0;
|
|
}
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
aoeblk_make_request(request_queue_t *q, struct bio *bio)
|
|
{
|
|
struct aoedev *d;
|
|
struct buf *buf;
|
|
struct sk_buff *sl;
|
|
ulong flags;
|
|
|
|
blk_queue_bounce(q, &bio);
|
|
|
|
d = bio->bi_bdev->bd_disk->private_data;
|
|
buf = mempool_alloc(d->bufpool, GFP_NOIO);
|
|
if (buf == NULL) {
|
|
printk(KERN_INFO "aoe: aoeblk_make_request: buf allocation "
|
|
"failure\n");
|
|
bio_endio(bio, bio->bi_size, -ENOMEM);
|
|
return 0;
|
|
}
|
|
memset(buf, 0, sizeof(*buf));
|
|
INIT_LIST_HEAD(&buf->bufs);
|
|
buf->start_time = jiffies;
|
|
buf->bio = bio;
|
|
buf->resid = bio->bi_size;
|
|
buf->sector = bio->bi_sector;
|
|
buf->bv = buf->bio->bi_io_vec;
|
|
buf->bv_resid = buf->bv->bv_len;
|
|
buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
|
|
|
|
spin_lock_irqsave(&d->lock, flags);
|
|
|
|
if ((d->flags & DEVFL_UP) == 0) {
|
|
printk(KERN_INFO "aoe: aoeblk_make_request: device %ld.%ld is not up\n",
|
|
d->aoemajor, d->aoeminor);
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
mempool_free(buf, d->bufpool);
|
|
bio_endio(bio, bio->bi_size, -ENXIO);
|
|
return 0;
|
|
}
|
|
|
|
list_add_tail(&buf->bufs, &d->bufq);
|
|
aoecmd_work(d);
|
|
|
|
sl = d->sendq_hd;
|
|
d->sendq_hd = d->sendq_tl = NULL;
|
|
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
|
|
aoenet_xmit(sl);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
aoeblk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
|
{
|
|
struct aoedev *d = bdev->bd_disk->private_data;
|
|
|
|
if ((d->flags & DEVFL_UP) == 0) {
|
|
printk(KERN_ERR "aoe: aoeblk_ioctl: disk not up\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
geo->cylinders = d->geo.cylinders;
|
|
geo->heads = d->geo.heads;
|
|
geo->sectors = d->geo.sectors;
|
|
return 0;
|
|
}
|
|
|
|
static struct block_device_operations aoe_bdops = {
|
|
.open = aoeblk_open,
|
|
.release = aoeblk_release,
|
|
.getgeo = aoeblk_getgeo,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
/* alloc_disk and add_disk can sleep */
|
|
void
|
|
aoeblk_gdalloc(void *vp)
|
|
{
|
|
struct aoedev *d = vp;
|
|
struct gendisk *gd;
|
|
ulong flags;
|
|
|
|
gd = alloc_disk(AOE_PARTITIONS);
|
|
if (gd == NULL) {
|
|
printk(KERN_ERR "aoe: aoeblk_gdalloc: cannot allocate disk "
|
|
"structure for %ld.%ld\n", d->aoemajor, d->aoeminor);
|
|
spin_lock_irqsave(&d->lock, flags);
|
|
d->flags &= ~DEVFL_WORKON;
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
return;
|
|
}
|
|
|
|
d->bufpool = mempool_create(MIN_BUFS,
|
|
mempool_alloc_slab, mempool_free_slab,
|
|
buf_pool_cache);
|
|
if (d->bufpool == NULL) {
|
|
printk(KERN_ERR "aoe: aoeblk_gdalloc: cannot allocate bufpool "
|
|
"for %ld.%ld\n", d->aoemajor, d->aoeminor);
|
|
put_disk(gd);
|
|
spin_lock_irqsave(&d->lock, flags);
|
|
d->flags &= ~DEVFL_WORKON;
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
return;
|
|
}
|
|
|
|
spin_lock_irqsave(&d->lock, flags);
|
|
blk_queue_make_request(&d->blkq, aoeblk_make_request);
|
|
gd->major = AOE_MAJOR;
|
|
gd->first_minor = d->sysminor * AOE_PARTITIONS;
|
|
gd->fops = &aoe_bdops;
|
|
gd->private_data = d;
|
|
gd->capacity = d->ssize;
|
|
snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%ld",
|
|
d->aoemajor, d->aoeminor);
|
|
|
|
gd->queue = &d->blkq;
|
|
d->gd = gd;
|
|
d->flags &= ~DEVFL_WORKON;
|
|
d->flags |= DEVFL_UP;
|
|
|
|
spin_unlock_irqrestore(&d->lock, flags);
|
|
|
|
add_disk(gd);
|
|
aoedisk_add_sysfs(d);
|
|
|
|
printk(KERN_INFO "aoe: %012llx e%lu.%lu v%04x has %llu "
|
|
"sectors\n", (unsigned long long)mac_addr(d->addr),
|
|
d->aoemajor, d->aoeminor,
|
|
d->fw_ver, (long long)d->ssize);
|
|
}
|
|
|
|
void
|
|
aoeblk_exit(void)
|
|
{
|
|
kmem_cache_destroy(buf_pool_cache);
|
|
}
|
|
|
|
int __init
|
|
aoeblk_init(void)
|
|
{
|
|
buf_pool_cache = kmem_cache_create("aoe_bufs",
|
|
sizeof(struct buf),
|
|
0, 0, NULL, NULL);
|
|
if (buf_pool_cache == NULL)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|