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