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 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * File descriptor usage
29 *
30 * The number of processes that can be effectively managed is limited to less
31 * than half the number of descriptors available: one for each process's
32 * psinfo, the other its pagedata. When managing more processes, file
33 * descriptors are revoked as needed, in such a way as to maximize the
34 * distribution of descriptors to pagedata which will be useful in meeting a
35 * cap without paging out the process's working set, while retaining some
36 * benefit from caching psinfo descriptors, and leaving enough available for
37 * use by external consumers, such as are needed for project enumeration or
38 * configuration file reading.
39 *
40 * Revokable file descriptors are opened and associated with a callback
41 * function which can be invoked to revoke them later. pagedata and psinfo
42 * descriptors are differentiated for the purposes of preferring pagedata over
43 * psinfo, which effectively places the performance of rcapd behind the
44 * importance of making good page selections. The one exception is that one
45 * psinfo descriptor is guaranteed a place at any time, for the benefit of
46 * psinfo updates of a presently currently-scanned process. Descriptors are
47 * otherwise revoked in LIFO order.
48 */
49
50 #include <sys/types.h>
51 #include <stdlib.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <limits.h>
55 #include <strings.h>
56 #include <unistd.h>
57 #include "rcapd_rfd.h"
58 #include "utils.h"
59
60 static rfd_t *tail; /* tail of global list */
61
62 static int rfd_revoke_next(rfd_class_t);
63
64 /*
65 * Return the previous rfd_t of the given class, starting at (and including)
66 * the given rfd_t.
67 */
68 static rfd_t *
rfd_find_prev_class(rfd_t * rfd,rfd_class_t class)69 rfd_find_prev_class(rfd_t *rfd, rfd_class_t class)
70 {
71 while (rfd != NULL && rfd->rfd_class != class)
72 rfd = rfd->rfd_prev;
73 return (rfd);
74 }
75
76 /*
77 * Revoke and free the given rfd_t, returning as close does.
78 */
79 static int
rfd_revoke_fd(rfd_t * rfd)80 rfd_revoke_fd(rfd_t *rfd)
81 {
82 if (rfd->rfd_revoke != NULL)
83 rfd->rfd_revoke(rfd);
84 return (rfd_close(rfd->rfd_fd));
85 }
86
87 /*
88 * Revoke the next file descriptor according to the above constraints. Return
89 * nonzero if there are none to revoke.
90 */
91 static int
rfd_revoke_next(rfd_class_t class)92 rfd_revoke_next(rfd_class_t class)
93 {
94 rfd_t *rfd = NULL;
95
96 if (tail == NULL) {
97 debug("nothing to revoke\n");
98 return (-1);
99 }
100
101 /*
102 * RESERVED-clsas descriptors are all equivalent and may not be revoked
103 * to satisfy another request of the same clsas. rfd_reserve() uses
104 * this to reserve descriptors by first allocating, then closing, these
105 * descriptors.
106 */
107 if (class != RFD_RESERVED)
108 rfd = rfd_find_prev_class(tail, RFD_RESERVED);
109
110 /*
111 * Next try psinfo descriptors, leaving at least one open. Revoke the
112 * second-last psinfo descriptor, if possible.
113 */
114 if (rfd == NULL) {
115 rfd = rfd_find_prev_class(tail, RFD_PSINFO);
116 if (rfd != NULL)
117 rfd = rfd->rfd_prev_class;
118 }
119
120 /*
121 * Otherwise, revoke the last descriptor allocated, taking the same
122 * care as above that it is not reserved, if the reserved kind is
123 * sought.
124 */
125 if (rfd == NULL) {
126 rfd = tail;
127 while (rfd != NULL && class == RFD_RESERVED && rfd->rfd_class ==
128 RFD_RESERVED)
129 rfd = rfd->rfd_prev;
130 }
131
132 if (rfd != NULL)
133 return (rfd_revoke_fd(rfd));
134
135 /*
136 * Nothing but reserved-class descriptors are revocable, while a
137 * reserved- class descriptor was sought.
138 */
139 return (-1);
140 }
141
142 /*
143 * Opens a file of the given class, which can later be revoked with the given
144 * callback. Returns as open does. The callback should reset any state that
145 * this caller establishes after the open, but should not close the descriptor,
146 * which will be done when the caller explicitly does so with rfd_close(), or
147 * the descriptor is revoked with rfd_revoke().
148 */
149 int
rfd_open(char * name,int revoke_ok,rfd_class_t class,void (* revoke)(struct rfd *),void * data,int oflag,mode_t mode)150 rfd_open(char *name, int revoke_ok, rfd_class_t class,
151 void(*revoke)(struct rfd *), void *data, int oflag, mode_t mode)
152 {
153 int fd;
154 rfd_t *rfd;
155
156 while ((fd = open(name, oflag, mode)) == -1 && (errno == ENFILE ||
157 errno == EMFILE)) {
158 if (revoke_ok) {
159 if (rfd_revoke_next(class) != 0)
160 return (-1);
161 } else
162 break;
163 }
164
165 if (fd != -1) {
166 /*
167 * Create rfd_t and link into list.
168 */
169 rfd = malloc(sizeof (*rfd));
170 if (rfd == NULL) {
171 (void) close(fd);
172 return (-1);
173 }
174 (void) bzero(rfd, sizeof (*rfd));
175 rfd->rfd_fd = fd;
176 rfd->rfd_class = class;
177 rfd->rfd_revoke = revoke;
178 rfd->rfd_data = data;
179 if (tail != NULL)
180 rfd->rfd_prev_class = rfd_find_prev_class(tail, class);
181 else
182 rfd->rfd_prev_class = tail;
183 rfd->rfd_prev = tail;
184 if (tail != NULL)
185 tail->rfd_next = rfd;
186 tail = rfd;
187 }
188
189 return (fd);
190 }
191
192 /*
193 * Close a given file descriptor, and return as close() does.
194 */
195 int
rfd_close(int fd)196 rfd_close(int fd)
197 {
198 rfd_t *nextclass;
199 rfd_t *rfdprev;
200 rfd_t *rfd;
201 #ifdef DEBUG
202 int freed = 0;
203 #endif /* DEBUG */
204
205 rfd = tail;
206 while (rfd != NULL) {
207 rfdprev = rfd->rfd_prev;
208 if (rfd->rfd_fd == fd) {
209 if (rfd->rfd_prev != NULL)
210 rfd->rfd_prev->rfd_next = rfd->rfd_next;
211 if (rfd->rfd_next != NULL)
212 rfd->rfd_next->rfd_prev = rfd->rfd_prev;
213 if (tail == rfd)
214 tail = rfd->rfd_prev;
215 for (nextclass = rfd->rfd_next; nextclass != NULL;
216 nextclass = nextclass->rfd_next)
217 if (nextclass->rfd_class == rfd->rfd_class) {
218 nextclass->rfd_prev_class =
219 rfd->rfd_prev_class;
220 break;
221 }
222 free(rfd);
223 #ifdef DEBUG
224 freed = 1;
225 #endif /* DEBUG */
226 break;
227 }
228 rfd = rfdprev;
229 }
230 ASSERT(freed == 1);
231 return (close(fd));
232 }
233
234 /*
235 * Makes sure at least n descriptors are available. Returns nonzero if
236 * successful.
237 */
238 int
rfd_reserve(int n)239 rfd_reserve(int n)
240 {
241 int i;
242 int fd = 0;
243 rfd_t *otail = NULL;
244 rfd_t *rfdnext;
245
246 for (i = 0; i < n && fd >= 0; i++) {
247 /*
248 * rfd_open() will append as many RFD_RESERVED-clsas
249 * descriptors to the current tail as are requested, revoking
250 * non-RFD_RESERVED-class descriptors until nothing else can be
251 * revoked or the reservation is met.
252 */
253 fd = rfd_open("/dev/null", 1, RFD_RESERVED, NULL, NULL,
254 O_RDONLY, 0);
255 if (otail == NULL)
256 otail = tail;
257 }
258
259 if (fd == -1)
260 debug("couldn't allocate %d descriptors\n", n);
261
262 while (otail != NULL) {
263 rfdnext = otail->rfd_next;
264 (void) rfd_close(otail->rfd_fd);
265 otail = rfdnext;
266 }
267
268 return (fd != -1);
269 }
270