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