M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

View File

@ -0,0 +1,6 @@
#
# Makefile for ATA over Ethernet
#
obj-$(CONFIG_ATA_OVER_ETH) += aoe.o
aoe-y := aoeblk.o aoechr.o aoecmd.o aoedev.o aoemain.o aoenet.o

View File

@ -0,0 +1,203 @@
/* Copyright (c) 2007 Coraid, Inc. See COPYING for GPL terms. */
#define VERSION "47"
#define AOE_MAJOR 152
#define DEVICE_NAME "aoe"
/* set AOE_PARTITIONS to 1 to use whole-disks only
* default is 16, which is 15 partitions plus the whole disk
*/
#ifndef AOE_PARTITIONS
#define AOE_PARTITIONS (16)
#endif
#define SYSMINOR(aoemajor, aoeminor) ((aoemajor) * NPERSHELF + (aoeminor))
#define AOEMAJOR(sysminor) ((sysminor) / NPERSHELF)
#define AOEMINOR(sysminor) ((sysminor) % NPERSHELF)
#define WHITESPACE " \t\v\f\n"
enum {
AOECMD_ATA,
AOECMD_CFG,
AOECMD_VEND_MIN = 0xf0,
AOEFL_RSP = (1<<3),
AOEFL_ERR = (1<<2),
AOEAFL_EXT = (1<<6),
AOEAFL_DEV = (1<<4),
AOEAFL_ASYNC = (1<<1),
AOEAFL_WRITE = (1<<0),
AOECCMD_READ = 0,
AOECCMD_TEST,
AOECCMD_PTEST,
AOECCMD_SET,
AOECCMD_FSET,
AOE_HVER = 0x10,
};
struct aoe_hdr {
unsigned char dst[6];
unsigned char src[6];
__be16 type;
unsigned char verfl;
unsigned char err;
__be16 major;
unsigned char minor;
unsigned char cmd;
__be32 tag;
};
struct aoe_atahdr {
unsigned char aflags;
unsigned char errfeat;
unsigned char scnt;
unsigned char cmdstat;
unsigned char lba0;
unsigned char lba1;
unsigned char lba2;
unsigned char lba3;
unsigned char lba4;
unsigned char lba5;
unsigned char res[2];
};
struct aoe_cfghdr {
__be16 bufcnt;
__be16 fwver;
unsigned char scnt;
unsigned char aoeccmd;
unsigned char cslen[2];
};
enum {
DEVFL_UP = 1, /* device is installed in system and ready for AoE->ATA commands */
DEVFL_TKILL = (1<<1), /* flag for timer to know when to kill self */
DEVFL_EXT = (1<<2), /* device accepts lba48 commands */
DEVFL_CLOSEWAIT = (1<<3), /* device is waiting for all closes to revalidate */
DEVFL_GDALLOC = (1<<4), /* need to alloc gendisk */
DEVFL_KICKME = (1<<5), /* slow polling network card catch */
DEVFL_NEWSIZE = (1<<6), /* need to update dev size in block layer */
BUFFL_FAIL = 1,
};
enum {
DEFAULTBCNT = 2 * 512, /* 2 sectors */
NPERSHELF = 16, /* number of slots per shelf address */
FREETAG = -1,
MIN_BUFS = 16,
NTARGETS = 8,
NAOEIFS = 8,
NSKBPOOLMAX = 128,
TIMERTICK = HZ / 10,
MINTIMER = HZ >> 2,
MAXTIMER = HZ << 1,
HELPWAIT = 20,
};
struct buf {
struct list_head bufs;
ulong stime; /* for disk stats */
ulong flags;
ulong nframesout;
ulong resid;
ulong bv_resid;
ulong bv_off;
sector_t sector;
struct bio *bio;
struct bio_vec *bv;
};
struct frame {
int tag;
ulong waited;
struct buf *buf;
char *bufaddr;
ulong bcnt;
sector_t lba;
struct sk_buff *skb;
};
struct aoeif {
struct net_device *nd;
unsigned char lost;
unsigned char lostjumbo;
ushort maxbcnt;
};
struct aoetgt {
unsigned char addr[6];
ushort nframes;
struct frame *frames;
struct aoeif ifs[NAOEIFS];
struct aoeif *ifp; /* current aoeif in use */
ushort nout;
ushort maxout;
u16 lasttag; /* last tag sent */
u16 useme;
ulong lastwadj; /* last window adjustment */
int wpkts, rpkts;
int dataref;
};
struct aoedev {
struct aoedev *next;
ulong sysminor;
ulong aoemajor;
u16 aoeminor;
u16 flags;
u16 nopen; /* (bd_openers isn't available without sleeping) */
u16 rttavg; /* round trip average of requests/responses */
u16 mintimer;
u16 fw_ver; /* version of blade's firmware */
struct work_struct work;/* disk create work struct */
struct gendisk *gd;
struct request_queue *blkq;
struct hd_geometry geo;
sector_t ssize;
struct timer_list timer;
spinlock_t lock;
struct sk_buff_head sendq;
struct sk_buff_head skbpool;
mempool_t *bufpool; /* for deadlock-free Buf allocation */
struct list_head bufq; /* queue of bios to work on */
struct buf *inprocess; /* the one we're currently working on */
struct aoetgt *targets[NTARGETS];
struct aoetgt **tgt; /* target in use when working */
struct aoetgt **htgt; /* target needing rexmit assistance */
};
int aoeblk_init(void);
void aoeblk_exit(void);
void aoeblk_gdalloc(void *);
void aoedisk_rm_sysfs(struct aoedev *d);
int aoechr_init(void);
void aoechr_exit(void);
void aoechr_error(char *);
void aoecmd_work(struct aoedev *d);
void aoecmd_cfg(ushort aoemajor, unsigned char aoeminor);
void aoecmd_ata_rsp(struct sk_buff *);
void aoecmd_cfg_rsp(struct sk_buff *);
void aoecmd_sleepwork(struct work_struct *);
void aoecmd_cleanslate(struct aoedev *);
struct sk_buff *aoecmd_ata_id(struct aoedev *);
int aoedev_init(void);
void aoedev_exit(void);
struct aoedev *aoedev_by_aoeaddr(int maj, int min);
struct aoedev *aoedev_by_sysminor_m(ulong sysminor);
void aoedev_downdev(struct aoedev *d);
int aoedev_flush(const char __user *str, size_t size);
int aoenet_init(void);
void aoenet_exit(void);
void aoenet_xmit(struct sk_buff_head *);
int is_aoe_netif(struct net_device *ifp);
int set_aoe_iflist(const char __user *str, size_t size);

View File

@ -0,0 +1,331 @@
/* Copyright (c) 2007 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoeblk.c
* block device routines
*/
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/ratelimit.h>
#include <linux/genhd.h>
#include <linux/netdevice.h>
#include <linux/mutex.h>
#include <linux/export.h>
#include "aoe.h"
static DEFINE_MUTEX(aoeblk_mutex);
static struct kmem_cache *buf_pool_cache;
static ssize_t aoedisk_show_state(struct device *dev,
struct device_attribute *attr, char *page)
{
struct gendisk *disk = dev_to_disk(dev);
struct aoedev *d = disk->private_data;
return snprintf(page, PAGE_SIZE,
"%s%s\n",
(d->flags & DEVFL_UP) ? "up" : "down",
(d->flags & DEVFL_KICKME) ? ",kickme" :
(d->nopen && !(d->flags & DEVFL_UP)) ? ",closewait" : "");
/* I'd rather see nopen exported so we can ditch closewait */
}
static ssize_t aoedisk_show_mac(struct device *dev,
struct device_attribute *attr, char *page)
{
struct gendisk *disk = dev_to_disk(dev);
struct aoedev *d = disk->private_data;
struct aoetgt *t = d->targets[0];
if (t == NULL)
return snprintf(page, PAGE_SIZE, "none\n");
return snprintf(page, PAGE_SIZE, "%pm\n", t->addr);
}
static ssize_t aoedisk_show_netif(struct device *dev,
struct device_attribute *attr, char *page)
{
struct gendisk *disk = dev_to_disk(dev);
struct aoedev *d = disk->private_data;
struct net_device *nds[8], **nd, **nnd, **ne;
struct aoetgt **t, **te;
struct aoeif *ifp, *e;
char *p;
memset(nds, 0, sizeof nds);
nd = nds;
ne = nd + ARRAY_SIZE(nds);
t = d->targets;
te = t + NTARGETS;
for (; t < te && *t; t++) {
ifp = (*t)->ifs;
e = ifp + NAOEIFS;
for (; ifp < e && ifp->nd; ifp++) {
for (nnd = nds; nnd < nd; nnd++)
if (*nnd == ifp->nd)
break;
if (nnd == nd && nd != ne)
*nd++ = ifp->nd;
}
}
ne = nd;
nd = nds;
if (*nd == NULL)
return snprintf(page, PAGE_SIZE, "none\n");
for (p = page; nd < ne; nd++)
p += snprintf(p, PAGE_SIZE - (p-page), "%s%s",
p == page ? "" : ",", (*nd)->name);
p += snprintf(p, PAGE_SIZE - (p-page), "\n");
return p-page;
}
/* firmware version */
static ssize_t aoedisk_show_fwver(struct device *dev,
struct device_attribute *attr, char *page)
{
struct gendisk *disk = dev_to_disk(dev);
struct aoedev *d = disk->private_data;
return snprintf(page, PAGE_SIZE, "0x%04x\n", (unsigned int) d->fw_ver);
}
static DEVICE_ATTR(state, S_IRUGO, aoedisk_show_state, NULL);
static DEVICE_ATTR(mac, S_IRUGO, aoedisk_show_mac, NULL);
static DEVICE_ATTR(netif, S_IRUGO, aoedisk_show_netif, NULL);
static struct device_attribute dev_attr_firmware_version = {
.attr = { .name = "firmware-version", .mode = S_IRUGO },
.show = aoedisk_show_fwver,
};
static struct attribute *aoe_attrs[] = {
&dev_attr_state.attr,
&dev_attr_mac.attr,
&dev_attr_netif.attr,
&dev_attr_firmware_version.attr,
NULL,
};
static const struct attribute_group attr_group = {
.attrs = aoe_attrs,
};
static int
aoedisk_add_sysfs(struct aoedev *d)
{
return sysfs_create_group(&disk_to_dev(d->gd)->kobj, &attr_group);
}
void
aoedisk_rm_sysfs(struct aoedev *d)
{
sysfs_remove_group(&disk_to_dev(d->gd)->kobj, &attr_group);
}
static int
aoeblk_open(struct block_device *bdev, fmode_t mode)
{
struct aoedev *d = bdev->bd_disk->private_data;
ulong flags;
mutex_lock(&aoeblk_mutex);
spin_lock_irqsave(&d->lock, flags);
if (d->flags & DEVFL_UP) {
d->nopen++;
spin_unlock_irqrestore(&d->lock, flags);
mutex_unlock(&aoeblk_mutex);
return 0;
}
spin_unlock_irqrestore(&d->lock, flags);
mutex_unlock(&aoeblk_mutex);
return -ENODEV;
}
static int
aoeblk_release(struct gendisk *disk, fmode_t mode)
{
struct aoedev *d = disk->private_data;
ulong flags;
spin_lock_irqsave(&d->lock, flags);
if (--d->nopen == 0) {
spin_unlock_irqrestore(&d->lock, flags);
aoecmd_cfg(d->aoemajor, d->aoeminor);
return 0;
}
spin_unlock_irqrestore(&d->lock, flags);
return 0;
}
static void
aoeblk_make_request(struct request_queue *q, struct bio *bio)
{
struct sk_buff_head queue;
struct aoedev *d;
struct buf *buf;
ulong flags;
blk_queue_bounce(q, &bio);
if (bio == NULL) {
printk(KERN_ERR "aoe: bio is NULL\n");
BUG();
return;
}
d = bio->bi_bdev->bd_disk->private_data;
if (d == NULL) {
printk(KERN_ERR "aoe: bd_disk->private_data is NULL\n");
BUG();
bio_endio(bio, -ENXIO);
return;
} else if (bio->bi_io_vec == NULL) {
printk(KERN_ERR "aoe: bi_io_vec is NULL\n");
BUG();
bio_endio(bio, -ENXIO);
return;
}
buf = mempool_alloc(d->bufpool, GFP_NOIO);
if (buf == NULL) {
printk(KERN_INFO "aoe: buf allocation failure\n");
bio_endio(bio, -ENOMEM);
return;
}
memset(buf, 0, sizeof(*buf));
INIT_LIST_HEAD(&buf->bufs);
buf->stime = jiffies;
buf->bio = bio;
buf->resid = bio->bi_size;
buf->sector = bio->bi_sector;
buf->bv = &bio->bi_io_vec[bio->bi_idx];
buf->bv_resid = buf->bv->bv_len;
WARN_ON(buf->bv_resid == 0);
buf->bv_off = buf->bv->bv_offset;
spin_lock_irqsave(&d->lock, flags);
if ((d->flags & DEVFL_UP) == 0) {
pr_info_ratelimited("aoe: device %ld.%d is not up\n",
d->aoemajor, d->aoeminor);
spin_unlock_irqrestore(&d->lock, flags);
mempool_free(buf, d->bufpool);
bio_endio(bio, -ENXIO);
return;
}
list_add_tail(&buf->bufs, &d->bufq);
aoecmd_work(d);
__skb_queue_head_init(&queue);
skb_queue_splice_init(&d->sendq, &queue);
spin_unlock_irqrestore(&d->lock, flags);
aoenet_xmit(&queue);
}
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: disk not up\n");
return -ENODEV;
}
geo->cylinders = d->geo.cylinders;
geo->heads = d->geo.heads;
geo->sectors = d->geo.sectors;
return 0;
}
static const 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: cannot allocate disk structure for %ld.%d\n",
d->aoemajor, d->aoeminor);
goto err;
}
d->bufpool = mempool_create_slab_pool(MIN_BUFS, buf_pool_cache);
if (d->bufpool == NULL) {
printk(KERN_ERR "aoe: cannot allocate bufpool for %ld.%d\n",
d->aoemajor, d->aoeminor);
goto err_disk;
}
d->blkq = blk_alloc_queue(GFP_KERNEL);
if (!d->blkq)
goto err_mempool;
blk_queue_make_request(d->blkq, aoeblk_make_request);
d->blkq->backing_dev_info.name = "aoe";
if (bdi_init(&d->blkq->backing_dev_info))
goto err_blkq;
spin_lock_irqsave(&d->lock, flags);
gd->major = AOE_MAJOR;
gd->first_minor = d->sysminor * AOE_PARTITIONS;
gd->fops = &aoe_bdops;
gd->private_data = d;
set_capacity(gd, d->ssize);
snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%d",
d->aoemajor, d->aoeminor);
gd->queue = d->blkq;
d->gd = gd;
d->flags &= ~DEVFL_GDALLOC;
d->flags |= DEVFL_UP;
spin_unlock_irqrestore(&d->lock, flags);
add_disk(gd);
aoedisk_add_sysfs(d);
return;
err_blkq:
blk_cleanup_queue(d->blkq);
d->blkq = NULL;
err_mempool:
mempool_destroy(d->bufpool);
err_disk:
put_disk(gd);
err:
spin_lock_irqsave(&d->lock, flags);
d->flags &= ~DEVFL_GDALLOC;
spin_unlock_irqrestore(&d->lock, flags);
}
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);
if (buf_pool_cache == NULL)
return -ENOMEM;
return 0;
}

View File

@ -0,0 +1,315 @@
/* Copyright (c) 2007 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoechr.c
* AoE character device driver
*/
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/skbuff.h>
#include <linux/export.h>
#include "aoe.h"
enum {
//MINOR_STAT = 1, (moved to sysfs)
MINOR_ERR = 2,
MINOR_DISCOVER,
MINOR_INTERFACES,
MINOR_REVALIDATE,
MINOR_FLUSH,
MSGSZ = 2048,
NMSG = 100, /* message backlog to retain */
};
struct aoe_chardev {
ulong minor;
char name[32];
};
enum { EMFL_VALID = 1 };
struct ErrMsg {
short flags;
short len;
char *msg;
};
static DEFINE_MUTEX(aoechr_mutex);
static struct ErrMsg emsgs[NMSG];
static int emsgs_head_idx, emsgs_tail_idx;
static struct completion emsgs_comp;
static spinlock_t emsgs_lock;
static int nblocked_emsgs_readers;
static struct class *aoe_class;
static struct aoe_chardev chardevs[] = {
{ MINOR_ERR, "err" },
{ MINOR_DISCOVER, "discover" },
{ MINOR_INTERFACES, "interfaces" },
{ MINOR_REVALIDATE, "revalidate" },
{ MINOR_FLUSH, "flush" },
};
static int
discover(void)
{
aoecmd_cfg(0xffff, 0xff);
return 0;
}
static int
interfaces(const char __user *str, size_t size)
{
if (set_aoe_iflist(str, size)) {
printk(KERN_ERR
"aoe: could not set interface list: too many interfaces\n");
return -EINVAL;
}
return 0;
}
static int
revalidate(const char __user *str, size_t size)
{
int major, minor, n;
ulong flags;
struct aoedev *d;
struct sk_buff *skb;
char buf[16];
if (size >= sizeof buf)
return -EINVAL;
buf[sizeof buf - 1] = '\0';
if (copy_from_user(buf, str, size))
return -EFAULT;
/* should be e%d.%d format */
n = sscanf(buf, "e%d.%d", &major, &minor);
if (n != 2) {
printk(KERN_ERR "aoe: invalid device specification\n");
return -EINVAL;
}
d = aoedev_by_aoeaddr(major, minor);
if (!d)
return -EINVAL;
spin_lock_irqsave(&d->lock, flags);
aoecmd_cleanslate(d);
loop:
skb = aoecmd_ata_id(d);
spin_unlock_irqrestore(&d->lock, flags);
/* try again if we are able to sleep a bit,
* otherwise give up this revalidation
*/
if (!skb && !msleep_interruptible(200)) {
spin_lock_irqsave(&d->lock, flags);
goto loop;
}
if (skb) {
struct sk_buff_head queue;
__skb_queue_head_init(&queue);
__skb_queue_tail(&queue, skb);
aoenet_xmit(&queue);
}
aoecmd_cfg(major, minor);
return 0;
}
void
aoechr_error(char *msg)
{
struct ErrMsg *em;
char *mp;
ulong flags, n;
n = strlen(msg);
spin_lock_irqsave(&emsgs_lock, flags);
em = emsgs + emsgs_tail_idx;
if ((em->flags & EMFL_VALID)) {
bail: spin_unlock_irqrestore(&emsgs_lock, flags);
return;
}
mp = kmalloc(n, GFP_ATOMIC);
if (mp == NULL) {
printk(KERN_ERR "aoe: allocation failure, len=%ld\n", n);
goto bail;
}
memcpy(mp, msg, n);
em->msg = mp;
em->flags |= EMFL_VALID;
em->len = n;
emsgs_tail_idx++;
emsgs_tail_idx %= ARRAY_SIZE(emsgs);
spin_unlock_irqrestore(&emsgs_lock, flags);
if (nblocked_emsgs_readers)
complete(&emsgs_comp);
}
static ssize_t
aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp)
{
int ret = -EINVAL;
switch ((unsigned long) filp->private_data) {
default:
printk(KERN_INFO "aoe: can't write to that file.\n");
break;
case MINOR_DISCOVER:
ret = discover();
break;
case MINOR_INTERFACES:
ret = interfaces(buf, cnt);
break;
case MINOR_REVALIDATE:
ret = revalidate(buf, cnt);
break;
case MINOR_FLUSH:
ret = aoedev_flush(buf, cnt);
}
if (ret == 0)
ret = cnt;
return ret;
}
static int
aoechr_open(struct inode *inode, struct file *filp)
{
int n, i;
mutex_lock(&aoechr_mutex);
n = iminor(inode);
filp->private_data = (void *) (unsigned long) n;
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
if (chardevs[i].minor == n) {
mutex_unlock(&aoechr_mutex);
return 0;
}
mutex_unlock(&aoechr_mutex);
return -EINVAL;
}
static int
aoechr_rel(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t
aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
unsigned long n;
char *mp;
struct ErrMsg *em;
ssize_t len;
ulong flags;
n = (unsigned long) filp->private_data;
if (n != MINOR_ERR)
return -EFAULT;
spin_lock_irqsave(&emsgs_lock, flags);
for (;;) {
em = emsgs + emsgs_head_idx;
if ((em->flags & EMFL_VALID) != 0)
break;
if (filp->f_flags & O_NDELAY) {
spin_unlock_irqrestore(&emsgs_lock, flags);
return -EAGAIN;
}
nblocked_emsgs_readers++;
spin_unlock_irqrestore(&emsgs_lock, flags);
n = wait_for_completion_interruptible(&emsgs_comp);
spin_lock_irqsave(&emsgs_lock, flags);
nblocked_emsgs_readers--;
if (n) {
spin_unlock_irqrestore(&emsgs_lock, flags);
return -ERESTARTSYS;
}
}
if (em->len > cnt) {
spin_unlock_irqrestore(&emsgs_lock, flags);
return -EAGAIN;
}
mp = em->msg;
len = em->len;
em->msg = NULL;
em->flags &= ~EMFL_VALID;
emsgs_head_idx++;
emsgs_head_idx %= ARRAY_SIZE(emsgs);
spin_unlock_irqrestore(&emsgs_lock, flags);
n = copy_to_user(buf, mp, len);
kfree(mp);
return n == 0 ? len : -EFAULT;
}
static const struct file_operations aoe_fops = {
.write = aoechr_write,
.read = aoechr_read,
.open = aoechr_open,
.release = aoechr_rel,
.owner = THIS_MODULE,
.llseek = noop_llseek,
};
static char *aoe_devnode(struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev));
}
int __init
aoechr_init(void)
{
int n, i;
n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
if (n < 0) {
printk(KERN_ERR "aoe: can't register char device\n");
return n;
}
init_completion(&emsgs_comp);
spin_lock_init(&emsgs_lock);
aoe_class = class_create(THIS_MODULE, "aoe");
if (IS_ERR(aoe_class)) {
unregister_chrdev(AOE_MAJOR, "aoechr");
return PTR_ERR(aoe_class);
}
aoe_class->devnode = aoe_devnode;
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
device_create(aoe_class, NULL,
MKDEV(AOE_MAJOR, chardevs[i].minor), NULL,
chardevs[i].name);
return 0;
}
void
aoechr_exit(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
class_destroy(aoe_class);
unregister_chrdev(AOE_MAJOR, "aoechr");
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,277 @@
/* Copyright (c) 2007 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoedev.c
* AoE device utility functions; maintains device list.
*/
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include "aoe.h"
static void dummy_timer(ulong);
static void aoedev_freedev(struct aoedev *);
static void freetgt(struct aoedev *d, struct aoetgt *t);
static void skbpoolfree(struct aoedev *d);
static struct aoedev *devlist;
static DEFINE_SPINLOCK(devlist_lock);
struct aoedev *
aoedev_by_aoeaddr(int maj, int min)
{
struct aoedev *d;
ulong flags;
spin_lock_irqsave(&devlist_lock, flags);
for (d=devlist; d; d=d->next)
if (d->aoemajor == maj && d->aoeminor == min)
break;
spin_unlock_irqrestore(&devlist_lock, flags);
return d;
}
static void
dummy_timer(ulong vp)
{
struct aoedev *d;
d = (struct aoedev *)vp;
if (d->flags & DEVFL_TKILL)
return;
d->timer.expires = jiffies + HZ;
add_timer(&d->timer);
}
void
aoedev_downdev(struct aoedev *d)
{
struct aoetgt **t, **te;
struct frame *f, *e;
struct buf *buf;
struct bio *bio;
t = d->targets;
te = t + NTARGETS;
for (; t < te && *t; t++) {
f = (*t)->frames;
e = f + (*t)->nframes;
for (; f < e; f->tag = FREETAG, f->buf = NULL, f++) {
if (f->tag == FREETAG || f->buf == NULL)
continue;
buf = f->buf;
bio = buf->bio;
if (--buf->nframesout == 0
&& buf != d->inprocess) {
mempool_free(buf, d->bufpool);
bio_endio(bio, -EIO);
}
}
(*t)->maxout = (*t)->nframes;
(*t)->nout = 0;
}
buf = d->inprocess;
if (buf) {
bio = buf->bio;
mempool_free(buf, d->bufpool);
bio_endio(bio, -EIO);
}
d->inprocess = NULL;
d->htgt = NULL;
while (!list_empty(&d->bufq)) {
buf = container_of(d->bufq.next, struct buf, bufs);
list_del(d->bufq.next);
bio = buf->bio;
mempool_free(buf, d->bufpool);
bio_endio(bio, -EIO);
}
if (d->gd)
set_capacity(d->gd, 0);
d->flags &= ~DEVFL_UP;
}
static void
aoedev_freedev(struct aoedev *d)
{
struct aoetgt **t, **e;
cancel_work_sync(&d->work);
if (d->gd) {
aoedisk_rm_sysfs(d);
del_gendisk(d->gd);
put_disk(d->gd);
}
t = d->targets;
e = t + NTARGETS;
for (; t < e && *t; t++)
freetgt(d, *t);
if (d->bufpool)
mempool_destroy(d->bufpool);
skbpoolfree(d);
blk_cleanup_queue(d->blkq);
kfree(d);
}
int
aoedev_flush(const char __user *str, size_t cnt)
{
ulong flags;
struct aoedev *d, **dd;
struct aoedev *rmd = NULL;
char buf[16];
int all = 0;
if (cnt >= 3) {
if (cnt > sizeof buf)
cnt = sizeof buf;
if (copy_from_user(buf, str, cnt))
return -EFAULT;
all = !strncmp(buf, "all", 3);
}
spin_lock_irqsave(&devlist_lock, flags);
dd = &devlist;
while ((d = *dd)) {
spin_lock(&d->lock);
if ((!all && (d->flags & DEVFL_UP))
|| (d->flags & (DEVFL_GDALLOC|DEVFL_NEWSIZE))
|| d->nopen) {
spin_unlock(&d->lock);
dd = &d->next;
continue;
}
*dd = d->next;
aoedev_downdev(d);
d->flags |= DEVFL_TKILL;
spin_unlock(&d->lock);
d->next = rmd;
rmd = d;
}
spin_unlock_irqrestore(&devlist_lock, flags);
while ((d = rmd)) {
rmd = d->next;
del_timer_sync(&d->timer);
aoedev_freedev(d); /* must be able to sleep */
}
return 0;
}
/* I'm not really sure that this is a realistic problem, but if the
network driver goes gonzo let's just leak memory after complaining. */
static void
skbfree(struct sk_buff *skb)
{
enum { Sms = 100, Tms = 3*1000};
int i = Tms / Sms;
if (skb == NULL)
return;
while (atomic_read(&skb_shinfo(skb)->dataref) != 1 && i-- > 0)
msleep(Sms);
if (i < 0) {
printk(KERN_ERR
"aoe: %s holds ref: %s\n",
skb->dev ? skb->dev->name : "netif",
"cannot free skb -- memory leaked.");
return;
}
skb_shinfo(skb)->nr_frags = skb->data_len = 0;
skb_trim(skb, 0);
dev_kfree_skb(skb);
}
static void
skbpoolfree(struct aoedev *d)
{
struct sk_buff *skb, *tmp;
skb_queue_walk_safe(&d->skbpool, skb, tmp)
skbfree(skb);
__skb_queue_head_init(&d->skbpool);
}
/* find it or malloc it */
struct aoedev *
aoedev_by_sysminor_m(ulong sysminor)
{
struct aoedev *d;
ulong flags;
spin_lock_irqsave(&devlist_lock, flags);
for (d=devlist; d; d=d->next)
if (d->sysminor == sysminor)
break;
if (d)
goto out;
d = kcalloc(1, sizeof *d, GFP_ATOMIC);
if (!d)
goto out;
INIT_WORK(&d->work, aoecmd_sleepwork);
spin_lock_init(&d->lock);
skb_queue_head_init(&d->sendq);
skb_queue_head_init(&d->skbpool);
init_timer(&d->timer);
d->timer.data = (ulong) d;
d->timer.function = dummy_timer;
d->timer.expires = jiffies + HZ;
add_timer(&d->timer);
d->bufpool = NULL; /* defer to aoeblk_gdalloc */
d->tgt = d->targets;
INIT_LIST_HEAD(&d->bufq);
d->sysminor = sysminor;
d->aoemajor = AOEMAJOR(sysminor);
d->aoeminor = AOEMINOR(sysminor);
d->mintimer = MINTIMER;
d->next = devlist;
devlist = d;
out:
spin_unlock_irqrestore(&devlist_lock, flags);
return d;
}
static void
freetgt(struct aoedev *d, struct aoetgt *t)
{
struct frame *f, *e;
f = t->frames;
e = f + t->nframes;
for (; f < e; f++)
skbfree(f->skb);
kfree(t->frames);
kfree(t);
}
void
aoedev_exit(void)
{
struct aoedev *d;
ulong flags;
while ((d = devlist)) {
devlist = d->next;
spin_lock_irqsave(&d->lock, flags);
aoedev_downdev(d);
d->flags |= DEVFL_TKILL;
spin_unlock_irqrestore(&d->lock, flags);
del_timer_sync(&d->timer);
aoedev_freedev(d);
}
}
int __init
aoedev_init(void)
{
return 0;
}

View File

@ -0,0 +1,111 @@
/* Copyright (c) 2007 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoemain.c
* Module initialization routines, discover timer
*/
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include "aoe.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sam Hopkins <sah@coraid.com>");
MODULE_DESCRIPTION("AoE block/char driver for 2.6.2 and newer 2.6 kernels");
MODULE_VERSION(VERSION);
enum { TINIT, TRUN, TKILL };
static void
discover_timer(ulong vp)
{
static struct timer_list t;
static volatile ulong die;
static spinlock_t lock;
ulong flags;
enum { DTIMERTICK = HZ * 60 }; /* one minute */
switch (vp) {
case TINIT:
init_timer(&t);
spin_lock_init(&lock);
t.data = TRUN;
t.function = discover_timer;
die = 0;
case TRUN:
spin_lock_irqsave(&lock, flags);
if (!die) {
t.expires = jiffies + DTIMERTICK;
add_timer(&t);
}
spin_unlock_irqrestore(&lock, flags);
aoecmd_cfg(0xffff, 0xff);
return;
case TKILL:
spin_lock_irqsave(&lock, flags);
die = 1;
spin_unlock_irqrestore(&lock, flags);
del_timer_sync(&t);
default:
return;
}
}
static void
aoe_exit(void)
{
discover_timer(TKILL);
aoenet_exit();
unregister_blkdev(AOE_MAJOR, DEVICE_NAME);
aoechr_exit();
aoedev_exit();
aoeblk_exit(); /* free cache after de-allocating bufs */
}
static int __init
aoe_init(void)
{
int ret;
ret = aoedev_init();
if (ret)
return ret;
ret = aoechr_init();
if (ret)
goto chr_fail;
ret = aoeblk_init();
if (ret)
goto blk_fail;
ret = aoenet_init();
if (ret)
goto net_fail;
ret = register_blkdev(AOE_MAJOR, DEVICE_NAME);
if (ret < 0) {
printk(KERN_ERR "aoe: can't register major\n");
goto blkreg_fail;
}
printk(KERN_INFO "aoe: AoE v%s initialised.\n", VERSION);
discover_timer(TINIT);
return 0;
blkreg_fail:
aoenet_exit();
net_fail:
aoeblk_exit();
blk_fail:
aoechr_exit();
chr_fail:
aoedev_exit();
printk(KERN_INFO "aoe: initialisation failure.\n");
return ret;
}
module_init(aoe_init);
module_exit(aoe_exit);

View File

@ -0,0 +1,172 @@
/* Copyright (c) 2007 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoenet.c
* Ethernet portion of AoE driver
*/
#include <linux/gfp.h>
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/netdevice.h>
#include <linux/moduleparam.h>
#include <net/net_namespace.h>
#include <asm/unaligned.h>
#include "aoe.h"
#define NECODES 5
static char *aoe_errlist[] =
{
"no such error",
"unrecognized command code",
"bad argument parameter",
"device unavailable",
"config string present",
"unsupported version"
};
enum {
IFLISTSZ = 1024,
};
static char aoe_iflist[IFLISTSZ];
module_param_string(aoe_iflist, aoe_iflist, IFLISTSZ, 0600);
MODULE_PARM_DESC(aoe_iflist, "aoe_iflist=\"dev1 [dev2 ...]\"");
#ifndef MODULE
static int __init aoe_iflist_setup(char *str)
{
strncpy(aoe_iflist, str, IFLISTSZ);
aoe_iflist[IFLISTSZ - 1] = '\0';
return 1;
}
__setup("aoe_iflist=", aoe_iflist_setup);
#endif
int
is_aoe_netif(struct net_device *ifp)
{
register char *p, *q;
register int len;
if (aoe_iflist[0] == '\0')
return 1;
p = aoe_iflist + strspn(aoe_iflist, WHITESPACE);
for (; *p; p = q + strspn(q, WHITESPACE)) {
q = p + strcspn(p, WHITESPACE);
if (q != p)
len = q - p;
else
len = strlen(p); /* last token in aoe_iflist */
if (strlen(ifp->name) == len && !strncmp(ifp->name, p, len))
return 1;
if (q == p)
break;
}
return 0;
}
int
set_aoe_iflist(const char __user *user_str, size_t size)
{
if (size >= IFLISTSZ)
return -EINVAL;
if (copy_from_user(aoe_iflist, user_str, size)) {
printk(KERN_INFO "aoe: copy from user failed\n");
return -EFAULT;
}
aoe_iflist[size] = 0x00;
return 0;
}
void
aoenet_xmit(struct sk_buff_head *queue)
{
struct sk_buff *skb, *tmp;
skb_queue_walk_safe(queue, skb, tmp) {
__skb_unlink(skb, queue);
dev_queue_xmit(skb);
}
}
/*
* (1) len doesn't include the header by default. I want this.
*/
static int
aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, struct net_device *orig_dev)
{
struct aoe_hdr *h;
u32 n;
if (dev_net(ifp) != &init_net)
goto exit;
skb = skb_share_check(skb, GFP_ATOMIC);
if (skb == NULL)
return 0;
if (skb_linearize(skb))
goto exit;
if (!is_aoe_netif(ifp))
goto exit;
skb_push(skb, ETH_HLEN); /* (1) */
h = (struct aoe_hdr *) skb_mac_header(skb);
n = get_unaligned_be32(&h->tag);
if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31))
goto exit;
if (h->verfl & AOEFL_ERR) {
n = h->err;
if (n > NECODES)
n = 0;
if (net_ratelimit())
printk(KERN_ERR
"%s%d.%d@%s; ecode=%d '%s'\n",
"aoe: error packet from ",
get_unaligned_be16(&h->major),
h->minor, skb->dev->name,
h->err, aoe_errlist[n]);
goto exit;
}
switch (h->cmd) {
case AOECMD_ATA:
aoecmd_ata_rsp(skb);
break;
case AOECMD_CFG:
aoecmd_cfg_rsp(skb);
break;
default:
if (h->cmd >= AOECMD_VEND_MIN)
break; /* don't complain about vendor commands */
printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd);
}
exit:
dev_kfree_skb(skb);
return 0;
}
static struct packet_type aoe_pt __read_mostly = {
.type = __constant_htons(ETH_P_AOE),
.func = aoenet_rcv,
};
int __init
aoenet_init(void)
{
dev_add_pack(&aoe_pt);
return 0;
}
void
aoenet_exit(void)
{
dev_remove_pack(&aoe_pt);
}