X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fblock%2Faoe%2Faoedev.c;h=eeea477d96016596ccd729a6f75d37d5e3d30a0e;hb=a348a7e6fdbcd2d5192a09719a479bb238fde727;hp=ec16c64dd114c37b30f2f97bb5e8f895c0a6e9bb;hpb=03347936afcba990525736ae39daa13f643eac5f;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index ec16c64..eeea477 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2004 Coraid, Inc. See COPYING for GPL terms. */ +/* Copyright (c) 2007 Coraid, Inc. See COPYING for GPL terms. */ /* * aoedev.c * AoE device utility functions; maintains device list. @@ -7,10 +7,16 @@ #include #include #include +#include #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 spinlock_t devlist_lock; +static DEFINE_SPINLOCK(devlist_lock); struct aoedev * aoedev_by_aoeaddr(int maj, int min) @@ -28,127 +34,219 @@ aoedev_by_aoeaddr(int maj, int min) return d; } -/* called with devlist lock held */ -static struct aoedev * -aoedev_newdev(ulong nframes) +static void +dummy_timer(ulong vp) { struct aoedev *d; - struct frame *f, *e; - - d = kcalloc(1, sizeof *d, GFP_ATOMIC); - if (d == NULL) - return NULL; - f = kcalloc(nframes, sizeof *f, GFP_ATOMIC); - if (f == NULL) { - kfree(d); - return NULL; - } - - d->nframes = nframes; - d->frames = f; - e = f + nframes; - for (; ftag = FREETAG; - - spin_lock_init(&d->lock); - init_timer(&d->timer); - d->bufpool = NULL; /* defer to aoeblk_gdalloc */ - INIT_LIST_HEAD(&d->bufq); - d->next = devlist; - devlist = d; - return 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; - d->flags |= DEVFL_TKILL; - del_timer(&d->timer); - - f = d->frames; - e = f + d->nframes; - for (; ftag = FREETAG, f->buf = NULL, f++) { - if (f->tag == FREETAG || f->buf == NULL) - continue; - buf = f->buf; - bio = buf->bio; - if (--buf->nframesout == 0) { - mempool_free(buf, d->bufpool); - bio_endio(bio, bio->bi_size, -EIO); + 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, bio->bi_size, -EIO); + bio_endio(bio, -EIO); } - if (d->nopen) - d->flags |= DEVFL_CLOSEWAIT; if (d->gd) - d->gd->capacity = 0; + set_capacity(d->gd, 0); d->flags &= ~DEVFL_UP; } -struct aoedev * -aoedev_set(ulong sysminor, unsigned char *addr, struct net_device *ifp, ulong bufcnt) +static void +aoedev_freedev(struct aoedev *d) +{ + struct aoetgt **t, **e; + + 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); + kfree(d); +} + +int +aoedev_flush(const char __user *str, size_t cnt) { - struct aoedev *d; 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); + } + flush_scheduled_work(); 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; +} - for (d=devlist; d; d=d->next) - if (d->sysminor == sysminor - || memcmp(d->addr, addr, sizeof d->addr) == 0) - break; - - if (d == NULL && (d = aoedev_newdev(bufcnt)) == NULL) { - spin_unlock_irqrestore(&devlist_lock, flags); - printk(KERN_INFO "aoe: aoedev_set: aoedev_newdev failure.\n"); - return NULL; +/* 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); +} - spin_unlock_irqrestore(&devlist_lock, flags); - spin_lock_irqsave(&d->lock, flags); +static void +skbpoolfree(struct aoedev *d) +{ + struct sk_buff *skb, *tmp; - d->ifp = ifp; + skb_queue_walk_safe(&d->skbpool, skb, tmp) + skbfree(skb); - if (d->sysminor != sysminor - || (d->flags & DEVFL_UP) == 0) { - aoedev_downdev(d); /* flushes outstanding frames */ - memcpy(d->addr, addr, sizeof d->addr); - d->sysminor = sysminor; - d->aoemajor = AOEMAJOR(sysminor); - d->aoeminor = AOEMINOR(sysminor); - } + __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); - spin_unlock_irqrestore(&d->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 -aoedev_freedev(struct aoedev *d) +freetgt(struct aoedev *d, struct aoetgt *t) { - if (d->gd) { - aoedisk_rm_sysfs(d); - del_gendisk(d->gd); - put_disk(d->gd); - } - kfree(d->frames); - if (d->bufpool) - mempool_destroy(d->bufpool); - kfree(d); + struct frame *f, *e; + + f = t->frames; + e = f + t->nframes; + for (; f < e; f++) + skbfree(f->skb); + kfree(t->frames); + kfree(t); } void @@ -164,6 +262,7 @@ aoedev_exit(void) spin_lock_irqsave(&d->lock, flags); aoedev_downdev(d); + d->flags |= DEVFL_TKILL; spin_unlock_irqrestore(&d->lock, flags); del_timer_sync(&d->timer); @@ -174,7 +273,5 @@ aoedev_exit(void) int __init aoedev_init(void) { - spin_lock_init(&devlist_lock); return 0; } -