aoeblk.c 6.8 KB
Newer Older
1
/* Copyright (c) 2012 Coraid, Inc.  See COPYING for GPL terms. */
L
Linus Torvalds 已提交
2 3 4 5 6
/*
 * aoeblk.c
 * block device routines
 */

7
#include <linux/kernel.h>
L
Linus Torvalds 已提交
8 9
#include <linux/hdreg.h>
#include <linux/blkdev.h>
10
#include <linux/backing-dev.h>
L
Linus Torvalds 已提交
11 12
#include <linux/fs.h>
#include <linux/ioctl.h>
13
#include <linux/slab.h>
14
#include <linux/ratelimit.h>
L
Linus Torvalds 已提交
15 16
#include <linux/genhd.h>
#include <linux/netdevice.h>
17
#include <linux/mutex.h>
18
#include <linux/export.h>
L
Linus Torvalds 已提交
19 20
#include "aoe.h"

21
static DEFINE_MUTEX(aoeblk_mutex);
22
static struct kmem_cache *buf_pool_cache;
L
Linus Torvalds 已提交
23

24 25
static ssize_t aoedisk_show_state(struct device *dev,
				  struct device_attribute *attr, char *page)
L
Linus Torvalds 已提交
26
{
27
	struct gendisk *disk = dev_to_disk(dev);
L
Linus Torvalds 已提交
28 29 30 31 32
	struct aoedev *d = disk->private_data;

	return snprintf(page, PAGE_SIZE,
			"%s%s\n",
			(d->flags & DEVFL_UP) ? "up" : "down",
33
			(d->flags & DEVFL_KICKME) ? ",kickme" :
34 35
			(d->nopen && !(d->flags & DEVFL_UP)) ? ",closewait" : "");
	/* I'd rather see nopen exported so we can ditch closewait */
L
Linus Torvalds 已提交
36
}
37 38
static ssize_t aoedisk_show_mac(struct device *dev,
				struct device_attribute *attr, char *page)
L
Linus Torvalds 已提交
39
{
40
	struct gendisk *disk = dev_to_disk(dev);
L
Linus Torvalds 已提交
41
	struct aoedev *d = disk->private_data;
42
	struct aoetgt *t = d->targets[0];
L
Linus Torvalds 已提交
43

44 45
	if (t == NULL)
		return snprintf(page, PAGE_SIZE, "none\n");
46
	return snprintf(page, PAGE_SIZE, "%pm\n", t->addr);
L
Linus Torvalds 已提交
47
}
48 49
static ssize_t aoedisk_show_netif(struct device *dev,
				  struct device_attribute *attr, char *page)
L
Linus Torvalds 已提交
50
{
51
	struct gendisk *disk = dev_to_disk(dev);
L
Linus Torvalds 已提交
52
	struct aoedev *d = disk->private_data;
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
	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;
		}
	}
L
Linus Torvalds 已提交
74

75 76 77 78 79 80 81 82 83
	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;
L
Linus Torvalds 已提交
84
}
85
/* firmware version */
86 87
static ssize_t aoedisk_show_fwver(struct device *dev,
				  struct device_attribute *attr, char *page)
88
{
89
	struct gendisk *disk = dev_to_disk(dev);
90 91 92 93
	struct aoedev *d = disk->private_data;

	return snprintf(page, PAGE_SIZE, "0x%04x\n", (unsigned int) d->fw_ver);
}
L
Linus Torvalds 已提交
94

95 96 97 98
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 = {
99
	.attr = { .name = "firmware-version", .mode = S_IRUGO },
100
	.show = aoedisk_show_fwver,
101
};
L
Linus Torvalds 已提交
102

103
static struct attribute *aoe_attrs[] = {
104 105 106 107 108
	&dev_attr_state.attr,
	&dev_attr_mac.attr,
	&dev_attr_netif.attr,
	&dev_attr_firmware_version.attr,
	NULL,
109 110 111 112 113 114 115
};

static const struct attribute_group attr_group = {
	.attrs = aoe_attrs,
};

static int
L
Linus Torvalds 已提交
116 117
aoedisk_add_sysfs(struct aoedev *d)
{
118
	return sysfs_create_group(&disk_to_dev(d->gd)->kobj, &attr_group);
L
Linus Torvalds 已提交
119 120 121 122
}
void
aoedisk_rm_sysfs(struct aoedev *d)
{
123
	sysfs_remove_group(&disk_to_dev(d->gd)->kobj, &attr_group);
L
Linus Torvalds 已提交
124 125 126
}

static int
A
Al Viro 已提交
127
aoeblk_open(struct block_device *bdev, fmode_t mode)
L
Linus Torvalds 已提交
128
{
A
Al Viro 已提交
129
	struct aoedev *d = bdev->bd_disk->private_data;
L
Linus Torvalds 已提交
130 131
	ulong flags;

132
	mutex_lock(&aoeblk_mutex);
L
Linus Torvalds 已提交
133 134 135 136
	spin_lock_irqsave(&d->lock, flags);
	if (d->flags & DEVFL_UP) {
		d->nopen++;
		spin_unlock_irqrestore(&d->lock, flags);
137
		mutex_unlock(&aoeblk_mutex);
L
Linus Torvalds 已提交
138 139 140
		return 0;
	}
	spin_unlock_irqrestore(&d->lock, flags);
141
	mutex_unlock(&aoeblk_mutex);
L
Linus Torvalds 已提交
142 143 144 145
	return -ENODEV;
}

static int
A
Al Viro 已提交
146
aoeblk_release(struct gendisk *disk, fmode_t mode)
L
Linus Torvalds 已提交
147
{
A
Al Viro 已提交
148
	struct aoedev *d = disk->private_data;
L
Linus Torvalds 已提交
149 150 151 152
	ulong flags;

	spin_lock_irqsave(&d->lock, flags);

153
	if (--d->nopen == 0) {
L
Linus Torvalds 已提交
154 155 156 157 158 159 160 161 162
		spin_unlock_irqrestore(&d->lock, flags);
		aoecmd_cfg(d->aoemajor, d->aoeminor);
		return 0;
	}
	spin_unlock_irqrestore(&d->lock, flags);

	return 0;
}

163
static void
164
aoeblk_request(struct request_queue *q)
L
Linus Torvalds 已提交
165 166
{
	struct aoedev *d;
167
	struct request *rq;
L
Linus Torvalds 已提交
168

169
	d = q->queuedata;
L
Linus Torvalds 已提交
170
	if ((d->flags & DEVFL_UP) == 0) {
171
		pr_info_ratelimited("aoe: device %ld.%d is not up\n",
E
Ed L. Cashin 已提交
172
			d->aoemajor, d->aoeminor);
173 174 175 176
		while ((rq = blk_peek_request(q))) {
			blk_start_request(rq);
			aoe_end_request(d, rq, 1);
		}
177
		return;
L
Linus Torvalds 已提交
178
	}
179
	aoecmd_work(d);
L
Linus Torvalds 已提交
180 181 182
}

static int
183
aoeblk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
L
Linus Torvalds 已提交
184
{
185
	struct aoedev *d = bdev->bd_disk->private_data;
L
Linus Torvalds 已提交
186 187

	if ((d->flags & DEVFL_UP) == 0) {
E
Ed L. Cashin 已提交
188
		printk(KERN_ERR "aoe: disk not up\n");
L
Linus Torvalds 已提交
189 190 191
		return -ENODEV;
	}

192 193 194 195
	geo->cylinders = d->geo.cylinders;
	geo->heads = d->geo.heads;
	geo->sectors = d->geo.sectors;
	return 0;
L
Linus Torvalds 已提交
196 197
}

198
static const struct block_device_operations aoe_bdops = {
A
Al Viro 已提交
199 200
	.open = aoeblk_open,
	.release = aoeblk_release,
201
	.getgeo = aoeblk_getgeo,
L
Linus Torvalds 已提交
202 203 204 205 206 207 208 209 210
	.owner = THIS_MODULE,
};

/* alloc_disk and add_disk can sleep */
void
aoeblk_gdalloc(void *vp)
{
	struct aoedev *d = vp;
	struct gendisk *gd;
211 212 213
	mempool_t *mp;
	struct request_queue *q;
	enum { KB = 1024, MB = KB * KB, READ_AHEAD = 2 * MB, };
L
Linus Torvalds 已提交
214 215 216 217
	ulong flags;

	gd = alloc_disk(AOE_PARTITIONS);
	if (gd == NULL) {
218
		pr_err("aoe: cannot allocate disk structure for %ld.%d\n",
E
Ed L. Cashin 已提交
219
			d->aoemajor, d->aoeminor);
220
		goto err;
L
Linus Torvalds 已提交
221 222
	}

223 224 225
	mp = mempool_create(MIN_BUFS, mempool_alloc_slab, mempool_free_slab,
		buf_pool_cache);
	if (mp == NULL) {
226
		printk(KERN_ERR "aoe: cannot allocate bufpool for %ld.%d\n",
E
Ed L. Cashin 已提交
227
			d->aoemajor, d->aoeminor);
228
		goto err_disk;
L
Linus Torvalds 已提交
229
	}
230 231 232 233 234 235 236
	q = blk_init_queue(aoeblk_request, &d->lock);
	if (q == NULL) {
		pr_err("aoe: cannot allocate block queue for %ld.%d\n",
			d->aoemajor, d->aoeminor);
		mempool_destroy(mp);
		goto err_disk;
	}
L
Linus Torvalds 已提交
237

238 239
	d->blkq = blk_alloc_queue(GFP_KERNEL);
	if (!d->blkq)
240
		goto err_mempool;
241
	d->blkq->backing_dev_info.name = "aoe";
242 243
	if (bdi_init(&d->blkq->backing_dev_info))
		goto err_blkq;
244
	spin_lock_irqsave(&d->lock, flags);
245
	blk_queue_max_hw_sectors(d->blkq, BLK_DEF_MAX_SECTORS);
246 247 248 249 250
	q->backing_dev_info.ra_pages = READ_AHEAD / PAGE_CACHE_SIZE;
	d->bufpool = mp;
	d->blkq = gd->queue = q;
	q->queuedata = d;
	d->gd = gd;
L
Linus Torvalds 已提交
251
	gd->major = AOE_MAJOR;
252
	gd->first_minor = d->sysminor;
L
Linus Torvalds 已提交
253 254
	gd->fops = &aoe_bdops;
	gd->private_data = d;
255
	set_capacity(gd, d->ssize);
256
	snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%d",
L
Linus Torvalds 已提交
257 258
		d->aoemajor, d->aoeminor);

259
	d->flags &= ~DEVFL_GDALLOC;
L
Linus Torvalds 已提交
260 261 262 263 264 265
	d->flags |= DEVFL_UP;

	spin_unlock_irqrestore(&d->lock, flags);

	add_disk(gd);
	aoedisk_add_sysfs(d);
266 267
	return;

268 269 270
err_blkq:
	blk_cleanup_queue(d->blkq);
	d->blkq = NULL;
271 272 273 274 275 276 277 278
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);
L
Linus Torvalds 已提交
279 280 281 282 283 284 285 286 287 288 289
}

void
aoeblk_exit(void)
{
	kmem_cache_destroy(buf_pool_cache);
}

int __init
aoeblk_init(void)
{
290
	buf_pool_cache = kmem_cache_create("aoe_bufs",
L
Linus Torvalds 已提交
291
					   sizeof(struct buf),
292
					   0, 0, NULL);
L
Linus Torvalds 已提交
293 294 295 296 297 298
	if (buf_pool_cache == NULL)
		return -ENOMEM;

	return 0;
}