mfi_disk.c revision 157197
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 157197 2006-03-28 01:59:11Z 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/module.h>
36#include <sys/malloc.h>
37
38#include <sys/bio.h>
39#include <sys/bus.h>
40#include <sys/conf.h>
41#include <sys/disk.h>
42#include <geom/geom_disk.h>
43
44#include <vm/vm.h>
45#include <vm/pmap.h>
46
47#include <machine/md_var.h>
48#include <machine/bus.h>
49#include <sys/rman.h>
50
51#include <dev/mfi/mfireg.h>
52#include <dev/mfi/mfi_ioctl.h>
53#include <dev/mfi/mfivar.h>
54
55static int	mfi_disk_probe(device_t dev);
56static int	mfi_disk_attach(device_t dev);
57static int	mfi_disk_detach(device_t dev);
58
59static disk_open_t	mfi_disk_open;
60static disk_close_t	mfi_disk_close;
61static disk_strategy_t	mfi_disk_strategy;
62static dumper_t		mfi_disk_dump;
63
64static devclass_t	mfi_disk_devclass;
65
66struct mfi_disk {
67	device_t	ld_dev;
68	int		ld_id;
69	int		ld_unit;
70	struct mfi_softc *ld_controller;
71	struct mfi_ld	*ld_ld;
72	struct disk	*ld_disk;
73};
74
75static device_method_t mfi_disk_methods[] = {
76	DEVMETHOD(device_probe,		mfi_disk_probe),
77	DEVMETHOD(device_attach,	mfi_disk_attach),
78	DEVMETHOD(device_detach,	mfi_disk_detach),
79	{ 0, 0 }
80};
81
82static driver_t mfi_disk_driver = {
83	"mfid",
84	mfi_disk_methods,
85	sizeof(struct mfi_disk)
86};
87
88DRIVER_MODULE(mfid, mfi, mfi_disk_driver, mfi_disk_devclass, 0, 0);
89
90static int
91mfi_disk_probe(device_t dev)
92{
93
94	return (0);
95}
96
97static int
98mfi_disk_attach(device_t dev)
99{
100	struct mfi_disk *sc;
101	struct mfi_ld *ld;
102	uint64_t sectors;
103	uint32_t secsize;
104
105	sc = device_get_softc(dev);
106	ld = device_get_ivars(dev);
107
108	sc->ld_dev = dev;
109	sc->ld_id = ld->ld_id;
110	sc->ld_unit = device_get_unit(dev);
111	sc->ld_ld = device_get_ivars(dev);
112	sc->ld_controller = device_get_softc(device_get_parent(dev));
113
114	sectors = sc->ld_ld->ld_sectors;
115	secsize = sc->ld_ld->ld_secsize;
116	if (secsize != MFI_SECTOR_LEN) {
117		device_printf(sc->ld_dev, "Reported sector length %d is not "
118		    "512, aborting\n", secsize);
119		free(sc->ld_ld, M_MFIBUF);
120		return (EINVAL);
121	}
122	TAILQ_INSERT_TAIL(&sc->ld_controller->mfi_ld_tqh, ld, ld_link);
123
124	device_printf(dev, "%juMB (%ju sectors) RAID\n",
125	    sectors / (1024 * 1024 / secsize), sectors);
126
127	sc->ld_disk = disk_alloc();
128	sc->ld_disk->d_drv1 = sc;
129	sc->ld_disk->d_maxsize = sc->ld_controller->mfi_max_io * secsize;
130	sc->ld_disk->d_name = "mfid";
131	sc->ld_disk->d_open = mfi_disk_open;
132	sc->ld_disk->d_close = mfi_disk_close;
133	sc->ld_disk->d_strategy = mfi_disk_strategy;
134	sc->ld_disk->d_dump = mfi_disk_dump;
135	sc->ld_disk->d_unit = sc->ld_unit;
136	sc->ld_disk->d_sectorsize = secsize;
137	sc->ld_disk->d_mediasize = sectors * secsize;
138	if (sc->ld_disk->d_mediasize >= (1 * 1024 * 1024)) {
139		sc->ld_disk->d_fwheads = 255;
140		sc->ld_disk->d_fwsectors = 63;
141	} else {
142		sc->ld_disk->d_fwheads = 64;
143		sc->ld_disk->d_fwsectors = 32;
144	}
145	disk_create(sc->ld_disk, DISK_VERSION);
146
147	return (0);
148}
149
150static int
151mfi_disk_detach(device_t dev)
152{
153	struct mfi_disk *sc;
154
155	sc = device_get_softc(dev);
156
157	if (sc->ld_disk->d_flags & DISKFLAG_OPEN)
158		return (EBUSY);
159
160	disk_destroy(sc->ld_disk);
161	return (0);
162}
163
164static int
165mfi_disk_open(struct disk *dp)
166{
167
168	return (0);
169}
170
171static int
172mfi_disk_close(struct disk *dp)
173{
174
175	return (0);
176}
177
178static void
179mfi_disk_strategy(struct bio *bio)
180{
181	struct mfi_disk *sc;
182	struct mfi_softc *controller;
183
184	sc = bio->bio_disk->d_drv1;
185
186	if (sc == NULL) {
187		bio->bio_error = EINVAL;
188		bio->bio_flags |= BIO_ERROR;
189		bio->bio_resid = bio->bio_bcount;
190		biodone(bio);
191		return;
192	}
193
194	controller = sc->ld_controller;
195	bio->bio_driver1 = (void *)(uintptr_t)sc->ld_id;
196	mtx_lock(&controller->mfi_io_lock);
197	mfi_enqueue_bio(controller, bio);
198	mfi_startio(controller);
199	mtx_unlock(&controller->mfi_io_lock);
200	return;
201}
202
203void
204mfi_disk_complete(struct bio *bio)
205{
206	struct mfi_disk *sc;
207	struct mfi_frame_header *hdr;
208
209	sc = bio->bio_disk->d_drv1;
210	hdr = bio->bio_driver1;
211
212	if (bio->bio_flags & BIO_ERROR) {
213		if (bio->bio_error == 0)
214			bio->bio_error = EIO;
215		disk_err(bio, "hard error", -1, 1);
216	} else {
217		bio->bio_resid = 0;
218	}
219	biodone(bio);
220}
221
222static int
223mfi_disk_dump(void *arg, void *virt, vm_offset_t phys, off_t offset, size_t len)
224{
225	struct mfi_disk *sc;
226	struct mfi_softc *parent_sc;
227	struct disk *dp;
228	int error;
229
230	dp = arg;
231	sc = dp->d_drv1;
232	parent_sc = sc->ld_controller;
233
234	if (len > 0) {
235		if ((error = mfi_dump_blocks(parent_sc, sc->ld_id, offset /
236		    sc->ld_ld->ld_secsize, virt, len)) != 0)
237			return (error);
238	} else {
239		/* mfi_sync_cache(parent_sc, sc->ld_id); */
240	}
241
242	return (0);
243}
244