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