mfi_disk.c revision 169451
1/*-
2 * Copyright (c) 2006 IronPort Systems
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/mfi/mfi_disk.c 169451 2007-05-10 15:33:41Z scottl $");
29
30#include "opt_mfi.h"
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/selinfo.h>
36#include <sys/module.h>
37#include <sys/malloc.h>
38#include <sys/uio.h>
39
40#include <sys/bio.h>
41#include <sys/bus.h>
42#include <sys/conf.h>
43#include <sys/disk.h>
44#include <geom/geom_disk.h>
45
46#include <vm/vm.h>
47#include <vm/pmap.h>
48
49#include <machine/md_var.h>
50#include <machine/bus.h>
51#include <sys/rman.h>
52
53#include <dev/mfi/mfireg.h>
54#include <dev/mfi/mfi_ioctl.h>
55#include <dev/mfi/mfivar.h>
56
57static int	mfi_disk_probe(device_t dev);
58static int	mfi_disk_attach(device_t dev);
59static int	mfi_disk_detach(device_t dev);
60
61static disk_open_t	mfi_disk_open;
62static disk_close_t	mfi_disk_close;
63static disk_strategy_t	mfi_disk_strategy;
64static dumper_t		mfi_disk_dump;
65
66static devclass_t	mfi_disk_devclass;
67
68static device_method_t mfi_disk_methods[] = {
69	DEVMETHOD(device_probe,		mfi_disk_probe),
70	DEVMETHOD(device_attach,	mfi_disk_attach),
71	DEVMETHOD(device_detach,	mfi_disk_detach),
72	{ 0, 0 }
73};
74
75static driver_t mfi_disk_driver = {
76	"mfid",
77	mfi_disk_methods,
78	sizeof(struct mfi_disk)
79};
80
81DRIVER_MODULE(mfid, mfi, mfi_disk_driver, mfi_disk_devclass, 0, 0);
82
83static int
84mfi_disk_probe(device_t dev)
85{
86
87	return (0);
88}
89
90static int
91mfi_disk_attach(device_t dev)
92{
93	struct mfi_disk *sc;
94	struct mfi_ld_info *ld_info;
95	uint64_t sectors;
96	uint32_t secsize;
97	char *state;
98
99	sc = device_get_softc(dev);
100	ld_info = device_get_ivars(dev);
101
102	sc->ld_dev = dev;
103	sc->ld_id = ld_info->ld_config.properties.ld.v.target_id;
104	sc->ld_unit = device_get_unit(dev);
105	sc->ld_info = ld_info;
106	sc->ld_controller = device_get_softc(device_get_parent(dev));
107	sc->ld_flags = 0;
108
109	sectors = ld_info->size;
110	secsize = MFI_SECTOR_LEN;
111	TAILQ_INSERT_TAIL(&sc->ld_controller->mfi_ld_tqh, sc, ld_link);
112
113	switch (ld_info->ld_config.params.state) {
114	case MFI_LD_STATE_OFFLINE:
115		state = "offline";
116		break;
117	case MFI_LD_STATE_PARTIALLY_DEGRADED:
118		state = "partially degraded";
119		break;
120	case MFI_LD_STATE_DEGRADED:
121		state = "degraded";
122		break;
123	case MFI_LD_STATE_OPTIMAL:
124		state = "optimal";
125		break;
126	default:
127		state = "unknown";
128		break;
129	}
130	device_printf(dev, "%juMB (%ju sectors) RAID volume '%s' is %s\n",
131		      sectors / (1024 * 1024 / secsize), sectors,
132		      ld_info->ld_config.properties.name,
133		      state);
134
135	sc->ld_disk = disk_alloc();
136	sc->ld_disk->d_drv1 = sc;
137	sc->ld_disk->d_maxsize = sc->ld_controller->mfi_max_io * secsize;
138	sc->ld_disk->d_name = "mfid";
139	sc->ld_disk->d_open = mfi_disk_open;
140	sc->ld_disk->d_close = mfi_disk_close;
141	sc->ld_disk->d_strategy = mfi_disk_strategy;
142	sc->ld_disk->d_dump = mfi_disk_dump;
143	sc->ld_disk->d_unit = sc->ld_unit;
144	sc->ld_disk->d_sectorsize = secsize;
145	sc->ld_disk->d_mediasize = sectors * secsize;
146	if (sc->ld_disk->d_mediasize >= (1 * 1024 * 1024)) {
147		sc->ld_disk->d_fwheads = 255;
148		sc->ld_disk->d_fwsectors = 63;
149	} else {
150		sc->ld_disk->d_fwheads = 64;
151		sc->ld_disk->d_fwsectors = 32;
152	}
153	disk_create(sc->ld_disk, DISK_VERSION);
154
155	return (0);
156}
157
158static int
159mfi_disk_detach(device_t dev)
160{
161	struct mfi_disk *sc;
162
163	sc = device_get_softc(dev);
164
165	if ((sc->ld_disk->d_flags & DISKFLAG_OPEN) ||
166	    (sc->ld_flags & MFI_DISK_FLAGS_OPEN))
167		return (EBUSY);
168
169	free(sc->ld_info, M_MFIBUF);
170	disk_destroy(sc->ld_disk);
171	return (0);
172}
173
174static int
175mfi_disk_open(struct disk *dp)
176{
177	struct mfi_disk *sc;
178
179	sc = dp->d_drv1;
180	mtx_lock(&sc->ld_controller->mfi_io_lock);
181	sc->ld_flags |= MFI_DISK_FLAGS_OPEN;
182	mtx_unlock(&sc->ld_controller->mfi_io_lock);
183
184	return (0);
185}
186
187static int
188mfi_disk_close(struct disk *dp)
189{
190	struct mfi_disk *sc;
191
192	sc = dp->d_drv1;
193	mtx_lock(&sc->ld_controller->mfi_io_lock);
194	sc->ld_flags &= ~MFI_DISK_FLAGS_OPEN;
195	mtx_unlock(&sc->ld_controller->mfi_io_lock);
196
197	return (0);
198}
199
200static void
201mfi_disk_strategy(struct bio *bio)
202{
203	struct mfi_disk *sc;
204	struct mfi_softc *controller;
205
206	sc = bio->bio_disk->d_drv1;
207
208	if (sc == NULL) {
209		bio->bio_error = EINVAL;
210		bio->bio_flags |= BIO_ERROR;
211		bio->bio_resid = bio->bio_bcount;
212		biodone(bio);
213		return;
214	}
215
216	controller = sc->ld_controller;
217	bio->bio_driver1 = (void *)(uintptr_t)sc->ld_id;
218	mtx_lock(&controller->mfi_io_lock);
219	mfi_enqueue_bio(controller, bio);
220	mfi_startio(controller);
221	mtx_unlock(&controller->mfi_io_lock);
222	return;
223}
224
225void
226mfi_disk_complete(struct bio *bio)
227{
228	struct mfi_disk *sc;
229	struct mfi_frame_header *hdr;
230
231	sc = bio->bio_disk->d_drv1;
232	hdr = bio->bio_driver1;
233
234	if (bio->bio_flags & BIO_ERROR) {
235		if (bio->bio_error == 0)
236			bio->bio_error = EIO;
237		disk_err(bio, "hard error", -1, 1);
238	} else {
239		bio->bio_resid = 0;
240	}
241	biodone(bio);
242}
243
244static int
245mfi_disk_dump(void *arg, void *virt, vm_offset_t phys, off_t offset, size_t len)
246{
247	struct mfi_disk *sc;
248	struct mfi_softc *parent_sc;
249	struct disk *dp;
250	int error;
251
252	dp = arg;
253	sc = dp->d_drv1;
254	parent_sc = sc->ld_controller;
255
256	if (len > 0) {
257		if ((error = mfi_dump_blocks(parent_sc, sc->ld_id, offset /
258		    MFI_SECTOR_LEN, virt, len)) != 0)
259			return (error);
260	} else {
261		/* mfi_sync_cache(parent_sc, sc->ld_id); */
262	}
263
264	return (0);
265}
266