123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- /* 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;
- }
|