 Hello everyone, my name is Tim Bird and I'm going to be talking today about kernel coding the upstream way. So it's my presentation, I'm a Principal Software Engineer at Sony Electronics and I included the abstract in the slides. That's just for people who view these slides afterwards so they can kind of get an overview of the talk. What I'm going to be talking today about is basically coding in EMS, that second bullet is the most important thing but I'm going to start with a little bit of an explanation about kernel development priorities and then at the end I'm going to go over some conclusions and give you some pointers to some resources. So let's talk first about kernel development priorities. So it's very, very important, Linux is used in a lot of places, it's super important that it's correct, that's kind of obvious, that consists of do things work properly and also security I would say falls under correctness, does it is it resistant to attacks and that type of thing. Then after that the next priority is performance, of course the kernel developers want things to be as fast and as small as possible and the kernel developers want as much resources of the compute environment available for user space so that's really important. And then the last priority and it's a very big priority is maintainability. Can things be maintained, does it require superhuman effort or can mere mortals come and maintain the kernel. So if you look at these in upstream priorities they're really kind of interrelated. So maintainable code has less bugs over time and therefore leads to correctness so you could make the argument that maintainability is actually more important. It's kind of like rock paper scissors where correctness is more important than performance, maintenance is more important than maintainability but if you don't have maintainability you're going to lose that correctness and so that's also very important. And the reason I bring those up is I think it provides the rationale for a lot of these coding idioms that I'm going to be talking about. So what are the coding idioms? I've divided the things I want to talk about into kind of these four main categories error handling structures and pointers, things having to do with code efficiency and then maintainability. So let's just dive right in. So let's start with null pointers. So when you're writing code for Linux kernel you should check your pointers for null on return from allocation. So hopefully that's kind of a first level computer science programming idiom but there are some kind of nuances in the kernel a little bit different. So first you should check in the subroutines immediately after allocation but you don't need to check for null if you can validate that null is not possible on the code path. So if you're calling your routine that itself is allocating the pointer and doing a null check you don't need to check that null because we don't want extraneous checks all over the place. However when you're doing this you do need to be mindful that code changes over time and so if you look at where you're getting your pointers from and you see that refactoring could easily introduce a code path where the null is not checked then you can go ahead and add a check. Another thing about null pointers is you'll see this a lot in the kernel people will have a macro called bug on which emits a bug if a pointer is null. That's actually kind of discouraged. The hardware will catch bugs on null pointer due references. The kernel has a down at address zero it's got a page of zeros and there's fault mechanisms for all the architectures to handle that so you really don't need to put explicit bug on's in your code. An error that you might see in oops from the kernel or panic would be bug unable to handle kernel null pointer due reference at you know some address I'm sure you've all seen that. That's a null pointer due reference that was some place that the null pointer was not caught. The other thing about null pointers is freeing null pointers is just fine in fact it's encouraged to just go ahead and free the pointer without doing an if on it first. So don't do if pointer k free just k free the pointer the kernel allocation mechanisms and the free mechanisms they know how to deal with null and so you don't need to use the extra cycles to check before you call. There's a lot of semantic checkers that will go through and find null due references so those are very useful to run a cost now has as some but anyway so that's that's good stuff on null pointers just a couple more notes about null pointers so null is not the same thing as zero zero is not the same thing as null well they have the same value internally it's it's a word value that's all zero but they have different types and just to keep people's mental model correct don't don't do things like assigning a pointer zero if you want a null pointer assign it null and it is is vastly preferred to check for null with if not pointer rather than checking against a specific value if pointer equals equals null so if you look at the stats in the Linux kernel I did a quick look at the Linux kernel to see how people were checking for null after came Alec which is one of the main routines used to allocate memory so you see 766 are using if pointer equals equals null which is the kind of the discouraged form five thousand if not pointer and then interestingly about a thousand where the null check was not within the next four lines so I'm sure there's no checks in there somewhere but just make sure you check your nulls next idiom is go-to's now normally go to statements are considered bad we've all been taught in our computer science and programming that you can create bad spaghetti code but the core kernel uses go-to's in a very specific way for error unwinding so the structure of the code when using go-to's in the kernel is you allocate resources you perform operations then you return success if there's some errors then you deallocate the resources in reverse order using labels and then you return failure so let's take a look at what this looks like in a very generic thing you have some function that does do a and if there's an error you go to out undo a you do be if there's an error go out undo be and then the return success and then the opposite order with the go-to label so let's look at an actual example from the kernel if you see this is from XFS open devices you don't need to read all the other code but just looking at the structure of the go-to's you see how they're nested and then the return path they're nested in a reverse order with labels that clearly identify kind of what's going on out close RT dev out close log dev and then just out and just a couple of more things I want to say about this here's here's the code kind of expanded out more you'll see that the success routine on the left there has a lot of well that's all the code that does the allocation and that's the first part of the routine the second part of the routine is all the error handling so you've got this separation between the flow of control when the code is operating correctly or when there's no errors and different things the other thing about this example is that you'll see the error errors are actually allocated pre-allocated ahead of time well these errors that's how things are getting detected but it's going into a variable that's going to be used in the final return and so just a couple of notes on the use of go-to's so it's useful to set up your error or whatever variable you're using error or ret ahead of time notice that the success return is in the middle of the routine so as you're looking at kernel source it's very common to see the the success return instead of at the end of the routine to see it in the middle of the routine the error handling this what what this does is it puts the error handling code out of the main flow so error paths which should be unlikely code are out of the main function cache footprint and the success case in the case where you have success you have less jumps or possibly shorter jumps now the one good kind of tip is to use an out underscore prefix on your go-to labels people also use other things like air underscore or there's no end of weird things but using the out prefix is kind of more common now having said that the preference is to use out underscore that's what most of the kernel uses but follow the style for your subsystem so and then the the other thing about go-to labels is try to have the rest of the label be descriptive of whatever undo your operation you're doing so underscore free underscore release underscore unlock and then in terms of resources for go-to's here's here's just looking at some of the labels that are popular in the kernel you notice that the most common go-to label in the kernel is out with about 15,000 instances but error colon is also a pretty popular one my personal preferences to avoid error and error out seems like out is winning the battle here so that's just when you're making up your labels you know use use something sensible now moving on to return values so just like in the Unix programming model in user space return values in the kernel use success they use return zero to indicate success on failure you will return a negative error number and that negative is important so the exception for this rule is that things that are functions that have a name that's a predicate like for instance PCI dev present if it's returning something that's obvious from the name of it that it's a true false just return zero for faults and one for true otherwise your if statements read weird so and then for functions that return a pointer you should return one of three values you should return either valid pointer null or error pointer with the air no that's a case you need to sneak some more information into the return value so the caller can check that value to see if see if there's an error with macro called is error and then they can convert it back using error pointer error so here's what's the rationale for for doing things this way well return codes have a limited value range right they're limited to a single integer or pointer basically one word is what you can get back so you have to overload things again you have to put the error name space if you will into the same space as the return value space but we have strong conventions here that helps keep everyone sane the error numbers if we if you need to look them up they're over an include UAPI asm generic error no H and make sure you use the right one for your subsystem and whatever the condition is that you're trying to report it's it this is actually pretty important so look at related drivers in your subsystem please do not make up your own error codes we have enough error codes to cover all possible cases that the thing here that's important is that air nose are part of the user API so this is part of the covenant or the promise between the kernel and user space user space is expecting a a certain number of errors for certain conditions and so even if it doesn't look right to you if it doesn't seem like it's the right code if that's what the subsystem is reporting then you should follow the follow those guidelines so you don't want to break your user apps by having your driver excuse me having your driver report some weird air no so excuse me some of the popular air nose are in the table here Ian Val you know ma'am you know devs and so you're gonna find it's pretty easy to find out what is the appropriate arrow sometimes there are debates but usually comes down to you just don't break user space just make sure you return what user space is expecting and can deal with and then return values for pointers you use these three macros error pointer in the thing that's returning the pointer and then is error and pointer error these are all defined in include Linux error it's pretty easy to use if you have an error condition instead of returning a null if you need to give an air no back then you just return error pointer with minus air no and then the caller who the routine that's receiving the pointer you should check that the pointer is an error with is there and then if you need to convert that back into an air no to whoever called you you use pointer error and then if you actually you can also use pointer error if you need to use that code in a print K and so excuse me so the general rule of thumb is you should always use is there before pointer error you will find some exceptions you can use pointer error without using is there if there's only one air no possible so we don't want to we don't want to build like a bunch of case statements where we're checking for you know Ian valid or you know it you know before it individually as nodes you just use is there and then and then branch as you need to so but you do occasionally see this idiom where if you know there's only one possible value people will just use pointer error and just compare with that one static value without the additional check the main thing to remember here with nulls is that just remember that null is not the only return value if you get an error you do have the capability to return some information an air no that can be used upstream of your routine let's see wrong okay in terms of function pointers so I I the kernel has its own brand of object-oriented programming and there is extensive use of function pointers in the kernel in structures so instead of using C++ virtual tables the kernel uses structs with function pointers now this comes in kind of two varieties there are structs that have there are structs that have a mixture of data and functions so things like struck device driver has a bunch of data but it also has seven function pointers just in the same structure but then there's also excuse me there's also some structures that are just purely for holding function pointers and then you use a fun a pointer to them included in other structures so the the biggest one example of this is struck file operations that has about 30 different function pointers depending on your kernel config and then so that here's an example of how you would use those the definition of file operate struck file operations is over in Linux FS dot H and you can see you know for for something that looks to the Linux kernel like it can do file what looks like it's a file it has LC read write read it or read write read write it or in a whole bunch of other functions but you don't have to use all of those you don't have to declare all of those I'll talk about that in a second so over in drivers care RTC you can see how you would declare an instance of file operation structures in this case called RTC F ops and you you assign the the variables the actual functions in your code that will be those pointers and then inside some other structure in this case RTC dev you'll actually have a reference to that instance of the file operations so in this case RTC F ops now just a note here on oh let's see let's talk about assigning so assigning the function pointers are assigned using a relatively new C stat tag structure initialization so that means you can you can assign as many functions as you need out of the total list of functions so in this case I think Zilli bus define maybe 10 or so of the routines for file operations and the system usually knows how to deal with with uninitialized function pointers what happens is because you're using C tag structure initialization the compiler will default all unnamed structure elements to zero or null and so that handles it for you and then the routines that that call through these function pointers will check for null if that's appropriate or whatever so so basically you have these object methods that are defined at compile time one thing that's kind of interesting is you think with all these these pointers flowing around that there's a lot of dynamic coding and things have things getting changed at runtime it's really not the case it's fairly uncommon for function pointers to be changed at runtime after initialization so once they're set usually that's that's what they are here's just some conventions for function pointer names you if you're going to define a new structure that has a bunch of function pointers you should call it something underscore operations and here's a bunch of ones that are already in their SMP operations tty operations seek operations for sequential file operations and of course the big granddaddy file operations these are this is the frequency of use 16 oh sixteen hundred times as file operations used in the kernel and then the name for a declaration of the structure is something something underscore ops or f ops or something like that and the name for the pointer member pointer which is a member in another structure is just the something ops and then the function names themselves the things that you assign into the structure just the convention is you have a prefix that was is like driver specific or subsystem specific and then underscore and then the operation name and so if everybody follows the conventions it helps to make it easier for people to follow the code just the rationale for this well some of this is obvious but function pointers allow for polymorphism so the caller does not need to know the type of the object so the file handling code in Linux doesn't need to know you know whether it's an RTC device or some other kind of device this is more straightforward and faster than virtual tables there's less runtime overhead and then if you look at really what operations that struck is essentially a hand managed virtual table and so it's it's similar to virtual tables is just hand managed this is transparent it's fairly easy once you understand the mechanisms to see what's going on the downside of course to function interaction is that you can't do compiler optimization you can't inline these functions but you really can't do that with any kind of polymorphism anyway so so it's really it's just the way it is there's an excellent description of this stuff in by Neil Brown function pointer resources just check out that article and I'll be in that net okay now moving on I'd like to talk about macro that's used quite heavily in the kernel called container of so container of is a macro that converts a pointer to a member of a structure to into a pointer to the containing structure so if you look at this example here we have a hardware structure clock hardware structure and that is a structure that's embedded inside another structure a struct clock divider and you can use container of to move backward from the internal object to the containing structure and there's actually a macro defined to clock divider which uses the container of macro to perform that operation so here's the definition of container of it's it's a little bit dense in terms of reading the code it's about five it's five lines long the middle three are not not too critical I'll but I'm gonna I'm gonna go through this a little bit just to show you what's going on here so the build bug on those middle three messages that is to check that the pointer that has been passed in has the same type as the item in the structure is supposed to be pointing to that's just that's a compile time check that there's no overhead at runtime and the interesting thing about that if you if you want to look at it in detail is this just shows how to do type checking with a macro if you if you take that build bug on out then really the container of just boils down to these two lines m pointer equals voice star pointer and then own pointer minus the offset of so the first line there is just the c casting for the to change things into avoid for the pointer arithmetic that's gonna happen on the next line offset of generates a compile time constant with the offset of the member item and then the only operation that perform which is kind of interesting the only thing that happens at runtime is that minus right there that's the old everything else you see evaporates this container of uses no registers it doesn't there's no conditionals actually executed in here so that's really interesting to me that these kernel macros that you the look really really complicated if you actually dig dig into me find that they have a way of evaporating into very very efficient implementations so container of resources here's here's some links if you want to read up on that so the next thing I want to talk about is kind of a higher level concept abstract concept called embedded anchor so an object is this is when an object is embedded into another one and the access to the parent object is gained by using a pointer to the the member so this is used a lot a lot of places in the kernel it's often used with container data structures so things like lists hashes and trees use the embedded anchor kind of coding pattern this allows objects to be in those containers instead of the container pointed to the object the container points to the anchor inside the object and you can have as many anchors as you want inside the object it's some common to have more than maybe three or four but but you can have as many as you want so there are examples of moving from the anchor to the object some of the common examples are interface to usb dev k object to device and then list entry of course is used all over the place so embedded anchor that the way this design pattern works is you use container of to backup from the anchor to the object it's used in all kinds of different collection types so doubly linked list singly linked list red black trees hash tables and interesting to note that not that these data collection types they use the same anchor so both hash tables and lists use struct H list node so you'll it's there's some efficiency there in terms of the code that's used so you might you might think that each of the collections has its own type but they don't this is the reverse of kind of how we normally think of how collections operate normally kind of you are used to you know the standard programming you'd see a list struct that would have a separate node allocated the points to each object in the list and so the the object that's part of the collection is different from the object that's being contained in the collection the Linux kernel that object structure is actually embedded so the object structure that's being put on the container or in the in the list has the the collection node structure embedded in it as an anchor however this is also used for other things besides collection so you see this for struck k object struck dev and many many more places so it's a very good idiom to kind of learn how to use so here's an embedded anchor diagram this is an example of multiple anchors kind of embedded you have a struck k object that's in stride of a struck device that's inside of a struck cxl and you can move between the two using appropriate macros and here's an example of how you have a struck h list node you can have multiple objects that are on a list in this case this is just the standard doubly linked list but one of the objects could also be on another list and so you can see that the list code only has to refer to its structures and it doesn't have to know anything at all about the objects that are contained on the list so some of the the notes on this is that this allows for the data structure code to be independent of the node type it does not require a separate allocation for the data structure for the node it's your anchor is allocated along with the parent object and this also allows passing around the pointer to the anchor if that's easier and more appropriate instead of the object and and this allows you to defer the referencing the object until needed which can save some cycles okay so moving on in lines if you're thinking about writing a macro well write an inline instead where possible the reason is that in lines are in lines are better because you get type checking of parameters less side effects okay other than that if you're not writing a macro and I'll give you some other examples when it's appropriate don't declare your functions as inline okay that is don't use in lines on kind of your normal code it's it's tempting to say oh I want this to run fast I want it to be in context of my routine so I'm gonna make this in line GCC will usually do a better job of you than figuring out in lines it knows what architecture running on number of registrants it's really easy for a developer to reason about the architecture they're writing the code for not so easy to reason about all of the other architectures that the code might run on so let the compiler do the work of inlining yes I know that there are already over 80,000 inline declarations in the kernel and about 1200 that use always inline but don't follow that example just don't don't declare your functions inline so they're okay so having said that there are some guidelines for when you should do it when you really know that the code you want to be that you want to declare as inline so if something is supposed to boil down to a single instruction like compare exchange okay you can declare that as inline also for extremely short code sequences if you know it's gonna boil down to just a few short lines of code that'll then that's okay and then also for stubs that the compiler will optimize away so you see a lot of function stubs that are in header if desk and it's okay to declare those as inline so here's some here's some resources on that now let's talk about macros having said that you should avoid macros at all casts I'm gonna talk about them anyway so let's talk about what if you're gonna declare a macro make sure you avoid side effects this is kind of CS 101 so the problem with macros of course is that whatever it's just a textual replacement and so you can have weird side effects if you're not careful the classic example is if you define max as I've shown here then if you if someone hands in n plus plus n may get declared or incremented twice but only sometimes so this is just a notorious source of bugs so the solution for this is don't use macros use inline functions instead of macros use temp variables in your macros if you have to do this to avoid that particular side effect and find an existing kernel macro the Mac kernels been around for like what coming up on 30 years now you don't there's probably a macro that already exists that will do what you need here just for fun look at how easy it is to declare a side effect free macro this is this is the definition for max in the Linux kernel so what looks like a very very simple operation if you're gonna actually do type checking and handle you know reduce side effects this is what it looks like so avoid macros if you can if you're gonna do it it's got to be at least this level of complexity to handle all the weird cases the next the next thing I want to talk about is do while you see this a lot in header files in the kernel and this is struck this is how to structure macros particularly macros that do multiple operations so that they can work in any context the the goal here is that all statements in the block are performed exactly once the rationale is that multi-statement macros they under certain conditions if you're not careful they'll act weird so for instance that that example I just had a perform food that did two operations if if you use it in this this first well let's see have you can use it in do while loops or you can use it in if statements that the problem child is this if statement that has no braces so if you if you expand that macro perform foo it under that if some condition without braces then it'll expand and unfortunately it'll cause that only the first only the first operation or statement of that macro will be performed which of course will be very very confusing these are very hard bugs to find so you see a lot of do while used in the kernel and if you have a macro that you is going to expand a multi multiple statements make sure you wrap it in a do while the other macro that you'll see a lot of in the kernel is likely unlikely and these are used to indicate the probability of conditional code and for and if statement so these are used to annotate the code they actually don't perform they don't perform any runtime function in the code but they're used by the compiler to structure the branches the normal meaning is that the compiler will avoid a jump in the case of the most likely branch of the conditional and that it helps avoid pipeline stalls the general rule of thumb is you should not use these if the lightning less of the branch is affected by the workload if the workload can make likely or unlikely the wrong thing then then don't do it you should just let the CPU branch predictor handle those cases there is a really weird thing if you have super high performance there's something in the kernel called static keys I'll let you read up on that so likely and unlikely are often used in our Linux error paths and some developers actually recommend against them saying that well the CPU has branch predictors it's going to do better than you but not all CPUs have branch predictors so I'm an embedded guy and especially low end embedded sometimes the processors don't have branch predictors so if you can help out a low end processor shave some cycles off I think they're useful the general guideline for when to use them is only if you are confident that the take and not taken ratio of the branch is greater than 10 to 1 so if they're if it's 90% likely or more then that's okay but you should note the developers are notoriously bad at estimating your ratios if you if in doubt just leave it off and let the CPU deal with it so if deaths don't put if deaths in your seat code just don't do it if so if you if you have to do conditional comp code which you will have to do I mean and I mean compile time conditional code compile out entire functions instead of parts of functions and then use conditional code to inline functions in the headers so let me let me show you what this looks like this is the wrong way to do conditional code depending on a kernel config variable in drivers USB hit core so you can you have an if death here which that if death is only only applies if that particular kernel config is used so that is the wrong way to do it is to put the if death in the C code the right way to do it is to take the function that's being called by that conditional and put it up in and put the if death up in the H file right so you have hit dev dot H and you notice that depending on config USB hit dev you either got the the reference the external reference to the function or you've got an empty function a hit dev hit event notice over on the very farhand right it's just an empty function what will happen in the C code is that if that function disappears hit dev hit event the entire conditional will also disappear so there's no there's no penalty performance cost for doing it that way the rationale for this is that the C code without if deaths is much more readable and because there's no performance penalty it's it's it's very useful to structure the code that way now this may require refactoring your code so you got to be careful that the refactoring cost doesn't exceed you know the maintainability improvement when and one attribute of this or one aspect is that when you're debugging you need to remember that there's a lot of code that is not actually there in the binary so you don't basically all the if there's if deaths in there in the header files but you're not seeing them and so when you're function tracing or when you're debugging got to remember that a lot of the code in Linux kind of evaporates and then there's a special macro for testing config variable so you can actually structure it like C code it's called is enabled and you can use this directly in in your C code instead of some kind of if death and though even though it's a macro and it's mostly done in pre-processing it it looks like C code so here's an example from usb hcd unmapped herbs set for the set up for DMA us along routine so you can use that in C code and notice that it's even done in conjunction with regular C code that so because of that and ampersand ampersand there the the second part of that if is going to evaporate away if if the is enabled does so it's pretty tricky but it's pretty nice and then this is actually mentioned in the coding style guide in the kernel okay now on to print case so the rule on print case don't add more print case to the kernel boot up sequence yes we know your initialization is important and you want to see what's going on but the start-up messages are already long enough embedded Linux in particular wants to boot quickly and if everyone if every single driver populated tons of print case it would really slow down boot so don't think about what users of your code want to see because there are many many more people who are not using your code who don't want to see your boot up messages so it's perfectly fine to do debug level or to put them in temporarily but don't by default you should not be adding print K messages to the start-up boot messages it's preferred instead of calling print K directly to use the PR underscore levels so there are there are what like eight different message levels in the kernel and there's a print K wrapper for each one of them well actually there's way more than that because there are subsystem specific print case and you should use those if they're available because they'll print out additional information so like dev print K will put some additional device information like the device name or something and automatically and so there's a whole bunch of these the in fact there's a ton of them so don't go and nuts and make your own new print K rappers literally there are hundreds I there may be thousands of print K rappers in the kernel here are some of the popular print K rappers and for each of these most of these also have their own like eight levels defined or for the different message levels so there's I'm sure there's a print K in the kernel that exists already that you can use okay last idiom is a switch statement fall through so there was work recently completed to analyze fall through for case blocks so it there's a weird thing in when you're doing case statements sometimes it's more efficient to structure your cases so that one case can fall through to the next one and continue doing some operations it's not super common but it does happen and it's very hard to tell when that break is intentional or not or the or in this case the missing break so annotations were added throughout the kernel to make sure that all of the all of the cases where fall through occurs are actually labeled in that process several bugs were fixed and because we found out that the fall through is unintentional it just was a missing break by mistake so the the good news is that now we can turn on a compiler optimization flag or not not a warning flag gcc-w implicit fall through and because we've annotated all the places where we believe that is the intended thing we can now catch an unintentional implicit fall through is a new code so now you can turn this flag on and if you happen to have missed a break by mistake you'll get a warning and those warnings should be rare enough that it comes to the attention of people so the bottom line is if you do a fall through in a switch statement if you're going to have a break a case or a case without a break make sure you annotate it and you you do that with a comment okay so this is one of those weird cases where the compiler is actually looking at the comments in the code and actually knows something about what to what to do with this in this case issue an error message and there's a resource for that okay so i have gone through this stuff really really quickly and i just want to point out some conclusions so if you so if you look at these idioms your kernel coding is really an exercise in balance you're trying to take into consideration other developers who have to read through your code and and you have to take into account other developers who are are executing your code and following these conventions is is helpful for everybody in terms of maintainability so when you're writing code you're taking into consideration a balance between the correctness the performance and the maintainabilities maintainability and it's important to keep in mind that maintainers are a scarce resource and they're overworked and so we need to make sure we do stuff that makes it easier for them so even if it requires a little bit of extra upfront cost for you to structure things properly that it's better in the long run for the whole ecosystem uh one thing about conventions and consistency it's usually just best to do things the same way as everyone else so it's easier mentally for the maintainer if they're looking at a piece of code if it looks the same as other people's code it's easier for them to deal with so if your code if you look at your code and it's kind of different than the other drivers in your subsystem you should take a look at that and kind of figure out why so most of the idioms are really about performance and maintainability i mean you can't really capture correctness in the idiom except that you know the maintainability helps with correctness uh in terms of performance you see a lot of these idioms are about shaving off you know like one or two instructions uh in the path of the kernel and these things add up so the kernel is very fast and the goal is to keep it that way by using these types of idioms there's lots of very very advanced algorithms in the kernel our read copy update x-rays the way the locking is done so the upstream is very very conscious of the impacts of all the code on cash lock contention those types of things so a lot of maintainability comes from this consistency but and this is the this is the caveat the consistency is for your maintainer subsystem this not necessarily across the eternal kernel so if i've said something here that your maintainer disagrees with uh if your subsystem maintainer tells you to do things a certain way just do it that way and uh you're gonna have a much easier time so with that uh i would like to point out some resources that are available uh in this area there's a lot of stuff when i when i started writing this talk i thought that all of these were unwritten uh that's the uh abstract but i found that some of them had been written about and some of these are actually in the coding style guide um and uh there's a development model patterns that's in actually in the kernel documentation uh oh let's see that's on lwm.net uh but that's in linux device drivers so some of these things are documented uh i highly recommend that you go see read this uh excellent series of articles by neil brown talking about kernel design patterns it will really help you kind of get your mind in the right mindset for doing kernel development anyway i hope that this information is useful and if you have any questions you can email me directly i'm risking a lot by putting my direct email on there but uh you can find it in a get tree anyway um anyway uh i'll go ahead i have a couple minutes left here so uh if there are any questions i'll take a look at those now so i see uh one of the questions is by nishant menon uh context of is error uh is error or null is not exactly prevalent any reasons um i don't know uh i didn't even know there wasn't is error or null so maybe that's why it's not prevalent so uh so in terms of answering this stuff uh i don't know if i i guess i just answer online um okay okay so here's another question how does error pointer overlap with actual valid addresses on 32 architectures with 4 gig or more ram so the airno airnos i think are limited to in value to under 4k uh there there are less than 4 000 airnos defined and so if you look at what what that means is if a pointer is treated as an unsigned uh that then what you're going to do is you're going to chop off the top 4k uh addresses so basically you're losing the very very top most page of memory that you could possibly address by uh by putting by using error pointer and so if you if you combine the fact that you're not allowed to use addresses in the zero range and uh error pointer makes it so you can't use the address in the top range it's not a huge i mean considering you have four billion addresses losing 8k is not too bad um let's see uh here's a question from thomas lauri so have you found any of these idioms useful in code bases outside of the kernel uh or are they really just unique to the designs decisions and environment of the kernel uh that's a good question i you know i don't i don't see a lot of these used outside the kernel i'm trying to think of which of these i mean you know checking for null pointers and you know avoiding side effects on macros but like the way that the kernel avoids side effects on macros is pretty complicated and like the whole embedded anchor thing that's quite different from what you see in other places i you know i was taught in my early cs career to um to always put your link pointer at address zero in your structure which i suppose is a form of it but it's kind of a a degenerate form of embedded anchor uh so i haven't i haven't well i don't do a lot of c coding outside the kernel so i may not be qualified to say but uh from what i can tell i don't see a lot of use of these idioms outside the kernel um let's see uh i can't pronounce that name but someone has asked for francy last i did performance work which is about five years ago i found with perf that gcc doesn't always get it right and needed a couple of inlines for performance tweak so i would say uh i think that's a fair comment uh basically that you know if uh if you really do know better than the compiler if you if you're going to the trouble of reading the assembly that's output by the by the compiler and uh determining that it it should have inline when it when it didn't i think it's fair to go ahead and add an inline um always in line there's also an always inline macro that says that forces the compiler uh ordinarily inline inline attribute is just a hint uh but you know i'm not saying don't use your good judgment just recognize that um what you're looking at on a particular cpu architecture that might not apply to some other architecture right there there is other cpu's that have less registers or they use them differently or or you know or even just the context of the call you someone may add a new caller to your routine uh and it just it's not appropriate for your code to be inline so just be careful with it but if you i mean if you know what you're doing then that's fine i should be too dogmatic about it and you can always just explain to people uh does uh allison shaken asks does dash w all include dash w implicit fall through um i don't remember i think they were planning on turning it on so i think most of this work for the implicit fall through stuff happened in five six and five seven maybe it was finished in five i don't know but they were talking about actually turning it on uh so adding it to the dash w all case um so that it would be one of the generic default warnings that you got uh so i think the goal is to have it be that way i don't know if they've actually done that yet though um uh let's see uh allison shaken asks another are there any gcc flags that we should use with the kernel that dash w all does not capture for example the fall through one wow that is a big question i uh i i don't know of any off the top of my head i think i'm going to have to defer that question uh there are probably some i know that frank roland has been working on um well a lot of people have been working on tools to analyze the warning messages and if we could get to a place where the warnings were silent even when you had dash w all that would be pretty nice um uh let's see uh um i have a question uh thanks a ton tim you have referred to several blogs on lgn dot net whether the blog should we follow as a beginner i think lgn dot net it is just the best resource and there's a page uh i'm about to run out of time so i hope i don't but there's a page called the kernel index so make sure you go look at the kernel index you can find a ton of information out there um and i'm afraid my uh time is uh about running out here so i want to say uh thank you for the questions i'll go over to slack and uh i'm sorry if i didn't get to your question here uh anyway uh we'll see you over on the boards uh bye