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 */
128static int
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
152MODULE = Sun::Solaris::Lgrp PACKAGE = Sun::Solaris::Lgrp
153PROTOTYPES: 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 #
161BOOT:
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 #
199lgrp_cookie_t
200lgrp_init(lgrp_view_t view = LGRP_VIEW_OS)
201  POSTCALL:
202	RETURN_UNDEF_IF_FAIL;
203
204lgrp_view_t
205lgrp_view(cookie)
206       lgrp_cookie_t cookie
207  POSTCALL:
208	RETURN_UNDEF_IF_FAIL;
209
210lgrp_affinity_t
211lgrp_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
218int
219lgrp_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
228int
229lgrp_cookie_stale(cookie)
230	lgrp_cookie_t cookie;
231  POSTCALL:
232	RETURN_UNDEF_IF_FAIL;
233
234int
235lgrp_fini(cookie)
236	lgrp_cookie_t cookie;
237  POSTCALL:
238	RETURN_UNDEF_IF_FAIL;
239	XSRETURN_YES;
240
241lgrp_id_t
242lgrp_home(idtype, id)
243	idtype_t idtype;
244	id_t id;
245  POSTCALL:
246	RETURN_UNDEF_IF_FAIL;
247
248int
249lgrp_latency(lgrp_id_t from,lgrp_id_t to)
250  POSTCALL:
251	RETURN_UNDEF_IF_FAIL;
252
253lgrp_mem_size_t
254lgrp_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
262int
263lgrp_nlgrps(cookie)
264	lgrp_cookie_t cookie;
265  POSTCALL:
266	RETURN_UNDEF_IF_FAIL;
267
268lgrp_id_t
269lgrp_root(cookie)
270	lgrp_cookie_t cookie
271  POSTCALL:
272	RETURN_UNDEF_IF_FAIL;
273
274int
275lgrp_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 #
281int
282lgrp_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 #
305void
306lgrp_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 #
334void
335lgrp_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
364void
365lgrp_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
391void
392lgrp_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