Revamp and split up DTrace headers. Add ioctl() debugging machinery.
It has always been annoying that we have a duplicate set of DTrace headers
in userspace, and further annoying that the DTrace header we have is such a
monolithic monster.
This fixes both of these, at the cost of a (very) little extra complexity when
maintaining the headers. It also adds automated machinery to verify that each
of the new headers is 'standalone enough'.
What does 'standalone enough' mean? This is the problem that has stopped us
sharing DTrace headers between userspace and kernelspace for a long time: they
depend on types that come from different places and have different definitions
in the kernel and in userspace, and can never be made to come from the same
place.
So we fix this by dictating that the headers are standalone *given* that certain
headers are included first. For userspace, this set is <sys/types.h>,
<ctf/types.h>, and <unistd.h>; for kernelspace, the set is <linux/types.h> and a
newly-introduced header included as <dtrace/types.h>. We avoid exposing this
requirement to header users by arranging for the header included as "dtrace.h"
in kernelspace and as <sys/dtrace.h> to include the prerequisite headers, and
enough other headers that the users of those headers can keep using them exactly
as they did before. (There is a single exception for dtrace/iocl.h: see below.)
Where have the headers gone? They have been split in two directions (and the
Kbuild machinery has been adjusted to have its include paths point at the
appropriate places).
All DTrace headers, even <linux/dtrace_cpu.h> in the global kernel tree, have
had 'defines headers' split out of them, named ${header}_defines.h. These
headers contain all typedefs, enums and #defines which do not depend on
visibility into structure definitions declared in the main non-defines header,
and opaque forwardings for all structure definitions declared in that header.
This means that users can include the defines header if they only want opaque
use of structures and relevant constants. (This has necessitated some
mechanical changes to the DTrace headers to convert uses of foo_t typedefs into
'struct foo' where necessary. Not all uses have been converted: only those that
need to be). An _defines header is always accompanied by a corresponding non
_defines header (even if it is just one #include), but the reverse is not true.
In addition to this split, the core dtrace.h header has been split into three
pieces:
- dtrace/include/uapi/linux/dtrace/*.h contains the majority of the headers,
split into _defines and further by section roughly corresponding with the
comment-delimited sections in the original file. The include paths have been
set up so that these can be used via
#include <linux/dtrace/blah.h>
in both userspace and kernelspace.
#include <linux/include/dtrace.h> includes all the headers in the same order
as they were originally.
These headers are installed in userspace and used by dtrace-util. They
should always contain a CDDL license header, and should not use any
kernel-specific types (modulo those that userspace normally uses, such as
those related to kernel-specific functionality such as ioctl()). If you use
a new kernel-specific type, please add a definition of it to
uts/common/sys/dtrace_types.h in userspace too.
The old dtrace_ioctl.h has been renamed to <linux/dtrace/ioctl.h> and
moved in here too. It has gained some extra machinery to help debug
ioctl() type size conflicts. If you call dtrace_ioctl_sizes(), then
dtrace_size_dbg_print() (not defined here) is repeatedly called with
two parameters, the name of each ioctl() type and the size of that type.
Please keep this list up to date, it is useful!
There is one unfortunate exception to the userspace-types rule here:
ioctl.h needs types from <linux/dtrace_cpu_defines.h>, which is not
even a userspace-installed header. So userspace must provide a copy of
this header with appropriate typedefs as well. (This should probably
be fixed in due course.)
<linux/dtrace/universal.h> defines constants and types used by virtually
everything in any way related to dtrace.
- dtrace/include/dtrace/*.h contains headers that are not shared with
userspace, included as <dtrace/blah.h>:
- <dtrace/types.h>: this contains the kernel-side definitions of types
used in the shared headers. (It has not been 'gardened' in any way,
so probably contains a lot of other types as well). This header is
installed into the same place as the shared userspace headers. This
header needs a bit of care maintaining, as not everything kernel-
side is allowable in it: see below.
- <dtrace/provider.h>: The provider API, and its corresponding
<dtrace/provider_defines.h> defines header. This includes <dtrace/types.h>
itself, so should be standalone -- however, this has not been in any way
tested yet, unlike for the userspace headers. This header is also
installed into the same place as the shared userspace headers.
- <dtrace/dtrace_impl.h> and <dtrace/dtrace_impl_defines.h>.
These headers contain definitions used only by the DTrace core, and are
not installed anywhere.
Note that because of the rules regarding kernel-specific types in the UAPI
DTrace headers, a number of uses of CONFIG_64BIT and CONFIG_BIG_ENDIAN have been
reverted to their Solaris-era-and-userspace _LP64 and _LITTLE_ENDIAN forms;
<dtrace/types.h> translates between the two. (We have also fixed up a
couple of places in core DTrace where the nonexistent _BIG_ENDIAN was used.)
There are several things called dtrace.h now, which might get confusing:
- dtrace.h at the top level is for the DTrace core and included providers
alone. Anything goes in here. It is never installed anywhere, and not even
standalone providers can see it.
- dtrace/include/uapi/linux/dtrace/dtrace.h is shared with userspace and with
standalone providers, and needs to follow the same rules as all such shared
headers.
We have two new packages, dtrace-modules-$kver-headers and
dtrace-modules-$kver-provider-headers; to make it easy for people to Require
them, they provide features named 'dtrace-modules-headers' and
'dtrace-modules-provider-headers' using the same incrementing API version number
as 'dtrace-kernel-interface' (both currently 1). dtrace-modules-headers serves
much the same purpose as dtrace-kernel-interface, tracking changes in the
userspace/kernelspace API: the dtrace-modules-provider-headers version number
tracks changes in the DTrace core/provider API.
The top-level Makefile has acquired two new rules, headers_install and
headers_check. The former is a simple emulation of the top-level
headers_install rule, and is used by the RPM build system. The latter checks to
be sure that the userspace-side headers can be #included on their own
(modulo only <unistd.h> et al, as above), and that dtrace/ioctl.h has all its
types in scope with appropriate sizes, since if they aren't everything will
appear to work until userspace tries to invoke the ioctl() and gets an -ENOTTY
error.
The latter is particularly difficult, since the size-checking machinery only
works for kernelspace builds, and the headers_check build is userspace. So that
machinery hacks up a sort of halfway-house to the kernel environment, enough to
do the ioctl() size checks but not enough to #include arbitrary kernel
headers (among other things, there are no CONFIG constants here). This means
that changes to those kernel-side headers which are included by this
machinery (in particular dtrace/types.h, dtrace_os.h) need to include a
headers_check run to make sure that machinery hasn't broken. In general,
surrounding suspect definitions with an #ifndef HEADERS_CHECK should suffice:
nearly all of dtrace_os.h is so surrounded (and if we ever move dtrace_id_t out
into the shared headers, as perhaps we should, this problem will become less
serious). (Feel free to make the headers_check dtrace/ioctl.h compilation
environment more like that used for the real kernel.)
Signed-off-by: Nick Alcock <nick.alcock@oracle.com>