So the previous set of compiler directives singlehandedly fixed a wide-ranging set of errors when I tried to build the Linux code. But there were plenty more in there -- here are some others I found interesting to track down.
The porting code I had brought over included a couple already, such as
likely and unlikely, which are hints to the compiler about the path it would do better to optimize for:#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0)
There were a couple more obvious ones, such as
__init and __exit used to designate the startup and shutdown functions for a Linux module. Since this code is all going to be one "module" (e.g. kext) on OS X and it comes with its own API for startup and shutdown, I just disabled those:#define __init #define __exit
Another simple copy from the Linux source was BUILD_BUG_ON, which aims to cause a compiler error:
#define BUILD_BUG_ON(condition) \
((void)sizeof(char[1 - 2*!!(condition ? 1 : 0)]))
While a little obscure, this relies on the fact that the calculated value will either be compiled out of existence or cause an error:(void)sizeof(char[0]) // Compiled away (void)sizeof(char[-1]) // Compiler error
The trickier ones were more embedded into the way things work in Linux, such as debugging.
WARN_ON prints a message when the provided expression evaluates to true, and returns the value for use in an if statement. So the usage looks like this:if(WARN_ON(foo == 3)) {
return -EINVAL;
}
Well, WARN_ON causes an Oops, similar to a panic on Linux except it doesn't stop the machine dead. OS X doesn't have an apparent analog. Further, the Linux implementation quickly descends into large functions in printk.c, which also doesn't have an OS X equivalent. Instead, I went with the workaround of defining a global function to save a bug to the log. And because it's not expected to happen very often, I went with a fairly straightforward one and then saved the message to the log with IOLog. The only tricky bit is that is uses a printf-style string, so I had to handle variable arguments:static void porting_print_warning(char *fmt, ...) {
char buffer[200] = "AppleIntelWiFiMVM FATAL ";
char *remainder = &buffer[24];
va_list args;
va_start(args, fmt);
vsnprintf(remainder, 176, fmt, args);
va_end(args);
IOLog(buffer);
}
That made the
WARN_ON implementation an easy copy from the Linux source, just replacing the function to call to log the actual warning:#define WARN_ON(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
porting_print_warning("at %s:%d/%s()!\n", \
__FILE__, __LINE__, __func__); \
unlikely(__ret_warn_on); \
})
The similar
BUG and BUG_ON are a little trickier, because they're supposed to print a message and then panic the kernel. Well, the panic part is OK, but I think the message part is the problem -- I believe IOLog saves to an in-memory buffer, and something else flushes the buffer to the system log file. And I expect when the line after IOLog is panic(); probably the buffer never gets flushed to the log file. I've read that OS X saves the panic data to nvram in order to display it on the next boot, exactly because nothing can be reliably saved to disk in the event of a panic. So here's my implementation, which probably doesn't really work as desired (a note for future investigation):#define BUG() do { \
IOLog("BUG: failure at %s:%d/%s()!\n", \
__FILE__, __LINE__, __func__); \
panic("BUG!"); \
} while (0)
#define BUG_ON(condition) do { \
if (unlikely(condition)) BUG(); \
} while (0)
Now, you may be curious about the specific syntax used in the last couple examples (I know I was):
({...; foo;})
do {...} while (0)
The first, it seems, is the way you write some code that does something and then return foo from a macro that will be substituted into the condition of an if statement (shown here after substitution):
if(({...; foo;})) {...}
But also works (albeit suboptimally) as the result of the if statement instead of the condition:if(foo) ({...; foo;});
The other is how you write a macro that returns nothing and may be used as the result of an if block with or without curly braces:
if(...)
do {...} while (0);
orif(...) {
do {...} while (0);
}
Without the do/while, you might end up with something like this after substitution:if(...)
IOLog("BUG: failure at %s:%d/%s()!\n", ...);
panic("BUG!");
Ah, code that sometimes logs but always panics. That would deserve its own Oops.
No comments:
Post a Comment