From owner-cvs-sys Wed Oct 25 13:56:46 1995 Return-Path: owner-cvs-sys Received: (from root@localhost) by freefall.freebsd.org (8.6.12/8.6.6) id NAA14365 for cvs-sys-outgoing; Wed, 25 Oct 1995 13:56:46 -0700 Received: from phaeton.artisoft.com (phaeton.Artisoft.COM [198.17.250.211]) by freefall.freebsd.org (8.6.12/8.6.6) with ESMTP id NAA14340 ; Wed, 25 Oct 1995 13:56:38 -0700 Received: (from terry@localhost) by phaeton.artisoft.com (8.6.11/8.6.9) id NAA19391; Wed, 25 Oct 1995 13:47:35 -0700 From: Terry Lambert Message-Id: <199510252047.NAA19391@phaeton.artisoft.com> Subject: Re: SYSCALL IDEAS [Was: cvs commit: src/sys/kern sysv_msg.c sysv_sem.c sysv_shm.c] To: bde@zeta.org.au (Bruce Evans) Date: Wed, 25 Oct 1995 13:47:35 -0700 (MST) Cc: bde@zeta.org.au, terry@lambert.org, CVS-commiters@freefall.freebsd.org, bde@freefall.freebsd.org, cvs-sys@freefall.freebsd.org, hackers@freebsd.org, swallace@ece.uci.edu In-Reply-To: <199510251506.BAA10257@godzilla.zeta.org.au> from "Bruce Evans" at Oct 26, 95 01:06:56 am X-Mailer: ELM [version 2.4 PL24] MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Content-Length: 14129 Sender: owner-cvs-sys@freebsd.org Precedence: bulk > >usage from not knowing about the stack? I think you are going to > >have to burn the cycles on an opaque function call in any case. > > I hope that null conversions won't cost anything. Conversions of > the form `int fd = uap->fd;' (actually machine-generated code to > load fd from an ABI-dependent offset from `void *uap') may even > have a negative cost if they happen to load fd into the right > register at the right time. It may have zero cost (a *net* negative cost), I agree. > >> Since we don't control foreign ABI's we shouldn't assume this. For > > > >That's fine. The size of arguments in iBCS2 and BSD is 'int'. It's > >either 'int' or 'long' or '*'. > > Which one? In NetBSD it's register_t, which may be longer than an > int. This causes problems. Yeah. They ignored the definition of "int". That's a problem. The real problem is the lack of atomic sized types and the use of "short" as a synonym for "16 bit value", "long" for "32 bit value" and "quad" for "64 bit value". The real screwup is when int goes greater than 32 bits, the standard *stupidly* requires long to go up as well because it can concieve of a maximally sized type named anything other than "long". That's a bug in the standard in not having mechanisms for obtaining sized types. For a 64 bit int (requiring a 64 bit long), short is either 16 or 32 bits (undefined) and we lose access to either 32 or 16 bit types (respectively). I guess the real question is is there an overflow condition for some (long)lvalue = (int)rvalue; I'd say there isn't, simply because no one has ever expected int's to get bigger than longs, and so the content range isn't there. The only failure modes are in pseudo randomness depending on integer overflow. > >So we take a hit when processing these non-standard mechanisms; we do > >so through the system call table for the ABI, so we will be taking > >a function encapsulation hit anyway. > > Inlining should remove the hit. Perhaps given smarter encapsulation > functions, the hit from syscall() could be removed: call the encapsulation > function directly from Xsyscall() and duplicate what syscall() does > (copyin(), etc, iff necessary) in each encapsulation function. I suppose this assumes that the compiler will correctly adjust the register graph for the inlined assembly's register usage? I don't believe GCC is capable of this now, and I *know* Microsoft's compiler dies when this happens. I think using registers for calls and inlining are antithetical. You may be able to allow the optimizer to choose register passing for you (or not), but the actual choice will vary based on optimization level, etc.. I think this is a tricky failure mode that would require explicit register designation (via #pragma?) which would *decrease* portability, not increase it. > >They are padded to the default bus transfer size for the machine, > >which is supposed to be 'int'. > > >I'd argue that 'int' was the wrong size [on the alpha], not that > >there was extraneous > >padding. > > This may be true if you control the ABI. You *do* control the ABI. You are either running a known ABI paradigm (ie: int push, sizeof(int) == sizeof(register_t), etc.), or you are running a compatability ABI, in which case you know at binary load time and can force alternate argument call gating without much trouble (but some runtime overhead: to be expected in any case of non-native code execution anyway). > >I'd really dealy love to know how there could be an endianess issue, > >considering system calls are only ever going to run as compiled code > >on one endianess of machine. > > To run user code on one machine and syscalls on another. I wouldn't > want that. I agree. The main issue I'm trying to consider here is emulated execution environments on the order of Xenix 286 support. Specifically, I have code for running a process in an emulated x86 environment and making system calls that are serviced by native code. The reason for doing this is running Intel iBCS2 code on PPC and Alpha. Clearly it's insufficient at present, without the ability to do a full 386 emulation, but it's sufficient for kernel interfaces to be allowed to be native rather than emulated as well. It's a sound concept, but it goes to hell under close approach, like what you'd have in wrappering all the system calls, or using register passing. > >> >What does it do? What use is the change? > >> > >> It avoids scattering unportable casts and ugly macros to perform them > >> throughout the "machine-independent" code. Now we have only unportable > >> casts. 4.4lite2 has slightly less unportable casts and ugly macros. > >> NetBSD has much less unportable casts and ugly macros. > > >The structure casts, I presume? > > Yes. Structure casts are not a portability problem (as below). > >The answer is to compile with the packing being the default for data > >matchup -- in the alpha case, 64 bits. For devices that need it, use > >packing #pragma's to tighten them up on a case by case basis, in the > >header file defining the structure. > > That won't help much. The problem is that syscall args don't form > a struct. Even if they are in memory, the packing may be different. This is not allowed to be the case. Either the nature of the push must be changed to ensure a register_t sized push, or the structure packing must be specifiable at declaration time, or (preferrably) both. This is the current case with GCC and AIX/Sun/DEC/SGI compilers, so it shouldn't be an issue. > This can probably be handled by using __attribute__ ((packed)) for > every arg to enforce a particular layout. But that would only work > with gcc. I suggest using machine-generated code of the form > `*(type *)(base + OFFSET)' where `type' and OFFSET depend on the arg. Code readability is an issue. The vnode_if.h file is an allowable exception because of the export layering used in vnode.h's inclusion of the file. The general case of machine generated code is a bad one for code readability. > >Aligned element accesses are faster anyway. > > OFFSET would usually be a multiple of the alignment so it would be > easy to calculate :-). Well, as long as sizeof(int) == sizeof(register_t), it wouldn't ever be an issue in the first place unless you tried to send arguments as themselves and the argument in question too multiple pushes to get there. That's only the quad arguments on Intel, and since the off_t definition is a standards violation, there's no problem with making an interface extension in those cases (already enumerated) to deal with it. > The ABI is a convention, and can't be changed. The ABI is an agreement between user and kernel space, and is abstracted from implementation by (1) binary file identification, (2) execution class, and (3) call gate. That means we can vary it without changing the underlying implementation, with the cost being restricted to the abstraction layering (already in place as additional overhead anyway for ABI class emulation) and additional overhead for non-native ABI's. > >> There may be no correct size. A size of 3 ints wouldn't work for > >> open("foo", 0) if the caller has perversely passed 2 args on the stack > >> at the top of the address space. Where are the ABI specs that disallow > >> this? > > >There are none. However, you are wrong; it would work, you'd just > >get a garbage value (stack direction grows the right direction for > >the third argument to be optional). Since in that case the garbage > >value is unreferenced (or the call generated a prototype warning > >...not 8-)), then it will work. > > I should get an EFAULT return if the args are at the top of the address > space like I said. I think this would be hard to enforce without dictating either stack probes or argument count pushing (ala the VAX Calling Standard) in any implementation. Consider a quad first argument on an Intel architecture. > >I have to point out that it would then be impossible to make system > >calls without prototype references. If this is a "fix" for the quad > >word passing problem (which is a "non-use of system call prototype" > >problem), then, isn't this just making things worse? > > It requires either prototypes or passing parameters of the correct > type just like it always did. Since there is a lot of broken code > out there, most compilers use inferior parameter passing conventions > to support the broken code. And? I don't see that as a problem. It's not possible for FreeBSD to dictate coding practices; unlike Microsoft, it isn't 70% of the market. Because of this, it is necessary to compromise to make the code run. We already do this in a number of cases. You might as well mandate that TERMIOS be the only tty control mechanism, and all applications that expect to run on FreeBSD "must conform". I wouldn't have a problem with an alternate execution class, and potentially trap gate, to cause there to be a "very fast" calling mechanism that is there *as*an*aternative*to*the*default* "portable" calling mechanism. But mandating that the default be such that the majority of code on the net would require patching to make it run (admittedly, mostly header files) is *bogus*. > >Callee pop only works when using the same stack. When you get to the > >kernel, the kernel thread (or just process) will be using its own stack, > >so that argument won't wash. My complaint on callee pop as a more > >fruitful pursuit was based on kernel-kernel calls, not user-kernel calls. > > Of course you wouldn't want to use it across interfaces, but how do > you stop a C compiler that is optimized for compiling user code from > producing wrong code for interfaces that it doesn't support? (Writing > masses of interface code in assembler isn't acceptable.) This is an interesting question... but I can't see a situation where it could be applicable. There *must* be a mechanism for making OS service requests to implement the standard libraries. There is no other option to avoid isolating the program from all of it's I/O (I/O, at least, must use system interfaces, even if everything else is faked, including lying about the time, etc.). The answer to your question is that the compiler must know about some system interface mechanism, then the system must provide the requested mechanism. I object to needing to include standard system call prototypes to allow their use. I put up with lseek/truncate/ftruncate/mmap BS because it's impossible to support all of quads, 32 bit ints, and trap-based call mechanisms and not violate either ANSI C, POSIX or both. I just happen to disagree with where the violation takes place. Note that prototypes of system calls screw up your ability to properly utilize the syscall(2) call gate mechanism the way it was intended. A user space checkpointing mechanism that defines it's own open interface and calls the real open call via syscall (to remember the file names) will fail to compiler when the inlined open conflicts with the user definition. And then you're back to unprototyped or buggered header files (which are *very* system dependent) to implement your application interface. It's a reduction in "portability to BSD". As a "marketing strategy", on should make an OS easy to port to and hard to port from. Idealogically, I'm opposed to the "hard to port from" part of this, but no matter what ideology you follow, making it "hard to port to" is a big, big mistake. > >Right now the screwable functions are truncate, ftruncate, seek, lseek, > >and mmap -- and mmap() is bogus because of the kernel address space > >restrictions currently on "vmio". The others are in violation of one > >or more standards because "quad" isn't an allowable type. Might as > >well violate them further by using inline references to the __syscall(2) > >instead of syscall(2) to get to them so that: (1) they are undefined > >without proper header inclusion, and (2) the padding is guaranteed > >(as the __syscall(2) states in the man page). That at least would solve > >the screwups without adding to them. > > I've thought of changing the compiler to always check format args and > print a diagnostic if there is a mismatch for a quad arg. Something > similar could be done for the above functions - make them builtins > and complain about type mismatches for them. For functions it's > easier to silently DTRT (promote the args). That's one approach, on the order of printf argument checking, and probably requiring a higher-than-default error/warning level to cause to be active. I think most people will fail this test. If you put a mechanism like this in, it's hard to argue that the compiler should bitch at the user instead of just pushing a high order 0 and "making the code work" -- turn the error to a warning. Probably a "correct" approach would be to either (1) push 64 bits per for all arguments, using type identification to decide on pushing a 0 for the quad high order dword or pushing a value that really exists at the time of the call, or (2) choose a different violation of the ANSI C and POSIX standards -- interface extension -- instead of passing quad's for the default truncate/seek/mmap call interfaces. The additional calls would only exist as inlines, not in libc, and would thus be unusable without prototypes (qtruncate/qftruncate/qseek/qlseek/qmmap). And you wouldn't even implement qmmap until the vmio file windowing is fixed in the kernel VM system. I think that changing the interface in such a way as to cause large amounts of "third party" (ie: ftp'able and/or commercial) code to "show its brokenness" is a mistake. The quad system call arguments are mistakes of this family. In any case, I think the benefits are questionable, and should be explored as an execution class other than the default execution class (ABI) before they are integrated. Even after they are integrated, portability would dictate that their use be optional. Regards, Terry Lambert terry@lambert.org --- Any opinions in this posting are my own and not those of my present or previous employers.