1/* Copyright (c) 2006 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 "aoe.h" 11 12static struct aoedev *devlist; 13static spinlock_t devlist_lock; 14 15int 16aoedev_isbusy(struct aoedev *d) 17{ 18 struct frame *f, *e; 19 20 f = d->frames; 21 e = f + d->nframes; 22 do { 23 if (f->tag != FREETAG) 24 return 1; 25 } while (++f < e); 26 27 return 0; 28} 29 30struct aoedev * 31aoedev_by_aoeaddr(int maj, int min) 32{ 33 struct aoedev *d; 34 ulong flags; 35 36 spin_lock_irqsave(&devlist_lock, flags); 37 38 for (d=devlist; d; d=d->next) 39 if (d->aoemajor == maj && d->aoeminor == min) 40 break; 41 42 spin_unlock_irqrestore(&devlist_lock, flags); 43 return d; 44} 45 46static void 47dummy_timer(ulong vp) 48{ 49 struct aoedev *d; 50 51 d = (struct aoedev *)vp; 52 if (d->flags & DEVFL_TKILL) 53 return; 54 d->timer.expires = jiffies + HZ; 55 add_timer(&d->timer); 56} 57 58/* called with devlist lock held */ 59static struct aoedev * 60aoedev_newdev(ulong nframes) 61{ 62 struct aoedev *d; 63 struct frame *f, *e; 64 65 d = kzalloc(sizeof *d, GFP_ATOMIC); 66 f = kcalloc(nframes, sizeof *f, GFP_ATOMIC); 67 switch (!d || !f) { 68 case 0: 69 d->nframes = nframes; 70 d->frames = f; 71 e = f + nframes; 72 for (; f<e; f++) { 73 f->tag = FREETAG; 74 f->skb = new_skb(ETH_ZLEN); 75 if (!f->skb) 76 break; 77 } 78 if (f == e) 79 break; 80 while (f > d->frames) { 81 f--; 82 dev_kfree_skb(f->skb); 83 } 84 default: 85 if (f) 86 kfree(f); 87 if (d) 88 kfree(d); 89 return NULL; 90 } 91 INIT_WORK(&d->work, aoecmd_sleepwork); 92 spin_lock_init(&d->lock); 93 init_timer(&d->timer); 94 d->timer.data = (ulong) d; 95 d->timer.function = dummy_timer; 96 d->timer.expires = jiffies + HZ; 97 add_timer(&d->timer); 98 d->bufpool = NULL; /* defer to aoeblk_gdalloc */ 99 INIT_LIST_HEAD(&d->bufq); 100 d->next = devlist; 101 devlist = d; 102 103 return d; 104} 105 106void 107aoedev_downdev(struct aoedev *d) 108{ 109 struct frame *f, *e; 110 struct buf *buf; 111 struct bio *bio; 112 113 f = d->frames; 114 e = f + d->nframes; 115 for (; f<e; f->tag = FREETAG, f->buf = NULL, f++) { 116 if (f->tag == FREETAG || f->buf == NULL) 117 continue; 118 buf = f->buf; 119 bio = buf->bio; 120 if (--buf->nframesout == 0) { 121 mempool_free(buf, d->bufpool); 122 bio_endio(bio, bio->bi_size, -EIO); 123 } 124 skb_shinfo(f->skb)->nr_frags = f->skb->data_len = 0; 125 } 126 d->inprocess = NULL; 127 128 while (!list_empty(&d->bufq)) { 129 buf = container_of(d->bufq.next, struct buf, bufs); 130 list_del(d->bufq.next); 131 bio = buf->bio; 132 mempool_free(buf, d->bufpool); 133 bio_endio(bio, bio->bi_size, -EIO); 134 } 135 136 if (d->gd) 137 d->gd->capacity = 0; 138 139 d->flags &= ~(DEVFL_UP | DEVFL_PAUSE); 140} 141 142/* find it or malloc it */ 143struct aoedev * 144aoedev_by_sysminor_m(ulong sysminor, ulong bufcnt) 145{ 146 struct aoedev *d; 147 ulong flags; 148 149 spin_lock_irqsave(&devlist_lock, flags); 150 151 for (d=devlist; d; d=d->next) 152 if (d->sysminor == sysminor) 153 break; 154 155 if (d == NULL) { 156 d = aoedev_newdev(bufcnt); 157 if (d == NULL) { 158 spin_unlock_irqrestore(&devlist_lock, flags); 159 printk(KERN_INFO "aoe: aoedev_newdev failure.\n"); 160 return NULL; 161 } 162 d->sysminor = sysminor; 163 d->aoemajor = AOEMAJOR(sysminor); 164 d->aoeminor = AOEMINOR(sysminor); 165 } 166 167 spin_unlock_irqrestore(&devlist_lock, flags); 168 return d; 169} 170 171static void 172aoedev_freedev(struct aoedev *d) 173{ 174 struct frame *f, *e; 175 176 if (d->gd) { 177 aoedisk_rm_sysfs(d); 178 del_gendisk(d->gd); 179 put_disk(d->gd); 180 } 181 f = d->frames; 182 e = f + d->nframes; 183 for (; f<e; f++) { 184 skb_shinfo(f->skb)->nr_frags = 0; 185 dev_kfree_skb(f->skb); 186 } 187 kfree(d->frames); 188 if (d->bufpool) 189 mempool_destroy(d->bufpool); 190 kfree(d); 191} 192 193void 194aoedev_exit(void) 195{ 196 struct aoedev *d; 197 ulong flags; 198 199 flush_scheduled_work(); 200 201 while ((d = devlist)) { 202 devlist = d->next; 203 204 spin_lock_irqsave(&d->lock, flags); 205 aoedev_downdev(d); 206 d->flags |= DEVFL_TKILL; 207 spin_unlock_irqrestore(&d->lock, flags); 208 209 del_timer_sync(&d->timer); 210 aoedev_freedev(d); 211 } 212} 213 214int __init 215aoedev_init(void) 216{ 217 spin_lock_init(&devlist_lock); 218 return 0; 219} 220