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 /*
23  * Copyright 2006 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 /*
30  * Lgrp.xs contains XS wrappers for the system locality group library
31  * liblgrp(3LIB).
32  */
33 
34 #include <sys/errno.h>
35 #include <sys/lgrp_user.h>
36 
37 /*
38  * On i386 Solaris defines SP, which conflicts with the perl definition of SP
39  * We don't need the Solaris one, so get rid of it to avoid warnings.
40  */
41 #undef SP
42 
43 /* Perl XS includes. */
44 #include "EXTERN.h"
45 #include "perl.h"
46 #include "XSUB.h"
47 
48 /* Return undef in scalar context and empty list in list context */
49 #define LGRP_BADVAL() {			\
50 	if (GIMME_V == G_ARRAY)		\
51 			XSRETURN_EMPTY;	\
52 		else			\
53 			XSRETURN_UNDEF;	\
54 }
55 
56 /*
57  * Push all values from input array onto the perl return stack.
58  */
59 #define	PUSHARRAY(array, nitems)	\
60 {					\
61 	int x;				\
62 					\
63 	if (nitems < 0) {		\
64 		LGRP_BADVAL()		\
65 	} else if (nitems > 0) {	\
66 		EXTEND(SP, nitems);	\
67 		for (x = 0; x < nitems; x++) {	\
68 			PUSHs(sv_2mortal(newSVnv(array[x])));	\
69 		}			\
70 	}				\
71 }
72 
73 /*
74  * Several constants are not present in the first version of the Lgrp API,
75  * we define them here.
76  *
77  * lgrp_resources() and lgrp_latency_cookie() only appear in API v2. If the
78  * module is linked with old version of liblgrp(3LIB) there is no lgrp_resources
79  * symbol in the library and perl wrapper returns empty list and sets errno to
80  * EINVAL.
81  *
82  * The lgrp_latency_cookie() is emulated using lgrp_latency().
83  */
84 #if LGRP_VER_CURRENT == 1
85 #define	LGRP_CONTENT_ALL LGRP_CONTENT_HIERARCHY
86 #define	LGRP_LAT_CPU_TO_MEM 	0
87 #define LGRP_RSRC_CPU           0       /* CPU resources */
88 #define LGRP_RSRC_MEM           1       /* memory resources */
89 
90 #define LGRP_RESOURCES(c, lgrp, type) \
91 	{ errno = EINVAL; LGRP_BADVAL(); }
92 
93 /*
94  * Simulate lgrp_latency_cookie() which just fails. This macro is never called
95  * and we just define it so that the C compiler will not complain about the
96  * missing symbol.
97  */
98 #define	lgrp_latency_cookie(c, f, t, b) (errno = EINVAL, -1)
99 
100 #else
101 #define	LGRP_RESOURCES(c, lgrp, type) { \
102 	int nr;				\
103 	lgrp_id_t *lgrps;		\
104 					\
105 	errno = 0;			\
106 	nr = lgrp_resources(c, lgrp, NULL, 0, type);	\
107 	if (nr < 0)			\
108 		LGRP_BADVAL();		\
109 	if (GIMME_V == G_SCALAR)	\
110 		XSRETURN_IV(nr);	\
111 	if (nr == 0) {			\
112 		XSRETURN_EMPTY;		\
113 	} else if (New(0, lgrps, nr, lgrp_id_t) == NULL) {	\
114 		errno = ENOMEM;		\
115 		LGRP_BADVAL();		\
116 	} else {			\
117 		nr = lgrp_resources(c, lgrp, lgrps, nr, type);	\
118 		PUSHARRAY(lgrps, nr);	\
119 		Safefree(lgrps);	\
120 	}				\
121 }
122 #endif
123 
124 /*
125  * Special version of lgrp_latency_cookie(). Use lgrp_latency() for liblgrp V1
126  * and lgrp_latency_cookie for V2.
127  */
128 static int
_lgrp_latency_cookie(lgrp_cookie_t cookie,lgrp_id_t from,lgrp_id_t to,int between)129 _lgrp_latency_cookie(lgrp_cookie_t cookie, lgrp_id_t from, lgrp_id_t to,
130 				   int between)
131 {
132 	return (LGRP_VER_CURRENT < 2 ?
133 	    lgrp_latency(from, to) :
134 	    lgrp_latency_cookie(cookie, from, to, between));
135 }
136 
137 /*
138  * Most functions in liblgrp return -1 on failure. The perl equivalent returns
139  * 'undef' instead. The macro should be call after the RETVAL is set to the
140  * return value of the function.
141  */
142 #define	RETURN_UNDEF_IF_FAIL { if (RETVAL < 0) XSRETURN_UNDEF; }
143 
144 /*
145  * End of C part, start of XS part.
146  *
147  * The XS code exported to perl is below here.  Note that the XS preprocessor
148  * has its own commenting syntax, so all comments from this point on are in
149  * that form.
150  */
151 
152 MODULE = Sun::Solaris::Lgrp PACKAGE = Sun::Solaris::Lgrp
153 PROTOTYPES: ENABLE
154 
155  #
156  # Define any constants that need to be exported.  By doing it this way we can
157  # avoid the overhead of using the DynaLoader package, and in addition constants
158  # defined using this mechanism are eligible for inlining by the perl
159  # interpreter at compile time.
160  #
161 BOOT:
162 	{
163 	HV *stash;
164 
165 	stash = gv_stashpv("Sun::Solaris::Lgrp", TRUE);
166 	newCONSTSUB(stash, "LGRP_AFF_NONE", newSViv(LGRP_AFF_NONE));
167 	newCONSTSUB(stash, "LGRP_AFF_STRONG", newSViv(LGRP_AFF_STRONG));
168 	newCONSTSUB(stash, "LGRP_AFF_WEAK", newSViv(LGRP_AFF_WEAK));
169 	newCONSTSUB(stash, "LGRP_VER_CURRENT", newSViv(LGRP_VER_CURRENT));
170 	newCONSTSUB(stash, "LGRP_VER_NONE", newSViv(LGRP_VER_NONE));
171 	newCONSTSUB(stash, "LGRP_NONE", newSViv(LGRP_NONE));
172 	newCONSTSUB(stash, "LGRP_RSRC_CPU", newSViv(LGRP_RSRC_CPU));
173 	newCONSTSUB(stash, "LGRP_RSRC_MEM", newSViv(LGRP_RSRC_MEM));
174 	newCONSTSUB(stash, "LGRP_CONTENT_HIERARCHY",
175 			newSViv(LGRP_CONTENT_HIERARCHY));
176 	newCONSTSUB(stash, "LGRP_CONTENT_DIRECT", newSViv(LGRP_CONTENT_DIRECT));
177 	newCONSTSUB(stash, "LGRP_VIEW_CALLER", newSViv(LGRP_VIEW_CALLER));
178 	newCONSTSUB(stash, "LGRP_VIEW_OS", newSViv(LGRP_VIEW_OS));
179 	newCONSTSUB(stash, "LGRP_MEM_SZ_FREE", newSViv(LGRP_MEM_SZ_FREE));
180 	newCONSTSUB(stash, "LGRP_MEM_SZ_INSTALLED",
181 			newSViv(LGRP_MEM_SZ_INSTALLED));
182 	newCONSTSUB(stash, "LGRP_CONTENT_ALL", newSViv(LGRP_CONTENT_ALL));
183 	newCONSTSUB(stash, "LGRP_LAT_CPU_TO_MEM", newSViv(LGRP_LAT_CPU_TO_MEM));
184 	newCONSTSUB(stash, "P_PID", newSViv(P_PID));
185 	newCONSTSUB(stash, "P_LWPID", newSViv(P_LWPID));
186 	newCONSTSUB(stash, "P_MYID", newSViv(P_MYID));
187 	}
188 
189  #
190  # The code below uses POSTCALL directive which allows to return 'undef'
191  # whenever a C function returns a negative value.
192  #
193 
194 
195  #
196  # lgrp_init([view])
197  # Use LGRP_VIEW_OS as the default view.
198  #
199 lgrp_cookie_t
200 lgrp_init(lgrp_view_t view = LGRP_VIEW_OS)
201   POSTCALL:
202 	RETURN_UNDEF_IF_FAIL;
203 
204 lgrp_view_t
205 lgrp_view(cookie)
206        lgrp_cookie_t cookie
207   POSTCALL:
208 	RETURN_UNDEF_IF_FAIL;
209 
210 lgrp_affinity_t
211 lgrp_affinity_get(idtype, id, lgrp)
212 	idtype_t idtype;
213 	id_t id;
214 	lgrp_id_t lgrp;
215   POSTCALL:
216 	RETURN_UNDEF_IF_FAIL;
217 
218 int
219 lgrp_affinity_set(idtype, id, lgrp, affinity)
220 	idtype_t idtype;
221 	id_t id;
222 	lgrp_id_t lgrp;
223 	lgrp_affinity_t affinity;
224   POSTCALL:
225 	RETURN_UNDEF_IF_FAIL;
226 	XSRETURN_YES;
227 
228 int
229 lgrp_cookie_stale(cookie)
230 	lgrp_cookie_t cookie;
231   POSTCALL:
232 	RETURN_UNDEF_IF_FAIL;
233 
234 int
235 lgrp_fini(cookie)
236 	lgrp_cookie_t cookie;
237   POSTCALL:
238 	RETURN_UNDEF_IF_FAIL;
239 	XSRETURN_YES;
240 
241 lgrp_id_t
242 lgrp_home(idtype, id)
243 	idtype_t idtype;
244 	id_t id;
245   POSTCALL:
246 	RETURN_UNDEF_IF_FAIL;
247 
248 int
249 lgrp_latency(lgrp_id_t from,lgrp_id_t to)
250   POSTCALL:
251 	RETURN_UNDEF_IF_FAIL;
252 
253 lgrp_mem_size_t
254 lgrp_mem_size(cookie, lgrp, type, content)
255 	lgrp_cookie_t	cookie
256 	lgrp_id_t	lgrp
257 	int		type
258 	lgrp_content_t	content
259   POSTCALL:
260 	RETURN_UNDEF_IF_FAIL;
261 
262 int
263 lgrp_nlgrps(cookie)
264 	lgrp_cookie_t cookie;
265   POSTCALL:
266 	RETURN_UNDEF_IF_FAIL;
267 
268 lgrp_id_t
269 lgrp_root(cookie)
270 	lgrp_cookie_t cookie
271   POSTCALL:
272 	RETURN_UNDEF_IF_FAIL;
273 
274 int
275 lgrp_version(int version = LGRP_VER_NONE)
276 
277  #
278  # lgrp_latency_cookie calls our internal wrapper  _lgrp_latency_cookie() which
279  # works for both old and new versions of liblgrp.
280  #
281 int
282 lgrp_latency_cookie(lgrp_cookie_t cookie, lgrp_id_t from, lgrp_id_t to, int between = 0)
283   CODE:
284 	RETVAL = _lgrp_latency_cookie(cookie, from, to, between);
285   POSTCALL:
286 	RETURN_UNDEF_IF_FAIL;
287   OUTPUT:
288 	RETVAL
289 
290  #
291  # Functions below convert C arrays into Perl lists. They use XS PPCODE
292  # directive to avoid implicit RETVAL assignments and manipulate perl
293  # stack directly.
294  #
295  # When called in scalar context functions return the number of elements
296  # in the list or undef on failure.
297  #
298  # The PUSHARRAY() macro defined above pushes all values from the C array to
299  # the perl stack.
300  #
301 
302  #
303  # @children = lgrp_children($cookie, $parent).
304  #
305 void
306 lgrp_children(cookie, lgrp)
307 	lgrp_cookie_t cookie;
308 	lgrp_id_t lgrp;
309   PREINIT:
310 	lgrp_id_t *lgrps;
311 	int	count;
312   PPCODE:
313 	errno = 0;
314 	if ((count = lgrp_children(cookie, lgrp, NULL, 0)) < 0)
315 		LGRP_BADVAL();
316 
317 	if (GIMME_V == G_SCALAR)
318 		XSRETURN_IV(count);
319 
320 	if (count > 0) {
321 		if (New(0, lgrps, count, lgrp_id_t) == NULL) {
322 			errno = ENOMEM;
323 			LGRP_BADVAL();
324 		} else {
325 			count = lgrp_children(cookie, lgrp, lgrps, count);
326 			PUSHARRAY(lgrps, count);
327 			Safefree(lgrps);
328 		}
329 	}
330 
331  #
332  # @parents = lgrp_parents($cookie, $lgrp).
333  #
334 void
335 lgrp_parents(cookie, lgrp)
336 	lgrp_cookie_t cookie;
337 	lgrp_id_t lgrp;
338   PREINIT:
339 	lgrp_id_t *lgrps;
340 	int count;
341   PPCODE:
342 	errno = 0;
343 	if ((count = lgrp_parents(cookie, lgrp, NULL, 0)) < 0)
344 		LGRP_BADVAL();
345 
346 	if (GIMME_V == G_SCALAR)
347 		XSRETURN_IV(count);
348 
349 	if (count > 0) {
350 		if (New(0, lgrps, count, lgrp_id_t) == NULL) {
351 			errno = ENOMEM;
352 			LGRP_BADVAL();
353 		} else {
354 			count = lgrp_parents(cookie, lgrp, lgrps, count);
355 			PUSHARRAY(lgrps, count);
356 			Safefree(lgrps);
357 		}
358 	}
359 
360  #
361  # @parents = lgrp_cpus($cookie, $lgrp, $content).
362  # Content should be LGRP_CONTENT_HIERARCHY or LGRP_CONTENT_ALL or
363  # 	LGRP_CONTENT_DIRECT
364 void
365 lgrp_cpus(cookie, lgrp, content)
366 	lgrp_cookie_t cookie;
367 	lgrp_id_t lgrp;
368 	lgrp_content_t content;
369   PREINIT:
370 	int ncpus;
371 	processorid_t *cpus;
372   PPCODE:
373 	errno = 0;
374 	if ((ncpus = lgrp_cpus(cookie, lgrp, NULL, 0, content)) < 0)
375 		LGRP_BADVAL();
376 
377 	if (GIMME_V == G_SCALAR)
378 		XSRETURN_IV(ncpus);
379 
380 	if (ncpus > 0) {
381 		if (New(0, cpus, ncpus, processorid_t) == NULL) {
382 			errno = ENOMEM;
383 			LGRP_BADVAL();
384 		} else {
385 			ncpus = lgrp_cpus(cookie, lgrp, cpus, ncpus, content);
386 			PUSHARRAY(cpus, ncpus);
387 			Safefree(cpus);
388 		}
389 	}
390 
391 void
392 lgrp_resources(cookie, lgrp, type)
393 	lgrp_cookie_t cookie;
394 	lgrp_id_t lgrp;
395 	int type;
396   PPCODE:
397 	LGRP_RESOURCES(cookie, lgrp, type);
398