xref: /illumos-gate/usr/src/cmd/rcap/rcapd/rcapd_collection.c (revision f012ee0c3db17469b492c2cf757226f3d7b1ebbc)
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
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 *
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
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
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
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
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 *
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
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