1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#ifndef _RDC_DISKQ_H
27#define	_RDC_DISKQ_H
28
29#ifdef	__cplusplus
30extern "C" {
31#endif
32
33#ifdef _KERNEL
34
35#define	RDC_DISKQ_HEADER_OFF	0		/* beginning of disk */
36#define	RDC_DISKQ_DATA_OFF	FBA_LEN(1024)	/* beginning of queue */
37
38typedef struct qentry {
39	int		magic;
40	int		type; 	/* special data ? io? bitmap? */
41	nsc_off_t	pos;	/* position it will be in the rdc_aio_t */
42	nsc_off_t	hpos;	/* starting pos of orig nsc_buf_t */
43	nsc_off_t	qpos;	/* where this info is in the queue */
44	nsc_size_t	len;	/* len */
45	int		flag;
46	int		iostatus;
47	uint32_t	setid;	/* krdc */
48	time_t		time;
49	void		*next;
50} q_data;
51
52typedef union io_dat {
53	q_data	dat;
54	char	dummy[512];
55} io_hdr;
56
57#define	RDC_IOHDR_MAGIC		0x494F4844 /* IOHD */
58#define	RDC_IOHDR_DONE  	0xDEADCAFE /* this q entry has been flushed */
59#define	RDC_IOHDR_WAITING  	0xBEEFCAFE /* this q entry is waiting for ack */
60
61/* type */
62#define	RDC_QUEUEIO	0x02
63
64#define	RDC_DISKQ_MAGIC	0x44534B51
65#define	RDC_DISKQ_VER_ORIG	0x01
66#define	RDC_DISKQ_VER_64BIT	0x02
67
68#ifdef	NSC_MULTI_TERABYTE
69#define	RDC_DISKQ_VERS	RDC_DISKQ_VER_64BIT
70#else
71#define	RDC_DISKQ_VERS	RDC_DISKQ_VER_ORIG
72#endif
73
74typedef struct diskqheader1 {
75	int	magic;
76	int	vers;
77	int	state;
78	int	head_offset; /* offset of meta-info of head (fbas) */
79	int	tail_offset; /* addr of next write (fbas) */
80	int	disk_size; /* allow growing ? (fbas) */
81	long 	nitems; /* items */
82	long	blocks; /* fbas */
83	int	qwrap; /* where the tail wrapped */
84	int	auxqwrap; /* if the tail wraps again, before head wraps once */
85	uint_t	seq_last; /* last sequence before suspend */
86	uint_t	ack_last; /* last ack before suspend */
87} diskq_header1;
88
89typedef struct diskqheader2 {
90	int	magic;
91	int	vers;
92	int	state;
93	uint64_t head_offset; /* offset of meta-info of head (fbas) */
94	uint64_t tail_offset; /* addr of next write (fbas) */
95	uint64_t disk_size; /* allow growing ? (fbas) */
96	uint64_t nitems; /* items */
97	uint64_t blocks; /* fbas */
98	uint64_t qwrap; /* where the tail wrapped */
99	uint64_t auxqwrap; /* if the tail wraps again, before head wraps once */
100	uint_t	seq_last; /* last sequence before suspend */
101	uint_t	ack_last; /* last ack before suspend */
102} diskq_header2;
103
104#ifdef	NSC_MULTI_TERABYTE
105typedef diskq_header2 diskq_header;
106#ifdef _LP64
107#define	RDC_DQFMT	"lu"
108#else
109#define	RDC_DQFMT	"llu"
110#endif
111#else
112typedef diskq_header1 diskq_header;
113#define	RDC_DQFMT	"ld"
114#endif
115typedef union headr {
116	diskq_header h;
117	char	dummy[512];
118} dqheader;
119
120/* flags for the state field in the header */
121
122#define	RDC_SHUTDOWN_OK		0x01
123#define	RDC_SHUTDOWN_BAD	0x02
124#define	QNXTIOWRAPD		0x04
125#define	QHEADWRAPD		0x08
126#define	QTAILBUSY		0x10 /* tell flusher not to grab, incomplete */
127#define	RDC_QNOBLOCK		0x10000 /* can also be passed out by status */
128#define	RDC_QBADRESUME		0x20 /* don't resume bit ref */
129#define	RDC_QFULL		0x40 /* the queue is in a full delay loop */
130#define	RDC_STOPPINGFLUSH	0x80
131
132#define	RDC_QFILLSTOP		0x01 /* diskq->memq flusher kill switch */
133#define	RDC_QFILLSLEEP		0x02 /* explicit diskq->memq flusher sleep */
134
135#define	RDC_MAX_DISKQREAD	0x1000 /* max 2 mb q read */
136
137typedef struct diskqueue { /* the incore info about the diskq */
138	dqheader	disk_hdr; /* info about the queue */
139	long		nitems_hwm;
140	long		blocks_hwm;
141	long		throttle_delay;
142	nsc_off_t	last_tail;	/* pos of the last tail write */
143	volatile int	inflbls;	/* number of inflight blocks */
144	volatile int	inflitems;	/* number of inflight blocks */
145
146	kmutex_t	disk_qlock; 	/* protects all things in diskq */
147					/* and all things in dqheader */
148
149	kmutex_t	head_lock;
150	kcondvar_t	busycv;
151	int		busycnt;
152	nsc_off_t	nxt_io;		/* flushers head pointer */
153	int		hdrcnt;		/* number of io_hdrs on list */
154	nsc_off_t	coalesc_bounds;	/* don't coalesce below this offset */
155	rdc_aio_t	*lastio;	/* cached copy of the last write on q */
156	io_hdr		*iohdrs;	/* flushed, not ack'd on queue */
157	io_hdr		*hdr_last;	/* tail of iohdr list */
158	kcondvar_t	qfullcv;	/* block, queue is full */
159} disk_queue;
160
161/* diskq macros  (gets) */
162
163#define	QHEAD(q) 		q->disk_hdr.h.head_offset
164#define	QNXTIO(q)		q->nxt_io
165#define	QTAIL(q)		q->disk_hdr.h.tail_offset
166#define	QNITEMS(q)		q->disk_hdr.h.nitems
167#define	QBLOCKS(q)		q->disk_hdr.h.blocks
168#define	QSTATE(q)		q->disk_hdr.h.state
169#define	IS_QSTATE(q, s)		(q->disk_hdr.h.state & s)
170#define	QSIZE(q)		q->disk_hdr.h.disk_size
171#define	QMAGIC(q)		q->disk_hdr.h.magic
172#define	QVERS(q)		q->disk_hdr.h.vers
173#define	QSEQ(q)			q->disk_hdr.h.seq_last
174#define	QACK(q)			q->disk_hdr.h.ack_last
175#define	QEMPTY(q)		((QTAIL(q) == QHEAD(q))&&(!(QNITEMS(q))))
176#define	QWRAP(q)		q->disk_hdr.h.qwrap
177#define	AUXQWRAP(q)		q->disk_hdr.h.auxqwrap
178#define	LASTQTAIL(q)		q->last_tail
179#define	QCOALBOUNDS(q)		q->coalesc_bounds
180
181/* diskq macros	(sets) */
182
183#define	INC_QHEAD(q, n)		q->disk_hdr.h.head_offset += n
184#define	INC_QNXTIO(q, n)	q->nxt_io += n
185#define	DEC_QNXTIO(q, n)	q->nxt_io -= n
186#define	DEC_QHEAD(q, n)		q->disk_hdr.h.head_offset -= n
187#define	INC_QTAIL(q, n)		q->disk_hdr.h.tail_offset += n
188#define	DEC_QTAIL(q, n)		q->disk_hdr.h.tail_offset -= n
189#define	INC_QNITEMS(q, n)	q->disk_hdr.h.nitems += n
190#define	DEC_QNITEMS(q, n)	q->disk_hdr.h.nitems -= n
191#define	INC_QBLOCKS(q, n)	q->disk_hdr.h.blocks += n
192#define	DEC_QBLOCKS(q, n)	q->disk_hdr.h.blocks -= n
193
194#define	SET_QMAGIC(q, n)	q->disk_hdr.h.magic = n
195#define	SET_QSTATE(q, n)	q->disk_hdr.h.state |= n
196#define	CLR_QSTATE(q, n)	q->disk_hdr.h.state &= ~n
197#define	SET_QHEAD(q, n)		q->disk_hdr.h.head_offset = n
198#define	SET_QNXTIO(q, n)	q->nxt_io = n
199#define	SET_QHDRCNT(q, n)	q->hdrcnt = n
200#define	SET_QTAIL(q, n)		q->disk_hdr.h.tail_offset = n
201#define	SET_LASTQTAIL(q, n)	q->last_tail = n
202#define	SET_LASTQWRITE(q, w)	q->last_qwrite = w
203#define	SET_QSIZE(q, n)		q->disk_hdr.h.disk_size = n
204#define	SET_QNITEMS(q, n)	q->disk_hdr.h.nitems = n
205#define	SET_QBLOCKS(q, n)	q->disk_hdr.h.blocks = n
206
207#define	SET_QWRAP(q, n)		q->disk_hdr.h.qwrap = n
208#define	CLR_QWRAP(q)		q->disk_hdr.h.qwrap = 0
209#define	SET_AUXQWRAP(q, n)	q->disk_hdr.h.auxqwrap = n
210#define	CLR_AUXQWRAP(q)		q->disk_hdr.h.auxqwrap = 0
211#define	SET_QCOALBOUNDS(q, n)	q->coalesc_bounds = n
212
213#define	WRAPQTAIL(q) \
214	do { \
215		if (QWRAP(q)) { \
216			SET_AUXQWRAP(q, QTAIL(q)); \
217		} else { \
218			SET_QWRAP(q, QTAIL(q)); \
219		} \
220		SET_QTAIL(q, RDC_DISKQ_DATA_OFF); \
221	} while (0)
222
223#define	DO_AUXQWRAP(q) \
224	do { \
225		SET_QWRAP(q, AUXQWRAP(q)); \
226		SET_AUXQWRAP(q, 0); \
227	} while (0)
228
229/* these can be wrapped by different threads, avoid the race */
230#define	WRAPQHEAD(q) \
231	do { \
232		if (IS_QSTATE(q, QNXTIOWRAPD)) { \
233			if (AUXQWRAP(q)) { \
234				DO_AUXQWRAP(q); \
235			} else { \
236				SET_QWRAP(q, 0); \
237			} \
238			CLR_QSTATE(q, QNXTIOWRAPD); \
239		} else { \
240			SET_QSTATE(q, QHEADWRAPD); \
241		} \
242		SET_QHEAD(q, RDC_DISKQ_DATA_OFF); \
243	} while (0)
244
245#define	WRAPQNXTIO(q)	\
246	do { \
247		if (IS_QSTATE(q, QHEADWRAPD)) { \
248			if (AUXQWRAP(q)) { \
249				DO_AUXQWRAP(q); \
250			} else { \
251				SET_QWRAP(q, 0); \
252			} \
253			CLR_QSTATE(q, QHEADWRAPD); \
254		} else { \
255			SET_QSTATE(q, QNXTIOWRAPD); \
256		} \
257		SET_QNXTIO(q, RDC_DISKQ_DATA_OFF); \
258	} while (0)
259
260#define	DQEND(q) (QWRAP(q)?QWRAP(q):QSIZE(q))
261
262#define	FITSONQ(q, n) \
263	(((QBLOCKS(q)+QNITEMS(q)+RDC_DISKQ_DATA_OFF+n) >= \
264		(uint64_t)DQEND(q))?0:1)
265
266/* diskq defines/macros (non-specific) */
267
268#define	RDC_NOLOG		0x00
269#define	RDC_WAIT		0x01
270#define	RDC_NOWAIT		0x02
271#define	RDC_DOLOG		0x04 /* put the group into logging */
272#define	RDC_NOFAIL		0x08 /* don't fail the queue, just init */
273#define	RDC_GROUP_LOCKED	0x10 /* trust me, I have the group lock */
274
275#define	RDC_WRITTEN		0x10 /* data has been commited to queue */
276#define	RDC_LAST		0x20 /* end of dequeued buffer, discard */
277
278/* CSTYLED */
279#define	RDC_BETWEEN(a,b,c)	(a<b?((c>=a)&&(c<=b)):((a!=b)&&((c<b)||(c>=a))))
280/* CSTYLED */
281
282#define	QHEADSHLDWRAP(q)	(QWRAP(q) && (QHEAD(q) >= QWRAP(q)))
283#define	QNXTIOSHLDWRAP(q)	(QWRAP(q) && (QNXTIO(q) >= QWRAP(q)))
284#define	QTAILSHLDWRAP(q, size)	(QTAIL(q) + size > QSIZE(q))
285#define	QCOALESCEOK(q, dec) ((q->lastio->iostatus & RDC_WRITTEN) && \
286	((QTAIL(q) > QNXTIO(q)) ? \
287	(((QTAIL(q) - dec) > QNXTIO(q)) && ((QTAIL(q) - dec) > \
288	QCOALBOUNDS(q))):\
289	(QNXTIOSHLDWRAP(q) && QTAIL(q) > RDC_DISKQ_DATA_OFF)))
290
291#define	QLOCK(q)		&q->disk_qlock
292#define	QTAILLOCK(q)		&q->tail_lock
293#define	QHEADLOCK(q)		&q->head_lock
294
295#define	QDISPLAY(q)		"qmagic: %x qvers: %d qstate: %x qhead: %" \
296	NSC_SZFMT " qnxtio: %" NSC_SZFMT " qtail: %" NSC_SZFMT " qtaillast: %" \
297	NSC_SZFMT " qsize: %" NSC_SZFMT " qnitems: %" RDC_DQFMT \
298	" qblocks: %" RDC_DQFMT " coalbounds %" NSC_SZFMT, QMAGIC(q), \
299	QVERS(q), QSTATE(q), QHEAD(q), QNXTIO(q), QTAIL(q), LASTQTAIL(q), \
300	QSIZE(q), QNITEMS(q), QBLOCKS(q), QCOALBOUNDS(q)
301
302#define	QDISPLAYND(q)		"m: %x v: %d s: %d h: %" NSC_SZFMT " n: %" \
303	NSC_SZFMT " t: %" NSC_SZFMT " l: %" NSC_SZFMT " z: %" NSC_SZFMT \
304	" i: %" RDC_DQFMT " b: %" RDC_DQFMT " w: %" NSC_SZFMT \
305	" a: %" NSC_SZFMT, \
306	QMAGIC(q), QVERS(q), QSTATE(q), QHEAD(q), \
307	QNXTIO(q), QTAIL(q), LASTQTAIL(q), QSIZE(q), QNITEMS(q), \
308	QBLOCKS(q), QWRAP(q), AUXQWRAP(q)
309
310/* Disk queue flusher state */
311#define	RDC_QFILL_AWAKE		(0)
312#define	RDC_QFILL_ASLEEP	(1)
313#define	RDC_QFILL_DEAD		(-1)
314
315/* functions */
316
317int rdc_add_diskq(rdc_config_t *uparms, spcs_s_info_t kstatus);
318int rdc_rem_diskq(rdc_config_t *uparms, spcs_s_info_t kstatus);
319int rdc_kill_diskq(rdc_config_t *uparms, spcs_s_info_t kstatus);
320int rdc_init_diskq(rdc_config_t *uparms, spcs_s_info_t kstatus);
321int rdc_lookup_diskq(char *path);
322int rdc_diskq_inuse(rdc_set_t *set, char *diskq);
323void rdc_dump_iohdrs(disk_queue *q);
324extern void rdc_fixlen(rdc_aio_t *aio);
325
326#endif /* _KERNEL */
327
328#ifdef	__cplusplus
329}
330#endif
331
332#endif /* _RDC_DISKQ_H */
333