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 #include <stdio.h>
30 #include <stdlib.h>
31 #include <strings.h>
32 #include "rcapd.h"
33 #include "utils.h"
34 
35 /*
36  * An abstract "collection" of processes.  Multiple types of collections can
37  * exist, one of which is selected at run-time.  Currently, the only one
38  * defined corresponds to project(4)s.
39  */
40 
41 #define	MAX(x, y) (((x) > (y)) ? (x) : (y))
42 
43 typedef struct {
44 	rcid_t		lfa_colid;
45 	lcollection_t	*lfa_found;
46 } lcollection_find_arg_t;
47 
48 extern void lcollection_update_project(lcollection_update_type_t,
49     void(*)(char *, int, uint64_t, int));
50 extern void lcollection_set_type_project();
51 static void lcollection_update_notification_cb(char *, int, uint64_t, int);
52 
53 rcid_t(*rc_getidbypsinfo)(psinfo_t *);
54 uint64_t phys_total = 0;
55 static lcollection_t *lcollection_head = NULL;
56 
57 void
58 lcollection_update(lcollection_update_type_t ut)
59 {
60 	if (rcfg.rcfg_mode == rctype_project)
61 		lcollection_update_project(ut,
62 		    lcollection_update_notification_cb);
63 	else
64 		die(gettext("unknown mode %s\n"), rcfg.rcfg_mode_name);
65 }
66 
67 /*
68  * Configure which collection type will be used.
69  */
70 void
71 lcollection_set_type(rctype_t type)
72 {
73 	switch (type) {
74 	case rctype_project:
75 		lcollection_set_type_project();
76 		break;
77 	default:
78 		/* can't happen */
79 		die(gettext("unknown mode %d\n"), type);
80 		/*NOTREACHED*/
81 	}
82 }
83 
84 /*
85  * Inserts a collection with the supplied identity, or updates the caps of an
86  * existing one.  The return value will have these bits set, depending on the
87  * previous and new cap values.  If no cap was displaced, and the requested cap
88  * is 0, no collection will be added, and the applicable *ZERO flags will be
89  * set.
90  *
91  *	LCST_CAP_CHANGED
92  *	LCST_CAP_REMOVED
93  *	LCSS_CAP_ZERO
94  */
95 lcollection_t *
96 lcollection_insert_update(rcid_t colid, uint64_t rss_cap, char *name,
97     int *changes)
98 {
99 	lcollection_t *lcol;
100 
101 	*changes = 0;
102 
103 	if (rss_cap == 0)
104 		*changes |= LCST_CAP_ZERO;
105 
106 	lcol = lcollection_find(colid);
107 
108 	/*
109 	 * If the specified collection is capped, add it to lcollection.
110 	 */
111 	if (lcol == NULL) {
112 		/*
113 		 * If the cap has been zeroed and the collection doesn't exist,
114 		 * don't create the collection just to remvoe the cap later.
115 		 */
116 		if (rss_cap == 0)
117 			return (NULL);
118 
119 		*changes |= LCST_CAP_CHANGED;
120 		lcol = malloc(sizeof (*lcol));
121 		if (lcol == NULL) {
122 			debug("not enough memory to monitor %s %s",
123 			    rcfg.rcfg_mode_name, name);
124 			return (NULL);
125 		}
126 		(void) bzero(lcol, sizeof (*lcol));
127 
128 		lcol->lcol_id = colid;
129 		debug("added collection %s\n", name);
130 		lcol->lcol_prev = NULL;
131 		lcol->lcol_next = lcollection_head;
132 		lcol->lcol_stat.lcols_min_rss = (uint64_t)-1;
133 		if (lcollection_head != NULL)
134 			lcollection_head->lcol_prev = lcol;
135 		lcollection_head = lcol;
136 	}
137 
138 	/*
139 	 * Set/update the collection's name.
140 	 */
141 	(void) strlcpy(lcol->lcol_name, name, sizeof (lcol->lcol_name));
142 
143 	/*
144 	 * Set cap flags.
145 	 */
146 	if (rss_cap != lcol->lcol_rss_cap) {
147 		*changes |= LCST_CAP_CHANGED;
148 		lcol->lcol_rss_cap = rss_cap;
149 		if (lcol->lcol_rss_cap == 0)
150 			*changes |= LCST_CAP_REMOVED;
151 	}
152 
153 	if (rss_cap > 0)
154 		lcol->lcol_mark++;
155 
156 	return (lcol);
157 }
158 
159 static void
160 lcollection_update_notification_cb(char *name, int changes, uint64_t rss_cap,
161     int mark)
162 {
163 	/*
164 	 * Assume the collection has been updated redundantly if its mark count
165 	 * exceeds 1, and that another notification is unnecessary.
166 	 */
167 	if (mark > 1)
168 		return;
169 
170 	if (changes & LCST_CAP_ZERO)
171 		debug("%s %s: %s\n", rcfg.rcfg_mode_name, name,
172 		    (changes & LCST_CAP_REMOVED) ? "cap removed" : "uncapped");
173 	else
174 		debug("%s %s: cap: %llukB\n", rcfg.rcfg_mode_name, name,
175 		    (unsigned long long)rss_cap);
176 }
177 
178 /*
179  * Function to walk list of collections and invoke the specified callback with
180  * the specified argument.  Callbacks are allowed to change the linkage of the
181  * collection on which they act.
182  */
183 void
184 list_walk_collection(int (*cb)(lcollection_t *, void *), void *arg)
185 {
186 	lcollection_t *lcol;
187 	lcollection_t *next;
188 
189 	lcol = lcollection_head;
190 	while (lcol != NULL) {
191 		next = lcol->lcol_next;
192 		if (cb(lcol, arg) != 0)
193 			return;
194 		lcol = next;
195 	}
196 }
197 
198 /*
199  * Returns a nonzero value if an lprocess_t is still a valid member of a given
200  * collection.
201  */
202 int
203 lcollection_member(lcollection_t *lcol, lprocess_t *lpc)
204 {
205 	lprocess_t *cur = lcol->lcol_lprocess;
206 
207 	while (cur != NULL)
208 		if (cur == lpc)
209 			return (1);
210 		else
211 			cur = cur->lpc_next;
212 	return (0);
213 }
214 
215 static int
216 lcollection_find_cb(lcollection_t *lcol, void *arg)
217 {
218 	if (lcol->lcol_id == ((lcollection_find_arg_t *)arg)->lfa_colid) {
219 		((lcollection_find_arg_t *)arg)->lfa_found = lcol;
220 		return (1);
221 	} else
222 		return (0);
223 }
224 
225 lcollection_t *
226 lcollection_find(id_t colid)
227 {
228 	lcollection_find_arg_t lfa;
229 
230 	lfa.lfa_colid = colid;
231 	lfa.lfa_found = NULL;
232 	list_walk_collection(lcollection_find_cb, &lfa);
233 
234 	return (lfa.lfa_found);
235 }
236 
237 /*
238  * Unlinks a collection from lcollection.
239  */
240 void
241 lcollection_free(lcollection_t *lcol)
242 {
243 	lprocess_t *lpc;
244 	lprocess_t *next;
245 
246 	lpc = lcol->lcol_lprocess;
247 	while (lpc != NULL) {
248 		next = lpc->lpc_next;
249 		if (lpc->lpc_collection == lcol)
250 			lprocess_free(lpc);
251 		lpc = next;
252 	}
253 
254 	/*
255 	 * Unlink the collection.
256 	 */
257 	if (lcol->lcol_prev != NULL)
258 		lcol->lcol_prev->lcol_next = lcol->lcol_next;
259 	if (lcol->lcol_next != NULL)
260 		lcol->lcol_next->lcol_prev = lcol->lcol_prev;
261 	if (lcollection_head == lcol)
262 		lcollection_head = lcol->lcol_next;
263 	lcol->lcol_next = lcol->lcol_prev = NULL;
264 
265 	free(lcol);
266 }
267