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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*
29  * This file contains functions that enables the rpc library to synchronize
30  * between various threads while they compete for a particular file descriptor.
31  */
32 
33 #include "mt.h"
34 #include "rpc_mt.h"
35 #include <rpc/rpc.h>
36 #include <errno.h>
37 #include <sys/poll.h>
38 #include <syslog.h>
39 #include <sys/types.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 
43 /*
44  * A block holds an array of maxBlockSize cell and associated recursive locks
45  */
46 
47 #define	CELLTBLSZ	1024
48 
49 typedef struct rpcfd_block {
50 	struct rpcfd_block *next;	/* Next Block */
51 	struct rpcfd_block *prev;	/* prev Block */
52 	int 	end;			/* fd of last lock in the list */
53 	mutex_t	lock[CELLTBLSZ];	/* recursive locks */
54 } rpcfd_block_t;
55 
56 mutex_t	rpc_fd_list_lock = DEFAULTMUTEX;	/* protects list manipulation */
57 
58 /* Following functions create and manipulates the dgfd lock object */
59 
60 static mutex_t *search(const void *handle, int fd);
61 static rpcfd_block_t *create_block(const void *handle, int fd);
62 
63 void *
rpc_fd_init(void)64 rpc_fd_init(void)
65 {
66 	/*
67 	 * Create first chunk of CELLTBLSZ
68 	 * (No lock is required for initial creation.)
69 	 */
70 	return (create_block(NULL, 0));
71 }
72 
73 /*
74  * If rpc_fd_lock() fails to acquire a lock, it returns non-zero (ENOMEM).
75  * (The operation can only fail due to a malloc() failure.)
76  * The caller of rpc_fd_lock() must call rpc_fd_unlock() even if
77  * rpc_fd_lock() failed.  This keeps _sigoff() and _sigon() balanced.
78  *
79  * If search() and create_block() fail for rpc_fd_lock(), then search()
80  * will fail for rpc_fd_unlock(), so mutex_lock() and mutex_unlock()
81  * calls will be balanced.  In any case, since the mutex is marked
82  * LOCK_ERRORCHECK, an additional mutex_unlock() does nothing.
83  */
84 int
rpc_fd_lock(const void * handle,int fd)85 rpc_fd_lock(const void *handle, int fd)
86 {
87 	mutex_t *mp;
88 	rpcfd_block_t *p;
89 
90 	_sigoff();
91 	(void) mutex_lock(&rpc_fd_list_lock);
92 	mp = search(handle, fd);
93 	if (mp == NULL) {
94 		p = create_block(handle, fd);
95 		if (p != NULL)
96 			mp = &p->lock[fd % CELLTBLSZ];
97 	}
98 	(void) mutex_unlock(&rpc_fd_list_lock);
99 	if (mp == NULL)
100 		return (ENOMEM);
101 	(void) mutex_lock(mp);
102 	return (0);
103 }
104 
105 void
rpc_fd_unlock(const void * handle,int fd)106 rpc_fd_unlock(const void *handle, int fd)
107 {
108 	mutex_t *mp;
109 
110 	(void) mutex_lock(&rpc_fd_list_lock);
111 	mp = search(handle, fd);
112 	(void) mutex_unlock(&rpc_fd_list_lock);
113 	if (mp != NULL)
114 		(void) mutex_unlock(mp);
115 	_sigon();
116 }
117 
118 static rpcfd_block_t *
create_block(const void * handle,int fd)119 create_block(const void *handle, int fd)
120 {
121 	rpcfd_block_t *l, *lprev;
122 	rpcfd_block_t *p;
123 	int i;
124 
125 	p = malloc(sizeof (rpcfd_block_t));
126 	if (p == NULL)
127 		return (NULL);
128 
129 	for (i = 0; i < CELLTBLSZ; i++) {
130 		(void) mutex_init(&p->lock[i],
131 			USYNC_THREAD | LOCK_RECURSIVE | LOCK_ERRORCHECK, NULL);
132 	}
133 	p->end = (((fd + CELLTBLSZ) / CELLTBLSZ) * CELLTBLSZ) - 1;
134 	lprev = NULL;
135 	for (l = (rpcfd_block_t *)handle; l; l = l->next) {
136 		lprev = l;
137 		if (fd < l->end)
138 			break;
139 	}
140 
141 	p->next = l;
142 	p->prev = lprev;
143 	if (lprev)
144 		lprev->next = p;
145 	if (l)
146 		l->prev = p;
147 
148 	return (p);
149 }
150 
151 /*
152  * Called with rpc_fd_list_lock held.
153  */
154 static mutex_t *
search(const void * handle,int fd)155 search(const void *handle, int fd)
156 {
157 	rpcfd_block_t *p;
158 
159 	for (p = (rpcfd_block_t *)handle; p; p = p->next) {
160 		if (fd <= p->end)
161 			return (&p->lock[fd % CELLTBLSZ]);
162 	}
163 
164 	return (NULL);
165 }
166