aoeblk.c 5.96 KB
Newer Older
Ed L. Cashin's avatar
Ed L. Cashin committed
1
/* Copyright (c) 2006 Coraid, Inc.  See COPYING for GPL terms. */
Linus Torvalds's avatar
Linus Torvalds committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
 * aoeblk.c
 * block device routines
 */

#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/genhd.h>
#include <linux/netdevice.h>
#include "aoe.h"

static kmem_cache_t *buf_pool_cache;

static ssize_t aoedisk_show_state(struct gendisk * disk, char *page)
{
	struct aoedev *d = disk->private_data;

	return snprintf(page, PAGE_SIZE,
			"%s%s\n",
			(d->flags & DEVFL_UP) ? "up" : "down",
24
25
26
			(d->flags & DEVFL_PAUSE) ? ",paused" :
			(d->nopen && !(d->flags & DEVFL_UP)) ? ",closewait" : "");
	/* I'd rather see nopen exported so we can ditch closewait */
Linus Torvalds's avatar
Linus Torvalds committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
}
static ssize_t aoedisk_show_mac(struct gendisk * disk, char *page)
{
	struct aoedev *d = disk->private_data;

	return snprintf(page, PAGE_SIZE, "%012llx\n",
			(unsigned long long)mac_addr(d->addr));
}
static ssize_t aoedisk_show_netif(struct gendisk * disk, char *page)
{
	struct aoedev *d = disk->private_data;

	return snprintf(page, PAGE_SIZE, "%s\n", d->ifp->name);
}
41
42
43
44
45
46
47
/* firmware version */
static ssize_t aoedisk_show_fwver(struct gendisk * disk, char *page)
{
	struct aoedev *d = disk->private_data;

	return snprintf(page, PAGE_SIZE, "0x%04x\n", (unsigned int) d->fw_ver);
}
Linus Torvalds's avatar
Linus Torvalds committed
48
49
50
51
52
53
54
55
56
57
58
59
60

static struct disk_attribute disk_attr_state = {
	.attr = {.name = "state", .mode = S_IRUGO },
	.show = aoedisk_show_state
};
static struct disk_attribute disk_attr_mac = {
	.attr = {.name = "mac", .mode = S_IRUGO },
	.show = aoedisk_show_mac
};
static struct disk_attribute disk_attr_netif = {
	.attr = {.name = "netif", .mode = S_IRUGO },
	.show = aoedisk_show_netif
};
61
62
63
64
static struct disk_attribute disk_attr_fwver = {
	.attr = {.name = "firmware-version", .mode = S_IRUGO },
	.show = aoedisk_show_fwver
};
Linus Torvalds's avatar
Linus Torvalds committed
65

66
67
68
69
70
71
72
73
74
75
76
77
static struct attribute *aoe_attrs[] = {
	&disk_attr_state.attr,
	&disk_attr_mac.attr,
	&disk_attr_netif.attr,
	&disk_attr_fwver.attr,
};

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

static int
Linus Torvalds's avatar
Linus Torvalds committed
78
79
aoedisk_add_sysfs(struct aoedev *d)
{
80
	return sysfs_create_group(&d->gd->kobj, &attr_group);
Linus Torvalds's avatar
Linus Torvalds committed
81
82
83
84
}
void
aoedisk_rm_sysfs(struct aoedev *d)
{
85
	sysfs_remove_group(&d->gd->kobj, &attr_group);
Linus Torvalds's avatar
Linus Torvalds committed
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
}

static int
aoeblk_open(struct inode *inode, struct file *filp)
{
	struct aoedev *d;
	ulong flags;

	d = inode->i_bdev->bd_disk->private_data;

	spin_lock_irqsave(&d->lock, flags);
	if (d->flags & DEVFL_UP) {
		d->nopen++;
		spin_unlock_irqrestore(&d->lock, flags);
		return 0;
	}
	spin_unlock_irqrestore(&d->lock, flags);
	return -ENODEV;
}

static int
aoeblk_release(struct inode *inode, struct file *filp)
{
	struct aoedev *d;
	ulong flags;

	d = inode->i_bdev->bd_disk->private_data;

	spin_lock_irqsave(&d->lock, flags);

116
	if (--d->nopen == 0) {
Linus Torvalds's avatar
Linus Torvalds committed
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
		spin_unlock_irqrestore(&d->lock, flags);
		aoecmd_cfg(d->aoemajor, d->aoeminor);
		return 0;
	}
	spin_unlock_irqrestore(&d->lock, flags);

	return 0;
}

static int
aoeblk_make_request(request_queue_t *q, struct bio *bio)
{
	struct aoedev *d;
	struct buf *buf;
	struct sk_buff *sl;
	ulong flags;

	blk_queue_bounce(q, &bio);

	d = bio->bi_bdev->bd_disk->private_data;
	buf = mempool_alloc(d->bufpool, GFP_NOIO);
	if (buf == NULL) {
Ed L. Cashin's avatar
Ed L. Cashin committed
139
		printk(KERN_INFO "aoe: buf allocation failure\n");
Linus Torvalds's avatar
Linus Torvalds committed
140
141
142
143
144
		bio_endio(bio, bio->bi_size, -ENOMEM);
		return 0;
	}
	memset(buf, 0, sizeof(*buf));
	INIT_LIST_HEAD(&buf->bufs);
145
	buf->start_time = jiffies;
Linus Torvalds's avatar
Linus Torvalds committed
146
147
148
	buf->bio = bio;
	buf->resid = bio->bi_size;
	buf->sector = bio->bi_sector;
Ed L. Cashin's avatar
Ed L. Cashin committed
149
150
	buf->bv = &bio->bi_io_vec[bio->bi_idx];
	WARN_ON(buf->bv->bv_len == 0);
Linus Torvalds's avatar
Linus Torvalds committed
151
152
153
154
155
156
	buf->bv_resid = buf->bv->bv_len;
	buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;

	spin_lock_irqsave(&d->lock, flags);

	if ((d->flags & DEVFL_UP) == 0) {
Ed L. Cashin's avatar
Ed L. Cashin committed
157
158
		printk(KERN_INFO "aoe: device %ld.%ld is not up\n",
			d->aoemajor, d->aoeminor);
Linus Torvalds's avatar
Linus Torvalds committed
159
160
161
162
163
164
165
166
		spin_unlock_irqrestore(&d->lock, flags);
		mempool_free(buf, d->bufpool);
		bio_endio(bio, bio->bi_size, -ENXIO);
		return 0;
	}

	list_add_tail(&buf->bufs, &d->bufq);

167
	aoecmd_work(d);
168
169
	sl = d->sendq_hd;
	d->sendq_hd = d->sendq_tl = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
170
171
172

	spin_unlock_irqrestore(&d->lock, flags);
	aoenet_xmit(sl);
173

Linus Torvalds's avatar
Linus Torvalds committed
174
175
176
177
	return 0;
}

static int
178
aoeblk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
Linus Torvalds's avatar
Linus Torvalds committed
179
{
180
	struct aoedev *d = bdev->bd_disk->private_data;
Linus Torvalds's avatar
Linus Torvalds committed
181
182

	if ((d->flags & DEVFL_UP) == 0) {
Ed L. Cashin's avatar
Ed L. Cashin committed
183
		printk(KERN_ERR "aoe: disk not up\n");
Linus Torvalds's avatar
Linus Torvalds committed
184
185
186
		return -ENODEV;
	}

187
188
189
190
	geo->cylinders = d->geo.cylinders;
	geo->heads = d->geo.heads;
	geo->sectors = d->geo.sectors;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
191
192
193
194
195
}

static struct block_device_operations aoe_bdops = {
	.open = aoeblk_open,
	.release = aoeblk_release,
196
	.getgeo = aoeblk_getgeo,
Linus Torvalds's avatar
Linus Torvalds committed
197
198
199
200
201
202
203
204
205
206
207
208
209
	.owner = THIS_MODULE,
};

/* alloc_disk and add_disk can sleep */
void
aoeblk_gdalloc(void *vp)
{
	struct aoedev *d = vp;
	struct gendisk *gd;
	ulong flags;

	gd = alloc_disk(AOE_PARTITIONS);
	if (gd == NULL) {
Ed L. Cashin's avatar
Ed L. Cashin committed
210
		printk(KERN_ERR "aoe: cannot allocate disk structure for %ld.%ld\n",
211
			d->aoemajor, d->aoeminor);
Linus Torvalds's avatar
Linus Torvalds committed
212
		spin_lock_irqsave(&d->lock, flags);
213
		d->flags &= ~DEVFL_GDALLOC;
Linus Torvalds's avatar
Linus Torvalds committed
214
215
216
217
		spin_unlock_irqrestore(&d->lock, flags);
		return;
	}

218
	d->bufpool = mempool_create_slab_pool(MIN_BUFS, buf_pool_cache);
Linus Torvalds's avatar
Linus Torvalds committed
219
	if (d->bufpool == NULL) {
Ed L. Cashin's avatar
Ed L. Cashin committed
220
		printk(KERN_ERR "aoe: cannot allocate bufpool for %ld.%ld\n",
221
			d->aoemajor, d->aoeminor);
Linus Torvalds's avatar
Linus Torvalds committed
222
223
		put_disk(gd);
		spin_lock_irqsave(&d->lock, flags);
224
		d->flags &= ~DEVFL_GDALLOC;
Linus Torvalds's avatar
Linus Torvalds committed
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
		spin_unlock_irqrestore(&d->lock, flags);
		return;
	}

	spin_lock_irqsave(&d->lock, flags);
	blk_queue_make_request(&d->blkq, aoeblk_make_request);
	gd->major = AOE_MAJOR;
	gd->first_minor = d->sysminor * AOE_PARTITIONS;
	gd->fops = &aoe_bdops;
	gd->private_data = d;
	gd->capacity = d->ssize;
	snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%ld",
		d->aoemajor, d->aoeminor);

	gd->queue = &d->blkq;
	d->gd = gd;
241
	d->flags &= ~DEVFL_GDALLOC;
Linus Torvalds's avatar
Linus Torvalds committed
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
	d->flags |= DEVFL_UP;

	spin_unlock_irqrestore(&d->lock, flags);

	add_disk(gd);
	aoedisk_add_sysfs(d);
}

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

int __init
aoeblk_init(void)
{
	buf_pool_cache = kmem_cache_create("aoe_bufs", 
					   sizeof(struct buf),
					   0, 0, NULL, NULL);
	if (buf_pool_cache == NULL)
		return -ENOMEM;

	return 0;
}