1227068Sambrisko/*-
2227068Sambrisko * Redistribution and use in source and binary forms, with or without
3227068Sambrisko * modification, are permitted provided that the following conditions
4227068Sambrisko * are met:
5227068Sambrisko *
6227068Sambrisko *            Copyright 1994-2009 The FreeBSD Project.
7233711Sambrisko *            All rights reserved.
8233711Sambrisko *
9227068Sambrisko * 1. Redistributions of source code must retain the above copyright
10227068Sambrisko *    notice, this list of conditions and the following disclaimer.
11227068Sambrisko * 2. Redistributions in binary form must reproduce the above copyright
12227068Sambrisko *    notice, this list of conditions and the following disclaimer in the
13227068Sambrisko *    documentation and/or other materials provided with the distribution.
14227068Sambrisko *
15227068Sambrisko *    THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT``AS IS'' AND
16227068Sambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17233711Sambrisko * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18233711Sambrisko * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FREEBSD PROJECT OR
19233711Sambrisko * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20233711Sambrisko * EXEMPLARY,OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21227068Sambrisko * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22233711Sambrisko * PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY THEORY
23227068Sambrisko * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24233711Sambrisko * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25227068Sambrisko * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26227068Sambrisko *
27233711Sambrisko * The views and conclusions contained in the software and documentation
28233711Sambrisko * are those of the authors and should not be interpreted as representing
29233711Sambrisko * official policies,either expressed or implied, of the FreeBSD Project.
30227068Sambrisko */
31227068Sambrisko
32227068Sambrisko#include <sys/cdefs.h>
33233711Sambrisko__FBSDID("$FreeBSD$");
34227068Sambrisko
35227068Sambrisko#include "opt_mfi.h"
36227068Sambrisko
37227068Sambrisko#include <sys/param.h>
38227068Sambrisko#include <sys/systm.h>
39227068Sambrisko#include <sys/kernel.h>
40227068Sambrisko#include <sys/selinfo.h>
41227068Sambrisko#include <sys/module.h>
42227068Sambrisko#include <sys/malloc.h>
43233711Sambrisko#include <sys/sysctl.h>
44227068Sambrisko#include <sys/uio.h>
45227068Sambrisko
46227068Sambrisko#include <sys/bio.h>
47227068Sambrisko#include <sys/bus.h>
48227068Sambrisko#include <sys/conf.h>
49227068Sambrisko#include <sys/disk.h>
50227068Sambrisko#include <geom/geom_disk.h>
51227068Sambrisko
52227068Sambrisko#include <vm/vm.h>
53227068Sambrisko#include <vm/pmap.h>
54227068Sambrisko
55227068Sambrisko#include <machine/md_var.h>
56227068Sambrisko#include <machine/bus.h>
57227068Sambrisko#include <sys/rman.h>
58227068Sambrisko
59227068Sambrisko#include <dev/mfi/mfireg.h>
60227068Sambrisko#include <dev/mfi/mfi_ioctl.h>
61227068Sambrisko#include <dev/mfi/mfivar.h>
62227068Sambrisko
63227068Sambriskostatic int	mfi_syspd_probe(device_t dev);
64227068Sambriskostatic int	mfi_syspd_attach(device_t dev);
65227068Sambriskostatic int	mfi_syspd_detach(device_t dev);
66227068Sambrisko
67227068Sambriskostatic disk_open_t	mfi_syspd_open;
68227068Sambriskostatic disk_close_t	mfi_syspd_close;
69227068Sambriskostatic disk_strategy_t	mfi_syspd_strategy;
70227068Sambriskostatic dumper_t		mfi_syspd_dump;
71227068Sambrisko
72227068Sambriskostatic devclass_t	mfi_syspd_devclass;
73227068Sambrisko
74227068Sambriskostatic device_method_t mfi_syspd_methods[] = {
75227068Sambrisko	DEVMETHOD(device_probe,		mfi_syspd_probe),
76227068Sambrisko	DEVMETHOD(device_attach,	mfi_syspd_attach),
77227068Sambrisko	DEVMETHOD(device_detach,	mfi_syspd_detach),
78227068Sambrisko	{ 0, 0 }
79227068Sambrisko};
80227068Sambrisko
81227068Sambriskostatic driver_t mfi_syspd_driver = {
82227068Sambrisko	"mfisyspd",
83227068Sambrisko	mfi_syspd_methods,
84227068Sambrisko	sizeof(struct mfi_system_pd)
85227068Sambrisko};
86227068Sambrisko
87227068SambriskoDRIVER_MODULE(mfisyspd, mfi, mfi_syspd_driver, mfi_syspd_devclass, 0, 0);
88227068Sambrisko
89227068Sambriskostatic int
90227068Sambriskomfi_syspd_probe(device_t dev)
91227068Sambrisko{
92227068Sambrisko	return (0);
93227068Sambrisko}
94227068Sambrisko
95227068Sambriskostatic int
96227068Sambriskomfi_syspd_attach(device_t dev)
97227068Sambrisko{
98227068Sambrisko	struct mfi_system_pd *sc;
99227068Sambrisko	struct mfi_pd_info *pd_info;
100242681Sambrisko	struct mfi_system_pending *syspd_pend;
101227068Sambrisko	uint64_t sectors;
102227068Sambrisko	uint32_t secsize;
103227068Sambrisko
104227068Sambrisko	sc = device_get_softc(dev);
105227068Sambrisko	pd_info = device_get_ivars(dev);
106227068Sambrisko	sc->pd_dev = dev;
107227068Sambrisko	sc->pd_id = pd_info->ref.v.device_id;
108227068Sambrisko	sc->pd_unit = device_get_unit(dev);
109227068Sambrisko	sc->pd_info = pd_info;
110227068Sambrisko	sc->pd_controller = device_get_softc(device_get_parent(dev));
111227068Sambrisko	sc->pd_flags = 0;
112227068Sambrisko
113227068Sambrisko	sectors = pd_info->raw_size;
114227068Sambrisko	secsize = MFI_SECTOR_LEN;
115227068Sambrisko	mtx_lock(&sc->pd_controller->mfi_io_lock);
116227068Sambrisko	TAILQ_INSERT_TAIL(&sc->pd_controller->mfi_syspd_tqh, sc, pd_link);
117242681Sambrisko	TAILQ_FOREACH(syspd_pend, &sc->pd_controller->mfi_syspd_pend_tqh,
118242681Sambrisko	    pd_link) {
119242681Sambrisko		TAILQ_REMOVE(&sc->pd_controller->mfi_syspd_pend_tqh,
120242681Sambrisko		    syspd_pend, pd_link);
121242681Sambrisko		free(syspd_pend, M_MFIBUF);
122242681Sambrisko		break;
123242681Sambrisko	}
124227068Sambrisko	mtx_unlock(&sc->pd_controller->mfi_io_lock);
125243078Seadler	device_printf(dev, "%juMB (%ju sectors) SYSPD volume (deviceid: %d)\n",
126243078Seadler		      sectors / (1024 * 1024 / secsize), sectors, sc->pd_id);
127227068Sambrisko	sc->pd_disk = disk_alloc();
128227068Sambrisko	sc->pd_disk->d_drv1 = sc;
129254330Ssbruno	sc->pd_disk->d_maxsize = min(sc->pd_controller->mfi_max_io * secsize,
130254330Ssbruno		(sc->pd_controller->mfi_max_sge - 1) * PAGE_SIZE);
131227068Sambrisko	sc->pd_disk->d_name = "mfisyspd";
132227068Sambrisko	sc->pd_disk->d_open = mfi_syspd_open;
133227068Sambrisko	sc->pd_disk->d_close = mfi_syspd_close;
134227068Sambrisko	sc->pd_disk->d_strategy = mfi_syspd_strategy;
135227068Sambrisko	sc->pd_disk->d_dump = mfi_syspd_dump;
136227068Sambrisko	sc->pd_disk->d_unit = sc->pd_unit;
137227068Sambrisko	sc->pd_disk->d_sectorsize = secsize;
138227068Sambrisko	sc->pd_disk->d_mediasize = sectors * secsize;
139227068Sambrisko	if (sc->pd_disk->d_mediasize >= (1 * 1024 * 1024)) {
140227068Sambrisko		sc->pd_disk->d_fwheads = 255;
141227068Sambrisko		sc->pd_disk->d_fwsectors = 63;
142227068Sambrisko	} else {
143227068Sambrisko		sc->pd_disk->d_fwheads = 64;
144227068Sambrisko		sc->pd_disk->d_fwsectors = 32;
145227068Sambrisko	}
146267084Skib	sc->pd_disk->d_flags = DISKFLAG_UNMAPPED_BIO;
147227068Sambrisko	disk_create(sc->pd_disk, DISK_VERSION);
148227068Sambrisko
149227068Sambrisko	device_printf(dev, " SYSPD volume attached\n");
150242681Sambrisko
151227068Sambrisko	return (0);
152227068Sambrisko}
153227068Sambrisko
154227068Sambriskostatic int
155227068Sambriskomfi_syspd_detach(device_t dev)
156227068Sambrisko{
157227068Sambrisko	struct mfi_system_pd *sc;
158227068Sambrisko
159227068Sambrisko	sc = device_get_softc(dev);
160227068Sambrisko	device_printf(dev, "Detaching syspd\n");
161227068Sambrisko	mtx_lock(&sc->pd_controller->mfi_io_lock);
162227068Sambrisko	if (((sc->pd_disk->d_flags & DISKFLAG_OPEN) ||
163227068Sambrisko	    (sc->pd_flags & MFI_DISK_FLAGS_OPEN)) &&
164227068Sambrisko	    (sc->pd_controller->mfi_keep_deleted_volumes ||
165227068Sambrisko	    sc->pd_controller->mfi_detaching)) {
166227068Sambrisko		mtx_unlock(&sc->pd_controller->mfi_io_lock);
167233711Sambrisko		device_printf(dev, "Cant detach syspd\n");
168227068Sambrisko		return (EBUSY);
169227068Sambrisko	}
170227068Sambrisko	mtx_unlock(&sc->pd_controller->mfi_io_lock);
171227068Sambrisko
172227068Sambrisko	disk_destroy(sc->pd_disk);
173227068Sambrisko	mtx_lock(&sc->pd_controller->mfi_io_lock);
174227068Sambrisko	TAILQ_REMOVE(&sc->pd_controller->mfi_syspd_tqh, sc, pd_link);
175227068Sambrisko	mtx_unlock(&sc->pd_controller->mfi_io_lock);
176227068Sambrisko	free(sc->pd_info, M_MFIBUF);
177227068Sambrisko	return (0);
178227068Sambrisko}
179227068Sambrisko
180227068Sambriskostatic int
181227068Sambriskomfi_syspd_open(struct disk *dp)
182227068Sambrisko{
183227068Sambrisko	struct mfi_system_pd *sc;
184227068Sambrisko	int error;
185227068Sambrisko
186227068Sambrisko	sc = dp->d_drv1;
187227068Sambrisko	mtx_lock(&sc->pd_controller->mfi_io_lock);
188227068Sambrisko	if (sc->pd_flags & MFI_DISK_FLAGS_DISABLED)
189227068Sambrisko		error = ENXIO;
190227068Sambrisko	else {
191227068Sambrisko		sc->pd_flags |= MFI_DISK_FLAGS_OPEN;
192227068Sambrisko		error = 0;
193227068Sambrisko	}
194227068Sambrisko	mtx_unlock(&sc->pd_controller->mfi_io_lock);
195227068Sambrisko	return (error);
196227068Sambrisko}
197227068Sambrisko
198227068Sambriskostatic int
199227068Sambriskomfi_syspd_close(struct disk *dp)
200227068Sambrisko{
201227068Sambrisko	struct mfi_system_pd *sc;
202227068Sambrisko
203227068Sambrisko	sc = dp->d_drv1;
204227068Sambrisko	mtx_lock(&sc->pd_controller->mfi_io_lock);
205227068Sambrisko	sc->pd_flags &= ~MFI_DISK_FLAGS_OPEN;
206227068Sambrisko	mtx_unlock(&sc->pd_controller->mfi_io_lock);
207227068Sambrisko
208227068Sambrisko	return (0);
209227068Sambrisko}
210227068Sambrisko
211227068Sambriskoint
212227068Sambriskomfi_syspd_disable(struct mfi_system_pd *sc)
213227068Sambrisko{
214227068Sambrisko
215233711Sambrisko	device_printf(sc->pd_dev, "syspd disable \n");
216227068Sambrisko	mtx_assert(&sc->pd_controller->mfi_io_lock, MA_OWNED);
217227068Sambrisko	if (sc->pd_flags & MFI_DISK_FLAGS_OPEN) {
218227068Sambrisko		if (sc->pd_controller->mfi_delete_busy_volumes)
219227068Sambrisko			return (0);
220233711Sambrisko		device_printf(sc->pd_dev,
221233711Sambrisko		    "Unable to delete busy syspd device\n");
222227068Sambrisko		return (EBUSY);
223227068Sambrisko	}
224227068Sambrisko	sc->pd_flags |= MFI_DISK_FLAGS_DISABLED;
225227068Sambrisko	return (0);
226227068Sambrisko}
227227068Sambrisko
228227068Sambriskovoid
229227068Sambriskomfi_syspd_enable(struct mfi_system_pd *sc)
230227068Sambrisko{
231227068Sambrisko
232233711Sambrisko	device_printf(sc->pd_dev, "syspd enable \n");
233227068Sambrisko	mtx_assert(&sc->pd_controller->mfi_io_lock, MA_OWNED);
234227068Sambrisko	sc->pd_flags &= ~MFI_DISK_FLAGS_DISABLED;
235227068Sambrisko}
236227068Sambrisko
237227068Sambriskostatic void
238227068Sambriskomfi_syspd_strategy(struct bio *bio)
239227068Sambrisko{
240227068Sambrisko	struct mfi_system_pd *sc;
241227068Sambrisko	struct mfi_softc *controller;
242227068Sambrisko
243227068Sambrisko	sc = bio->bio_disk->d_drv1;
244227068Sambrisko
245227068Sambrisko	if (sc == NULL) {
246227068Sambrisko		bio->bio_error = EINVAL;
247227068Sambrisko		bio->bio_flags |= BIO_ERROR;
248227068Sambrisko		bio->bio_resid = bio->bio_bcount;
249227068Sambrisko		biodone(bio);
250227068Sambrisko		return;
251227068Sambrisko	}
252227068Sambrisko
253227068Sambrisko	controller = sc->pd_controller;
254227068Sambrisko	bio->bio_driver1 = (void *)(uintptr_t)sc->pd_id;
255227068Sambrisko	/* Mark it as system PD IO */
256227068Sambrisko	bio->bio_driver2 = (void *)MFI_SYS_PD_IO;
257227068Sambrisko	mtx_lock(&controller->mfi_io_lock);
258227068Sambrisko	mfi_enqueue_bio(controller, bio);
259227068Sambrisko	mfi_startio(controller);
260227068Sambrisko	mtx_unlock(&controller->mfi_io_lock);
261227068Sambrisko	return;
262227068Sambrisko}
263227068Sambrisko
264227068Sambriskostatic int
265233711Sambriskomfi_syspd_dump(void *arg, void *virt, vm_offset_t phys, off_t offset,
266233711Sambrisko    size_t len)
267227068Sambrisko{
268227068Sambrisko	struct mfi_system_pd *sc;
269227068Sambrisko	struct mfi_softc *parent_sc;
270227068Sambrisko	struct disk *dp;
271227068Sambrisko	int error;
272227068Sambrisko
273227068Sambrisko	dp = arg;
274227068Sambrisko	sc = dp->d_drv1;
275227068Sambrisko	parent_sc = sc->pd_controller;
276227068Sambrisko
277227068Sambrisko	if (len > 0) {
278233711Sambrisko		if ((error = mfi_dump_syspd_blocks(parent_sc,
279233711Sambrisko		    sc->pd_id, offset / MFI_SECTOR_LEN, virt, len)) != 0)
280227068Sambrisko			return (error);
281227068Sambrisko	} else {
282227068Sambrisko		/* mfi_sync_cache(parent_sc, sc->ld_id); */
283227068Sambrisko	}
284227068Sambrisko	return (0);
285227068Sambrisko}
286