xref: /onnv-gate/usr/src/uts/common/io/winlockio.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  * This is the lock device driver.
31*0Sstevel@tonic-gate  *
32*0Sstevel@tonic-gate  * The lock driver provides a variation of inter-process mutexes with the
33*0Sstevel@tonic-gate  * following twist in semantics:
34*0Sstevel@tonic-gate  *	A waiter for a lock after a set timeout can "break" the lock and
35*0Sstevel@tonic-gate  *	grab it from the current owner (without informing the owner).
36*0Sstevel@tonic-gate  *
37*0Sstevel@tonic-gate  * These semantics result in temporarily multiple processes thinking they
38*0Sstevel@tonic-gate  * own the lock. This usually does not make sense for cases where locks are
39*0Sstevel@tonic-gate  * used to protect a critical region and it is important to serialize access
40*0Sstevel@tonic-gate  * to data structures. As breaking the lock will also lose the serialization
41*0Sstevel@tonic-gate  * and result in corrupt data structures.
42*0Sstevel@tonic-gate  *
43*0Sstevel@tonic-gate  * The usage for winlock driver is primarily driven by the graphics system
44*0Sstevel@tonic-gate  * when doing DGA (direct graphics access) graphics. The locks are used to
45*0Sstevel@tonic-gate  * protect access to the frame buffer (presumably reflects back to the screen)
46*0Sstevel@tonic-gate  * between competing processes that directly write to the screen as opposed
47*0Sstevel@tonic-gate  * to going through the window server etc.
48*0Sstevel@tonic-gate  * In this case, the result of breaking the lock at worst causes the screen
49*0Sstevel@tonic-gate  * image to be distorted and is easily fixed by doing a "refresh"
50*0Sstevel@tonic-gate  *
51*0Sstevel@tonic-gate  * In well-behaved applications, the lock is held for a very short time and
52*0Sstevel@tonic-gate  * the breaking semantics do not come into play. Not having this feature and
53*0Sstevel@tonic-gate  * using normal inter-process mutexes will result in a misbehaved application
54*0Sstevel@tonic-gate  * from grabbing the screen writing capability from the window manager and
55*0Sstevel@tonic-gate  * effectively make the system look like it is hung (mouse pointer does not
56*0Sstevel@tonic-gate  * move).
57*0Sstevel@tonic-gate  *
58*0Sstevel@tonic-gate  * A secondary aspect of the winlock driver is that it allows for extremely
59*0Sstevel@tonic-gate  * fast lock acquire/release in cases where there is low contention. A memory
60*0Sstevel@tonic-gate  * write is all that is needed (not even a function call). And the window
61*0Sstevel@tonic-gate  * manager is the only DGA writer usually and this optimized for. Occasionally
62*0Sstevel@tonic-gate  * some processes might do DGA graphics and cause kernel faults to handle
63*0Sstevel@tonic-gate  * the contention/locking (and that has got to be slow!).
64*0Sstevel@tonic-gate  *
65*0Sstevel@tonic-gate  * The following IOCTLs are supported:
66*0Sstevel@tonic-gate  *
67*0Sstevel@tonic-gate  *   GRABPAGEALLOC:
68*0Sstevel@tonic-gate  *	Compatibility with old cgsix device driver lockpage ioctls.
69*0Sstevel@tonic-gate  *	Lockpages created this way must be an entire page for compatibility with
70*0Sstevel@tonic-gate  *	older software.	 This ioctl allocates a lock context with its own
71*0Sstevel@tonic-gate  *	private lock page.  The unique "ident" that identifies this lock is
72*0Sstevel@tonic-gate  *	returned.
73*0Sstevel@tonic-gate  *
74*0Sstevel@tonic-gate  *   GRABPAGEFREE:
75*0Sstevel@tonic-gate  *	Compatibility with cgsix device driver lockpage ioctls.	 This
76*0Sstevel@tonic-gate  *	ioctl releases the lock context allocated by GRABPAGEALLOC.
77*0Sstevel@tonic-gate  *
78*0Sstevel@tonic-gate  *   GRABLOCKINFO:
79*0Sstevel@tonic-gate  *	Returns a one-word flag.  '1' means that multiple clients may
80*0Sstevel@tonic-gate  *	access this lock page.	Older device drivers returned '0',
81*0Sstevel@tonic-gate  *	meaning that only two clients could access a lock page.
82*0Sstevel@tonic-gate  *
83*0Sstevel@tonic-gate  *   GRABATTACH:
84*0Sstevel@tonic-gate  *	Not supported.	This ioctl would have grabbed all lock pages
85*0Sstevel@tonic-gate  *	on behalf of the calling program.
86*0Sstevel@tonic-gate  *
87*0Sstevel@tonic-gate  *   WINLOCKALLOC:
88*0Sstevel@tonic-gate  *	Allocate a lock context.  This ioctl accepts a key value.  as
89*0Sstevel@tonic-gate  *	its argument.  If the key is zero, a new lock context is
90*0Sstevel@tonic-gate  *	created, and its "ident" is returned.	If the key is nonzero,
91*0Sstevel@tonic-gate  *	all existing contexts are checked to see if they match they
92*0Sstevel@tonic-gate  *	key.  If a match is found, its reference count is incremented
93*0Sstevel@tonic-gate  *	and its ident is returned, otherwise a new context is created
94*0Sstevel@tonic-gate  *	and its ident is returned.
95*0Sstevel@tonic-gate  *
96*0Sstevel@tonic-gate  *   WINLOCKFREE:
97*0Sstevel@tonic-gate  *	Free a lock context.  This ioctl accepts the ident of a lock
98*0Sstevel@tonic-gate  *	context and decrements its reference count.  Once the reference
99*0Sstevel@tonic-gate  *	count reaches zero *and* all mappings are released, the lock
100*0Sstevel@tonic-gate  *	context is freed.  When all the lock context in the lock page are
101*0Sstevel@tonic-gate  *	freed, the lock page is freed as well.
102*0Sstevel@tonic-gate  *
103*0Sstevel@tonic-gate  *   WINLOCKSETTIMEOUT:
104*0Sstevel@tonic-gate  *	Set lock timeout for a context.	 This ioctl accepts the ident
105*0Sstevel@tonic-gate  *	of a lock context and a timeout value in milliseconds.
106*0Sstevel@tonic-gate  *	Whenever lock contention occurs, the timer is started and the lock is
107*0Sstevel@tonic-gate  *	broken after the timeout expires. If timeout value is zero, lock does
108*0Sstevel@tonic-gate  *	not timeout.  This value will be rounded to the nearest clock
109*0Sstevel@tonic-gate  *	tick, so don't try to use it for real-time control or something.
110*0Sstevel@tonic-gate  *
111*0Sstevel@tonic-gate  *   WINLOCKGETTIMEOUT:
112*0Sstevel@tonic-gate  *	Get lock timeout from a context.
113*0Sstevel@tonic-gate  *
114*0Sstevel@tonic-gate  *   WINLOCKDUMP:
115*0Sstevel@tonic-gate  *	Dump state of this device.
116*0Sstevel@tonic-gate  *
117*0Sstevel@tonic-gate  *
118*0Sstevel@tonic-gate  * How /dev/winlock works:
119*0Sstevel@tonic-gate  *
120*0Sstevel@tonic-gate  *   Every lock context consists of two mappings for the client to the lock
121*0Sstevel@tonic-gate  *   page.  These mappings are known as the "lock page" and "unlock page"
122*0Sstevel@tonic-gate  *   to the client. The first mmap to the lock context (identified by the
123*0Sstevel@tonic-gate  *   sy_ident field returns during alloc) allocates mapping to the lock page,
124*0Sstevel@tonic-gate  *   the second mmap allocates a mapping to the unlock page.
125*0Sstevel@tonic-gate  *	The mappings dont have to be ordered in virtual address space, but do
126*0Sstevel@tonic-gate  *   need to be ordered in time. Mapping and unmapping of these lock and unlock
127*0Sstevel@tonic-gate  *   pages should happen in pairs. Doing them one at a time or unmapping one
128*0Sstevel@tonic-gate  *   and leaving one mapped etc cause undefined behaviors.
129*0Sstevel@tonic-gate  *	The mappings are always of length PAGESIZE, and type MAP_SHARED.
130*0Sstevel@tonic-gate  *
131*0Sstevel@tonic-gate  *   The first ioctl is to ALLOC a lock, either based on a key (if trying to
132*0Sstevel@tonic-gate  *	grab a preexisting lock) or 0 (gets a default new one)
133*0Sstevel@tonic-gate  *	This ioctl returns a value in sy_ident which is needed to do the
134*0Sstevel@tonic-gate  *	later mmaps and FREE/other ioctls.
135*0Sstevel@tonic-gate  *
136*0Sstevel@tonic-gate  *   The "page number" portion of the sy_ident needs to be passed as the
137*0Sstevel@tonic-gate  *	file offset when doing an mmap for both the lock page and unlock page
138*0Sstevel@tonic-gate  *
139*0Sstevel@tonic-gate  *   The value returned by mmap ( a user virtual address) needs to be
140*0Sstevel@tonic-gate  *	incremented by the "page offset" portion of sy_ident to obtain the
141*0Sstevel@tonic-gate  *	pointer to the actual lock. (Skipping this step, does not cause any
142*0Sstevel@tonic-gate  *	visible error, but the process will be using the wrong lock!)
143*0Sstevel@tonic-gate  *
144*0Sstevel@tonic-gate  *	On a fork(), the child process will inherit the mappings for free, but
145*0Sstevel@tonic-gate  *   will not inherit the parent's lock ownership if any. The child should NOT
146*0Sstevel@tonic-gate  *   do an explicit FREE on the lock context unless it did an explicit ALLOC.
147*0Sstevel@tonic-gate  *	Only one process at a time is allowed to have a valid hat
148*0Sstevel@tonic-gate  *   mapping to a lock page. This is enforced by this driver.
149*0Sstevel@tonic-gate  *   A client acquires a lock by writing a '1' to the lock page.
150*0Sstevel@tonic-gate  *   Note, that it is not necessary to read and veryify that the lock is '0'
151*0Sstevel@tonic-gate  *	prior to writing a '1' in it.
152*0Sstevel@tonic-gate  *   If it does not already have a valid mapping to that page, the driver
153*0Sstevel@tonic-gate  *   takes a fault (devmap_access), loads the client mapping
154*0Sstevel@tonic-gate  *   and allows the client to continue.	 The client releases the lock by
155*0Sstevel@tonic-gate  *   writing a '0' to the unlock page.	Again, if it does not have a valid
156*0Sstevel@tonic-gate  *   mapping to the unlock page, the segment driver takes a fault,
157*0Sstevel@tonic-gate  *   loads the mapping, and lets the client continue.  From this point
158*0Sstevel@tonic-gate  *   forward, the client can make as many locks and unlocks as it
159*0Sstevel@tonic-gate  *   wants, without any more faults into the kernel.
160*0Sstevel@tonic-gate  *
161*0Sstevel@tonic-gate  *   If a different process wants to acquire a lock, it takes a page fault
162*0Sstevel@tonic-gate  *   when it writes the '1' to the lock page.  If the segment driver sees
163*0Sstevel@tonic-gate  *   that the lock page contained a zero, then it invalidates the owner's
164*0Sstevel@tonic-gate  *   mappings and gives the mappings to this process.
165*0Sstevel@tonic-gate  *
166*0Sstevel@tonic-gate  *   If there is already a '1' in the lock page when the second client
167*0Sstevel@tonic-gate  *   tries to access the lock page, then a lock exists.	 The segment
168*0Sstevel@tonic-gate  *   driver sleeps the second client and, if applicable, starts the
169*0Sstevel@tonic-gate  *   timeout on the lock.  The owner's mapping to the unlock page
170*0Sstevel@tonic-gate  *   is invalidated so that the driver will be woken again when the owner
171*0Sstevel@tonic-gate  *   releases the lock.
172*0Sstevel@tonic-gate  *
173*0Sstevel@tonic-gate  *   When the locking client finally writes a '0' to the unlock page, the
174*0Sstevel@tonic-gate  *   segment driver takes another fault.  The client is given a valid
175*0Sstevel@tonic-gate  *   mapping, not to the unlock page, but to the "trash page", and allowed
176*0Sstevel@tonic-gate  *   to continue.  Meanwhile, the sleeping client is given a valid mapping
177*0Sstevel@tonic-gate  *   to the lock/unlock pages and allowed to continue as well.
178*0Sstevel@tonic-gate  *
179*0Sstevel@tonic-gate  * RFE: There is a leak if process exits before freeing allocated locks
180*0Sstevel@tonic-gate  * But currently not tracking which locks were allocated by which
181*0Sstevel@tonic-gate  * process and we do not have a clean entry point into the driver
182*0Sstevel@tonic-gate  * to do garbage collection. If the interface used a file descriptor for each
183*0Sstevel@tonic-gate  * lock it allocs, then the driver can free up stuff in the _close routine
184*0Sstevel@tonic-gate  */
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate #include <sys/types.h>		/* various type defn's */
187*0Sstevel@tonic-gate #include <sys/debug.h>
188*0Sstevel@tonic-gate #include <sys/param.h>		/* various kernel limits */
189*0Sstevel@tonic-gate #include <sys/time.h>
190*0Sstevel@tonic-gate #include <sys/errno.h>
191*0Sstevel@tonic-gate #include <sys/kmem.h>		/* defines kmem_alloc() */
192*0Sstevel@tonic-gate #include <sys/conf.h>		/* defines cdevsw */
193*0Sstevel@tonic-gate #include <sys/file.h>		/* various file modes, etc. */
194*0Sstevel@tonic-gate #include <sys/uio.h>		/* UIO stuff */
195*0Sstevel@tonic-gate #include <sys/ioctl.h>
196*0Sstevel@tonic-gate #include <sys/cred.h>		/* defines cred struct */
197*0Sstevel@tonic-gate #include <sys/mman.h>		/* defines mmap(2) parameters */
198*0Sstevel@tonic-gate #include <sys/stat.h>		/* defines S_IFCHR */
199*0Sstevel@tonic-gate #include <sys/cmn_err.h>	/* use cmn_err */
200*0Sstevel@tonic-gate #include <sys/ddi.h>		/* ddi stuff */
201*0Sstevel@tonic-gate #include <sys/sunddi.h>		/* ddi stuff */
202*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h>	/* ddi stuff */
203*0Sstevel@tonic-gate #include <sys/winlockio.h>	/* defines ioctls, flags, data structs */
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate static int	winlock_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
206*0Sstevel@tonic-gate static int	winlock_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
207*0Sstevel@tonic-gate 			size_t *, uint_t);
208*0Sstevel@tonic-gate static int	winlocksegmap(dev_t, off_t, struct as *, caddr_t *, off_t,
209*0Sstevel@tonic-gate 			uint_t, uint_t, uint_t, cred_t *);
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate static struct cb_ops	winlock_cb_ops = {
212*0Sstevel@tonic-gate 	nulldev,		/* open */
213*0Sstevel@tonic-gate 	nulldev,		/* close */
214*0Sstevel@tonic-gate 	nodev,			/* strategy */
215*0Sstevel@tonic-gate 	nodev,			/* print */
216*0Sstevel@tonic-gate 	nodev,			/* dump */
217*0Sstevel@tonic-gate 	nodev,			/* read */
218*0Sstevel@tonic-gate 	nodev,			/* write */
219*0Sstevel@tonic-gate 	winlock_ioctl,		/* ioctl */
220*0Sstevel@tonic-gate 	winlock_devmap,		/* devmap */
221*0Sstevel@tonic-gate 	nodev,			/* mmap */
222*0Sstevel@tonic-gate 	winlocksegmap,		/* segmap */
223*0Sstevel@tonic-gate 	nochpoll,		/* poll */
224*0Sstevel@tonic-gate 	ddi_prop_op,		/* prop_op */
225*0Sstevel@tonic-gate 	NULL,			/* streamtab */
226*0Sstevel@tonic-gate 	D_NEW|D_MP|D_DEVMAP,	/* Driver compatibility flag */
227*0Sstevel@tonic-gate 	0,			/* rev */
228*0Sstevel@tonic-gate 	nodev,			/* aread */
229*0Sstevel@tonic-gate 	nodev			/* awrite */
230*0Sstevel@tonic-gate };
231*0Sstevel@tonic-gate 
232*0Sstevel@tonic-gate static int winlock_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
233*0Sstevel@tonic-gate static int winlock_attach(dev_info_t *, ddi_attach_cmd_t);
234*0Sstevel@tonic-gate static int winlock_detach(dev_info_t *, ddi_detach_cmd_t);
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate static struct dev_ops	winlock_ops = {
237*0Sstevel@tonic-gate 	DEVO_REV,
238*0Sstevel@tonic-gate 	0,			/* refcount */
239*0Sstevel@tonic-gate 	winlock_info,		/* info */
240*0Sstevel@tonic-gate 	nulldev,		/* identify */
241*0Sstevel@tonic-gate 	nulldev,		/* probe */
242*0Sstevel@tonic-gate 	winlock_attach,		/* attach */
243*0Sstevel@tonic-gate 	winlock_detach,		/* detach */
244*0Sstevel@tonic-gate 	nodev,			/* reset */
245*0Sstevel@tonic-gate 	&winlock_cb_ops,	/* driver ops */
246*0Sstevel@tonic-gate 	NULL,			/* bus ops */
247*0Sstevel@tonic-gate 	NULL			/* power */
248*0Sstevel@tonic-gate };
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate static int winlockmap_map(devmap_cookie_t, dev_t, uint_t, offset_t, size_t,
251*0Sstevel@tonic-gate 		void **);
252*0Sstevel@tonic-gate static void winlockmap_unmap(devmap_cookie_t, void *, offset_t, size_t,
253*0Sstevel@tonic-gate 		devmap_cookie_t, void **, devmap_cookie_t, void **);
254*0Sstevel@tonic-gate static int winlockmap_dup(devmap_cookie_t, void *,
255*0Sstevel@tonic-gate 		devmap_cookie_t, void **);
256*0Sstevel@tonic-gate static int winlockmap_access(devmap_cookie_t, void *, offset_t, size_t,
257*0Sstevel@tonic-gate 		uint_t, uint_t);
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate static
260*0Sstevel@tonic-gate struct devmap_callback_ctl winlockmap_ops = {
261*0Sstevel@tonic-gate 	DEVMAP_OPS_REV,
262*0Sstevel@tonic-gate 	winlockmap_map,
263*0Sstevel@tonic-gate 	winlockmap_access,
264*0Sstevel@tonic-gate 	winlockmap_dup,
265*0Sstevel@tonic-gate 	winlockmap_unmap,
266*0Sstevel@tonic-gate };
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate #if DEBUG
269*0Sstevel@tonic-gate static	int	lock_debug = 0;
270*0Sstevel@tonic-gate #define	DEBUGF(level, args)	{ if (lock_debug >= (level)) cmn_err args; }
271*0Sstevel@tonic-gate #else
272*0Sstevel@tonic-gate #define	DEBUGF(level, args)
273*0Sstevel@tonic-gate #endif
274*0Sstevel@tonic-gate 
275*0Sstevel@tonic-gate /* Driver supports two styles of locks */
276*0Sstevel@tonic-gate enum winlock_style { NEWSTYLE_LOCK, OLDSTYLE_LOCK };
277*0Sstevel@tonic-gate 
278*0Sstevel@tonic-gate /*
279*0Sstevel@tonic-gate  * These structures describe a lock context.  We permit multiple
280*0Sstevel@tonic-gate  * clients (not just two) to access a lock page
281*0Sstevel@tonic-gate  *
282*0Sstevel@tonic-gate  * The "cookie" identifies the lock context. It is the page number portion
283*0Sstevel@tonic-gate  * sy_ident returned on lock allocation. Cookie is used in later ioctls.
284*0Sstevel@tonic-gate  * "cookie" is lockid * PAGESIZE
285*0Sstevel@tonic-gate  * "lockptr" is the kernel virtual address to the lock itself
286*0Sstevel@tonic-gate  * The page offset portion of lockptr is the page offset portion of sy_ident
287*0Sstevel@tonic-gate  */
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate /*
290*0Sstevel@tonic-gate  * per-process information about locks.  This is the private field of
291*0Sstevel@tonic-gate  * a devmap mapping.  Note that usually *two* mappings point to this.
292*0Sstevel@tonic-gate  */
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate /*
295*0Sstevel@tonic-gate  * Each process using winlock is associated with a segproc structure
296*0Sstevel@tonic-gate  * In various driver entry points, we need to search to find the right
297*0Sstevel@tonic-gate  * segproc structure (If we were using file handles for each lock this
298*0Sstevel@tonic-gate  * would not have been necessary).
299*0Sstevel@tonic-gate  * It would have been simple to use the process pid (and ddi_get_pid)
300*0Sstevel@tonic-gate  * However, during fork devmap_dup is called in the parent process context
301*0Sstevel@tonic-gate  * and using the pid complicates the code by introducing orphans.
302*0Sstevel@tonic-gate  * Instead we use the as pointer for the process as a cookie
303*0Sstevel@tonic-gate  * which requires delving into various non-DDI kosher structs
304*0Sstevel@tonic-gate  */
305*0Sstevel@tonic-gate typedef struct segproc {
306*0Sstevel@tonic-gate 	struct segproc	*next;		/* next client of this lock */
307*0Sstevel@tonic-gate 	struct seglock	*lp;		/* associated lock context */
308*0Sstevel@tonic-gate 	devmap_cookie_t	lockseg;	/* lock mapping, if any */
309*0Sstevel@tonic-gate 	devmap_cookie_t unlockseg;	/* unlock mapping, if any */
310*0Sstevel@tonic-gate 	void		*tag;		/* process as pointer as tag */
311*0Sstevel@tonic-gate 	uint_t		flag;		/* see "flag bits" in winlockio.h */
312*0Sstevel@tonic-gate } SegProc;
313*0Sstevel@tonic-gate 
314*0Sstevel@tonic-gate #define	ID(sdp)		((sdp)->tag)
315*0Sstevel@tonic-gate #define	CURPROC_ID	(void *)(curproc->p_as)
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate /* per lock context information */
318*0Sstevel@tonic-gate 
319*0Sstevel@tonic-gate typedef struct seglock {
320*0Sstevel@tonic-gate 	struct seglock	*next;		/* next lock */
321*0Sstevel@tonic-gate 	uint_t		sleepers;	/* nthreads sleeping on this lock */
322*0Sstevel@tonic-gate 	uint_t		alloccount;	/* how many times created? */
323*0Sstevel@tonic-gate 	uint_t		cookie;		/* mmap() offset (page #) into device */
324*0Sstevel@tonic-gate 	uint_t		key;		/* key, if any */
325*0Sstevel@tonic-gate 	enum winlock_style	style;	/* style of lock - OLDSTYLE, NEWSTYLE */
326*0Sstevel@tonic-gate 	clock_t		timeout;	/* sleep time in ticks */
327*0Sstevel@tonic-gate 	ddi_umem_cookie_t umem_cookie;	/* cookie for umem allocated memory */
328*0Sstevel@tonic-gate 	int		*lockptr;	/* kernel virtual addr of lock */
329*0Sstevel@tonic-gate 	struct segproc	*clients;	/* list of clients of this lock */
330*0Sstevel@tonic-gate 	struct segproc	*owner;		/* current owner of lock */
331*0Sstevel@tonic-gate 	kmutex_t	mutex;		/* mutex for lock */
332*0Sstevel@tonic-gate 	kcondvar_t	locksleep;	/* for sleeping on lock */
333*0Sstevel@tonic-gate } SegLock;
334*0Sstevel@tonic-gate 
335*0Sstevel@tonic-gate #define	LOCK(lp)	(*((lp)->lockptr))
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate /*
338*0Sstevel@tonic-gate  * Number of locks that can fit in a page. Driver can support only that many.
339*0Sstevel@tonic-gate  * For oldsytle locks, it is relatively easy to increase the limit as each
340*0Sstevel@tonic-gate  * is in a separate page (MAX_LOCKS mostly serves to prevent runaway allocation
341*0Sstevel@tonic-gate  * For newstyle locks, this is trickier as the code needs to allow for mapping
342*0Sstevel@tonic-gate  * into the second or third page of the cookie for some locks.
343*0Sstevel@tonic-gate  */
344*0Sstevel@tonic-gate #define	MAX_LOCKS	(PAGESIZE/sizeof (int))
345*0Sstevel@tonic-gate 
346*0Sstevel@tonic-gate #define	LOCKTIME	3	/* Default lock timeout in seconds */
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 
349*0Sstevel@tonic-gate /* Protections setting for winlock user mappings */
350*0Sstevel@tonic-gate #define	WINLOCK_PROT	(PROT_READ|PROT_WRITE|PROT_USER)
351*0Sstevel@tonic-gate 
352*0Sstevel@tonic-gate /*
353*0Sstevel@tonic-gate  * The trash page is where unwanted writes go
354*0Sstevel@tonic-gate  * when a process is releasing a lock.
355*0Sstevel@tonic-gate  */
356*0Sstevel@tonic-gate static	ddi_umem_cookie_t trashpage_cookie = NULL;
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate /* For newstyle allocations a common page of locks is used */
359*0Sstevel@tonic-gate static	caddr_t	lockpage = NULL;
360*0Sstevel@tonic-gate static	ddi_umem_cookie_t lockpage_cookie = NULL;
361*0Sstevel@tonic-gate 
362*0Sstevel@tonic-gate static	dev_info_t	*winlock_dip = NULL;
363*0Sstevel@tonic-gate static	kmutex_t	winlock_mutex;
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate /*
366*0Sstevel@tonic-gate  * winlock_mutex protects
367*0Sstevel@tonic-gate  *	lock_list
368*0Sstevel@tonic-gate  *	lock_free_list
369*0Sstevel@tonic-gate  *	"next" field in SegLock
370*0Sstevel@tonic-gate  *	next_lock
371*0Sstevel@tonic-gate  *	trashpage_cookie
372*0Sstevel@tonic-gate  *	lockpage & lockpage_cookie
373*0Sstevel@tonic-gate  *
374*0Sstevel@tonic-gate  * SegLock_mutex protects
375*0Sstevel@tonic-gate  *	rest of fields in SegLock
376*0Sstevel@tonic-gate  *	All fields in list of SegProc (lp->clients)
377*0Sstevel@tonic-gate  *
378*0Sstevel@tonic-gate  * Lock ordering is winlock_mutex->SegLock_mutex
379*0Sstevel@tonic-gate  * During devmap/seg operations SegLock_mutex acquired without winlock_mutex
380*0Sstevel@tonic-gate  *
381*0Sstevel@tonic-gate  * During devmap callbacks, the pointer to SegProc is stored as the private
382*0Sstevel@tonic-gate  * data in the devmap handle. This pointer will not go stale (i.e., the
383*0Sstevel@tonic-gate  * SegProc getting deleted) as the SegProc is not deleted until both the
384*0Sstevel@tonic-gate  * lockseg and unlockseg have been unmapped and the pointers stored in
385*0Sstevel@tonic-gate  * the devmap handles have been NULL'ed.
386*0Sstevel@tonic-gate  * But before this pointer is used to access any fields (other than the 'lp')
387*0Sstevel@tonic-gate  * lp->mutex must be held.
388*0Sstevel@tonic-gate  */
389*0Sstevel@tonic-gate 
390*0Sstevel@tonic-gate /*
391*0Sstevel@tonic-gate  * The allocation code tries to allocate from lock_free_list
392*0Sstevel@tonic-gate  * first, otherwise it uses kmem_zalloc.  When lock list is idle, all
393*0Sstevel@tonic-gate  * locks in lock_free_list are kmem_freed
394*0Sstevel@tonic-gate  */
395*0Sstevel@tonic-gate static	SegLock	*lock_list = NULL;		/* in-use locks */
396*0Sstevel@tonic-gate static	SegLock	*lock_free_list = NULL;		/* free locks */
397*0Sstevel@tonic-gate static	int	next_lock = 0;			/* next lock cookie */
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate /* Routines to find a lock in lock_list based on offset or key */
400*0Sstevel@tonic-gate static SegLock *seglock_findlock(uint_t);
401*0Sstevel@tonic-gate static SegLock *seglock_findkey(uint_t);
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate /* Routines to find and allocate SegProc structures */
404*0Sstevel@tonic-gate static SegProc *seglock_find_specific(SegLock *, void *);
405*0Sstevel@tonic-gate static SegProc *seglock_alloc_specific(SegLock *, void *);
406*0Sstevel@tonic-gate #define	seglock_findclient(lp)	seglock_find_specific((lp), CURPROC_ID)
407*0Sstevel@tonic-gate #define	seglock_allocclient(lp)	seglock_alloc_specific((lp), CURPROC_ID)
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate /* Delete client from lock's client list */
410*0Sstevel@tonic-gate static void seglock_deleteclient(SegLock *, SegProc *);
411*0Sstevel@tonic-gate static void garbage_collect_lock(SegLock *, SegProc *);
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate /* Create a new lock */
414*0Sstevel@tonic-gate static SegLock *seglock_createlock(enum winlock_style);
415*0Sstevel@tonic-gate /* Destroy lock */
416*0Sstevel@tonic-gate static void seglock_destroylock(SegLock *);
417*0Sstevel@tonic-gate static void lock_destroyall(void);
418*0Sstevel@tonic-gate 
419*0Sstevel@tonic-gate /* Helper functions in winlockmap_access */
420*0Sstevel@tonic-gate static int give_mapping(SegLock *, SegProc *, uint_t);
421*0Sstevel@tonic-gate static int lock_giveup(SegLock *, int);
422*0Sstevel@tonic-gate static int seglock_lockfault(devmap_cookie_t, SegProc *, SegLock *, uint_t);
423*0Sstevel@tonic-gate 
424*0Sstevel@tonic-gate /* routines called from ioctl */
425*0Sstevel@tonic-gate static int seglock_graballoc(intptr_t, enum winlock_style, int);
426*0Sstevel@tonic-gate static int seglock_grabinfo(intptr_t, int);
427*0Sstevel@tonic-gate static int seglock_grabfree(intptr_t, int);
428*0Sstevel@tonic-gate static int seglock_gettimeout(intptr_t, int);
429*0Sstevel@tonic-gate static int seglock_settimeout(intptr_t, int);
430*0Sstevel@tonic-gate static void seglock_dump_all(void);
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate static	int
433*0Sstevel@tonic-gate winlock_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
434*0Sstevel@tonic-gate {
435*0Sstevel@tonic-gate 	DEBUGF(1, (CE_CONT, "winlock_attach, devi=%p, cmd=%d\n",
436*0Sstevel@tonic-gate 		(void *)devi, (int)cmd));
437*0Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
438*0Sstevel@tonic-gate 		return (DDI_FAILURE);
439*0Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "winlock", S_IFCHR, 0, DDI_PSEUDO, 0)
440*0Sstevel@tonic-gate 	    == DDI_FAILURE) {
441*0Sstevel@tonic-gate 		return (DDI_FAILURE);
442*0Sstevel@tonic-gate 	}
443*0Sstevel@tonic-gate 	winlock_dip = devi;
444*0Sstevel@tonic-gate 	ddi_report_dev(devi);
445*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
446*0Sstevel@tonic-gate }
447*0Sstevel@tonic-gate 
448*0Sstevel@tonic-gate /*ARGSUSED*/
449*0Sstevel@tonic-gate static	int
450*0Sstevel@tonic-gate winlock_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
451*0Sstevel@tonic-gate {
452*0Sstevel@tonic-gate 	DEBUGF(1, (CE_CONT, "winlock_detach, devi=%p, cmd=%d\n",
453*0Sstevel@tonic-gate 		(void *)devi, (int)cmd));
454*0Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
455*0Sstevel@tonic-gate 		return (DDI_FAILURE);
456*0Sstevel@tonic-gate 
457*0Sstevel@tonic-gate 	mutex_enter(&winlock_mutex);
458*0Sstevel@tonic-gate 	if (lock_list != NULL) {
459*0Sstevel@tonic-gate 		mutex_exit(&winlock_mutex);
460*0Sstevel@tonic-gate 		return (DDI_FAILURE);
461*0Sstevel@tonic-gate 	}
462*0Sstevel@tonic-gate 	ASSERT(lock_free_list == NULL);
463*0Sstevel@tonic-gate 
464*0Sstevel@tonic-gate 	DEBUGF(1, (CE_CONT, "detach freeing trashpage and lockpage\n"));
465*0Sstevel@tonic-gate 	/* destroy any common stuff created */
466*0Sstevel@tonic-gate 	if (trashpage_cookie != NULL) {
467*0Sstevel@tonic-gate 		ddi_umem_free(trashpage_cookie);
468*0Sstevel@tonic-gate 		trashpage_cookie = NULL;
469*0Sstevel@tonic-gate 	}
470*0Sstevel@tonic-gate 	if (lockpage != NULL) {
471*0Sstevel@tonic-gate 		ddi_umem_free(lockpage_cookie);
472*0Sstevel@tonic-gate 		lockpage = NULL;
473*0Sstevel@tonic-gate 		lockpage_cookie = NULL;
474*0Sstevel@tonic-gate 	}
475*0Sstevel@tonic-gate 	winlock_dip = NULL;
476*0Sstevel@tonic-gate 	mutex_exit(&winlock_mutex);
477*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
478*0Sstevel@tonic-gate }
479*0Sstevel@tonic-gate 
480*0Sstevel@tonic-gate /*ARGSUSED*/
481*0Sstevel@tonic-gate static	int
482*0Sstevel@tonic-gate winlock_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
483*0Sstevel@tonic-gate {
484*0Sstevel@tonic-gate 	register int error;
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 	/* initialize result */
487*0Sstevel@tonic-gate 	*result = NULL;
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate 	/* only valid instance (i.e., getminor) is 0 */
490*0Sstevel@tonic-gate 	if (getminor((dev_t)arg) >= 1)
491*0Sstevel@tonic-gate 		return (DDI_FAILURE);
492*0Sstevel@tonic-gate 
493*0Sstevel@tonic-gate 	switch (infocmd) {
494*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
495*0Sstevel@tonic-gate 		if (winlock_dip == NULL)
496*0Sstevel@tonic-gate 			error = DDI_FAILURE;
497*0Sstevel@tonic-gate 		else {
498*0Sstevel@tonic-gate 			*result = (void *)winlock_dip;
499*0Sstevel@tonic-gate 			error = DDI_SUCCESS;
500*0Sstevel@tonic-gate 		}
501*0Sstevel@tonic-gate 		break;
502*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
503*0Sstevel@tonic-gate 		*result = (void *)0;
504*0Sstevel@tonic-gate 		error = DDI_SUCCESS;
505*0Sstevel@tonic-gate 		break;
506*0Sstevel@tonic-gate 	default:
507*0Sstevel@tonic-gate 		error = DDI_FAILURE;
508*0Sstevel@tonic-gate 	}
509*0Sstevel@tonic-gate 	return (error);
510*0Sstevel@tonic-gate }
511*0Sstevel@tonic-gate 
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate /*ARGSUSED*/
514*0Sstevel@tonic-gate int
515*0Sstevel@tonic-gate winlock_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
516*0Sstevel@tonic-gate 	cred_t *cred, int *rval)
517*0Sstevel@tonic-gate {
518*0Sstevel@tonic-gate 	DEBUGF(1, (CE_CONT, "winlockioctl: cmd=%d, arg=0x%p\n",
519*0Sstevel@tonic-gate 		cmd, (void *)arg));
520*0Sstevel@tonic-gate 
521*0Sstevel@tonic-gate 	switch (cmd) {
522*0Sstevel@tonic-gate 	/*
523*0Sstevel@tonic-gate 	 * ioctls that used to be handled by framebuffers (defined in fbio.h)
524*0Sstevel@tonic-gate 	 * RFE: No code really calls the GRAB* ioctls now. Should EOL.
525*0Sstevel@tonic-gate 	 */
526*0Sstevel@tonic-gate 
527*0Sstevel@tonic-gate 	case GRABPAGEALLOC:
528*0Sstevel@tonic-gate 		return (seglock_graballoc(arg, OLDSTYLE_LOCK, mode));
529*0Sstevel@tonic-gate 	case GRABPAGEFREE:
530*0Sstevel@tonic-gate 		return (seglock_grabfree(arg, mode));
531*0Sstevel@tonic-gate 	case GRABLOCKINFO:
532*0Sstevel@tonic-gate 		return (seglock_grabinfo(arg, mode));
533*0Sstevel@tonic-gate 	case GRABATTACH:
534*0Sstevel@tonic-gate 		return (EINVAL); /* GRABATTACH is not supported (never was) */
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 	case WINLOCKALLOC:
537*0Sstevel@tonic-gate 		return (seglock_graballoc(arg, NEWSTYLE_LOCK, mode));
538*0Sstevel@tonic-gate 	case WINLOCKFREE:
539*0Sstevel@tonic-gate 		return (seglock_grabfree(arg, mode));
540*0Sstevel@tonic-gate 	case WINLOCKSETTIMEOUT:
541*0Sstevel@tonic-gate 		return (seglock_settimeout(arg, mode));
542*0Sstevel@tonic-gate 	case WINLOCKGETTIMEOUT:
543*0Sstevel@tonic-gate 		return (seglock_gettimeout(arg, mode));
544*0Sstevel@tonic-gate 	case WINLOCKDUMP:
545*0Sstevel@tonic-gate 		seglock_dump_all();
546*0Sstevel@tonic-gate 		return (0);
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate #ifdef DEBUG
549*0Sstevel@tonic-gate 	case (WIOC|255):
550*0Sstevel@tonic-gate 		lock_debug = arg;
551*0Sstevel@tonic-gate 		return (0);
552*0Sstevel@tonic-gate #endif
553*0Sstevel@tonic-gate 
554*0Sstevel@tonic-gate 	default:
555*0Sstevel@tonic-gate 		return (ENOTTY);		/* Why is this not EINVAL */
556*0Sstevel@tonic-gate 	}
557*0Sstevel@tonic-gate }
558*0Sstevel@tonic-gate 
559*0Sstevel@tonic-gate int
560*0Sstevel@tonic-gate winlocksegmap(
561*0Sstevel@tonic-gate 	dev_t	dev,		/* major:minor */
562*0Sstevel@tonic-gate 	off_t	off,		/* device offset from mmap(2) */
563*0Sstevel@tonic-gate 	struct as *as,		/* user's address space. */
564*0Sstevel@tonic-gate 	caddr_t	*addr,		/* address from mmap(2) */
565*0Sstevel@tonic-gate 	off_t	len,		/* length from mmap(2) */
566*0Sstevel@tonic-gate 	uint_t	prot,		/* user wants this access */
567*0Sstevel@tonic-gate 	uint_t	maxprot,	/* this is the maximum the user can have */
568*0Sstevel@tonic-gate 	uint_t	flags,		/* flags from mmap(2) */
569*0Sstevel@tonic-gate 	cred_t	*cred)
570*0Sstevel@tonic-gate {
571*0Sstevel@tonic-gate 	DEBUGF(1, (CE_CONT, "winlock_segmap off=%lx, len=0x%lx\n", off, len));
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate 	/* Only MAP_SHARED mappings are supported */
574*0Sstevel@tonic-gate 	if ((flags & MAP_TYPE) == MAP_PRIVATE) {
575*0Sstevel@tonic-gate 		return (EINVAL);
576*0Sstevel@tonic-gate 	}
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate 	/* Use devmap_setup to setup the mapping */
579*0Sstevel@tonic-gate 	return (devmap_setup(dev, (offset_t)off, as, addr, (size_t)len, prot,
580*0Sstevel@tonic-gate 		maxprot, flags, cred));
581*0Sstevel@tonic-gate }
582*0Sstevel@tonic-gate 
583*0Sstevel@tonic-gate /*ARGSUSED*/
584*0Sstevel@tonic-gate int
585*0Sstevel@tonic-gate winlock_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
586*0Sstevel@tonic-gate     size_t *maplen, uint_t model)
587*0Sstevel@tonic-gate {
588*0Sstevel@tonic-gate 	SegLock *lp;
589*0Sstevel@tonic-gate 	int err;
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate 	DEBUGF(1, (CE_CONT, "winlock devmap: off=%llx, len=%lx, dhp=%p\n",
592*0Sstevel@tonic-gate 		off, len, (void *)dhp));
593*0Sstevel@tonic-gate 
594*0Sstevel@tonic-gate 	*maplen = 0;
595*0Sstevel@tonic-gate 
596*0Sstevel@tonic-gate 	/* Check if the lock exists, i.e., has been created by alloc */
597*0Sstevel@tonic-gate 	/* off is the sy_ident returned in the alloc ioctl */
598*0Sstevel@tonic-gate 	if ((lp = seglock_findlock((uint_t)off)) == NULL) {
599*0Sstevel@tonic-gate 		return (ENXIO);
600*0Sstevel@tonic-gate 	}
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 	/*
603*0Sstevel@tonic-gate 	 * The offset bits in mmap(2) offset has to be same as in lockptr
604*0Sstevel@tonic-gate 	 * OR the offset should be 0 (i.e. masked off)
605*0Sstevel@tonic-gate 	 */
606*0Sstevel@tonic-gate 	if (((off & PAGEOFFSET) != 0) &&
607*0Sstevel@tonic-gate 	    ((off ^ (uintptr_t)(lp->lockptr)) & (offset_t)PAGEOFFSET) != 0) {
608*0Sstevel@tonic-gate 		DEBUGF(2, (CE_CONT,
609*0Sstevel@tonic-gate 			"mmap offset %llx mismatch with lockptr %p\n",
610*0Sstevel@tonic-gate 			off, (void *)lp->lockptr));
611*0Sstevel@tonic-gate 		mutex_exit(&lp->mutex);	/* mutex held by seglock_findlock */
612*0Sstevel@tonic-gate 		return (EINVAL);
613*0Sstevel@tonic-gate 	}
614*0Sstevel@tonic-gate 
615*0Sstevel@tonic-gate 	/* Only supports PAGESIZE length mappings */
616*0Sstevel@tonic-gate 	if (len != PAGESIZE) {
617*0Sstevel@tonic-gate 		mutex_exit(&lp->mutex);	/* mutex held by seglock_findlock */
618*0Sstevel@tonic-gate 		return (EINVAL);
619*0Sstevel@tonic-gate 	}
620*0Sstevel@tonic-gate 
621*0Sstevel@tonic-gate 	/*
622*0Sstevel@tonic-gate 	 * Set up devmap to point at page associated with lock
623*0Sstevel@tonic-gate 	 * RFE: At this point we dont know if this is a lockpage or unlockpage
624*0Sstevel@tonic-gate 	 * a lockpage would not need DEVMAP_ALLOW_REMAP setting
625*0Sstevel@tonic-gate 	 * We could have kept track of the mapping order here,
626*0Sstevel@tonic-gate 	 * but devmap framework does not support storing any state in this
627*0Sstevel@tonic-gate 	 * devmap callback as it does not callback for error cleanup if some
628*0Sstevel@tonic-gate 	 * other error happens in the framework.
629*0Sstevel@tonic-gate 	 * RFE: We should modify the winlock mmap interface so that the
630*0Sstevel@tonic-gate 	 * user process marks in the offset passed in whether this is for a
631*0Sstevel@tonic-gate 	 * lock or unlock mapping instead of guessing based on order of maps
632*0Sstevel@tonic-gate 	 * This would cleanup other things (such as in fork)
633*0Sstevel@tonic-gate 	 */
634*0Sstevel@tonic-gate 	if ((err = devmap_umem_setup(dhp, winlock_dip, &winlockmap_ops,
635*0Sstevel@tonic-gate 	    lp->umem_cookie, 0, PAGESIZE, WINLOCK_PROT,
636*0Sstevel@tonic-gate 	    DEVMAP_ALLOW_REMAP, 0)) < 0) {
637*0Sstevel@tonic-gate 		mutex_exit(&lp->mutex);	/* held by seglock_findlock */
638*0Sstevel@tonic-gate 		return (err);
639*0Sstevel@tonic-gate 	}
640*0Sstevel@tonic-gate 	/*
641*0Sstevel@tonic-gate 	 * No mappings are loaded to those segments yet. The correctness
642*0Sstevel@tonic-gate 	 * of the winlock semantics depends on the devmap framework/seg_dev NOT
643*0Sstevel@tonic-gate 	 * loading the translations without calling _access callback.
644*0Sstevel@tonic-gate 	 */
645*0Sstevel@tonic-gate 
646*0Sstevel@tonic-gate 	mutex_exit(&lp->mutex);	/* mutex held by seglock_findlock */
647*0Sstevel@tonic-gate 	*maplen = PAGESIZE;
648*0Sstevel@tonic-gate 	return (0);
649*0Sstevel@tonic-gate }
650*0Sstevel@tonic-gate 
651*0Sstevel@tonic-gate /*
652*0Sstevel@tonic-gate  * This routine is called by the devmap framework after the devmap entry point
653*0Sstevel@tonic-gate  * above and the mapping is setup in seg_dev.
654*0Sstevel@tonic-gate  * We store the pointer to the per-process context in the devmap private data.
655*0Sstevel@tonic-gate  */
656*0Sstevel@tonic-gate /*ARGSUSED*/
657*0Sstevel@tonic-gate static int
658*0Sstevel@tonic-gate winlockmap_map(devmap_cookie_t dhp, dev_t dev, uint_t flags, offset_t off,
659*0Sstevel@tonic-gate 	size_t len, void **pvtp)
660*0Sstevel@tonic-gate {
661*0Sstevel@tonic-gate 	SegLock *lp = seglock_findlock((uint_t)off); /* returns w/ mutex held */
662*0Sstevel@tonic-gate 	SegProc *sdp;
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 	ASSERT(len == PAGESIZE);
665*0Sstevel@tonic-gate 
666*0Sstevel@tonic-gate 	/* Find the per-process context for this lock, alloc one if not found */
667*0Sstevel@tonic-gate 	sdp = seglock_allocclient(lp);
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 	/*
670*0Sstevel@tonic-gate 	 * RFE: Determining which is a lock vs unlock seg is based on order
671*0Sstevel@tonic-gate 	 * of mmaps, we should change that to be derivable from off
672*0Sstevel@tonic-gate 	 */
673*0Sstevel@tonic-gate 	if (sdp->lockseg == NULL) {
674*0Sstevel@tonic-gate 		sdp->lockseg = dhp;
675*0Sstevel@tonic-gate 	} else if (sdp->unlockseg == NULL) {
676*0Sstevel@tonic-gate 		sdp->unlockseg = dhp;
677*0Sstevel@tonic-gate 	} else {
678*0Sstevel@tonic-gate 		/* attempting to map lock more than twice */
679*0Sstevel@tonic-gate 		mutex_exit(&lp->mutex);	/* mutex held by seglock_findlock */
680*0Sstevel@tonic-gate 		return (ENOMEM);
681*0Sstevel@tonic-gate 	}
682*0Sstevel@tonic-gate 
683*0Sstevel@tonic-gate 	*pvtp = sdp;
684*0Sstevel@tonic-gate 	mutex_exit(&lp->mutex);	/* mutex held by seglock_findlock */
685*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
686*0Sstevel@tonic-gate }
687*0Sstevel@tonic-gate 
688*0Sstevel@tonic-gate /*
689*0Sstevel@tonic-gate  * duplicate a segment, as in fork()
690*0Sstevel@tonic-gate  * On fork, the child inherits the mappings to the lock
691*0Sstevel@tonic-gate  *	lp->alloccount is NOT incremented, so child should not do a free().
692*0Sstevel@tonic-gate  *	Semantics same as if done an alloc(), map(), map().
693*0Sstevel@tonic-gate  *	This way it would work fine if doing an exec() variant later
694*0Sstevel@tonic-gate  *	Child does not inherit any UFLAGS set in parent
695*0Sstevel@tonic-gate  * The lock and unlock pages are started off unmapped, i.e., child does not
696*0Sstevel@tonic-gate  *	own the lock.
697*0Sstevel@tonic-gate  * The code assumes that the child process has a valid pid at this point
698*0Sstevel@tonic-gate  * RFE: This semantics depends on fork not duplicating the hat mappings
699*0Sstevel@tonic-gate  *	(which is the current implementation). To enforce it would need to
700*0Sstevel@tonic-gate  *	call devmap_unload from here - not clear if that is allowed.
701*0Sstevel@tonic-gate  */
702*0Sstevel@tonic-gate 
703*0Sstevel@tonic-gate static int
704*0Sstevel@tonic-gate winlockmap_dup(devmap_cookie_t dhp, void *oldpvt, devmap_cookie_t new_dhp,
705*0Sstevel@tonic-gate 	void **newpvt)
706*0Sstevel@tonic-gate {
707*0Sstevel@tonic-gate 	SegProc *sdp = (SegProc *)oldpvt;
708*0Sstevel@tonic-gate 	SegProc *ndp;
709*0Sstevel@tonic-gate 	SegLock *lp = sdp->lp;
710*0Sstevel@tonic-gate 
711*0Sstevel@tonic-gate 	mutex_enter(&lp->mutex);
712*0Sstevel@tonic-gate 	ASSERT((dhp == sdp->lockseg) || (dhp == sdp->unlockseg));
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate 	/*
715*0Sstevel@tonic-gate 	 * Note: At this point, the child process does have a pid, but
716*0Sstevel@tonic-gate 	 * the arguments passed to as_dup and hence to devmap_dup dont pass it
717*0Sstevel@tonic-gate 	 * down. So we cannot use normal seglock_findclient - which finds the
718*0Sstevel@tonic-gate 	 * parent sdp itself!
719*0Sstevel@tonic-gate 	 * Instead we allocate the child's SegProc by using the child as pointer
720*0Sstevel@tonic-gate 	 * RFE: we are using the as stucture which means peeking into the
721*0Sstevel@tonic-gate 	 * devmap_cookie. This is not DDI-compliant. Need a compliant way of
722*0Sstevel@tonic-gate 	 * getting at either the as or, better, a way to get the child's new pid
723*0Sstevel@tonic-gate 	 */
724*0Sstevel@tonic-gate 	ndp = seglock_alloc_specific(lp,
725*0Sstevel@tonic-gate 		(void *)((devmap_handle_t *)new_dhp)->dh_seg->s_as);
726*0Sstevel@tonic-gate 	ASSERT(ndp != sdp);
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 	if (sdp->lockseg == dhp) {
729*0Sstevel@tonic-gate 		ASSERT(ndp->lockseg == NULL);
730*0Sstevel@tonic-gate 		ndp->lockseg = new_dhp;
731*0Sstevel@tonic-gate 	} else {
732*0Sstevel@tonic-gate 		ASSERT(sdp->unlockseg == dhp);
733*0Sstevel@tonic-gate 		ASSERT(ndp->unlockseg == NULL);
734*0Sstevel@tonic-gate 		ndp->unlockseg = new_dhp;
735*0Sstevel@tonic-gate 		if (sdp->flag & TRASHPAGE) {
736*0Sstevel@tonic-gate 			ndp->flag |= TRASHPAGE;
737*0Sstevel@tonic-gate 		}
738*0Sstevel@tonic-gate 	}
739*0Sstevel@tonic-gate 	mutex_exit(&lp->mutex);
740*0Sstevel@tonic-gate 	*newpvt = (void *)ndp;
741*0Sstevel@tonic-gate 	return (0);
742*0Sstevel@tonic-gate }
743*0Sstevel@tonic-gate 
744*0Sstevel@tonic-gate 
745*0Sstevel@tonic-gate /*ARGSUSED*/
746*0Sstevel@tonic-gate static void
747*0Sstevel@tonic-gate winlockmap_unmap(devmap_cookie_t dhp, void *pvtp, offset_t off, size_t len,
748*0Sstevel@tonic-gate 	devmap_cookie_t new_dhp1, void **newpvtp1,
749*0Sstevel@tonic-gate 	devmap_cookie_t new_dhp2, void **newpvtp2)
750*0Sstevel@tonic-gate {
751*0Sstevel@tonic-gate 	SegProc	*sdp = (SegProc *)pvtp;
752*0Sstevel@tonic-gate 	SegLock	*lp = sdp->lp;
753*0Sstevel@tonic-gate 
754*0Sstevel@tonic-gate 	/*
755*0Sstevel@tonic-gate 	 * We always create PAGESIZE length mappings, so there should never
756*0Sstevel@tonic-gate 	 * be a partial unmapping case
757*0Sstevel@tonic-gate 	 */
758*0Sstevel@tonic-gate 	ASSERT((new_dhp1 == NULL) && (new_dhp2 == NULL));
759*0Sstevel@tonic-gate 
760*0Sstevel@tonic-gate 	mutex_enter(&lp->mutex);
761*0Sstevel@tonic-gate 	ASSERT((dhp == sdp->lockseg) || (dhp == sdp->unlockseg));
762*0Sstevel@tonic-gate 	/* make sure this process doesn't own the lock */
763*0Sstevel@tonic-gate 	if (sdp == lp->owner) {
764*0Sstevel@tonic-gate 		/*
765*0Sstevel@tonic-gate 		 * Not handling errors - i.e., errors in unloading mapping
766*0Sstevel@tonic-gate 		 * As part of unmapping hat/seg structure get torn down anyway
767*0Sstevel@tonic-gate 		 */
768*0Sstevel@tonic-gate 		(void) lock_giveup(lp, 0);
769*0Sstevel@tonic-gate 	}
770*0Sstevel@tonic-gate 
771*0Sstevel@tonic-gate 	ASSERT(sdp != lp->owner);
772*0Sstevel@tonic-gate 	if (sdp->lockseg == dhp) {
773*0Sstevel@tonic-gate 		sdp->lockseg = NULL;
774*0Sstevel@tonic-gate 	} else {
775*0Sstevel@tonic-gate 		ASSERT(sdp->unlockseg == dhp);
776*0Sstevel@tonic-gate 		sdp->unlockseg = NULL;
777*0Sstevel@tonic-gate 		sdp->flag &= ~TRASHPAGE;	/* clear flag if set */
778*0Sstevel@tonic-gate 	}
779*0Sstevel@tonic-gate 
780*0Sstevel@tonic-gate 	garbage_collect_lock(lp, sdp);
781*0Sstevel@tonic-gate }
782*0Sstevel@tonic-gate 
783*0Sstevel@tonic-gate /*ARGSUSED*/
784*0Sstevel@tonic-gate static int
785*0Sstevel@tonic-gate winlockmap_access(devmap_cookie_t dhp, void *pvt, offset_t off, size_t len,
786*0Sstevel@tonic-gate 	uint_t type, uint_t rw)
787*0Sstevel@tonic-gate {
788*0Sstevel@tonic-gate 	SegProc *sdp = (SegProc *)pvt;
789*0Sstevel@tonic-gate 	SegLock *lp = sdp->lp;
790*0Sstevel@tonic-gate 	int err;
791*0Sstevel@tonic-gate 
792*0Sstevel@tonic-gate 	/* Driver handles only DEVMAP_ACCESS type of faults */
793*0Sstevel@tonic-gate 	if (type != DEVMAP_ACCESS)
794*0Sstevel@tonic-gate 		return (-1);
795*0Sstevel@tonic-gate 
796*0Sstevel@tonic-gate 	mutex_enter(&lp->mutex);
797*0Sstevel@tonic-gate 	ASSERT((dhp == sdp->lockseg) || (dhp == sdp->unlockseg));
798*0Sstevel@tonic-gate 
799*0Sstevel@tonic-gate 	/* should be using a SegProc that corresponds to current process */
800*0Sstevel@tonic-gate 	ASSERT(ID(sdp) == CURPROC_ID);
801*0Sstevel@tonic-gate 
802*0Sstevel@tonic-gate 	/*
803*0Sstevel@tonic-gate 	 * If process is faulting but does not have both segments mapped
804*0Sstevel@tonic-gate 	 * return error (should cause a segv).
805*0Sstevel@tonic-gate 	 * RFE: could give it a permanent trashpage
806*0Sstevel@tonic-gate 	 */
807*0Sstevel@tonic-gate 	if ((sdp->lockseg == NULL) || (sdp->unlockseg == NULL)) {
808*0Sstevel@tonic-gate 		err = -1;
809*0Sstevel@tonic-gate 	} else {
810*0Sstevel@tonic-gate 		err = seglock_lockfault(dhp, sdp, lp, rw);
811*0Sstevel@tonic-gate 	}
812*0Sstevel@tonic-gate 	mutex_exit(&lp->mutex);
813*0Sstevel@tonic-gate 	return (err);
814*0Sstevel@tonic-gate }
815*0Sstevel@tonic-gate 
816*0Sstevel@tonic-gate 	/* INTERNAL ROUTINES START HERE */
817*0Sstevel@tonic-gate 
818*0Sstevel@tonic-gate 
819*0Sstevel@tonic-gate 
820*0Sstevel@tonic-gate /*
821*0Sstevel@tonic-gate  * search the lock_list list for the specified cookie
822*0Sstevel@tonic-gate  * The cookie is the sy_ident field returns by ALLOC ioctl.
823*0Sstevel@tonic-gate  * This has two parts:
824*0Sstevel@tonic-gate  * the pageoffset bits contain offset into the lock page.
825*0Sstevel@tonic-gate  * the pagenumber bits contain the lock id.
826*0Sstevel@tonic-gate  * The user code is supposed to pass in only the pagenumber portion
827*0Sstevel@tonic-gate  *	(i.e. mask off the pageoffset bits). However the code below
828*0Sstevel@tonic-gate  *	does the mask in case the users are not diligent
829*0Sstevel@tonic-gate  * if found, returns with mutex for SegLock structure held
830*0Sstevel@tonic-gate  */
831*0Sstevel@tonic-gate static SegLock *
832*0Sstevel@tonic-gate seglock_findlock(uint_t cookie)
833*0Sstevel@tonic-gate {
834*0Sstevel@tonic-gate 	SegLock	*lp;
835*0Sstevel@tonic-gate 
836*0Sstevel@tonic-gate 	cookie &= (uint_t)PAGEMASK;   /* remove pageoffset bits to get cookie */
837*0Sstevel@tonic-gate 	mutex_enter(&winlock_mutex);
838*0Sstevel@tonic-gate 	for (lp = lock_list; lp != NULL; lp = lp->next) {
839*0Sstevel@tonic-gate 		mutex_enter(&lp->mutex);
840*0Sstevel@tonic-gate 		if (cookie == lp->cookie) {
841*0Sstevel@tonic-gate 			break;	/* return with lp->mutex held */
842*0Sstevel@tonic-gate 		}
843*0Sstevel@tonic-gate 		mutex_exit(&lp->mutex);
844*0Sstevel@tonic-gate 	}
845*0Sstevel@tonic-gate 	mutex_exit(&winlock_mutex);
846*0Sstevel@tonic-gate 	return (lp);
847*0Sstevel@tonic-gate }
848*0Sstevel@tonic-gate 
849*0Sstevel@tonic-gate /*
850*0Sstevel@tonic-gate  * search the lock_list list for the specified non-zero key
851*0Sstevel@tonic-gate  * if found, returns with lock for SegLock structure held
852*0Sstevel@tonic-gate  */
853*0Sstevel@tonic-gate static SegLock *
854*0Sstevel@tonic-gate seglock_findkey(uint_t key)
855*0Sstevel@tonic-gate {
856*0Sstevel@tonic-gate 	SegLock	*lp;
857*0Sstevel@tonic-gate 
858*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&winlock_mutex));
859*0Sstevel@tonic-gate 	/* The driver allows multiple locks with key 0, dont search */
860*0Sstevel@tonic-gate 	if (key == 0)
861*0Sstevel@tonic-gate 		return (NULL);
862*0Sstevel@tonic-gate 	for (lp = lock_list; lp != NULL; lp = lp->next) {
863*0Sstevel@tonic-gate 		mutex_enter(&lp->mutex);
864*0Sstevel@tonic-gate 		if (key == lp->key)
865*0Sstevel@tonic-gate 			break;
866*0Sstevel@tonic-gate 		mutex_exit(&lp->mutex);
867*0Sstevel@tonic-gate 	}
868*0Sstevel@tonic-gate 	return (lp);
869*0Sstevel@tonic-gate }
870*0Sstevel@tonic-gate 
871*0Sstevel@tonic-gate /*
872*0Sstevel@tonic-gate  * Create a new lock context.
873*0Sstevel@tonic-gate  * Returns with SegLock mutex held
874*0Sstevel@tonic-gate  */
875*0Sstevel@tonic-gate 
876*0Sstevel@tonic-gate static SegLock *
877*0Sstevel@tonic-gate seglock_createlock(enum winlock_style style)
878*0Sstevel@tonic-gate {
879*0Sstevel@tonic-gate 	SegLock	*lp;
880*0Sstevel@tonic-gate 
881*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, "seglock_createlock: free_list=%p, next_lock %d\n",
882*0Sstevel@tonic-gate 		(void *)lock_free_list, next_lock));
883*0Sstevel@tonic-gate 
884*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&winlock_mutex));
885*0Sstevel@tonic-gate 	if (lock_free_list != NULL) {
886*0Sstevel@tonic-gate 		lp = lock_free_list;
887*0Sstevel@tonic-gate 		lock_free_list = lp->next;
888*0Sstevel@tonic-gate 	} else if (next_lock >= MAX_LOCKS) {
889*0Sstevel@tonic-gate 		return (NULL);
890*0Sstevel@tonic-gate 	} else {
891*0Sstevel@tonic-gate 		lp = kmem_zalloc(sizeof (SegLock), KM_SLEEP);
892*0Sstevel@tonic-gate 		lp->cookie = (next_lock + 1) * (uint_t)PAGESIZE;
893*0Sstevel@tonic-gate 		mutex_init(&lp->mutex, NULL, MUTEX_DEFAULT, NULL);
894*0Sstevel@tonic-gate 		cv_init(&lp->locksleep, NULL, CV_DEFAULT, NULL);
895*0Sstevel@tonic-gate 		++next_lock;
896*0Sstevel@tonic-gate 	}
897*0Sstevel@tonic-gate 
898*0Sstevel@tonic-gate 	mutex_enter(&lp->mutex);
899*0Sstevel@tonic-gate 	ASSERT((lp->cookie/PAGESIZE) <= next_lock);
900*0Sstevel@tonic-gate 
901*0Sstevel@tonic-gate 	if (style == OLDSTYLE_LOCK) {
902*0Sstevel@tonic-gate 		lp->lockptr = (int *)ddi_umem_alloc(PAGESIZE,
903*0Sstevel@tonic-gate 			DDI_UMEM_SLEEP, &(lp->umem_cookie));
904*0Sstevel@tonic-gate 	} else {
905*0Sstevel@tonic-gate 		lp->lockptr = ((int *)lockpage) + ((lp->cookie/PAGESIZE) - 1);
906*0Sstevel@tonic-gate 		lp->umem_cookie = lockpage_cookie;
907*0Sstevel@tonic-gate 	}
908*0Sstevel@tonic-gate 
909*0Sstevel@tonic-gate 	ASSERT(lp->lockptr != NULL);
910*0Sstevel@tonic-gate 	lp->style = style;
911*0Sstevel@tonic-gate 	lp->sleepers = 0;
912*0Sstevel@tonic-gate 	lp->alloccount = 1;
913*0Sstevel@tonic-gate 	lp->timeout = LOCKTIME*hz;
914*0Sstevel@tonic-gate 	lp->clients = NULL;
915*0Sstevel@tonic-gate 	lp->owner = NULL;
916*0Sstevel@tonic-gate 	LOCK(lp) = 0;
917*0Sstevel@tonic-gate 	lp->next = lock_list;
918*0Sstevel@tonic-gate 	lock_list = lp;
919*0Sstevel@tonic-gate 	return (lp);
920*0Sstevel@tonic-gate }
921*0Sstevel@tonic-gate 
922*0Sstevel@tonic-gate /*
923*0Sstevel@tonic-gate  * Routine to destory a lock structure.
924*0Sstevel@tonic-gate  * This routine is called while holding the lp->mutex but not the
925*0Sstevel@tonic-gate  * winlock_mutex.
926*0Sstevel@tonic-gate  */
927*0Sstevel@tonic-gate 
928*0Sstevel@tonic-gate static void
929*0Sstevel@tonic-gate seglock_destroylock(SegLock *lp)
930*0Sstevel@tonic-gate {
931*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&lp->mutex));
932*0Sstevel@tonic-gate 	ASSERT(!MUTEX_HELD(&winlock_mutex));
933*0Sstevel@tonic-gate 
934*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, "destroying lock cookie %d key %d\n",
935*0Sstevel@tonic-gate 		lp->cookie, lp->key));
936*0Sstevel@tonic-gate 
937*0Sstevel@tonic-gate 	ASSERT(lp->alloccount == 0);
938*0Sstevel@tonic-gate 	ASSERT(lp->clients == NULL);
939*0Sstevel@tonic-gate 	ASSERT(lp->owner == NULL);
940*0Sstevel@tonic-gate 	ASSERT(lp->sleepers == 0);
941*0Sstevel@tonic-gate 
942*0Sstevel@tonic-gate 	/* clean up/release fields in lp */
943*0Sstevel@tonic-gate 	if (lp->style == OLDSTYLE_LOCK) {
944*0Sstevel@tonic-gate 		ddi_umem_free(lp->umem_cookie);
945*0Sstevel@tonic-gate 	}
946*0Sstevel@tonic-gate 	lp->umem_cookie = NULL;
947*0Sstevel@tonic-gate 	lp->lockptr = NULL;
948*0Sstevel@tonic-gate 	lp->key = 0;
949*0Sstevel@tonic-gate 
950*0Sstevel@tonic-gate 	/*
951*0Sstevel@tonic-gate 	 * Reduce cookie by 1, makes it non page-aligned and invalid
952*0Sstevel@tonic-gate 	 * This prevents any valid lookup from finding this lock
953*0Sstevel@tonic-gate 	 * so when we drop the lock and regrab it it will still
954*0Sstevel@tonic-gate 	 * be there and nobody else would have attached to it
955*0Sstevel@tonic-gate 	 */
956*0Sstevel@tonic-gate 	lp->cookie--;
957*0Sstevel@tonic-gate 
958*0Sstevel@tonic-gate 	/* Drop and reacquire mutexes in right order */
959*0Sstevel@tonic-gate 	mutex_exit(&lp->mutex);
960*0Sstevel@tonic-gate 	mutex_enter(&winlock_mutex);
961*0Sstevel@tonic-gate 	mutex_enter(&lp->mutex);
962*0Sstevel@tonic-gate 
963*0Sstevel@tonic-gate 	/* reincrement the cookie to get the original valid cookie */
964*0Sstevel@tonic-gate 	lp->cookie++;
965*0Sstevel@tonic-gate 	ASSERT((lp->cookie & PAGEOFFSET) == 0);
966*0Sstevel@tonic-gate 	ASSERT(lp->alloccount == 0);
967*0Sstevel@tonic-gate 	ASSERT(lp->clients == NULL);
968*0Sstevel@tonic-gate 	ASSERT(lp->owner == NULL);
969*0Sstevel@tonic-gate 	ASSERT(lp->sleepers == 0);
970*0Sstevel@tonic-gate 
971*0Sstevel@tonic-gate 	/* Remove lp from lock_list */
972*0Sstevel@tonic-gate 	if (lock_list == lp) {
973*0Sstevel@tonic-gate 		lock_list = lp->next;
974*0Sstevel@tonic-gate 	} else {
975*0Sstevel@tonic-gate 		SegLock *tmp = lock_list;
976*0Sstevel@tonic-gate 		while (tmp->next != lp) {
977*0Sstevel@tonic-gate 			tmp = tmp->next;
978*0Sstevel@tonic-gate 			ASSERT(tmp != NULL);
979*0Sstevel@tonic-gate 		}
980*0Sstevel@tonic-gate 		tmp->next = lp->next;
981*0Sstevel@tonic-gate 	}
982*0Sstevel@tonic-gate 
983*0Sstevel@tonic-gate 	/* Add to lock_free_list */
984*0Sstevel@tonic-gate 	lp->next = lock_free_list;
985*0Sstevel@tonic-gate 	lock_free_list = lp;
986*0Sstevel@tonic-gate 	mutex_exit(&lp->mutex);
987*0Sstevel@tonic-gate 
988*0Sstevel@tonic-gate 	/* Check if all locks deleted and cleanup */
989*0Sstevel@tonic-gate 	if (lock_list == NULL) {
990*0Sstevel@tonic-gate 		lock_destroyall();
991*0Sstevel@tonic-gate 	}
992*0Sstevel@tonic-gate 
993*0Sstevel@tonic-gate 	mutex_exit(&winlock_mutex);
994*0Sstevel@tonic-gate }
995*0Sstevel@tonic-gate 
996*0Sstevel@tonic-gate /* Routine to find a SegProc corresponding to the tag */
997*0Sstevel@tonic-gate 
998*0Sstevel@tonic-gate static SegProc *
999*0Sstevel@tonic-gate seglock_find_specific(SegLock *lp, void *tag)
1000*0Sstevel@tonic-gate {
1001*0Sstevel@tonic-gate 	SegProc *sdp;
1002*0Sstevel@tonic-gate 
1003*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&lp->mutex));
1004*0Sstevel@tonic-gate 	ASSERT(tag != NULL);
1005*0Sstevel@tonic-gate 	for (sdp = lp->clients; sdp != NULL; sdp = sdp->next) {
1006*0Sstevel@tonic-gate 		if (ID(sdp) == tag)
1007*0Sstevel@tonic-gate 			break;
1008*0Sstevel@tonic-gate 	}
1009*0Sstevel@tonic-gate 	return (sdp);
1010*0Sstevel@tonic-gate }
1011*0Sstevel@tonic-gate 
1012*0Sstevel@tonic-gate /* Routine to find (and if needed allocate) a SegProc corresponding to tag */
1013*0Sstevel@tonic-gate 
1014*0Sstevel@tonic-gate static SegProc *
1015*0Sstevel@tonic-gate seglock_alloc_specific(SegLock *lp, void *tag)
1016*0Sstevel@tonic-gate {
1017*0Sstevel@tonic-gate 	SegProc *sdp;
1018*0Sstevel@tonic-gate 
1019*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&lp->mutex));
1020*0Sstevel@tonic-gate 	ASSERT(tag != NULL);
1021*0Sstevel@tonic-gate 
1022*0Sstevel@tonic-gate 	/* Search and return if existing one found */
1023*0Sstevel@tonic-gate 	sdp = seglock_find_specific(lp, tag);
1024*0Sstevel@tonic-gate 	if (sdp != NULL)
1025*0Sstevel@tonic-gate 		return (sdp);
1026*0Sstevel@tonic-gate 
1027*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, "Allocating segproc structure for tag %p lock %d\n",
1028*0Sstevel@tonic-gate 		    tag, lp->cookie));
1029*0Sstevel@tonic-gate 
1030*0Sstevel@tonic-gate 	/* Allocate a new SegProc */
1031*0Sstevel@tonic-gate 	sdp = kmem_zalloc(sizeof (SegProc), KM_SLEEP);
1032*0Sstevel@tonic-gate 	sdp->next = lp->clients;
1033*0Sstevel@tonic-gate 	lp->clients = sdp;
1034*0Sstevel@tonic-gate 	sdp->lp = lp;
1035*0Sstevel@tonic-gate 	ID(sdp) = tag;
1036*0Sstevel@tonic-gate 	return (sdp);
1037*0Sstevel@tonic-gate }
1038*0Sstevel@tonic-gate 
1039*0Sstevel@tonic-gate /*
1040*0Sstevel@tonic-gate  * search a context's client list for the given client and delete
1041*0Sstevel@tonic-gate  */
1042*0Sstevel@tonic-gate 
1043*0Sstevel@tonic-gate static void
1044*0Sstevel@tonic-gate seglock_deleteclient(SegLock *lp, SegProc *sdp)
1045*0Sstevel@tonic-gate {
1046*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&lp->mutex));
1047*0Sstevel@tonic-gate 	ASSERT(lp->owner != sdp);	/* Not current owner of lock */
1048*0Sstevel@tonic-gate 	ASSERT(sdp->lockseg == NULL);	/* Mappings torn down */
1049*0Sstevel@tonic-gate 	ASSERT(sdp->unlockseg == NULL);
1050*0Sstevel@tonic-gate 
1051*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, "Deleting segproc structure for pid %d lock %d\n",
1052*0Sstevel@tonic-gate 		ddi_get_pid(), lp->cookie));
1053*0Sstevel@tonic-gate 	if (lp->clients == sdp) {
1054*0Sstevel@tonic-gate 		lp->clients = sdp->next;
1055*0Sstevel@tonic-gate 	} else {
1056*0Sstevel@tonic-gate 		SegProc *tmp = lp->clients;
1057*0Sstevel@tonic-gate 		while (tmp->next != sdp) {
1058*0Sstevel@tonic-gate 			tmp = tmp->next;
1059*0Sstevel@tonic-gate 			ASSERT(tmp != NULL);
1060*0Sstevel@tonic-gate 		}
1061*0Sstevel@tonic-gate 		tmp->next = sdp->next;
1062*0Sstevel@tonic-gate 	}
1063*0Sstevel@tonic-gate 	kmem_free(sdp, sizeof (SegProc));
1064*0Sstevel@tonic-gate }
1065*0Sstevel@tonic-gate 
1066*0Sstevel@tonic-gate /*
1067*0Sstevel@tonic-gate  * Routine to verify if a SegProc and SegLock
1068*0Sstevel@tonic-gate  * structures are empty/idle.
1069*0Sstevel@tonic-gate  * Destroys the structures if they are ready
1070*0Sstevel@tonic-gate  * Can be called with sdp == NULL if want to verify only the lock state
1071*0Sstevel@tonic-gate  * caller should hold the lp->mutex
1072*0Sstevel@tonic-gate  * and this routine drops the mutex
1073*0Sstevel@tonic-gate  */
1074*0Sstevel@tonic-gate static void
1075*0Sstevel@tonic-gate garbage_collect_lock(SegLock *lp, SegProc *sdp)
1076*0Sstevel@tonic-gate {
1077*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&lp->mutex));
1078*0Sstevel@tonic-gate 	/* see if both segments unmapped from client structure */
1079*0Sstevel@tonic-gate 	if ((sdp != NULL) && (sdp->lockseg == NULL) && (sdp->unlockseg == NULL))
1080*0Sstevel@tonic-gate 		seglock_deleteclient(lp, sdp);
1081*0Sstevel@tonic-gate 
1082*0Sstevel@tonic-gate 	/* see if this is last client in the entire lock context */
1083*0Sstevel@tonic-gate 	if ((lp->clients == NULL) && (lp->alloccount == 0)) {
1084*0Sstevel@tonic-gate 		seglock_destroylock(lp);
1085*0Sstevel@tonic-gate 	} else {
1086*0Sstevel@tonic-gate 		mutex_exit(&lp->mutex);
1087*0Sstevel@tonic-gate 	}
1088*0Sstevel@tonic-gate }
1089*0Sstevel@tonic-gate 
1090*0Sstevel@tonic-gate 
1091*0Sstevel@tonic-gate /* IOCTLS START HERE */
1092*0Sstevel@tonic-gate 
1093*0Sstevel@tonic-gate static int
1094*0Sstevel@tonic-gate seglock_grabinfo(intptr_t arg, int mode)
1095*0Sstevel@tonic-gate {
1096*0Sstevel@tonic-gate 	int i = 1;
1097*0Sstevel@tonic-gate 
1098*0Sstevel@tonic-gate 	/* multiple clients per lock supported - see comments up top */
1099*0Sstevel@tonic-gate 	if (ddi_copyout((caddr_t)&i, (caddr_t)arg, sizeof (int), mode) != 0)
1100*0Sstevel@tonic-gate 		return (EFAULT);
1101*0Sstevel@tonic-gate 	return (0);
1102*0Sstevel@tonic-gate }
1103*0Sstevel@tonic-gate 
1104*0Sstevel@tonic-gate static int
1105*0Sstevel@tonic-gate seglock_graballoc(intptr_t arg, enum winlock_style style, int mode) /* IOCTL */
1106*0Sstevel@tonic-gate {
1107*0Sstevel@tonic-gate 	struct seglock	*lp;
1108*0Sstevel@tonic-gate 	uint_t		key;
1109*0Sstevel@tonic-gate 	struct		winlockalloc wla;
1110*0Sstevel@tonic-gate 	int		err;
1111*0Sstevel@tonic-gate 
1112*0Sstevel@tonic-gate 	if (style == OLDSTYLE_LOCK) {
1113*0Sstevel@tonic-gate 		key = 0;
1114*0Sstevel@tonic-gate 	} else {
1115*0Sstevel@tonic-gate 		if (ddi_copyin((caddr_t)arg, (caddr_t)&wla, sizeof (wla),
1116*0Sstevel@tonic-gate 		    mode)) {
1117*0Sstevel@tonic-gate 			return (EFAULT);
1118*0Sstevel@tonic-gate 		}
1119*0Sstevel@tonic-gate 		key = wla.sy_key;
1120*0Sstevel@tonic-gate 	}
1121*0Sstevel@tonic-gate 
1122*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT,
1123*0Sstevel@tonic-gate 		"seglock_graballoc: key=%u, style=%d\n", key, style));
1124*0Sstevel@tonic-gate 
1125*0Sstevel@tonic-gate 	mutex_enter(&winlock_mutex);
1126*0Sstevel@tonic-gate 	/* Allocate lockpage on first new style alloc */
1127*0Sstevel@tonic-gate 	if ((lockpage == NULL) && (style == NEWSTYLE_LOCK)) {
1128*0Sstevel@tonic-gate 		lockpage = ddi_umem_alloc(PAGESIZE, DDI_UMEM_SLEEP,
1129*0Sstevel@tonic-gate 				&lockpage_cookie);
1130*0Sstevel@tonic-gate 	}
1131*0Sstevel@tonic-gate 
1132*0Sstevel@tonic-gate 	/* Allocate trashpage on first alloc (any style) */
1133*0Sstevel@tonic-gate 	if (trashpage_cookie == NULL) {
1134*0Sstevel@tonic-gate 		(void) ddi_umem_alloc(PAGESIZE, DDI_UMEM_TRASH | DDI_UMEM_SLEEP,
1135*0Sstevel@tonic-gate 					&trashpage_cookie);
1136*0Sstevel@tonic-gate 	}
1137*0Sstevel@tonic-gate 
1138*0Sstevel@tonic-gate 	if ((lp = seglock_findkey(key)) != NULL) {
1139*0Sstevel@tonic-gate 		DEBUGF(2, (CE_CONT, "alloc: found lock key %d cookie %d\n",
1140*0Sstevel@tonic-gate 			key, lp->cookie));
1141*0Sstevel@tonic-gate 		++lp->alloccount;
1142*0Sstevel@tonic-gate 	} else if ((lp = seglock_createlock(style)) != NULL) {
1143*0Sstevel@tonic-gate 		DEBUGF(2, (CE_CONT, "alloc: created lock key %d cookie %d\n",
1144*0Sstevel@tonic-gate 			key, lp->cookie));
1145*0Sstevel@tonic-gate 		lp->key = key;
1146*0Sstevel@tonic-gate 	} else {
1147*0Sstevel@tonic-gate 		DEBUGF(2, (CE_CONT, "alloc: cannot create lock key %d\n", key));
1148*0Sstevel@tonic-gate 		mutex_exit(&winlock_mutex);
1149*0Sstevel@tonic-gate 		return (ENOMEM);
1150*0Sstevel@tonic-gate 	}
1151*0Sstevel@tonic-gate 	ASSERT((lp != NULL) && MUTEX_HELD(&lp->mutex));
1152*0Sstevel@tonic-gate 
1153*0Sstevel@tonic-gate 	mutex_exit(&winlock_mutex);
1154*0Sstevel@tonic-gate 
1155*0Sstevel@tonic-gate 	if (style == OLDSTYLE_LOCK) {
1156*0Sstevel@tonic-gate 		err = ddi_copyout((caddr_t)&lp->cookie, (caddr_t)arg,
1157*0Sstevel@tonic-gate 			sizeof (lp->cookie), mode);
1158*0Sstevel@tonic-gate 	} else {
1159*0Sstevel@tonic-gate 		wla.sy_ident = lp->cookie +
1160*0Sstevel@tonic-gate 		    (uint_t)((uintptr_t)(lp->lockptr) & PAGEOFFSET);
1161*0Sstevel@tonic-gate 		err = ddi_copyout((caddr_t)&wla, (caddr_t)arg,
1162*0Sstevel@tonic-gate 		    sizeof (wla), mode);
1163*0Sstevel@tonic-gate 	}
1164*0Sstevel@tonic-gate 
1165*0Sstevel@tonic-gate 	if (err) {
1166*0Sstevel@tonic-gate 		/* On error, should undo allocation */
1167*0Sstevel@tonic-gate 		lp->alloccount--;
1168*0Sstevel@tonic-gate 
1169*0Sstevel@tonic-gate 		/* Verify and delete if lock is unused now */
1170*0Sstevel@tonic-gate 		garbage_collect_lock(lp, NULL);
1171*0Sstevel@tonic-gate 		return (EFAULT);
1172*0Sstevel@tonic-gate 	}
1173*0Sstevel@tonic-gate 
1174*0Sstevel@tonic-gate 	mutex_exit(&lp->mutex);
1175*0Sstevel@tonic-gate 	return (0);
1176*0Sstevel@tonic-gate }
1177*0Sstevel@tonic-gate 
1178*0Sstevel@tonic-gate static int
1179*0Sstevel@tonic-gate seglock_grabfree(intptr_t arg, int mode)	/* IOCTL */
1180*0Sstevel@tonic-gate {
1181*0Sstevel@tonic-gate 	struct seglock	*lp;
1182*0Sstevel@tonic-gate 	uint_t	offset;
1183*0Sstevel@tonic-gate 
1184*0Sstevel@tonic-gate 	if (ddi_copyin((caddr_t)arg, &offset, sizeof (offset), mode)
1185*0Sstevel@tonic-gate 	    != 0) {
1186*0Sstevel@tonic-gate 		return (EFAULT);
1187*0Sstevel@tonic-gate 	}
1188*0Sstevel@tonic-gate 	DEBUGF(2, (CE_CONT, "seglock_grabfree: offset=%u", offset));
1189*0Sstevel@tonic-gate 
1190*0Sstevel@tonic-gate 	if ((lp = seglock_findlock(offset)) == NULL) {
1191*0Sstevel@tonic-gate 		DEBUGF(2, (CE_CONT, "did not find lock\n"));
1192*0Sstevel@tonic-gate 		return (EINVAL);
1193*0Sstevel@tonic-gate 	}
1194*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT, " lock key %d, cookie %d, alloccount %d\n",
1195*0Sstevel@tonic-gate 		lp->key, lp->cookie, lp->alloccount));
1196*0Sstevel@tonic-gate 
1197*0Sstevel@tonic-gate 	if (lp->alloccount > 0)
1198*0Sstevel@tonic-gate 		lp->alloccount--;
1199*0Sstevel@tonic-gate 
1200*0Sstevel@tonic-gate 	/* Verify and delete if lock is unused now */
1201*0Sstevel@tonic-gate 	garbage_collect_lock(lp, NULL);
1202*0Sstevel@tonic-gate 	return (0);
1203*0Sstevel@tonic-gate }
1204*0Sstevel@tonic-gate 
1205*0Sstevel@tonic-gate 
1206*0Sstevel@tonic-gate /*
1207*0Sstevel@tonic-gate  * Sets timeout in lock and UFLAGS in client
1208*0Sstevel@tonic-gate  *	the UFLAGS are stored in the client structure and persistent only
1209*0Sstevel@tonic-gate  *	till the unmap of the lock pages. If the process sets UFLAGS
1210*0Sstevel@tonic-gate  *	does a map of the lock/unlock pages and unmaps them, the client
1211*0Sstevel@tonic-gate  *	structure will get deleted and the UFLAGS will be lost. The process
1212*0Sstevel@tonic-gate  *	will need to resetup the flags.
1213*0Sstevel@tonic-gate  */
1214*0Sstevel@tonic-gate static int
1215*0Sstevel@tonic-gate seglock_settimeout(intptr_t arg, int mode)	/* IOCTL */
1216*0Sstevel@tonic-gate {
1217*0Sstevel@tonic-gate 	SegLock		*lp;
1218*0Sstevel@tonic-gate 	SegProc		*sdp;
1219*0Sstevel@tonic-gate 	struct winlocktimeout		wlt;
1220*0Sstevel@tonic-gate 
1221*0Sstevel@tonic-gate 	if (ddi_copyin((caddr_t)arg, &wlt, sizeof (wlt), mode) != 0) {
1222*0Sstevel@tonic-gate 		return (EFAULT);
1223*0Sstevel@tonic-gate 	}
1224*0Sstevel@tonic-gate 
1225*0Sstevel@tonic-gate 	if ((lp = seglock_findlock(wlt.sy_ident)) == NULL)
1226*0Sstevel@tonic-gate 		return (EINVAL);
1227*0Sstevel@tonic-gate 
1228*0Sstevel@tonic-gate 	lp->timeout = MSEC_TO_TICK_ROUNDUP(wlt.sy_timeout);
1229*0Sstevel@tonic-gate 	/* if timeout modified, wake up any sleepers */
1230*0Sstevel@tonic-gate 	if (lp->sleepers > 0) {
1231*0Sstevel@tonic-gate 		cv_broadcast(&lp->locksleep);
1232*0Sstevel@tonic-gate 	}
1233*0Sstevel@tonic-gate 
1234*0Sstevel@tonic-gate 	/*
1235*0Sstevel@tonic-gate 	 * If the process is trying to set UFLAGS,
1236*0Sstevel@tonic-gate 	 *	Find the client segproc and allocate one if needed
1237*0Sstevel@tonic-gate 	 *	Set the flags preserving the kernel flags
1238*0Sstevel@tonic-gate 	 * If the process is clearing UFLAGS
1239*0Sstevel@tonic-gate 	 *	Find the client segproc but dont allocate one if does not exist
1240*0Sstevel@tonic-gate 	 */
1241*0Sstevel@tonic-gate 	if (wlt.sy_flags & UFLAGS) {
1242*0Sstevel@tonic-gate 		sdp = seglock_allocclient(lp);
1243*0Sstevel@tonic-gate 		sdp->flag = sdp->flag & KFLAGS | wlt.sy_flags & UFLAGS;
1244*0Sstevel@tonic-gate 	} else if ((sdp = seglock_findclient(lp)) != NULL) {
1245*0Sstevel@tonic-gate 		sdp->flag = sdp->flag & KFLAGS;
1246*0Sstevel@tonic-gate 		/* If clearing UFLAGS leaves the segment or lock idle, delete */
1247*0Sstevel@tonic-gate 		garbage_collect_lock(lp, sdp);
1248*0Sstevel@tonic-gate 		return (0);
1249*0Sstevel@tonic-gate 	}
1250*0Sstevel@tonic-gate 	mutex_exit(&lp->mutex);	/* mutex held by seglock_findlock */
1251*0Sstevel@tonic-gate 	return (0);
1252*0Sstevel@tonic-gate }
1253*0Sstevel@tonic-gate 
1254*0Sstevel@tonic-gate static int
1255*0Sstevel@tonic-gate seglock_gettimeout(intptr_t arg, int mode)
1256*0Sstevel@tonic-gate {
1257*0Sstevel@tonic-gate 	SegLock		*lp;
1258*0Sstevel@tonic-gate 	SegProc		*sdp;
1259*0Sstevel@tonic-gate 	struct winlocktimeout		wlt;
1260*0Sstevel@tonic-gate 
1261*0Sstevel@tonic-gate 	if (ddi_copyin((caddr_t)arg, &wlt, sizeof (wlt), mode) != 0)
1262*0Sstevel@tonic-gate 		return (EFAULT);
1263*0Sstevel@tonic-gate 
1264*0Sstevel@tonic-gate 	if ((lp = seglock_findlock(wlt.sy_ident)) == NULL)
1265*0Sstevel@tonic-gate 		return (EINVAL);
1266*0Sstevel@tonic-gate 
1267*0Sstevel@tonic-gate 	wlt.sy_timeout = TICK_TO_MSEC(lp->timeout);
1268*0Sstevel@tonic-gate 	/*
1269*0Sstevel@tonic-gate 	 * If this process has an active allocated lock return those flags
1270*0Sstevel@tonic-gate 	 *	Dont allocate a client structure on gettimeout
1271*0Sstevel@tonic-gate 	 * If not, return 0.
1272*0Sstevel@tonic-gate 	 */
1273*0Sstevel@tonic-gate 	if ((sdp = seglock_findclient(lp)) != NULL) {
1274*0Sstevel@tonic-gate 		wlt.sy_flags = sdp->flag & UFLAGS;
1275*0Sstevel@tonic-gate 	} else {
1276*0Sstevel@tonic-gate 		wlt.sy_flags = 0;
1277*0Sstevel@tonic-gate 	}
1278*0Sstevel@tonic-gate 	mutex_exit(&lp->mutex);	/* mutex held by seglock_findlock */
1279*0Sstevel@tonic-gate 
1280*0Sstevel@tonic-gate 	if (ddi_copyout(&wlt, (caddr_t)arg, sizeof (wlt), mode) != 0)
1281*0Sstevel@tonic-gate 		return (EFAULT);
1282*0Sstevel@tonic-gate 
1283*0Sstevel@tonic-gate 	return (0);
1284*0Sstevel@tonic-gate }
1285*0Sstevel@tonic-gate 
1286*0Sstevel@tonic-gate /*
1287*0Sstevel@tonic-gate  * Handle lock segment faults here...
1288*0Sstevel@tonic-gate  *
1289*0Sstevel@tonic-gate  * This is where the magic happens.
1290*0Sstevel@tonic-gate  */
1291*0Sstevel@tonic-gate 
1292*0Sstevel@tonic-gate /* ARGSUSED */
1293*0Sstevel@tonic-gate static	int
1294*0Sstevel@tonic-gate seglock_lockfault(devmap_cookie_t dhp, SegProc *sdp, SegLock *lp, uint_t rw)
1295*0Sstevel@tonic-gate {
1296*0Sstevel@tonic-gate 	SegProc *owner = lp->owner;
1297*0Sstevel@tonic-gate 	int err;
1298*0Sstevel@tonic-gate 
1299*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&lp->mutex));
1300*0Sstevel@tonic-gate 	DEBUGF(3, (CE_CONT,
1301*0Sstevel@tonic-gate 		"seglock_lockfault: hdl=%p, sdp=%p, lp=%p owner=%p\n",
1302*0Sstevel@tonic-gate 		(void *)dhp, (void *)sdp, (void *)lp, (void *)owner));
1303*0Sstevel@tonic-gate 
1304*0Sstevel@tonic-gate 	/* lockfault is always called with sdp in current process context */
1305*0Sstevel@tonic-gate 	ASSERT(ID(sdp) == CURPROC_ID);
1306*0Sstevel@tonic-gate 
1307*0Sstevel@tonic-gate 	/* If Lock has no current owner, give the mapping to new owner */
1308*0Sstevel@tonic-gate 	if (owner == NULL) {
1309*0Sstevel@tonic-gate 		DEBUGF(4, (CE_CONT, " lock has no current owner\n"));
1310*0Sstevel@tonic-gate 		return (give_mapping(lp, sdp, rw));
1311*0Sstevel@tonic-gate 	}
1312*0Sstevel@tonic-gate 
1313*0Sstevel@tonic-gate 	if (owner == sdp) {
1314*0Sstevel@tonic-gate 		/*
1315*0Sstevel@tonic-gate 		 * Current owner is faulting on owned lock segment OR
1316*0Sstevel@tonic-gate 		 * Current owner is faulting on unlock page and has no waiters
1317*0Sstevel@tonic-gate 		 * Then can give the mapping to current owner
1318*0Sstevel@tonic-gate 		 */
1319*0Sstevel@tonic-gate 	    if ((sdp->lockseg == dhp) || (lp->sleepers == 0)) {
1320*0Sstevel@tonic-gate 		DEBUGF(4, (CE_CONT, "lock owner faulting\n"));
1321*0Sstevel@tonic-gate 		return (give_mapping(lp, sdp, rw));
1322*0Sstevel@tonic-gate 	    } else {
1323*0Sstevel@tonic-gate 		/*
1324*0Sstevel@tonic-gate 		 * Owner must be writing to unlock page and there are waiters.
1325*0Sstevel@tonic-gate 		 * other cases have been checked earlier.
1326*0Sstevel@tonic-gate 		 * Release the lock, owner, and owners mappings
1327*0Sstevel@tonic-gate 		 * As the owner is trying to write to the unlock page, leave
1328*0Sstevel@tonic-gate 		 * it with a trashpage mapping and wake up the sleepers
1329*0Sstevel@tonic-gate 		 */
1330*0Sstevel@tonic-gate 		ASSERT((dhp == sdp->unlockseg) && (lp->sleepers != 0));
1331*0Sstevel@tonic-gate 		DEBUGF(4, (CE_CONT, " owner fault on unlock seg w/ sleeper\n"));
1332*0Sstevel@tonic-gate 		return (lock_giveup(lp, 1));
1333*0Sstevel@tonic-gate 	    }
1334*0Sstevel@tonic-gate 	}
1335*0Sstevel@tonic-gate 
1336*0Sstevel@tonic-gate 	ASSERT(owner != sdp);
1337*0Sstevel@tonic-gate 
1338*0Sstevel@tonic-gate 	/*
1339*0Sstevel@tonic-gate 	 * If old owner faulting on trash unlock mapping,
1340*0Sstevel@tonic-gate 	 * load hat mappings to trash page
1341*0Sstevel@tonic-gate 	 * RFE: non-owners should NOT be faulting on unlock mapping as they
1342*0Sstevel@tonic-gate 	 * as first supposed to fault on the lock seg. We could give them
1343*0Sstevel@tonic-gate 	 * a trash page or return error.
1344*0Sstevel@tonic-gate 	 */
1345*0Sstevel@tonic-gate 	if ((sdp->unlockseg == dhp) && (sdp->flag & TRASHPAGE)) {
1346*0Sstevel@tonic-gate 		DEBUGF(4, (CE_CONT, " old owner reloads trash mapping\n"));
1347*0Sstevel@tonic-gate 		return (devmap_load(sdp->unlockseg, lp->cookie, PAGESIZE,
1348*0Sstevel@tonic-gate 			DEVMAP_ACCESS, rw));
1349*0Sstevel@tonic-gate 	}
1350*0Sstevel@tonic-gate 
1351*0Sstevel@tonic-gate 	/*
1352*0Sstevel@tonic-gate 	 * Non-owner faulting. Need to check current LOCK state.
1353*0Sstevel@tonic-gate 	 *
1354*0Sstevel@tonic-gate 	 * Before reading lock value in LOCK(lp), we must make sure that
1355*0Sstevel@tonic-gate 	 * the owner cannot change its value before we change mappings
1356*0Sstevel@tonic-gate 	 * or else we could end up either with a hung process
1357*0Sstevel@tonic-gate 	 * or more than one process thinking they have the lock.
1358*0Sstevel@tonic-gate 	 * We do that by unloading the owner's mappings
1359*0Sstevel@tonic-gate 	 */
1360*0Sstevel@tonic-gate 	DEBUGF(4, (CE_CONT, " owner loses mappings to check lock state\n"));
1361*0Sstevel@tonic-gate 	err = devmap_unload(owner->lockseg, lp->cookie, PAGESIZE);
1362*0Sstevel@tonic-gate 	err |= devmap_unload(owner->unlockseg, lp->cookie, PAGESIZE);
1363*0Sstevel@tonic-gate 	if (err != 0)
1364*0Sstevel@tonic-gate 		return (err);	/* unable to remove owner mapping */
1365*0Sstevel@tonic-gate 
1366*0Sstevel@tonic-gate 	/*
1367*0Sstevel@tonic-gate 	 * If lock is not held, then current owner mappings were
1368*0Sstevel@tonic-gate 	 * unloaded above and we can give the lock to the new owner
1369*0Sstevel@tonic-gate 	 */
1370*0Sstevel@tonic-gate 	if (LOCK(lp) == 0) {
1371*0Sstevel@tonic-gate 		DEBUGF(4, (CE_CONT,
1372*0Sstevel@tonic-gate 			"Free lock (%p): Giving mapping to new owner %d\n",
1373*0Sstevel@tonic-gate 			(void *)lp, ddi_get_pid()));
1374*0Sstevel@tonic-gate 		return (give_mapping(lp, sdp, rw));
1375*0Sstevel@tonic-gate 	}
1376*0Sstevel@tonic-gate 
1377*0Sstevel@tonic-gate 	DEBUGF(4, (CE_CONT, "  lock held, sleeping\n"));
1378*0Sstevel@tonic-gate 
1379*0Sstevel@tonic-gate 	/*
1380*0Sstevel@tonic-gate 	 * A non-owning process tried to write (presumably to the lockpage,
1381*0Sstevel@tonic-gate 	 * but it doesn't matter) but the lock is held; we need to sleep for
1382*0Sstevel@tonic-gate 	 * the lock while there is an owner.
1383*0Sstevel@tonic-gate 	 */
1384*0Sstevel@tonic-gate 
1385*0Sstevel@tonic-gate 	lp->sleepers++;
1386*0Sstevel@tonic-gate 	while ((owner = lp->owner) != NULL) {
1387*0Sstevel@tonic-gate 		int rval;
1388*0Sstevel@tonic-gate 
1389*0Sstevel@tonic-gate 		if ((lp->timeout == 0) || (owner->flag & SY_NOTIMEOUT)) {
1390*0Sstevel@tonic-gate 			/*
1391*0Sstevel@tonic-gate 			 * No timeout has been specified for this lock;
1392*0Sstevel@tonic-gate 			 * we'll simply sleep on the condition variable.
1393*0Sstevel@tonic-gate 			 */
1394*0Sstevel@tonic-gate 			rval = cv_wait_sig(&lp->locksleep, &lp->mutex);
1395*0Sstevel@tonic-gate 		} else {
1396*0Sstevel@tonic-gate 			/*
1397*0Sstevel@tonic-gate 			 * A timeout _has_ been specified for this lock. We need
1398*0Sstevel@tonic-gate 			 * to wake up and possibly steal this lock if the owner
1399*0Sstevel@tonic-gate 			 * does not let it go. Note that all sleepers on a lock
1400*0Sstevel@tonic-gate 			 * with a timeout wait; the sleeper with the earliest
1401*0Sstevel@tonic-gate 			 * timeout will wakeup, and potentially steal the lock
1402*0Sstevel@tonic-gate 			 * Stealing the lock will cause a broadcast on the
1403*0Sstevel@tonic-gate 			 * locksleep cv and thus kick the other timed waiters
1404*0Sstevel@tonic-gate 			 * and cause everyone to restart in a new timedwait
1405*0Sstevel@tonic-gate 			 */
1406*0Sstevel@tonic-gate 			rval = cv_timedwait_sig(&lp->locksleep,
1407*0Sstevel@tonic-gate 			    &lp->mutex, ddi_get_lbolt() + lp->timeout);
1408*0Sstevel@tonic-gate 		}
1409*0Sstevel@tonic-gate 
1410*0Sstevel@tonic-gate 		/*
1411*0Sstevel@tonic-gate 		 * Timeout and still old owner - steal lock
1412*0Sstevel@tonic-gate 		 * Force-Release lock and give old owner a trashpage mapping
1413*0Sstevel@tonic-gate 		 */
1414*0Sstevel@tonic-gate 		if ((rval == -1) && (lp->owner == owner)) {
1415*0Sstevel@tonic-gate 			/*
1416*0Sstevel@tonic-gate 			 * if any errors in lock_giveup, go back and sleep/retry
1417*0Sstevel@tonic-gate 			 * If successful, will break out of loop
1418*0Sstevel@tonic-gate 			 */
1419*0Sstevel@tonic-gate 			cmn_err(CE_NOTE, "Process %d timed out on lock %d\n",
1420*0Sstevel@tonic-gate 				ddi_get_pid(), lp->cookie);
1421*0Sstevel@tonic-gate 			(void) lock_giveup(lp, 1);
1422*0Sstevel@tonic-gate 		} else if (rval == 0) { /* signal pending */
1423*0Sstevel@tonic-gate 			cmn_err(CE_NOTE,
1424*0Sstevel@tonic-gate 			    "Process %d signalled while waiting on lock %d\n",
1425*0Sstevel@tonic-gate 			    ddi_get_pid(), lp->cookie);
1426*0Sstevel@tonic-gate 			lp->sleepers--;
1427*0Sstevel@tonic-gate 			return (FC_MAKE_ERR(EINTR));
1428*0Sstevel@tonic-gate 		}
1429*0Sstevel@tonic-gate 	}
1430*0Sstevel@tonic-gate 
1431*0Sstevel@tonic-gate 	lp->sleepers--;
1432*0Sstevel@tonic-gate 	/*
1433*0Sstevel@tonic-gate 	 * Give mapping to this process and save a fault later
1434*0Sstevel@tonic-gate 	 */
1435*0Sstevel@tonic-gate 	return (give_mapping(lp, sdp, rw));
1436*0Sstevel@tonic-gate }
1437*0Sstevel@tonic-gate 
1438*0Sstevel@tonic-gate /*
1439*0Sstevel@tonic-gate  * Utility: give a valid mapping to lock and unlock pages to current process.
1440*0Sstevel@tonic-gate  * Caller responsible for unloading old owner's mappings
1441*0Sstevel@tonic-gate  */
1442*0Sstevel@tonic-gate 
1443*0Sstevel@tonic-gate static int
1444*0Sstevel@tonic-gate give_mapping(SegLock *lp, SegProc *sdp, uint_t rw)
1445*0Sstevel@tonic-gate {
1446*0Sstevel@tonic-gate 	int err = 0;
1447*0Sstevel@tonic-gate 
1448*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&lp->mutex));
1449*0Sstevel@tonic-gate 	ASSERT(!((lp->owner == NULL) && (LOCK(lp) != 0)));
1450*0Sstevel@tonic-gate 	/* give_mapping is always called with sdp in current process context */
1451*0Sstevel@tonic-gate 	ASSERT(ID(sdp) == CURPROC_ID);
1452*0Sstevel@tonic-gate 
1453*0Sstevel@tonic-gate 	/* remap any old trash mappings */
1454*0Sstevel@tonic-gate 	if (sdp->flag & TRASHPAGE) {
1455*0Sstevel@tonic-gate 		/* current owner should not have a trash mapping */
1456*0Sstevel@tonic-gate 		ASSERT(sdp != lp->owner);
1457*0Sstevel@tonic-gate 
1458*0Sstevel@tonic-gate 		DEBUGF(4, (CE_CONT,
1459*0Sstevel@tonic-gate 		    "new owner %d remapping old trash mapping\n",
1460*0Sstevel@tonic-gate 		    ddi_get_pid()));
1461*0Sstevel@tonic-gate 		if ((err = devmap_umem_remap(sdp->unlockseg, winlock_dip,
1462*0Sstevel@tonic-gate 		    lp->umem_cookie, 0, PAGESIZE, WINLOCK_PROT, 0, 0)) != 0) {
1463*0Sstevel@tonic-gate 			/*
1464*0Sstevel@tonic-gate 			 * unable to remap old trash page,
1465*0Sstevel@tonic-gate 			 * abort before changing owner
1466*0Sstevel@tonic-gate 			 */
1467*0Sstevel@tonic-gate 			DEBUGF(4, (CE_CONT,
1468*0Sstevel@tonic-gate 			    "aborting: error in umem_remap %d\n", err));
1469*0Sstevel@tonic-gate 			return (err);
1470*0Sstevel@tonic-gate 		}
1471*0Sstevel@tonic-gate 		sdp->flag &= ~TRASHPAGE;
1472*0Sstevel@tonic-gate 	}
1473*0Sstevel@tonic-gate 
1474*0Sstevel@tonic-gate 	/* we have a new owner now */
1475*0Sstevel@tonic-gate 	lp->owner = sdp;
1476*0Sstevel@tonic-gate 
1477*0Sstevel@tonic-gate 	if ((err = devmap_load(sdp->lockseg, lp->cookie, PAGESIZE,
1478*0Sstevel@tonic-gate 	    DEVMAP_ACCESS, rw)) != 0) {
1479*0Sstevel@tonic-gate 		return (err);
1480*0Sstevel@tonic-gate 	}
1481*0Sstevel@tonic-gate 	DEBUGF(4, (CE_CONT, "new owner %d gets lock mapping", ddi_get_pid()));
1482*0Sstevel@tonic-gate 
1483*0Sstevel@tonic-gate 	if (lp->sleepers) {
1484*0Sstevel@tonic-gate 		/* Force unload unlock mapping if there are waiters */
1485*0Sstevel@tonic-gate 		DEBUGF(4, (CE_CONT,
1486*0Sstevel@tonic-gate 		    " lock has %d sleepers => remove unlock mapping\n",
1487*0Sstevel@tonic-gate 		    lp->sleepers));
1488*0Sstevel@tonic-gate 		err = devmap_unload(sdp->unlockseg, lp->cookie, PAGESIZE);
1489*0Sstevel@tonic-gate 	} else {
1490*0Sstevel@tonic-gate 		/*
1491*0Sstevel@tonic-gate 		 * while here, give new owner a valid mapping to unlock
1492*0Sstevel@tonic-gate 		 * page so we don't get called again.
1493*0Sstevel@tonic-gate 		 */
1494*0Sstevel@tonic-gate 		DEBUGF(4, (CE_CONT, " and unlock mapping\n"));
1495*0Sstevel@tonic-gate 		err = devmap_load(sdp->unlockseg, lp->cookie, PAGESIZE,
1496*0Sstevel@tonic-gate 			DEVMAP_ACCESS, PROT_WRITE);
1497*0Sstevel@tonic-gate 	}
1498*0Sstevel@tonic-gate 	return (err);
1499*0Sstevel@tonic-gate }
1500*0Sstevel@tonic-gate 
1501*0Sstevel@tonic-gate /*
1502*0Sstevel@tonic-gate  * Unload owner's mappings, release the lock and wakeup any sleepers
1503*0Sstevel@tonic-gate  * If trash, then the old owner is given a trash mapping
1504*0Sstevel@tonic-gate  *	=> old owner held lock too long and caused a timeout
1505*0Sstevel@tonic-gate  */
1506*0Sstevel@tonic-gate static int
1507*0Sstevel@tonic-gate lock_giveup(SegLock *lp, int trash)
1508*0Sstevel@tonic-gate {
1509*0Sstevel@tonic-gate 	SegProc *owner = lp->owner;
1510*0Sstevel@tonic-gate 
1511*0Sstevel@tonic-gate 	DEBUGF(4, (CE_CONT, "winlock_giveup: lp=%p, owner=%p, trash %d\n",
1512*0Sstevel@tonic-gate 	    (void *)lp, (void *)ID(lp->owner), trash));
1513*0Sstevel@tonic-gate 
1514*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&lp->mutex));
1515*0Sstevel@tonic-gate 	ASSERT(owner != NULL);
1516*0Sstevel@tonic-gate 
1517*0Sstevel@tonic-gate 	/*
1518*0Sstevel@tonic-gate 	 * owner loses lockpage/unlockpage mappings and gains a
1519*0Sstevel@tonic-gate 	 * trashpage mapping, if needed.
1520*0Sstevel@tonic-gate 	 */
1521*0Sstevel@tonic-gate 	if (!trash) {
1522*0Sstevel@tonic-gate 		/*
1523*0Sstevel@tonic-gate 		 * We do not handle errors in devmap_unload in the !trash case,
1524*0Sstevel@tonic-gate 		 * as the process is attempting to unmap/exit or otherwise
1525*0Sstevel@tonic-gate 		 * release the lock. Errors in unloading the mapping are not
1526*0Sstevel@tonic-gate 		 * going to affect that (unmap does not take error return).
1527*0Sstevel@tonic-gate 		 */
1528*0Sstevel@tonic-gate 		(void) devmap_unload(owner->lockseg, lp->cookie, PAGESIZE);
1529*0Sstevel@tonic-gate 		(void) devmap_unload(owner->unlockseg, lp->cookie, PAGESIZE);
1530*0Sstevel@tonic-gate 	} else {
1531*0Sstevel@tonic-gate 		int err;
1532*0Sstevel@tonic-gate 
1533*0Sstevel@tonic-gate 		if (err = devmap_unload(owner->lockseg, lp->cookie, PAGESIZE)) {
1534*0Sstevel@tonic-gate 			/* error unloading lockseg mapping. abort giveup */
1535*0Sstevel@tonic-gate 			return (err);
1536*0Sstevel@tonic-gate 		}
1537*0Sstevel@tonic-gate 
1538*0Sstevel@tonic-gate 		/*
1539*0Sstevel@tonic-gate 		 * old owner gets mapping to trash page so it can continue
1540*0Sstevel@tonic-gate 		 * devmap_umem_remap does a hat_unload (and does it holding
1541*0Sstevel@tonic-gate 		 * the right locks), so no need to devmap_unload on unlockseg
1542*0Sstevel@tonic-gate 		 */
1543*0Sstevel@tonic-gate 		if ((err = devmap_umem_remap(owner->unlockseg, winlock_dip,
1544*0Sstevel@tonic-gate 		    trashpage_cookie, 0, PAGESIZE, WINLOCK_PROT, 0, 0)) != 0) {
1545*0Sstevel@tonic-gate 			/* error remapping to trash page, abort giveup */
1546*0Sstevel@tonic-gate 			return (err);
1547*0Sstevel@tonic-gate 		}
1548*0Sstevel@tonic-gate 		owner->flag |= TRASHPAGE;
1549*0Sstevel@tonic-gate 		/*
1550*0Sstevel@tonic-gate 		 * Preload mapping to trash page by calling devmap_load
1551*0Sstevel@tonic-gate 		 * However, devmap_load can only be called on the faulting
1552*0Sstevel@tonic-gate 		 * process context and not on the owner's process context
1553*0Sstevel@tonic-gate 		 * we preload only if we happen to be in owner process context
1554*0Sstevel@tonic-gate 		 * Other processes will fault on the unlock mapping
1555*0Sstevel@tonic-gate 		 * and be given a trash mapping at that time.
1556*0Sstevel@tonic-gate 		 */
1557*0Sstevel@tonic-gate 		if (ID(owner) == CURPROC_ID) {
1558*0Sstevel@tonic-gate 		    (void) devmap_load(owner->unlockseg, lp->cookie, PAGESIZE,
1559*0Sstevel@tonic-gate 			DEVMAP_ACCESS, PROT_WRITE);
1560*0Sstevel@tonic-gate 		}
1561*0Sstevel@tonic-gate 	}
1562*0Sstevel@tonic-gate 
1563*0Sstevel@tonic-gate 	lp->owner = NULL;
1564*0Sstevel@tonic-gate 
1565*0Sstevel@tonic-gate 	/* Clear the lock value in underlying page so new owner can grab it */
1566*0Sstevel@tonic-gate 	LOCK(lp) = 0;
1567*0Sstevel@tonic-gate 
1568*0Sstevel@tonic-gate 	if (lp->sleepers) {
1569*0Sstevel@tonic-gate 		DEBUGF(4, (CE_CONT, "  waking up, lp=%p\n", (void *)lp));
1570*0Sstevel@tonic-gate 		cv_broadcast(&lp->locksleep);
1571*0Sstevel@tonic-gate 	}
1572*0Sstevel@tonic-gate 	return (0);
1573*0Sstevel@tonic-gate }
1574*0Sstevel@tonic-gate 
1575*0Sstevel@tonic-gate /*
1576*0Sstevel@tonic-gate  * destroy all allocated memory.
1577*0Sstevel@tonic-gate  */
1578*0Sstevel@tonic-gate 
1579*0Sstevel@tonic-gate static void
1580*0Sstevel@tonic-gate lock_destroyall(void)
1581*0Sstevel@tonic-gate {
1582*0Sstevel@tonic-gate 	SegLock	*lp, *lpnext;
1583*0Sstevel@tonic-gate 
1584*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&winlock_mutex));
1585*0Sstevel@tonic-gate 	ASSERT(lock_list == NULL);
1586*0Sstevel@tonic-gate 
1587*0Sstevel@tonic-gate 	DEBUGF(1, (CE_CONT, "Lock list empty. Releasing free list\n"));
1588*0Sstevel@tonic-gate 	for (lp = lock_free_list; lp != NULL; lp = lpnext) {
1589*0Sstevel@tonic-gate 		mutex_enter(&lp->mutex);
1590*0Sstevel@tonic-gate 		lpnext =  lp->next;
1591*0Sstevel@tonic-gate 		ASSERT(lp->clients == NULL);
1592*0Sstevel@tonic-gate 		ASSERT(lp->owner == NULL);
1593*0Sstevel@tonic-gate 		ASSERT(lp->alloccount == 0);
1594*0Sstevel@tonic-gate 		mutex_destroy(&lp->mutex);
1595*0Sstevel@tonic-gate 		cv_destroy(&lp->locksleep);
1596*0Sstevel@tonic-gate 		kmem_free(lp, sizeof (SegLock));
1597*0Sstevel@tonic-gate 	}
1598*0Sstevel@tonic-gate 	lock_free_list = NULL;
1599*0Sstevel@tonic-gate 	next_lock = 0;
1600*0Sstevel@tonic-gate }
1601*0Sstevel@tonic-gate 
1602*0Sstevel@tonic-gate 
1603*0Sstevel@tonic-gate /* RFE: create mdb walkers instead of dump routines? */
1604*0Sstevel@tonic-gate static void
1605*0Sstevel@tonic-gate seglock_dump_all(void)
1606*0Sstevel@tonic-gate {
1607*0Sstevel@tonic-gate 	SegLock	*lp;
1608*0Sstevel@tonic-gate 
1609*0Sstevel@tonic-gate 	mutex_enter(&winlock_mutex);
1610*0Sstevel@tonic-gate 	cmn_err(CE_CONT, "ID\tKEY\tNALLOC\tATTCH\tOWNED\tLOCK\tWAITER\n");
1611*0Sstevel@tonic-gate 
1612*0Sstevel@tonic-gate 	cmn_err(CE_CONT, "Lock List:\n");
1613*0Sstevel@tonic-gate 	for (lp = lock_list; lp != NULL; lp = lp->next) {
1614*0Sstevel@tonic-gate 		mutex_enter(&lp->mutex);
1615*0Sstevel@tonic-gate 		cmn_err(CE_CONT, "%d\t%d\t%u\t%c\t%c\t%c\t%d\n",
1616*0Sstevel@tonic-gate 		    lp->cookie, lp->key, lp->alloccount,
1617*0Sstevel@tonic-gate 		    lp->clients ? 'Y' : 'N',
1618*0Sstevel@tonic-gate 		    lp->owner ? 'Y' : 'N',
1619*0Sstevel@tonic-gate 		    lp->lockptr != 0 && LOCK(lp) ? 'Y' : 'N',
1620*0Sstevel@tonic-gate 		    lp->sleepers);
1621*0Sstevel@tonic-gate 		mutex_exit(&lp->mutex);
1622*0Sstevel@tonic-gate 	}
1623*0Sstevel@tonic-gate 	cmn_err(CE_CONT, "Free Lock List:\n");
1624*0Sstevel@tonic-gate 	for (lp = lock_free_list; lp != NULL; lp = lp->next) {
1625*0Sstevel@tonic-gate 		mutex_enter(&lp->mutex);
1626*0Sstevel@tonic-gate 		cmn_err(CE_CONT, "%d\t%d\t%u\t%c\t%c\t%c\t%d\n",
1627*0Sstevel@tonic-gate 		    lp->cookie, lp->key, lp->alloccount,
1628*0Sstevel@tonic-gate 		    lp->clients ? 'Y' : 'N',
1629*0Sstevel@tonic-gate 		    lp->owner ? 'Y' : 'N',
1630*0Sstevel@tonic-gate 		    lp->lockptr != 0 && LOCK(lp) ? 'Y' : 'N',
1631*0Sstevel@tonic-gate 		    lp->sleepers);
1632*0Sstevel@tonic-gate 		mutex_exit(&lp->mutex);
1633*0Sstevel@tonic-gate 	}
1634*0Sstevel@tonic-gate 
1635*0Sstevel@tonic-gate #ifdef DEBUG
1636*0Sstevel@tonic-gate 	if (lock_debug < 3) {
1637*0Sstevel@tonic-gate 		mutex_exit(&winlock_mutex);
1638*0Sstevel@tonic-gate 		return;
1639*0Sstevel@tonic-gate 	}
1640*0Sstevel@tonic-gate 
1641*0Sstevel@tonic-gate 	for (lp = lock_list; lp != NULL; lp = lp->next) {
1642*0Sstevel@tonic-gate 		SegProc	*sdp;
1643*0Sstevel@tonic-gate 
1644*0Sstevel@tonic-gate 		mutex_enter(&lp->mutex);
1645*0Sstevel@tonic-gate 		cmn_err(CE_CONT,
1646*0Sstevel@tonic-gate 		    "lock %p, key=%d, cookie=%d, nalloc=%u, lock=%d, wait=%d\n",
1647*0Sstevel@tonic-gate 		    (void *)lp, lp->key, lp->cookie, lp->alloccount,
1648*0Sstevel@tonic-gate 		    lp->lockptr != 0 ? LOCK(lp) : -1, lp->sleepers);
1649*0Sstevel@tonic-gate 
1650*0Sstevel@tonic-gate 		cmn_err(CE_CONT,
1651*0Sstevel@tonic-gate 		    "style=%d, lockptr=%p, timeout=%ld, clients=%p, owner=%p\n",
1652*0Sstevel@tonic-gate 		    lp->style, (void *)lp->lockptr, lp->timeout,
1653*0Sstevel@tonic-gate 		    (void *)lp->clients, (void *)lp->owner);
1654*0Sstevel@tonic-gate 
1655*0Sstevel@tonic-gate 
1656*0Sstevel@tonic-gate 		for (sdp = lp->clients; sdp != NULL; sdp = sdp->next) {
1657*0Sstevel@tonic-gate 			cmn_err(CE_CONT, "  client %p%s, lp=%p, flag=%x, "
1658*0Sstevel@tonic-gate 			    "process tag=%p, lockseg=%p, unlockseg=%p\n",
1659*0Sstevel@tonic-gate 			    (void *)sdp, sdp == lp->owner ? " (owner)" : "",
1660*0Sstevel@tonic-gate 			    (void *)sdp->lp, sdp->flag, (void *)ID(sdp),
1661*0Sstevel@tonic-gate 			    (void *)sdp->lockseg, (void *)sdp->unlockseg);
1662*0Sstevel@tonic-gate 		}
1663*0Sstevel@tonic-gate 		mutex_exit(&lp->mutex);
1664*0Sstevel@tonic-gate 	}
1665*0Sstevel@tonic-gate #endif
1666*0Sstevel@tonic-gate 	mutex_exit(&winlock_mutex);
1667*0Sstevel@tonic-gate }
1668*0Sstevel@tonic-gate 
1669*0Sstevel@tonic-gate #include <sys/modctl.h>
1670*0Sstevel@tonic-gate 
1671*0Sstevel@tonic-gate static struct modldrv modldrv = {
1672*0Sstevel@tonic-gate 	&mod_driverops,		/* Type of module.  This one is a driver */
1673*0Sstevel@tonic-gate 	"Winlock Driver v%I%",	/* Name of the module */
1674*0Sstevel@tonic-gate 	&winlock_ops,		/* driver ops */
1675*0Sstevel@tonic-gate };
1676*0Sstevel@tonic-gate 
1677*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
1678*0Sstevel@tonic-gate 	MODREV_1,
1679*0Sstevel@tonic-gate 	(void *)&modldrv,
1680*0Sstevel@tonic-gate 	0,
1681*0Sstevel@tonic-gate 	0,
1682*0Sstevel@tonic-gate 	0
1683*0Sstevel@tonic-gate };
1684*0Sstevel@tonic-gate 
1685*0Sstevel@tonic-gate int
1686*0Sstevel@tonic-gate _init(void)
1687*0Sstevel@tonic-gate {
1688*0Sstevel@tonic-gate 	int e;
1689*0Sstevel@tonic-gate 
1690*0Sstevel@tonic-gate 	mutex_init(&winlock_mutex, NULL, MUTEX_DEFAULT, NULL);
1691*0Sstevel@tonic-gate 	e = mod_install(&modlinkage);
1692*0Sstevel@tonic-gate 	if (e) {
1693*0Sstevel@tonic-gate 		mutex_destroy(&winlock_mutex);
1694*0Sstevel@tonic-gate 	}
1695*0Sstevel@tonic-gate 	return (e);
1696*0Sstevel@tonic-gate }
1697*0Sstevel@tonic-gate 
1698*0Sstevel@tonic-gate 
1699*0Sstevel@tonic-gate int
1700*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
1701*0Sstevel@tonic-gate {
1702*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1703*0Sstevel@tonic-gate }
1704*0Sstevel@tonic-gate 
1705*0Sstevel@tonic-gate int
1706*0Sstevel@tonic-gate _fini(void)
1707*0Sstevel@tonic-gate {
1708*0Sstevel@tonic-gate 	int	e;
1709*0Sstevel@tonic-gate 
1710*0Sstevel@tonic-gate 	e = mod_remove(&modlinkage);
1711*0Sstevel@tonic-gate 	if (e == 0) {
1712*0Sstevel@tonic-gate 		mutex_destroy(&winlock_mutex);
1713*0Sstevel@tonic-gate 	}
1714*0Sstevel@tonic-gate 	return (e);
1715*0Sstevel@tonic-gate }
1716