1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * User Objects.
28 *
29 * User objects are used to manage and protect resources that
30 * have been created for a user context.  Each user object
31 * maintains a reference count and a read/write mutex to
32 * provide the appropriate access to the object depending
33 * on the operation at hand.
34 *
35 * For example when initializing or creating a PD user object,
36 * the active context would hold a write lock, but to simply
37 * reference the PD object as in a CQ create operation, a
38 * read lock is only required.
39 *
40 * Each user object also maintains a "live" flag.  If this flag
41 * is not set, then lookups on this user object will fail
42 * even if it still resides in the associated user object
43 * management table.  This specifically handles the case
44 * where a get operation blocks and does not acquire the lock
45 * until after the object has been destroyed (but not yet
46 * released).  Destroy operations set the "live" flag to 0
47 * prior to dropping their write lock on the user object.
48 * This allows the reader to realize when it receives the
49 * lock that the object has been destroyed so it can then
50 * release it's reference to the user object, and allow it to
51 * be freed (the storage will not be freed until the last reference
52 * is released).
53 */
54#include	<sys/debug.h>
55#include	<sys/kmem.h>
56#include	<sys/sunddi.h>
57#include	<sys/ib/clients/of/sol_ofs/sol_ofs_common.h>
58
59extern char	*sol_ofs_dbg_str;
60static sol_ofs_uobj_t *ofs_uobj_find(sol_ofs_uobj_table_t *,
61    uint_t, int);
62
63/*
64 * Function:
65 *	sol_ofs_uobj_tbl_init
66 * Input:
67 *	uo_tbl	- A pointer to the user object resource management table
68 *		  to initialize.
69 * Output:
70 *	None
71 * Returns:
72 *	None
73 * Description:
74 * 	Initializes the specified user object resource managment table.
75 */
76void
77sol_ofs_uobj_tbl_init(sol_ofs_uobj_table_t *uo_tbl, size_t uobj_sz)
78{
79	ASSERT(uo_tbl != NULL);
80
81	rw_init(&uo_tbl->uobj_tbl_lock, NULL, RW_DRIVER, NULL);
82	uo_tbl->uobj_tbl_used_blks = 0;
83	uo_tbl->uobj_tbl_num_blks = 0;
84	uo_tbl->uobj_tbl_uo_cnt = 0;
85	uo_tbl->uobj_tbl_uo_sz = uobj_sz;
86	uo_tbl->uobj_tbl_uo_root = NULL;
87}
88
89/*
90 * Function:
91 *	sol_ofs_uobj_tbl_fini
92 * Input:
93 *	uo_tbl	- A pointer to the user object resource management table
94 *		  to be released.
95 * Output:
96 *	None
97 * Returns:
98 *	None
99 * Description:
100 * 	Releases any resources held by the specified user object resource
101 *	managment table.  The table is no longer valid upon return. NOTE:
102 *	the table should be empty when this routine is called, so this
103 *	really is more of just a sanity check.
104 */
105void
106sol_ofs_uobj_tbl_fini(sol_ofs_uobj_table_t *uo_tbl)
107{
108	int			i, j;
109	uint32_t	size;
110	sol_ofs_uobj_blk_t	*blk;
111
112	ASSERT(uo_tbl != NULL);
113
114	rw_enter(&uo_tbl->uobj_tbl_lock, RW_WRITER);
115
116	if (uo_tbl->uobj_tbl_uo_cnt > 0) {
117		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
118		    "UOBJ TBL FINI: object count not zero (cnt=%d)",
119		    uo_tbl->uobj_tbl_uo_cnt);
120	}
121
122	/*
123	 * Go through the roots looking for blocks to free.  Warn if any
124	 * our found (there shouldn't be any).
125	 */
126	for (i = 0; i < uo_tbl->uobj_tbl_used_blks; i++) {
127		blk = uo_tbl->uobj_tbl_uo_root[i];
128		if (!blk) {
129			continue;
130		}
131		for (j = 0; j < SOL_OFS_UO_BLKSZ; j++) {
132			if (blk->ofs_uoblk_blks[j])   {
133				/*
134				 * This is an error, we may want to free
135				 * ultimately sol_ofs_uobj_free
136				 * (blk->ofs_uoblk_blks[j]);
137				 */
138				SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
139				    "UOBJ TBL FINI: blk %p, slot %d non null",
140				    blk, j);
141			}
142		}
143		kmem_free(blk, sizeof (*blk));
144	}
145
146	if (uo_tbl->uobj_tbl_uo_root) {
147
148		size = uo_tbl->uobj_tbl_num_blks *
149		    sizeof (sol_ofs_uobj_blk_t *);
150		kmem_free(uo_tbl->uobj_tbl_uo_root, size);
151	}
152
153	rw_exit(&uo_tbl->uobj_tbl_lock);
154	rw_destroy(&uo_tbl->uobj_tbl_lock);
155}
156
157/*
158 * Function:
159 *	uverbs_uob_init
160 * Input:
161 * 	uobj        - Pointer to the user object to initialize.
162 *	user_handle - A user space handle to associates with the object.
163 *	              Generally used to identify object in asynchronous
164 *	              notifications.
165 *	uob_type   - The type of user object.
166 * Ouput:
167 *	uobj       - Initialized user object.
168 * Returns:
169 * 	None
170 * Description:
171 *	Initialize a new user object.  The object will have one reference
172 *	placed on it.
173 */
174void
175sol_ofs_uobj_init(sol_ofs_uobj_t *uobj,
176    uint64_t user_handle, sol_ofs_uobj_type_t  uobj_type)
177{
178	uobj->uo_user_handle = user_handle;
179	uobj->uo_refcnt = 1;
180	uobj->uo_type = uobj_type;
181	uobj->uo_id = -1;
182	uobj->uo_live = 0;
183	rw_init(&uobj->uo_lock, NULL, RW_DRIVER, NULL);
184	mutex_init(&uobj->uo_reflock, NULL, MUTEX_DRIVER, NULL);
185}
186
187/*
188 * Function:
189 *	ofs_uobj_fini
190 * Input:
191 * 	uobj        - Pointer to the user object to be cleaned up.
192 * Ouput:
193 *	None
194 * Returns:
195 * 	None
196 * Description:
197 *	Performs user object cleanup prior to releasing memory.
198 */
199static void
200ofs_uobj_fini(sol_ofs_uobj_t *uobj)
201{
202	rw_destroy(&uobj->uo_lock);
203	mutex_destroy(&uobj->uo_reflock);
204}
205
206/*
207 * Function:
208 *	sol_ofs_uobj_ref
209 * Input:
210 * 	uobj        - Pointer to the user object
211 * Ouput:
212 *	None
213 * Returns:
214 * 	None
215 * Description:
216 *	Place a reference on the specified user object.
217 */
218void
219sol_ofs_uobj_ref(sol_ofs_uobj_t *uobj)
220{
221	mutex_enter(&uobj->uo_reflock);
222	uobj->uo_refcnt++;
223	ASSERT(uobj->uo_refcnt != 0);
224	mutex_exit(&uobj->uo_reflock);
225}
226
227/*
228 * Function:
229 *	sol_ofs_uobj_deref
230 * Input:
231 * 	uobj        - Pointer to the user object
232 *	free_func   - Pointer to release function, called if the
233 *                    last reference is removed for the user object.
234 * Ouput:
235 *	None
236 * Returns:
237 * 	None
238 * Description:
239 *	Remove a reference to a user object.  If a free function
240 *	was specified and the last reference is released, then the
241 *	free function is invoked to release the user object.
242 */
243void
244sol_ofs_uobj_deref(sol_ofs_uobj_t *uobj,
245    void (*free_func)(sol_ofs_uobj_t *uobj))
246{
247	SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "UOBJ_DEREF: uobj = %p, "
248	    "refcnt=%d", uobj, uobj->uo_refcnt);
249
250	mutex_enter(&uobj->uo_reflock);
251
252	ASSERT(uobj->uo_refcnt != 0);
253	uobj->uo_refcnt--;
254	if (uobj->uo_refcnt == 0) {
255		mutex_exit(&uobj->uo_reflock);
256		if (free_func)
257			free_func(uobj);
258	} else {
259		mutex_exit(&uobj->uo_reflock);
260	}
261}
262
263/*
264 * Function:
265 *	sol_ofs_uobj_add
266 * Input:
267 *	uo_tbl	- A pointer to the user object resource management table
268 *		  to which the object should be added.
269 *	uobj    - A pointer ot the user object to be added; a reference
270 *	          should exist on this object prior to addition, and the
271 *		  object should be removed prior to all references being
272 *		  removed.
273 * Output:
274 *	uobj	- The user object "uo_id" is updated and should be
275 *		  used in subsequent lookup operations.
276 * Returns:
277 *	DDI_SUCCESS on success, else error code.
278 * Description:
279 * 	Add a user object to the specified user object resource management
280 *	table.
281 *
282 */
283int
284sol_ofs_uobj_add(sol_ofs_uobj_table_t *uo_tbl, sol_ofs_uobj_t *uobj)
285{
286	int		i, j, empty = -1;
287	sol_ofs_uobj_blk_t	*blk;
288
289	rw_enter(&uo_tbl->uobj_tbl_lock, RW_WRITER);
290
291	/*
292	 * Try to find an empty slot for the new user object.
293	 */
294	for (i = 0; i < uo_tbl->uobj_tbl_used_blks; i++) {
295		blk = uo_tbl->uobj_tbl_uo_root[i];
296		if (blk != NULL && blk->ofs_uo_blk_avail > 0) {
297			SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str,
298			    "UOBJ ADD: table:%p, available blks:%d",
299			    uo_tbl, blk->ofs_uo_blk_avail);
300			for (j = 0; j < SOL_OFS_UO_BLKSZ; j++) {
301				if (blk->ofs_uoblk_blks[j] == NULL) {
302					blk->ofs_uoblk_blks[j] = uobj;
303					uobj->uo_id = j + (i *
304					    SOL_OFS_UO_BLKSZ);
305					uobj->uo_uobj_sz =
306					    uo_tbl->uobj_tbl_uo_sz;
307					blk->ofs_uo_blk_avail--;
308					uo_tbl->uobj_tbl_uo_cnt++;
309					goto obj_added;
310				}
311			}
312		} else if (blk == NULL && empty < 0) {
313			/*
314			 * Remember the first empty blk we came across.
315			 */
316			empty = i;
317		}
318	}
319
320	/*
321	 * No entries were available, we must allocate a new block.  If we did
322	 * not find a empty block available, then we must allocate/reallocate
323	 * the root array (copying any existing blk pointers to it).
324	 */
325	if (empty < 0) {
326		if (uo_tbl->uobj_tbl_used_blks == uo_tbl->uobj_tbl_num_blks) {
327			sol_ofs_uobj_blk_t	**p;
328			uint_t		newsz;
329
330			newsz = uo_tbl->uobj_tbl_num_blks + SOL_OFS_UO_BLKSZ;
331			SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str,
332			    "UOBJ ADD: Increasing uobj table size to %d",
333			    newsz);
334
335			p = kmem_zalloc(newsz * sizeof (*p), KM_NOSLEEP);
336			if (!p) {
337				SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
338				    "UOBJ ADD: Mem alloc fail\n");
339				rw_exit(&uo_tbl->uobj_tbl_lock);
340				return (1);
341			}
342
343			if (uo_tbl->uobj_tbl_uo_root) {
344				uint_t	oldsz;
345
346				oldsz = (uint_t)uo_tbl->uobj_tbl_num_blks *
347				    (int)(sizeof (*p));
348				bcopy(uo_tbl->uobj_tbl_uo_root, p, oldsz);
349				kmem_free(uo_tbl->uobj_tbl_uo_root, oldsz);
350			}
351			uo_tbl->uobj_tbl_uo_root = p;
352			uo_tbl->uobj_tbl_num_blks = newsz;
353		}
354		empty = uo_tbl->uobj_tbl_used_blks;
355		uo_tbl->uobj_tbl_used_blks++;
356	}
357
358	/*
359	 * There are enough free block pointers in the root, allocate
360	 * a new block.
361	 */
362	blk = kmem_zalloc(sizeof (*blk), KM_NOSLEEP);
363	if (!blk) {
364		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
365		    "UOBJ ADD: Mem alloc fail\n");
366		rw_exit(&uo_tbl->uobj_tbl_lock);
367		return (1);
368	}
369	ASSERT(uo_tbl->uobj_tbl_uo_root[empty] == NULL);
370	uo_tbl->uobj_tbl_uo_root[empty] = blk;
371	blk->ofs_uo_blk_avail = SOL_OFS_UO_BLKSZ - 1;
372
373	/*
374	 * Use the first slot in this new block to add the new user object.
375	 */
376	uobj->uo_id = empty * SOL_OFS_UO_BLKSZ;
377	blk->ofs_uoblk_blks[0] = uobj;
378	uobj->uo_uobj_sz = uo_tbl->uobj_tbl_uo_sz;
379	uo_tbl->uobj_tbl_uo_cnt++;
380
381obj_added:
382	rw_exit(&uo_tbl->uobj_tbl_lock);
383	return (0);
384}
385
386/*
387 * Function:
388 *	sol_ofs_uobj_remove
389 * Input:
390 *	uo_tbl	- A pointer to the user object resource management table
391 *		  from which the object should be removed.
392 *	uobj    - A pointer ot the user object to be removed.
393 * Output:
394 *	None
395 * Returns:
396 *	A pointer to the user object that was removed on success, otherwise
397 *	NULL.
398 * Description:
399 * 	Remove a user object from the specified user resource management
400 *	table.
401 *
402 *	The uobj uo_lock must be held as a writer before calling this.
403 */
404sol_ofs_uobj_t *
405sol_ofs_uobj_remove(sol_ofs_uobj_table_t *uo_tbl, sol_ofs_uobj_t *uobj)
406{
407	uint_t			i, j;
408	sol_ofs_uobj_blk_t	*blk;
409	sol_ofs_uobj_t		*p;
410
411	ASSERT(uo_tbl != NULL);
412	ASSERT(uobj != NULL);
413
414	p = NULL;
415	rw_enter(&uo_tbl->uobj_tbl_lock, RW_WRITER);
416
417	if (!uobj->uo_live) {
418		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
419		    "UOBJ REMOVE: object 0x%P, already removed", (void *)uobj);
420		goto remove_done;
421	}
422
423	if ((uo_tbl->uobj_tbl_uo_cnt == 0) || !(uo_tbl->uobj_tbl_uo_root)) {
424		/*
425		 * The table is empty, just return not found
426		 * Don't panic, userland app could have double free'd
427		 * let them deal with it.
428		 */
429		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
430		    "UOBJ REMOVE: table 0x%P empty", (void *)uo_tbl);
431		goto remove_done;
432	}
433
434	i = uobj->uo_id / SOL_OFS_UO_BLKSZ;
435	j = uobj->uo_id % SOL_OFS_UO_BLKSZ;
436
437	if (i >= uo_tbl->uobj_tbl_used_blks) {
438		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
439		    "UOBJ REMOVE: object id %d exceeds table size",
440		    uobj->uo_id);
441		goto remove_done;
442	}
443
444	ASSERT(i < uo_tbl->uobj_tbl_num_blks);
445
446	blk = uo_tbl->uobj_tbl_uo_root[i];
447	if (blk == NULL) {
448		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
449		    "UOBJ REMOVE: object id %d points to invalid root",
450		    uobj->uo_id);
451		goto remove_done;
452	}
453
454	if (blk->ofs_uoblk_blks[j] == NULL) {
455		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
456		    "UOBJ REMOVE: object id %d points to invalid block",
457		    uobj->uo_id);
458		goto remove_done;
459	}
460
461	/*
462	 * Mark as dead
463	 */
464	uobj->uo_live = 0;
465
466	p = blk->ofs_uoblk_blks[j];
467	blk->ofs_uoblk_blks[j] = NULL;
468	blk->ofs_uo_blk_avail++;
469	if (blk->ofs_uo_blk_avail == SOL_OFS_UO_BLKSZ) {
470		kmem_free(blk, sizeof (*blk));
471		uo_tbl->uobj_tbl_uo_root[i] = NULL;
472	}
473	uo_tbl->uobj_tbl_uo_cnt--;
474
475remove_done:
476	rw_exit(&uo_tbl->uobj_tbl_lock);
477	return (p);
478}
479
480/*
481 * Function:
482 *	ofs_uobj_find
483 * Input:
484 *	uo_tbl	- A pointer to the user object resource management table
485 *		  to be used for the lookup.
486 *	uo_id	- The user object ID to lookup.  This ID was set when
487 *		  the object was added to the resource management table.
488 *	add_ref	- A non zero value indicates that the user objects reference
489 *		  count should be updated to reflect and additional
490 *		  reference before it is returned.
491 * Output:
492 *	None
493 * Returns:
494 *	A pointer to the user object associated with the uo_id if found,
495 *	otherwise NULL.
496 * Description:
497 * 	Lookup and return a user object from the specified user resource
498 *	management table.
499 */
500static sol_ofs_uobj_t *
501ofs_uobj_find(sol_ofs_uobj_table_t *uo_tbl, uint32_t uo_id, int add_ref)
502{
503	uint32_t		i, j;
504	sol_ofs_uobj_blk_t	*blk;
505	sol_ofs_uobj_t		*uobj;
506
507	ASSERT(uo_tbl != NULL);
508	uobj = NULL;
509
510	rw_enter(&uo_tbl->uobj_tbl_lock, RW_READER);
511
512	if ((uo_tbl->uobj_tbl_uo_cnt == 0) || !(uo_tbl->uobj_tbl_uo_root)) {
513		/*
514		 * The table is empty, just return not found
515		 * Don't panic, userland app could have double free'd
516		 * let them deal with it.
517		 */
518		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
519		    "UOBJ FIND: id %d in tbl 0x%P - tbl empty", uo_id,
520		    (void *)uo_tbl);
521		goto find_done;
522	}
523
524	i = uo_id / SOL_OFS_UO_BLKSZ;
525	j = uo_id % SOL_OFS_UO_BLKSZ;
526
527	if (i >= uo_tbl->uobj_tbl_used_blks) {
528		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
529		    "UOBJ FIND: Index not valid, %d", uo_id);
530		goto find_done;
531	}
532
533	/*
534	 * Get the user object, and if valid perform a get (ref++).
535	 * The caller issuing the find, must release the reference
536	 * when done.
537	 */
538	blk = uo_tbl->uobj_tbl_uo_root[i];
539	if (blk != NULL) {
540		ASSERT(i < uo_tbl->uobj_tbl_num_blks);
541
542		uobj = blk->ofs_uoblk_blks[j];
543
544		if (uobj == NULL) {
545			SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
546			    "UOBJ FIND: Index %d not found, blk = %p",
547			    uo_id, blk->ofs_uoblk_blks[j]);
548		} else if (add_ref) {
549			sol_ofs_uobj_ref(uobj);
550		}
551	} else {
552		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
553		    "UOBJ FIND: Uobject not found, %d", uo_id);
554		goto find_done;
555	}
556
557find_done:
558	rw_exit(&uo_tbl->uobj_tbl_lock);
559	return (uobj);
560}
561
562/*
563 * Function:
564 *	sol_ofs_uobj_get_read
565 * Input:
566 *	tbl         - Pointer to the user object managment table to
567 *	              be used in the lookup.
568 *	uo_id       - The ID to object mapping, assigned to the user
569 *	              object at addition to the table.
570 * Ouput:
571 *	None
572 * Returns:
573 * 	A pointer to the user object associated with uo_id or NULL
574 *      if the entry does not exist.
575 * Description:
576 *	Lookup a user object and place a reference on it.  Acquires
577 *	the object with a READ lock.  The reference and lock should
578 *	be released using the sol_ofs_uobj_put() call.
579 */
580sol_ofs_uobj_t *
581sol_ofs_uobj_get_read(sol_ofs_uobj_table_t *tbl, uint32_t uo_id)
582{
583	sol_ofs_uobj_t *uobj;
584
585	uobj = ofs_uobj_find(tbl, uo_id, 1);
586	if (!uobj)
587		return (NULL);
588
589	rw_enter(&uobj->uo_lock, RW_READER);
590
591	/*
592	 * If object was destroyed before we got the lock, just release
593	 * our reference and indicate we didn't find the object.
594	 */
595	if (!uobj->uo_live) {
596		sol_ofs_uobj_put(uobj);
597		return (NULL);
598	}
599	return (uobj);
600}
601
602/*
603 * Function:
604 *	sol_ofs_uobj_get_write
605 * Input:
606 *	tbl         - Pointer to the user object managment table to
607 *	              be used in the lookup.
608 *	uo_id       - The ID to object mapping, assigned to the user
609 *	              object at addition to the table.
610 * Ouput:
611 *	None
612 * Returns:
613 * 	A pointer to the user object associated with uo_id or NULL
614 *      if the entry does not exist.
615 * Description:
616 *	Lookup a user object and place a reference on it.  Acquires
617 *	the object with a WRITE lock.  The reference and lock should
618 *	be released using the sol_ofs_uobj_put() call.
619 */
620sol_ofs_uobj_t *
621sol_ofs_uobj_get_write(sol_ofs_uobj_table_t *tbl, uint32_t uo_id)
622{
623	sol_ofs_uobj_t *uobj;
624
625
626	uobj = ofs_uobj_find(tbl, uo_id, 1);
627	if (!uobj)
628		return (NULL);
629
630	rw_enter(&uobj->uo_lock, RW_WRITER);
631
632	/*
633	 * If object was destroyed before we got the lock, just release
634	 * our reference and indicate we didn't find the object.
635	 */
636	if (!uobj->uo_live) {
637		sol_ofs_uobj_put(uobj);
638		return (NULL);
639	}
640	return (uobj);
641}
642
643/*
644 * Function:
645 *	sol_ofs_uobj_free
646 * Input:
647 *	uobj	-  A pointer to the Solaris User Verbs kernel agent user
648 *	           object to be freed.
649 * Output:
650 *	None.
651 * Returns:
652 *	None.
653 * Description:
654 * 	Called when the user object is no longer referenced, it will release
655 *	any user object resources and free the container object memory.
656 *	NOTE: Currently there is a stipulation that the user object be the
657 *	first element of any user object specialization.
658 */
659void
660sol_ofs_uobj_free(sol_ofs_uobj_t *uobj)
661{
662	size_t	sz;
663
664	ASSERT(uobj);
665
666	/*
667	 * Cleanup common user object and then free memory using
668	 * length based on associated object type.
669	 */
670	ofs_uobj_fini(uobj);
671
672	sz = uobj->uo_uobj_sz;
673	if (sz)
674		kmem_free(uobj, sz);
675}
676
677/*
678 * Function:
679 *	sol_ofs_uobj_put
680 * Input:
681 * 	uobj        - Pointer to the user object
682 * Ouput:
683 *	None
684 * Returns:
685 * 	None
686 * Description:
687 *	Remove a lock associated with a user object, and decrement
688 *	the reference held. On the last deference the user object
689 *	will be freed.
690 */
691void
692sol_ofs_uobj_put(sol_ofs_uobj_t *uobj)
693{
694	rw_exit(&uobj->uo_lock);
695	sol_ofs_uobj_deref(uobj, sol_ofs_uobj_free);
696}
697