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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <strings.h>
31 #include "rcapd.h"
32 #include "utils.h"
33
34 /*
35 * An abstract "collection" of processes. Multiple types of collections can
36 * exist, one of which is selected at run-time. Currently, the only one
37 * defined corresponds to project(4)s.
38 */
39
40 #define MAX(x, y) (((x) > (y)) ? (x) : (y))
41
42 typedef struct {
43 rcid_t *lfa_colidp;
44 lcollection_t *lfa_found;
45 } lcollection_find_arg_t;
46
47 extern void lcollection_update_project(lcollection_update_type_t,
48 void(*)(char *, char *, int, uint64_t, int));
49 extern void lcollection_update_zone(lcollection_update_type_t,
50 void(*)(char *, char *, int, uint64_t, int));
51 static void lcollection_update_notification_cb(char *, char *, int, uint64_t,
52 int);
53
54 rcid_t(*rc_getidbypsinfo)(psinfo_t *);
55 uint64_t phys_total = 0;
56 static lcollection_t *lcollection_head = NULL;
57
58 void
lcollection_update(lcollection_update_type_t ut)59 lcollection_update(lcollection_update_type_t ut)
60 {
61 lcollection_update_zone(ut, lcollection_update_notification_cb);
62 lcollection_update_project(ut, lcollection_update_notification_cb);
63 }
64
65 /*
66 * Inserts a collection with the supplied identity, or updates the caps of an
67 * existing one. The return value will have these bits set, depending on the
68 * previous and new cap values. If no cap was displaced, and the requested cap
69 * is 0, no collection will be added, and the applicable *ZERO flags will be
70 * set.
71 *
72 * LCST_CAP_CHANGED
73 * LCST_CAP_REMOVED
74 * LCSS_CAP_ZERO
75 */
76 lcollection_t *
lcollection_insert_update(rcid_t * colidp,uint64_t rss_cap,char * name,int * changes)77 lcollection_insert_update(rcid_t *colidp, uint64_t rss_cap, char *name,
78 int *changes)
79 {
80 lcollection_t *lcol;
81
82 *changes = 0;
83
84 if (rss_cap == 0)
85 *changes |= LCST_CAP_ZERO;
86
87 lcol = lcollection_find(colidp);
88
89 /*
90 * If the specified collection is capped, add it to lcollection.
91 */
92 if (lcol == NULL) {
93 /*
94 * If the cap has been zeroed and the collection doesn't exist,
95 * don't create the collection just to remvoe the cap later.
96 */
97 if (rss_cap == 0)
98 return (NULL);
99
100 *changes |= LCST_CAP_CHANGED;
101 lcol = malloc(sizeof (*lcol));
102 if (lcol == NULL) {
103 debug("not enough memory to monitor %s %s",
104 (colidp->rcid_type == RCIDT_PROJECT ?
105 "project" : "zone"), name);
106 return (NULL);
107 }
108 (void) bzero(lcol, sizeof (*lcol));
109
110 lcol->lcol_id = *colidp;
111 debug("added collection %s\n", name);
112 lcol->lcol_prev = NULL;
113 lcol->lcol_next = lcollection_head;
114 lcol->lcol_stat.lcols_min_rss = (uint64_t)-1;
115 if (lcollection_head != NULL)
116 lcollection_head->lcol_prev = lcol;
117 lcollection_head = lcol;
118 }
119
120 /*
121 * Set/update the collection's name.
122 */
123 (void) strlcpy(lcol->lcol_name, name, sizeof (lcol->lcol_name));
124
125 /*
126 * Set cap flags.
127 */
128 if (rss_cap != lcol->lcol_rss_cap) {
129 *changes |= LCST_CAP_CHANGED;
130 lcol->lcol_rss_cap = rss_cap;
131 if (lcol->lcol_rss_cap == 0)
132 *changes |= LCST_CAP_REMOVED;
133 }
134
135 if (rss_cap > 0)
136 lcol->lcol_mark++;
137
138 return (lcol);
139 }
140
141 static void
lcollection_update_notification_cb(char * col_type,char * name,int changes,uint64_t rss_cap,int mark)142 lcollection_update_notification_cb(char *col_type, char *name, int changes,
143 uint64_t rss_cap, int mark)
144 {
145 /*
146 * Assume the collection has been updated redundantly if its mark count
147 * exceeds 1, and that another notification is unnecessary.
148 */
149 if (mark > 1)
150 return;
151
152 if (changes & LCST_CAP_ZERO)
153 debug("%s %s: %s\n", col_type, name,
154 (changes & LCST_CAP_REMOVED) ? "cap removed" : "uncapped");
155 else
156 debug("%s %s: cap: %llukB\n", col_type, name,
157 (unsigned long long)rss_cap);
158 }
159
160 /*
161 * Function to walk list of collections and invoke the specified callback with
162 * the specified argument. Callbacks are allowed to change the linkage of the
163 * collection on which they act.
164 */
165 void
list_walk_collection(int (* cb)(lcollection_t *,void *),void * arg)166 list_walk_collection(int (*cb)(lcollection_t *, void *), void *arg)
167 {
168 lcollection_t *lcol;
169 lcollection_t *next;
170
171 lcol = lcollection_head;
172 while (lcol != NULL) {
173 next = lcol->lcol_next;
174 if (cb(lcol, arg) != 0)
175 return;
176 lcol = next;
177 }
178 }
179
180 /*
181 * Returns a nonzero value if an lprocess_t is still a valid member of a given
182 * collection.
183 */
184 int
lcollection_member(lcollection_t * lcol,lprocess_t * lpc)185 lcollection_member(lcollection_t *lcol, lprocess_t *lpc)
186 {
187 lprocess_t *cur = lcol->lcol_lprocess;
188
189 while (cur != NULL)
190 if (cur == lpc)
191 return (1);
192 else
193 cur = cur->lpc_next;
194 return (0);
195 }
196
197 static int
lcollection_find_cb(lcollection_t * lcol,void * arg)198 lcollection_find_cb(lcollection_t *lcol, void *arg)
199 {
200 rcid_t *colidp = ((lcollection_find_arg_t *)arg)->lfa_colidp;
201
202 if (lcol->lcol_id.rcid_type == colidp->rcid_type &&
203 lcol->lcol_id.rcid_val == colidp->rcid_val) {
204 ((lcollection_find_arg_t *)arg)->lfa_found = lcol;
205 return (1);
206 }
207
208 return (0);
209 }
210
211 lcollection_t *
lcollection_find(rcid_t * colidp)212 lcollection_find(rcid_t *colidp)
213 {
214 lcollection_find_arg_t lfa;
215
216 lfa.lfa_colidp = colidp;
217 lfa.lfa_found = NULL;
218 list_walk_collection(lcollection_find_cb, &lfa);
219
220 return (lfa.lfa_found);
221 }
222
223 /*
224 * Unlinks a collection from lcollection.
225 */
226 void
lcollection_free(lcollection_t * lcol)227 lcollection_free(lcollection_t *lcol)
228 {
229 lprocess_t *lpc;
230 lprocess_t *next;
231
232 lpc = lcol->lcol_lprocess;
233 while (lpc != NULL) {
234 next = lpc->lpc_next;
235 if (lpc->lpc_collection == lcol)
236 lprocess_free(lpc);
237 lpc = next;
238 }
239
240 /*
241 * Unlink the collection.
242 */
243 if (lcol->lcol_prev != NULL)
244 lcol->lcol_prev->lcol_next = lcol->lcol_next;
245 if (lcol->lcol_next != NULL)
246 lcol->lcol_next->lcol_prev = lcol->lcol_prev;
247 if (lcollection_head == lcol)
248 lcollection_head = lcol->lcol_next;
249 lcol->lcol_next = lcol->lcol_prev = NULL;
250
251 free(lcol);
252 }
253