mfi_disk.c revision 234429
1202375Srdivacky/*-
2202375Srdivacky * Copyright (c) 2006 IronPort Systems
3202375Srdivacky * All rights reserved.
4202375Srdivacky *
5202375Srdivacky * Redistribution and use in source and binary forms, with or without
6202375Srdivacky * modification, are permitted provided that the following conditions
7202375Srdivacky * are met:
8202375Srdivacky * 1. Redistributions of source code must retain the above copyright
9202375Srdivacky *    notice, this list of conditions and the following disclaimer.
10202375Srdivacky * 2. Redistributions in binary form must reproduce the above copyright
11202375Srdivacky *    notice, this list of conditions and the following disclaimer in the
12202375Srdivacky *    documentation and/or other materials provided with the distribution.
13202375Srdivacky *
14202375Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15202375Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16249423Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17221345Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18202375Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19249423Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20202375Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21202375Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22202375Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23202375Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24202375Srdivacky * SUCH DAMAGE.
25249423Sdim */
26249423Sdim
27202375Srdivacky#include <sys/cdefs.h>
28202375Srdivacky__FBSDID("$FreeBSD: stable/9/sys/dev/mfi/mfi_disk.c 234429 2012-04-18 18:31:11Z ambrisko $");
29202375Srdivacky
30202375Srdivacky#include "opt_mfi.h"
31202375Srdivacky
32202375Srdivacky#include <sys/param.h>
33202375Srdivacky#include <sys/systm.h>
34202375Srdivacky#include <sys/kernel.h>
35202375Srdivacky#include <sys/selinfo.h>
36202375Srdivacky#include <sys/module.h>
37202375Srdivacky#include <sys/malloc.h>
38202375Srdivacky#include <sys/sysctl.h>
39249423Sdim#include <sys/uio.h>
40202375Srdivacky
41202375Srdivacky#include <sys/bio.h>
42202375Srdivacky#include <sys/bus.h>
43249423Sdim#include <sys/conf.h>
44202375Srdivacky#include <sys/disk.h>
45202375Srdivacky#include <geom/geom_disk.h>
46202375Srdivacky
47249423Sdim#include <vm/vm.h>
48202375Srdivacky#include <vm/pmap.h>
49202375Srdivacky
50202375Srdivacky#include <machine/md_var.h>
51202375Srdivacky#include <machine/bus.h>
52202375Srdivacky#include <sys/rman.h>
53202375Srdivacky
54202375Srdivacky#include <dev/mfi/mfireg.h>
55202375Srdivacky#include <dev/mfi/mfi_ioctl.h>
56202375Srdivacky#include <dev/mfi/mfivar.h>
57202375Srdivacky
58202375Srdivackystatic int	mfi_disk_probe(device_t dev);
59249423Sdimstatic int	mfi_disk_attach(device_t dev);
60202375Srdivackystatic int	mfi_disk_detach(device_t dev);
61202375Srdivacky
62202375Srdivackystatic disk_open_t	mfi_disk_open;
63202375Srdivackystatic disk_close_t	mfi_disk_close;
64202375Srdivackystatic disk_strategy_t	mfi_disk_strategy;
65202375Srdivackystatic dumper_t		mfi_disk_dump;
66202375Srdivacky
67202375Srdivackystatic devclass_t	mfi_disk_devclass;
68202375Srdivacky
69202375Srdivackystatic device_method_t mfi_disk_methods[] = {
70202375Srdivacky	DEVMETHOD(device_probe,		mfi_disk_probe),
71202375Srdivacky	DEVMETHOD(device_attach,	mfi_disk_attach),
72202375Srdivacky	DEVMETHOD(device_detach,	mfi_disk_detach),
73202375Srdivacky	{ 0, 0 }
74202375Srdivacky};
75202375Srdivacky
76202375Srdivackystatic driver_t mfi_disk_driver = {
77202375Srdivacky	"mfid",
78202375Srdivacky	mfi_disk_methods,
79202375Srdivacky	sizeof(struct mfi_disk)
80202375Srdivacky};
81202375Srdivacky
82202375SrdivackyDRIVER_MODULE(mfid, mfi, mfi_disk_driver, mfi_disk_devclass, 0, 0);
83202375Srdivacky
84202375Srdivackystatic int
85202375Srdivackymfi_disk_probe(device_t dev)
86202375Srdivacky{
87202375Srdivacky
88202375Srdivacky	return (0);
89202375Srdivacky}
90202375Srdivacky
91202375Srdivackystatic int
92202375Srdivackymfi_disk_attach(device_t dev)
93234353Sdim{
94249423Sdim	struct mfi_disk *sc;
95202375Srdivacky	struct mfi_ld_info *ld_info;
96202375Srdivacky	uint64_t sectors;
97234353Sdim	uint32_t secsize;
98234353Sdim	char *state;
99234353Sdim
100234353Sdim	sc = device_get_softc(dev);
101234353Sdim	ld_info = device_get_ivars(dev);
102234353Sdim
103202375Srdivacky	sc->ld_dev = dev;
104202375Srdivacky	sc->ld_id = ld_info->ld_config.properties.ld.v.target_id;
105202375Srdivacky	sc->ld_unit = device_get_unit(dev);
106202375Srdivacky	sc->ld_info = ld_info;
107202375Srdivacky	sc->ld_controller = device_get_softc(device_get_parent(dev));
108202375Srdivacky	sc->ld_flags = 0;
109204792Srdivacky
110204792Srdivacky	sectors = ld_info->size;
111204792Srdivacky	secsize = MFI_SECTOR_LEN;
112202375Srdivacky	mtx_lock(&sc->ld_controller->mfi_io_lock);
113234353Sdim	TAILQ_INSERT_TAIL(&sc->ld_controller->mfi_ld_tqh, sc, ld_link);
114204792Srdivacky	mtx_unlock(&sc->ld_controller->mfi_io_lock);
115204792Srdivacky
116204792Srdivacky	switch (ld_info->ld_config.params.state) {
117204792Srdivacky	case MFI_LD_STATE_OFFLINE:
118204792Srdivacky		state = "offline";
119204792Srdivacky		break;
120204792Srdivacky	case MFI_LD_STATE_PARTIALLY_DEGRADED:
121249423Sdim		state = "partially degraded";
122218893Sdim		break;
123218893Sdim	case MFI_LD_STATE_DEGRADED:
124202375Srdivacky		state = "degraded";
125204792Srdivacky		break;
126202375Srdivacky	case MFI_LD_STATE_OPTIMAL:
127202375Srdivacky		state = "optimal";
128202375Srdivacky		break;
129202375Srdivacky	default:
130202375Srdivacky		state = "unknown";
131202375Srdivacky		break;
132202375Srdivacky	}
133202375Srdivacky	device_printf(dev, "%juMB (%ju sectors) RAID volume '%s' is %s\n",
134202375Srdivacky		      sectors / (1024 * 1024 / secsize), sectors,
135202375Srdivacky		      ld_info->ld_config.properties.name,
136202375Srdivacky		      state);
137202375Srdivacky
138202375Srdivacky	sc->ld_disk = disk_alloc();
139202375Srdivacky	sc->ld_disk->d_drv1 = sc;
140202375Srdivacky	sc->ld_disk->d_maxsize = min(sc->ld_controller->mfi_max_io * secsize,
141202375Srdivacky	    (sc->ld_controller->mfi_max_sge - 1) * PAGE_SIZE);
142202375Srdivacky	sc->ld_disk->d_name = "mfid";
143202375Srdivacky	sc->ld_disk->d_open = mfi_disk_open;
144202375Srdivacky	sc->ld_disk->d_close = mfi_disk_close;
145202375Srdivacky	sc->ld_disk->d_strategy = mfi_disk_strategy;
146202375Srdivacky	sc->ld_disk->d_dump = mfi_disk_dump;
147202375Srdivacky	sc->ld_disk->d_unit = sc->ld_unit;
148202375Srdivacky	sc->ld_disk->d_sectorsize = secsize;
149202375Srdivacky	sc->ld_disk->d_mediasize = sectors * secsize;
150218893Sdim	if (sc->ld_disk->d_mediasize >= (1 * 1024 * 1024)) {
151218893Sdim		sc->ld_disk->d_fwheads = 255;
152218893Sdim		sc->ld_disk->d_fwsectors = 63;
153218893Sdim	} else {
154218893Sdim		sc->ld_disk->d_fwheads = 64;
155218893Sdim		sc->ld_disk->d_fwsectors = 32;
156218893Sdim	}
157249423Sdim	disk_create(sc->ld_disk, DISK_VERSION);
158218893Sdim
159218893Sdim	return (0);
160218893Sdim}
161218893Sdim
162218893Sdimstatic int
163218893Sdimmfi_disk_detach(device_t dev)
164218893Sdim{
165218893Sdim	struct mfi_disk *sc;
166218893Sdim
167218893Sdim	sc = device_get_softc(dev);
168202375Srdivacky
169249423Sdim	mtx_lock(&sc->ld_controller->mfi_io_lock);
170202375Srdivacky	if (((sc->ld_disk->d_flags & DISKFLAG_OPEN) ||
171202375Srdivacky	    (sc->ld_flags & MFI_DISK_FLAGS_OPEN)) &&
172202375Srdivacky	    (sc->ld_controller->mfi_keep_deleted_volumes ||
173202375Srdivacky	    sc->ld_controller->mfi_detaching)) {
174202375Srdivacky		mtx_unlock(&sc->ld_controller->mfi_io_lock);
175202375Srdivacky		return (EBUSY);
176263508Sdim	}
177202375Srdivacky	mtx_unlock(&sc->ld_controller->mfi_io_lock);
178202375Srdivacky
179202375Srdivacky	disk_destroy(sc->ld_disk);
180202375Srdivacky	mtx_lock(&sc->ld_controller->mfi_io_lock);
181202375Srdivacky	TAILQ_REMOVE(&sc->ld_controller->mfi_ld_tqh, sc, ld_link);
182202375Srdivacky	mtx_unlock(&sc->ld_controller->mfi_io_lock);
183263508Sdim	free(sc->ld_info, M_MFIBUF);
184202375Srdivacky	return (0);
185202375Srdivacky}
186202375Srdivacky
187202375Srdivackystatic int
188202375Srdivackymfi_disk_open(struct disk *dp)
189202375Srdivacky{
190202375Srdivacky	struct mfi_disk *sc;
191202375Srdivacky	int error;
192202375Srdivacky
193202375Srdivacky	sc = dp->d_drv1;
194202375Srdivacky	mtx_lock(&sc->ld_controller->mfi_io_lock);
195202375Srdivacky	if (sc->ld_flags & MFI_DISK_FLAGS_DISABLED)
196202375Srdivacky		error = ENXIO;
197202375Srdivacky	else {
198202375Srdivacky		sc->ld_flags |= MFI_DISK_FLAGS_OPEN;
199202375Srdivacky		error = 0;
200202375Srdivacky	}
201202375Srdivacky	mtx_unlock(&sc->ld_controller->mfi_io_lock);
202202375Srdivacky
203202375Srdivacky	return (error);
204202375Srdivacky}
205202375Srdivacky
206202375Srdivackystatic int
207202375Srdivackymfi_disk_close(struct disk *dp)
208202375Srdivacky{
209202375Srdivacky	struct mfi_disk *sc;
210202375Srdivacky
211202375Srdivacky	sc = dp->d_drv1;
212263508Sdim	mtx_lock(&sc->ld_controller->mfi_io_lock);
213202375Srdivacky	sc->ld_flags &= ~MFI_DISK_FLAGS_OPEN;
214218893Sdim	mtx_unlock(&sc->ld_controller->mfi_io_lock);
215218893Sdim
216202375Srdivacky	return (0);
217249423Sdim}
218218893Sdim
219202375Srdivackyint
220202375Srdivackymfi_disk_disable(struct mfi_disk *sc)
221202375Srdivacky{
222202375Srdivacky
223202375Srdivacky	mtx_assert(&sc->ld_controller->mfi_io_lock, MA_OWNED);
224202375Srdivacky	if (sc->ld_flags & MFI_DISK_FLAGS_OPEN) {
225202375Srdivacky		if (sc->ld_controller->mfi_delete_busy_volumes)
226202375Srdivacky			return (0);
227202375Srdivacky		device_printf(sc->ld_dev, "Unable to delete busy ld device\n");
228202375Srdivacky		return (EBUSY);
229202375Srdivacky	}
230202375Srdivacky	sc->ld_flags |= MFI_DISK_FLAGS_DISABLED;
231202375Srdivacky	return (0);
232263508Sdim}
233202375Srdivacky
234218893Sdimvoid
235218893Sdimmfi_disk_enable(struct mfi_disk *sc)
236202375Srdivacky{
237249423Sdim
238218893Sdim	mtx_assert(&sc->ld_controller->mfi_io_lock, MA_OWNED);
239202375Srdivacky	sc->ld_flags &= ~MFI_DISK_FLAGS_DISABLED;
240202375Srdivacky}
241202375Srdivacky
242202375Srdivackystatic void
243202375Srdivackymfi_disk_strategy(struct bio *bio)
244202375Srdivacky{
245202375Srdivacky	struct mfi_disk *sc;
246202375Srdivacky	struct mfi_softc *controller;
247202375Srdivacky
248202375Srdivacky	sc = bio->bio_disk->d_drv1;
249202375Srdivacky	controller = sc->ld_controller;
250202375Srdivacky
251202375Srdivacky	if (sc == NULL) {
252263508Sdim		bio->bio_error = EINVAL;
253202375Srdivacky		bio->bio_flags |= BIO_ERROR;
254202375Srdivacky		bio->bio_resid = bio->bio_bcount;
255202375Srdivacky		biodone(bio);
256202375Srdivacky		return;
257202375Srdivacky	}
258202375Srdivacky
259202375Srdivacky	if (controller->adpreset){
260202375Srdivacky		bio->bio_error = EBUSY;
261202375Srdivacky		return;
262202375Srdivacky	}
263202375Srdivacky
264202375Srdivacky	if (controller->hw_crit_error){
265202375Srdivacky		bio->bio_error = EBUSY;
266251662Sdim		return;
267251662Sdim	}
268249423Sdim
269202375Srdivacky	if (controller->issuepend_done == 0){
270202375Srdivacky		bio->bio_error = EBUSY;
271204792Srdivacky		return;
272204792Srdivacky	}
273249423Sdim
274202375Srdivacky	bio->bio_driver1 = (void *)(uintptr_t)sc->ld_id;
275202375Srdivacky	/* Mark it as LD IO */
276249423Sdim	bio->bio_driver2 = (void *)MFI_LD_IO;
277202375Srdivacky	mtx_lock(&controller->mfi_io_lock);
278202375Srdivacky	mfi_enqueue_bio(controller, bio);
279263508Sdim	mfi_startio(controller);
280202375Srdivacky	mtx_unlock(&controller->mfi_io_lock);
281202375Srdivacky	return;
282202375Srdivacky}
283249423Sdim
284202375Srdivackyvoid
285204792Srdivackymfi_disk_complete(struct bio *bio)
286202375Srdivacky{
287202375Srdivacky	struct mfi_disk *sc;
288202375Srdivacky	struct mfi_frame_header *hdr;
289202375Srdivacky
290202375Srdivacky	sc = bio->bio_disk->d_drv1;
291202375Srdivacky	hdr = bio->bio_driver1;
292204792Srdivacky
293202375Srdivacky	if (bio->bio_flags & BIO_ERROR) {
294202375Srdivacky		if (bio->bio_error == 0)
295202375Srdivacky			bio->bio_error = EIO;
296263508Sdim		disk_err(bio, "hard error", -1, 1);
297202375Srdivacky	} else {
298202375Srdivacky		bio->bio_resid = 0;
299202375Srdivacky	}
300202375Srdivacky	biodone(bio);
301249423Sdim}
302202375Srdivacky
303204792Srdivackystatic int
304202375Srdivackymfi_disk_dump(void *arg, void *virt, vm_offset_t phys, off_t offset, size_t len)
305202375Srdivacky{
306202375Srdivacky	struct mfi_disk *sc;
307202375Srdivacky	struct mfi_softc *parent_sc;
308202375Srdivacky	struct disk *dp;
309202375Srdivacky	int error;
310202375Srdivacky
311204792Srdivacky	dp = arg;
312202375Srdivacky	sc = dp->d_drv1;
313202375Srdivacky	parent_sc = sc->ld_controller;
314202375Srdivacky
315202375Srdivacky	if (len > 0) {
316202375Srdivacky		if ((error = mfi_dump_blocks(parent_sc, sc->ld_id, offset /
317202375Srdivacky		    MFI_SECTOR_LEN, virt, len)) != 0)
318202375Srdivacky			return (error);
319202375Srdivacky	} else {
320202375Srdivacky		/* mfi_sync_cache(parent_sc, sc->ld_id); */
321202375Srdivacky	}
322202375Srdivacky
323202375Srdivacky	return (0);
324202375Srdivacky}
325202375Srdivacky