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 * 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 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 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 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 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 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