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
42typedef struct {
43	rcid_t		*lfa_colidp;
44	lcollection_t	*lfa_found;
45} lcollection_find_arg_t;
46
47extern void lcollection_update_project(lcollection_update_type_t,
48    void(*)(char *, char *, int, uint64_t, int));
49extern void lcollection_update_zone(lcollection_update_type_t,
50    void(*)(char *, char *, int, uint64_t, int));
51static void lcollection_update_notification_cb(char *, char *, int, uint64_t,
52    int);
53
54rcid_t(*rc_getidbypsinfo)(psinfo_t *);
55uint64_t phys_total = 0;
56static lcollection_t *lcollection_head = NULL;
57
58void
59lcollection_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 */
76lcollection_t *
77lcollection_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
141static void
142lcollection_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 */
165void
166list_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 */
184int
185lcollection_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
197static int
198lcollection_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
211lcollection_t *
212lcollection_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 */
226void
227lcollection_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