pst-raid.c revision 102058
1101100Ssos/*-
2101100Ssos * Copyright (c) 2001,2002 S�ren Schmidt <sos@FreeBSD.org>
3101100Ssos * All rights reserved.
4101100Ssos *
5101100Ssos * Redistribution and use in source and binary forms, with or without
6101100Ssos * modification, are permitted provided that the following conditions
7101100Ssos * are met:
8101100Ssos * 1. Redistributions of source code must retain the above copyright
9101100Ssos *    notice, this list of conditions and the following disclaimer,
10101100Ssos *    without modification, immediately at the beginning of the file.
11101100Ssos * 2. Redistributions in binary form must reproduce the above copyright
12101100Ssos *    notice, this list of conditions and the following disclaimer in the
13101100Ssos *    documentation and/or other materials provided with the distribution.
14101100Ssos * 3. The name of the author may not be used to endorse or promote products
15101100Ssos *    derived from this software without specific prior written permission.
16101100Ssos *
17101100Ssos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18101100Ssos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19101100Ssos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20101100Ssos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21101100Ssos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22101100Ssos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23101100Ssos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24101100Ssos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25101100Ssos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26101100Ssos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27101100Ssos *
28101100Ssos * $FreeBSD: head/sys/dev/pst/pst-raid.c 102058 2002-08-18 12:20:33Z sos $
29101100Ssos */
30101100Ssos
31101100Ssos#include <sys/param.h>
32101100Ssos#include <sys/systm.h>
33101100Ssos#include <sys/kernel.h>
34101100Ssos#include <sys/module.h>
35101100Ssos#include <sys/bus.h>
36101100Ssos#include <sys/bio.h>
37101100Ssos#include <sys/conf.h>
38101100Ssos#include <sys/disk.h>
39101100Ssos#include <sys/devicestat.h>
40101100Ssos#include <sys/eventhandler.h>
41101100Ssos#include <sys/malloc.h>
42101100Ssos#include <sys/lock.h>
43101100Ssos#include <sys/mutex.h>
44101100Ssos#include <vm/vm.h>
45101100Ssos#include <vm/pmap.h>
46101100Ssos#include <machine/stdarg.h>
47101100Ssos#include <machine/resource.h>
48101100Ssos#include <machine/bus.h>
49101100Ssos#include <sys/rman.h>
50101100Ssos#include <pci/pcivar.h>
51101100Ssos#include <pci/pcireg.h>
52101100Ssos
53101100Ssos#include "dev/pst/pst-iop.h"
54101100Ssos
55101100Ssos/* device structures */
56101100Ssosstatic d_strategy_t pststrategy;
57101100Ssosstatic struct cdevsw pst_cdevsw = {
58101100Ssos    /* open */	nullopen,
59101100Ssos    /* close */ nullclose,
60101100Ssos    /* read */	physread,
61101100Ssos    /* write */ physwrite,
62101100Ssos    /* ioctl */ noioctl,
63101100Ssos    /* poll */	nopoll,
64101100Ssos    /* mmap */	nommap,
65101100Ssos    /* strat */ pststrategy,
66101100Ssos    /* name */	"pst",
67101100Ssos    /* maj */	200,
68101100Ssos    /* dump */	nodump,
69101100Ssos    /* psize */ nopsize,
70101100Ssos    /* flags */ D_DISK,
71101100Ssos};
72101100Ssosstatic struct cdevsw pstdisk_cdevsw;
73101100Ssos
74101100Ssosstruct pst_softc {
75102058Ssos    struct iop_softc		*iop;
76102058Ssos    struct i2o_lct_entry	*lct;
77102058Ssos    struct i2o_bsa_device	*info;
78102058Ssos    dev_t			device;
79102058Ssos    struct devstat		stats;
80102058Ssos    struct disk			disk;
81102058Ssos    struct bio_queue_head	queue;
82102058Ssos    struct mtx			mtx;
83102058Ssos    int				outstanding;
84101100Ssos};
85101100Ssos
86101100Ssosstruct pst_request {
87101100Ssos    struct pst_softc		*psc;		/* pointer to softc */
88101100Ssos    u_int32_t			mfa;		/* frame addreess */
89101100Ssos    struct callout_handle	timeout_handle; /* handle for untimeout */
90101100Ssos    struct bio			*bp;		/* associated bio ptr */
91101100Ssos};
92101100Ssos
93101100Ssos/* prototypes */
94101100Ssosstatic int pst_probe(device_t);
95101100Ssosstatic int pst_attach(device_t);
96101100Ssosstatic int pst_shutdown(device_t);
97101100Ssosstatic void pst_start(struct pst_softc *);
98101100Ssosstatic void pst_done(struct iop_softc *, u_int32_t, struct i2o_single_reply *);
99101100Ssosstatic int pst_rw(struct pst_request *);
100101100Ssosstatic void pst_timeout(struct pst_request *);
101101100Ssosstatic void bpack(int8_t *, int8_t *, int);
102101100Ssos
103101100Ssos/* local vars */
104101100Ssosstatic MALLOC_DEFINE(M_PSTRAID, "pst", "Promise SuperTrak RAID driver");
105101100Ssos
106101100Ssosint
107101100Ssospst_add_raid(struct iop_softc *sc, struct i2o_lct_entry *lct)
108101100Ssos{
109101100Ssos    struct pst_softc *psc;
110101100Ssos    device_t child = device_add_child(sc->dev, "pst", -1);
111101100Ssos
112101100Ssos    if (!child)
113101100Ssos	return ENOMEM;
114101100Ssos    psc = malloc(sizeof(struct pst_softc), M_PSTRAID, M_NOWAIT | M_ZERO);
115101100Ssos    psc->iop = sc;
116101100Ssos    psc->lct = lct;
117101100Ssos    device_set_softc(child, psc);
118101100Ssos    return bus_generic_attach(sc->dev);
119101100Ssos}
120101100Ssos
121101100Ssosstatic int
122101100Ssospst_probe(device_t dev)
123101100Ssos{
124101100Ssos    device_set_desc(dev, "Promise SuperTrak RAID");
125101100Ssos    return 0;
126101100Ssos}
127101100Ssos
128101100Ssosstatic int
129101100Ssospst_attach(device_t dev)
130101100Ssos{
131101100Ssos    struct pst_softc *psc = device_get_softc(dev);
132101100Ssos    struct i2o_get_param_reply *reply;
133101100Ssos    struct i2o_device_identity *ident;
134101100Ssos    int lun = device_get_unit(dev);
135101100Ssos    int8_t name [32];
136101100Ssos
137101100Ssos    if (!(reply = iop_get_util_params(psc->iop, psc->lct->local_tid,
138101100Ssos				      I2O_PARAMS_OPERATION_FIELD_GET,
139101100Ssos				      I2O_BSA_DEVICE_INFO_GROUP_NO)))
140101100Ssos	return ENODEV;
141101100Ssos
142101100Ssos    if (!(psc->info = (struct i2o_bsa_device *)
143101100Ssos	    malloc(sizeof(struct i2o_bsa_device), M_PSTRAID, M_NOWAIT))) {
144101100Ssos	contigfree(reply, PAGE_SIZE, M_PSTRAID);
145101100Ssos	return ENOMEM;
146101100Ssos    }
147101100Ssos    bcopy(reply->result, psc->info, sizeof(struct i2o_bsa_device));
148101100Ssos    contigfree(reply, PAGE_SIZE, M_PSTRAID);
149101100Ssos
150101100Ssos    if (!(reply = iop_get_util_params(psc->iop, psc->lct->local_tid,
151101100Ssos				      I2O_PARAMS_OPERATION_FIELD_GET,
152101100Ssos				      I2O_UTIL_DEVICE_IDENTITY_GROUP_NO)))
153101100Ssos	return ENODEV;
154101100Ssos    ident = (struct i2o_device_identity *)reply->result;
155101100Ssos#ifdef PSTDEBUG
156101100Ssos    printf("pst: vendor=<%.16s> product=<%.16s>\n",
157101100Ssos	   ident->vendor, ident->product);
158101100Ssos    printf("pst: description=<%.16s> revision=<%.8s>\n",
159101100Ssos	   ident->description, ident->revision);
160101100Ssos    printf("pst: capacity=%lld blocksize=%d\n",
161101100Ssos	   psc->info->capacity, psc->info->block_size);
162101100Ssos#endif
163101100Ssos    bpack(ident->vendor, ident->vendor, 16);
164101100Ssos    bpack(ident->product, ident->product, 16);
165101100Ssos    sprintf(name, "%s %s", ident->vendor, ident->product);
166101100Ssos    contigfree(reply, PAGE_SIZE, M_PSTRAID);
167101100Ssos
168101100Ssos    bioq_init(&psc->queue);
169101100Ssos    mtx_init(&psc->mtx, "pst lock", MTX_DEF, 0);
170101100Ssos
171101100Ssos    psc->device = disk_create(lun, &psc->disk, 0, &pst_cdevsw, &pstdisk_cdevsw);
172101100Ssos    psc->device->si_drv1 = psc;
173101100Ssos    psc->device->si_iosize_max = 64 * 1024; /*I2O_SGL_MAX_SEGS * PAGE_SIZE;*/
174101100Ssos
175101100Ssos    bzero(&psc->disk.d_label, sizeof(struct disklabel));
176101100Ssos    psc->disk.d_label.d_secsize = psc->info->block_size;
177101100Ssos    psc->disk.d_label.d_nsectors = 63;
178101100Ssos    psc->disk.d_label.d_ntracks = 255;
179101100Ssos    psc->disk.d_label.d_ncylinders =
180101100Ssos	(psc->info->capacity / psc->info->block_size) / (255 * 63);
181101100Ssos    psc->disk.d_label.d_secpercyl = 255 * 63;
182101100Ssos    psc->disk.d_label.d_secperunit =
183101100Ssos	psc->info->capacity / psc->info->block_size;
184101100Ssos
185101100Ssos    devstat_add_entry(&psc->stats, "pst", lun, psc->info->block_size,
186101100Ssos		      DEVSTAT_NO_ORDERED_TAGS,
187101100Ssos		      DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE,
188101100Ssos		      DEVSTAT_PRIORITY_DISK);
189101100Ssos
190101100Ssos    printf("pst%d: %lluMB <%.40s> [%d/%d/%d] on %.16s\n", lun,
191101100Ssos	   (unsigned long long)psc->disk.d_label.d_secperunit / (1024 * 2),
192101100Ssos	   name, psc->disk.d_label.d_ncylinders, 255, 63,
193101100Ssos	   device_get_nameunit(psc->iop->dev));
194101100Ssos
195101100Ssos    EVENTHANDLER_REGISTER(shutdown_post_sync, pst_shutdown,
196101100Ssos			  dev, SHUTDOWN_PRI_FIRST);
197101100Ssos    return 0;
198101100Ssos}
199101100Ssos
200101100Ssosstatic int
201101100Ssospst_shutdown(device_t dev)
202101100Ssos{
203101100Ssos    struct pst_softc *psc = device_get_softc(dev);
204101100Ssos    struct i2o_bsa_cache_flush_message *msg;
205101100Ssos    int mfa;
206101100Ssos
207101100Ssos    mfa = iop_get_mfa(psc->iop);
208101100Ssos    msg = (struct i2o_bsa_cache_flush_message *)(psc->iop->ibase + mfa);
209101100Ssos    bzero(msg, sizeof(struct i2o_bsa_cache_flush_message));
210101100Ssos    msg->version_offset = 0x01;
211101100Ssos    msg->message_flags = 0x0;
212101100Ssos    msg->message_size = sizeof(struct i2o_bsa_cache_flush_message) >> 2;
213101100Ssos    msg->target_address = psc->lct->local_tid;
214101100Ssos    msg->initiator_address = I2O_TID_HOST;
215101100Ssos    msg->function = I2O_BSA_CACHE_FLUSH;
216101100Ssos    msg->control_flags = 0x0; /* 0x80 = post progress reports */
217101100Ssos    if (iop_queue_wait_msg(psc->iop, mfa, (struct i2o_basic_message *)msg))
218101100Ssos	printf("pst: shutdown failed!\n");
219101100Ssos    return 0;
220101100Ssos}
221101100Ssos
222101100Ssosstatic void
223101100Ssospststrategy(struct bio *bp)
224101100Ssos{
225101100Ssos    struct pst_softc *psc = bp->bio_dev->si_drv1;
226101100Ssos
227101100Ssos    mtx_lock(&psc->mtx);
228101100Ssos    bioqdisksort(&psc->queue, bp);
229101100Ssos    pst_start(psc);
230101100Ssos    mtx_unlock(&psc->mtx);
231101100Ssos}
232101100Ssos
233101100Ssosstatic void
234101100Ssospst_start(struct pst_softc *psc)
235101100Ssos{
236101100Ssos    struct pst_request *request;
237101100Ssos    struct bio *bp;
238101100Ssos    u_int32_t mfa;
239101100Ssos
240101100Ssos    if (psc->outstanding < (I2O_IOP_OUTBOUND_FRAME_COUNT - 1) &&
241101100Ssos	(bp = bioq_first(&psc->queue))) {
242101100Ssos	if ((mfa = iop_get_mfa(psc->iop)) != 0xffffffff) {
243101100Ssos	    if (!(request = malloc(sizeof(struct pst_request),
244101100Ssos				   M_PSTRAID, M_NOWAIT | M_ZERO))) {
245101100Ssos		printf("pst: out of memory in start\n");
246101100Ssos		iop_free_mfa(psc->iop, mfa);
247101100Ssos		return;
248101100Ssos	    }
249101100Ssos	    psc->outstanding++;
250101100Ssos	    request->psc = psc;
251101100Ssos	    request->mfa = mfa;
252101100Ssos	    request->bp = bp;
253101100Ssos	    if (dumping)
254101100Ssos		request->timeout_handle.callout = NULL;
255101100Ssos	    else
256101100Ssos		request->timeout_handle =
257102058Ssos		    timeout((timeout_t*)pst_timeout, request, 10 * hz);
258101100Ssos	    bioq_remove(&psc->queue, bp);
259101100Ssos	    devstat_start_transaction(&psc->stats);
260101100Ssos	    if (pst_rw(request)) {
261101100Ssos		biofinish(request->bp, &psc->stats, EIO);
262101100Ssos		iop_free_mfa(request->psc->iop, request->mfa);
263101100Ssos		psc->outstanding--;
264101100Ssos		free(request, M_PSTRAID);
265101100Ssos	    }
266101100Ssos	}
267101100Ssos    }
268101100Ssos}
269101100Ssos
270101100Ssosstatic void
271101100Ssospst_done(struct iop_softc *sc, u_int32_t mfa, struct i2o_single_reply *reply)
272101100Ssos{
273101100Ssos    struct pst_request *request =
274101100Ssos	(struct pst_request *)reply->transaction_context;
275101100Ssos    struct pst_softc *psc = request->psc;
276101100Ssos
277101100Ssos    untimeout((timeout_t *)pst_timeout, request, request->timeout_handle);
278101100Ssos    request->bp->bio_resid = request->bp->bio_bcount - reply->donecount;
279101100Ssos    biofinish(request->bp, &psc->stats, reply->status ? EIO : 0);
280101100Ssos    free(request, M_PSTRAID);
281101100Ssos    mtx_lock(&psc->mtx);
282101100Ssos    psc->iop->reg->oqueue = mfa;
283101100Ssos    psc->outstanding--;
284101100Ssos    pst_start(psc);
285101100Ssos    mtx_unlock(&psc->mtx);
286101100Ssos}
287101100Ssos
288101100Ssosstatic void
289101100Ssospst_timeout(struct pst_request *request)
290101100Ssos{
291101100Ssos    printf("pst: timeout mfa=0x%08x cmd=0x%02x\n",
292101100Ssos	   request->mfa, request->bp->bio_cmd);
293101100Ssos    mtx_lock(&request->psc->mtx);
294101100Ssos    iop_free_mfa(request->psc->iop, request->mfa);
295101100Ssos    if ((request->mfa = iop_get_mfa(request->psc->iop)) == 0xffffffff) {
296101100Ssos	printf("pst: timeout no mfa possible\n");
297101100Ssos	biofinish(request->bp, &request->psc->stats, EIO);
298101100Ssos	request->psc->outstanding--;
299101100Ssos	mtx_unlock(&request->psc->mtx);
300101100Ssos	return;
301101100Ssos    }
302101100Ssos    if (dumping)
303101100Ssos	request->timeout_handle.callout = NULL;
304101100Ssos    else
305101100Ssos	request->timeout_handle =
306101100Ssos	    timeout((timeout_t*)pst_timeout, request, 10 * hz);
307101100Ssos    if (pst_rw(request)) {
308101100Ssos	iop_free_mfa(request->psc->iop, request->mfa);
309101100Ssos	biofinish(request->bp, &request->psc->stats, EIO);
310101100Ssos	request->psc->outstanding--;
311101100Ssos    }
312101100Ssos    mtx_unlock(&request->psc->mtx);
313101100Ssos}
314101100Ssos
315101100Ssosint
316101100Ssospst_rw(struct pst_request *request)
317101100Ssos{
318101100Ssos    struct i2o_bsa_rw_block_message *msg;
319101100Ssos    int sgl_flag;
320101100Ssos
321101100Ssos    msg = (struct i2o_bsa_rw_block_message *)
322101100Ssos	  (request->psc->iop->ibase + request->mfa);
323101100Ssos    bzero(msg, sizeof(struct i2o_bsa_rw_block_message));
324101100Ssos    msg->version_offset = 0x81;
325101100Ssos    msg->message_flags = 0x0;
326101100Ssos    msg->message_size = sizeof(struct i2o_bsa_rw_block_message) >> 2;
327101100Ssos    msg->target_address = request->psc->lct->local_tid;
328101100Ssos    msg->initiator_address = I2O_TID_HOST;
329101100Ssos    switch (request->bp->bio_cmd) {
330101100Ssos    case BIO_READ:
331101100Ssos	msg->function = I2O_BSA_BLOCK_READ;
332101100Ssos	msg->control_flags = 0x0; /* 0x0c = read cache + readahead */
333101100Ssos	msg->fetch_ahead = 0x0; /* 8 Kb */
334101100Ssos	sgl_flag = 0;
335101100Ssos	break;
336101100Ssos    case BIO_WRITE:
337101100Ssos	msg->function = I2O_BSA_BLOCK_WRITE;
338101100Ssos	msg->control_flags = 0x0; /* 0x10 = write behind cache */
339101100Ssos	msg->fetch_ahead = 0x0;
340101100Ssos	sgl_flag = I2O_SGL_DIR;
341101100Ssos	break;
342101100Ssos    default:
343101100Ssos	printf("pst: unknown command type\n");
344101100Ssos	return -1;
345101100Ssos    }
346101100Ssos    msg->initiator_context = (u_int32_t)pst_done;
347101100Ssos    msg->transaction_context = (u_int32_t)request;
348101100Ssos    msg->time_multiplier = 1;
349101100Ssos    msg->bytecount = request->bp->bio_bcount;
350101100Ssos    msg->lba = ((u_int64_t)request->bp->bio_pblkno) * (DEV_BSIZE * 1LL);
351101100Ssos    if (!iop_create_sgl((struct i2o_basic_message *)msg, request->bp->bio_data,
352101100Ssos			request->bp->bio_bcount, sgl_flag))
353101100Ssos	return -1;
354101100Ssos    request->psc->iop->reg->iqueue = request->mfa;
355101100Ssos    return 0;
356101100Ssos}
357101100Ssos
358101100Ssosstatic void
359101100Ssosbpack(int8_t *src, int8_t *dst, int len)
360101100Ssos{
361101100Ssos    int i, j, blank;
362101100Ssos    int8_t *ptr, *buf = dst;
363101100Ssos
364101100Ssos    for (i = j = blank = 0 ; i < len; i++) {
365101100Ssos	if (blank && src[i] == ' ') continue;
366101100Ssos	if (blank && src[i] != ' ') {
367101100Ssos	    dst[j++] = src[i];
368101100Ssos	    blank = 0;
369101100Ssos	    continue;
370101100Ssos	}
371101100Ssos	if (src[i] == ' ') {
372101100Ssos	    blank = 1;
373101100Ssos	    if (i == 0)
374101100Ssos		continue;
375101100Ssos	}
376101100Ssos	dst[j++] = src[i];
377101100Ssos    }
378101100Ssos    if (j < len)
379101100Ssos	dst[j] = 0x00;
380101100Ssos    for (ptr = buf; ptr < buf+len; ++ptr)
381101100Ssos	if (!*ptr)
382101100Ssos	    *ptr = ' ';
383101100Ssos    for (ptr = buf + len - 1; ptr >= buf && *ptr == ' '; --ptr)
384101100Ssos        *ptr = 0;
385101100Ssos}
386101100Ssos
387101100Ssosstatic device_method_t pst_methods[] = {
388101100Ssos    DEVMETHOD(device_probe,	pst_probe),
389101100Ssos    DEVMETHOD(device_attach,	pst_attach),
390101100Ssos    { 0, 0 }
391101100Ssos};
392101100Ssos
393101100Ssosstatic driver_t pst_driver = {
394101100Ssos    "pst",
395101100Ssos    pst_methods,
396101100Ssos    sizeof(struct pst_softc),
397101100Ssos};
398101100Ssos
399101100Ssosstatic devclass_t pst_devclass;
400101100Ssos
401101100SsosDRIVER_MODULE(pst, pstpci, pst_driver, pst_devclass, 0, 0);
402