linux/drivers/block/aoe/aoedev.c
<<
>>
Prefs
   1/* Copyright (c) 2007 Coraid, Inc.  See COPYING for GPL terms. */
   2/*
   3 * aoedev.c
   4 * AoE device utility functions; maintains device list.
   5 */
   6
   7#include <linux/hdreg.h>
   8#include <linux/blkdev.h>
   9#include <linux/netdevice.h>
  10#include <linux/delay.h>
  11#include "aoe.h"
  12
  13static void dummy_timer(ulong);
  14static void aoedev_freedev(struct aoedev *);
  15static void freetgt(struct aoedev *d, struct aoetgt *t);
  16static void skbpoolfree(struct aoedev *d);
  17
  18static struct aoedev *devlist;
  19static DEFINE_SPINLOCK(devlist_lock);
  20
  21struct aoedev *
  22aoedev_by_aoeaddr(int maj, int min)
  23{
  24        struct aoedev *d;
  25        ulong flags;
  26
  27        spin_lock_irqsave(&devlist_lock, flags);
  28
  29        for (d=devlist; d; d=d->next)
  30                if (d->aoemajor == maj && d->aoeminor == min)
  31                        break;
  32
  33        spin_unlock_irqrestore(&devlist_lock, flags);
  34        return d;
  35}
  36
  37static void
  38dummy_timer(ulong vp)
  39{
  40        struct aoedev *d;
  41
  42        d = (struct aoedev *)vp;
  43        if (d->flags & DEVFL_TKILL)
  44                return;
  45        d->timer.expires = jiffies + HZ;
  46        add_timer(&d->timer);
  47}
  48
  49void
  50aoedev_downdev(struct aoedev *d)
  51{
  52        struct aoetgt **t, **te;
  53        struct frame *f, *e;
  54        struct buf *buf;
  55        struct bio *bio;
  56
  57        t = d->targets;
  58        te = t + NTARGETS;
  59        for (; t < te && *t; t++) {
  60                f = (*t)->frames;
  61                e = f + (*t)->nframes;
  62                for (; f < e; f->tag = FREETAG, f->buf = NULL, f++) {
  63                        if (f->tag == FREETAG || f->buf == NULL)
  64                                continue;
  65                        buf = f->buf;
  66                        bio = buf->bio;
  67                        if (--buf->nframesout == 0
  68                        && buf != d->inprocess) {
  69                                mempool_free(buf, d->bufpool);
  70                                bio_endio(bio, -EIO);
  71                        }
  72                }
  73                (*t)->maxout = (*t)->nframes;
  74                (*t)->nout = 0;
  75        }
  76        buf = d->inprocess;
  77        if (buf) {
  78                bio = buf->bio;
  79                mempool_free(buf, d->bufpool);
  80                bio_endio(bio, -EIO);
  81        }
  82        d->inprocess = NULL;
  83        d->htgt = NULL;
  84
  85        while (!list_empty(&d->bufq)) {
  86                buf = container_of(d->bufq.next, struct buf, bufs);
  87                list_del(d->bufq.next);
  88                bio = buf->bio;
  89                mempool_free(buf, d->bufpool);
  90                bio_endio(bio, -EIO);
  91        }
  92
  93        if (d->gd)
  94                set_capacity(d->gd, 0);
  95
  96        d->flags &= ~DEVFL_UP;
  97}
  98
  99static void
 100aoedev_freedev(struct aoedev *d)
 101{
 102        struct aoetgt **t, **e;
 103
 104        if (d->gd) {
 105                aoedisk_rm_sysfs(d);
 106                del_gendisk(d->gd);
 107                put_disk(d->gd);
 108        }
 109        t = d->targets;
 110        e = t + NTARGETS;
 111        for (; t < e && *t; t++)
 112                freetgt(d, *t);
 113        if (d->bufpool)
 114                mempool_destroy(d->bufpool);
 115        skbpoolfree(d);
 116        blk_cleanup_queue(d->blkq);
 117        kfree(d);
 118}
 119
 120int
 121aoedev_flush(const char __user *str, size_t cnt)
 122{
 123        ulong flags;
 124        struct aoedev *d, **dd;
 125        struct aoedev *rmd = NULL;
 126        char buf[16];
 127        int all = 0;
 128
 129        if (cnt >= 3) {
 130                if (cnt > sizeof buf)
 131                        cnt = sizeof buf;
 132                if (copy_from_user(buf, str, cnt))
 133                        return -EFAULT;
 134                all = !strncmp(buf, "all", 3);
 135        }
 136
 137        flush_scheduled_work();
 138        spin_lock_irqsave(&devlist_lock, flags);
 139        dd = &devlist;
 140        while ((d = *dd)) {
 141                spin_lock(&d->lock);
 142                if ((!all && (d->flags & DEVFL_UP))
 143                || (d->flags & (DEVFL_GDALLOC|DEVFL_NEWSIZE))
 144                || d->nopen) {
 145                        spin_unlock(&d->lock);
 146                        dd = &d->next;
 147                        continue;
 148                }
 149                *dd = d->next;
 150                aoedev_downdev(d);
 151                d->flags |= DEVFL_TKILL;
 152                spin_unlock(&d->lock);
 153                d->next = rmd;
 154                rmd = d;
 155        }
 156        spin_unlock_irqrestore(&devlist_lock, flags);
 157        while ((d = rmd)) {
 158                rmd = d->next;
 159                del_timer_sync(&d->timer);
 160                aoedev_freedev(d);      /* must be able to sleep */
 161        }
 162        return 0;
 163}
 164
 165/* I'm not really sure that this is a realistic problem, but if the
 166network driver goes gonzo let's just leak memory after complaining. */
 167static void
 168skbfree(struct sk_buff *skb)
 169{
 170        enum { Sms = 100, Tms = 3*1000};
 171        int i = Tms / Sms;
 172
 173        if (skb == NULL)
 174                return;
 175        while (atomic_read(&skb_shinfo(skb)->dataref) != 1 && i-- > 0)
 176                msleep(Sms);
 177        if (i < 0) {
 178                printk(KERN_ERR
 179                        "aoe: %s holds ref: %s\n",
 180                        skb->dev ? skb->dev->name : "netif",
 181                        "cannot free skb -- memory leaked.");
 182                return;
 183        }
 184        skb_shinfo(skb)->nr_frags = skb->data_len = 0;
 185        skb_trim(skb, 0);
 186        dev_kfree_skb(skb);
 187}
 188
 189static void
 190skbpoolfree(struct aoedev *d)
 191{
 192        struct sk_buff *skb, *tmp;
 193
 194        skb_queue_walk_safe(&d->skbpool, skb, tmp)
 195                skbfree(skb);
 196
 197        __skb_queue_head_init(&d->skbpool);
 198}
 199
 200/* find it or malloc it */
 201struct aoedev *
 202aoedev_by_sysminor_m(ulong sysminor)
 203{
 204        struct aoedev *d;
 205        ulong flags;
 206
 207        spin_lock_irqsave(&devlist_lock, flags);
 208
 209        for (d=devlist; d; d=d->next)
 210                if (d->sysminor == sysminor)
 211                        break;
 212        if (d)
 213                goto out;
 214        d = kcalloc(1, sizeof *d, GFP_ATOMIC);
 215        if (!d)
 216                goto out;
 217        INIT_WORK(&d->work, aoecmd_sleepwork);
 218        spin_lock_init(&d->lock);
 219        skb_queue_head_init(&d->sendq);
 220        skb_queue_head_init(&d->skbpool);
 221        init_timer(&d->timer);
 222        d->timer.data = (ulong) d;
 223        d->timer.function = dummy_timer;
 224        d->timer.expires = jiffies + HZ;
 225        add_timer(&d->timer);
 226        d->bufpool = NULL;      /* defer to aoeblk_gdalloc */
 227        d->tgt = d->targets;
 228        INIT_LIST_HEAD(&d->bufq);
 229        d->sysminor = sysminor;
 230        d->aoemajor = AOEMAJOR(sysminor);
 231        d->aoeminor = AOEMINOR(sysminor);
 232        d->mintimer = MINTIMER;
 233        d->next = devlist;
 234        devlist = d;
 235 out:
 236        spin_unlock_irqrestore(&devlist_lock, flags);
 237        return d;
 238}
 239
 240static void
 241freetgt(struct aoedev *d, struct aoetgt *t)
 242{
 243        struct frame *f, *e;
 244
 245        f = t->frames;
 246        e = f + t->nframes;
 247        for (; f < e; f++)
 248                skbfree(f->skb);
 249        kfree(t->frames);
 250        kfree(t);
 251}
 252
 253void
 254aoedev_exit(void)
 255{
 256        struct aoedev *d;
 257        ulong flags;
 258
 259        flush_scheduled_work();
 260
 261        while ((d = devlist)) {
 262                devlist = d->next;
 263
 264                spin_lock_irqsave(&d->lock, flags);
 265                aoedev_downdev(d);
 266                d->flags |= DEVFL_TKILL;
 267                spin_unlock_irqrestore(&d->lock, flags);
 268
 269                del_timer_sync(&d->timer);
 270                aoedev_freedev(d);
 271        }
 272}
 273
 274int __init
 275aoedev_init(void)
 276{
 277        return 0;
 278}
 279