Archive for July, 2006

ntstrsafe.h prefast warnings

Monday, July 17th, 2006

I’m a bit of a warnings freak. I compile with /W4 /Wp64 /WX at all times. I run PREfast with no filters, and I either whack on my code until it’s silent or I annotate it with a #pragma warning(suppress:asdf) if appropriate.

Unfortunately, current builds of a couple of my drivers that use the Safe String Functions in ntstrsafe.h cause PREfast to spew forth tons of warnings. I’m guessing ntstrsafe.h just needs annotations too, but it’s annoying. It’s way easier to spot an error when you’re expecting an empty logfile than it is to find the error among a dozen noise errors.

Anyway, on the upside, I finally had occasion to printf into a unicode string, so I got to try out the strsafe support for UNICODE_STRING strings. Looks good. The only thing that’s a little weird for me is the need to manually initialize the UNICODE_STRING beforehand, so I’m not sure if I prefer this way or the previous way.

Blogroll updated… finally…

Sunday, July 16th, 2006

I’ve finally linked most of the blogs that I read. Only took two years. How rude.

NDIS needs updated types

Sunday, July 16th, 2006

Here’s hoping they change this for Vista (although it’s getting late…):

EXPORT
NDIS_STATUS
NdisAllocateMemoryWithTag(
    OUT PVOID *                 VirtualAddress,
    IN  UINT                    Length,
    IN  ULONG                   Tag
    );

That second parameter is a pain. Lots of APIs return SIZE_T for sizes or lengths of things, and SIZE_T is 64 bits wide on x64. But this API takes a UINT, so you need to use a cast a lot of the time. And you know how I feel about C casts.

In fact, there are only a few places in all of ndis.h that SIZE_T is used, and they’re all in new NetBuffer-related APIs. Compare that to the main headers:

C:\\WinDDK\\5384\\inc\\ddk>grep SIZE_T wdm.h|wc -l
48

C:\\WinDDK\\5384\\inc\\ddk>grep SIZE_T ntddk.h|wc -l
60

C:\\WinDDK\\5384\\inc\\ddk\\>grep SIZE_T ntifs.h|wc -l
33

C:\\WinDDK\\5384\\inc\\ddk>grep SIZE_T ndis.h|wc -l
5

I haven’t gone through the APIs to see where SIZE_T would be useful, but I suspect having zero non-NetBuffer SIZE_T APIs isn’t a good sign.

[UPDATED: fixed a confusing description, and again because Wordpres ate my homework...]

x64 debugging

Sunday, July 16th, 2006

Ken Johnson has been running a great series entitled Introduction to x64 debugging. Its a bit of a twist if you’re used to x86. Highly recommended.

Now that Ken has managed to get his blog server stabilized, you may even be able to read the articles. :-)

What is false sharing?

Sunday, July 16th, 2006

I’m waiting for a build so I thought I’d finally get back to some technical blogging for a bit.

False sharing is one way among many to hurt performance in a highly concurrent system. To understand the issue, you have to know something about how CPUs work. Modern CPUs implement various levels caching of instructions and data. Usually these caches are organized into “cache lines”, which are fixed-size hunks of cache designed to hold the contents of a memory range. Cache lines vary in size on a per-processor (even per-model) basis.

False sharing can occur if you have two variables that are designed to be independent of each other located on the same cache line. If two threads are using the two variables, contention for the cache line can cause caching of the variable to be effectively disabled, while at the same time soaking up lots of memory bus bandwidth. Even worse, having one variable requested by another CPU invalidates the entire cache line, so it effectively disables caching of *all* variables on the cache line. For a 128-byte cache line, you’re looking at cache misses on 32 different ULONG-sized variables.

The solution to the problem is to align important (unrelated) data structures on cache line boundaries. This way, CPU A can be working with one variable, while CPU B works with another, and effectiveness of the CPUs’ caches is maintained. Profiling (L2 cache misses in particular) can help you detect if this is happening to you.

A little light reading on the topic:

I’d be curious to hear perf-tuning war stories along these lines.

Apologies for the formatting problems

Saturday, July 15th, 2006

I was just informed that my blog has looked like crap for the last week or so, due to a bad combination of my not using IE very often and not reading my own blog very often. Anyway, I moved Ken’s super-long debugging article to a dedicated page and linked it from the original post, so things are ship-shape again.

NdisIMInitializeDeviceInstanceEx documentation anomalies

Sunday, July 9th, 2006

The DDK documentation for NdisIMInitializeDeviceInstanceEx is a bit odd in a few places. To clarify it a bit, I’m going to take you through how NDIS Intermediate (IM) driver binding works in the case of a 1:1 filter IM, such as the Passthru sample.

This all started as I was going through an IM driver doing some code review. I noticed a lot of string manipulation code. That always makes me suspicious, since it is a very common source of bugs. This code seemed to be doing the same thing that the DDK passthru sample does – read names from the UpperBindings entry in the registry and use that value to initialize virtual adapters representing the underlying physical adapters. I had never really given this much thought, so I decided to investigate the details.

This string manipulation happens in the course of the ProtocolBindAdapter callback, of course. Names are used in a few places in this code path.

Some ancient history, subject to the limits of my foggy memory: Back in the pre-PnP days, protocols went looking for adapters to bind to based on configuration information stored in the registry. The whole bindings process in the NT4 days was a mess – go look at an interpreted INF for an NT4 net component to see how ugly it got. (There was even a little sub-industry that developed around writing obfuscated net component installation INFs because they were so hard to write. Fortunately, INFs are a lot easier now. Well, at least they’re shorter.) Anyway, those legacy protocols used to figure out which adapters they wanted to bind to and call NdisOpenAdapter on them, using the instance name.

ProtocolBindAdapter is now basically the only thing that calls NdisOpenAdapter in a PnP protocol. And, conveniently, it has the adapter’s instance name passed in as a parameter, which the protocol then passes directly to NdisOpenAdapter. Easy enough.

The other place that strings are used is in the call to NdisIMInitializeDeviceInstance(Ex). Looking at the DDK doc for the function (linked above), the first oddball thing that pops out is the fact that DeviceContext isn’t hyperlinked to its description. This is apparently because there’s an extra apostrophe before the D in Devicecontext. Looking around, there are a couple of more places that the extra apostrophe shows up on this page. Makes you wonder if the documentation got garbled somehow.

The function is prototyped as:

NDIS_STATUS
NdisIMInitializeDeviceInstanceEx(
IN NDIS_HANDLE  DriverHandle,
IN PNDIS_STRING  DriverInstance,
IN NDIS_HANDLE  DeviceContext  OPTIONAL
);

That second argument asks for a string representing the “driver instance”. That is a pretty uncommon term in the NDIS world; it turns out that it should probably be “device instance” or “adapter instance”, either of which would make much more sense in this context. Particularly given that the name of the function has the words “DeviceInstance” in it.

That still doesn’t tell us what the argument actually *does*, though. The documentation is unhelpful:

DriverInstance
Pointer to an NDIS_STRING type that describes a caller-initialized counted string, in the system-default character set, naming the registry key in which the driver stores information about its virtual NIC and, possibly, binding-specific information. For Windows 2000 and later drivers, this string contains Unicode characters. That is, for Windows 2000 and later, NDIS defines the NDIS_STRING type as a UNICODE_STRING type.

Pet peeve: I wish the NDIS docs would quit putting the UNICODE_STRING disclaimer on every string argument in every function. I get it by now.

Anyway, the description references the name of a registry key where we store binding-specific information. But, looking at PassThru, that’s not remotely what this argument is used for. It is not the name of a registry key – it’s the name of a device instance that’s written into UpperBindings by the class installer. Looking at it in the registry (HKLM\System\CCS\Services\Parameters\Adapters\{adapter guid}), it’s just the name of a device instance, it’s a REG_SZ, and the value is a GUID that identifies a particular instance of an adapter exported by the IM driver itself.

PassThru says:

//
// Read the "UpperBindings" reserved key that contains a list
// of device names representing our miniport instances corresponding
// to this lower binding. Since this is a 1:1 IM driver, this key
// contains exactly one name.
//
// If we want to implement a N:1 mux driver (N adapter instances
// over a single lower binding), then UpperBindings will be a
// MULTI_SZ containing a list of device names - we would loop through
// this list, calling NdisIMInitializeDeviceInstanceEx once for
// each name in it.

There are a couple of problems here. First, UpperBindings (as created by the class installer) is not a REG_MULTI_SZ; it’s a REG_SZ – disassemble the class installer and check for yourself, or if you’re smarter/lazier than that, search google and see what Elyias Yakub from Microsoft has to say on the point. Besides, it wouldn’t make sense – PassThru is a 1:1 filter IM, so it can’t export more than one adapter per underlying nic at a time or it wouldn’t be 1:1 any more.

The answer is this: The UpperBindings key contains the name of the virtual device instance that we should create to represent the underlying miniport’s device. It needs to match the name of the root-enumerated virtual device instance that PnP (may have) started (check HKLM\System\CCS\Enum\Root\[driver] and the Net class GUID under HKLM\System\CCS\Class\{guid} to see where this comes from). This is the step that creates the “XYZ Ethernet Adapter – Passthru Miniport” device that shows up in Device Manager.

If you don’t specify the name of a device that is already started (or that will be started soon by PnP), NDIS will silently fail to complete the device instance initialization, since the device you’re telling NDIS to initialize never actually shows up. That is exactly why you have to get the name of the device instance to start from UpperBindings. That is where the class installer connects the dots between the underlying miniport’s device and the virtual miniport device the IM should create.

So, to recap, here’s what IM binding looks like:

  1. The underlying miniport starts via PnP, which causes NDIS to look for bound protocols and call the relevant ProtocolBindAdapter handlers. The protocol in question is the IM driver (PassThru in this case).
  2. The IM’s ProtocolBindAdapter gets the underlying adapter’s name passed in as a parameter. It calls NdisOpenAdapter to complete the underlying binding.
  3. It then creates a virtual miniport device instance to represent the underlying device interface, using NdisIMInitializeDeviceInterface(Ex). As a part of that call, it identifies *which* of its root-enumerated virtual devices to start using the (single) value from UpperBindings.

This is obviously recursive, too – my IM test box has three IMs running at the moment, and you can easily follow the chain using !ndiskd.miniports in the kernel debugger.

One parting shot: the documentation on the return value is also wrong, as a result of all of this. The problem NDIS will reject your call for is NOT based on DriverHandle being initialized already (which is actually a requirement for the call to succeed, as far as I can tell); it’s based on having already initialized the device instance (called DrivderInstance in the docs).

I hope this clears some things up about this mysterious call.

Quit re-writing linked lists

Saturday, July 8th, 2006

Everyone that went to CS school has (hopefully) had to write linked list code in a data structures class at some point. Not too difficult, but just tricky enough that it makes for a good starter project.

Well, the good news is that, apart from school, you never have to write linked list code again. Moreover, you probably never should write linked list code again. Microsoft has provided a perfectly serviceable doubly linked circular list structure and a set of APIs and macros to go with it: behold the power of LIST_ENTRY!

LIST_ENTRY is simple:

typedef struct _LIST_ENTRY {
  struct _LIST_ENTRY *Flink;
  struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY;

Manipulation is even simpler; you can (and should) almost always use built-in macros or functions to handle common operations: InsertHeadList, RemoveEntryList, etc. These operations are fast and easy, although if you need interlocking, be sure to use the spinlock-protected ExInterlocked* functions. Or, if spin locks aren’t right for your application, roll your own interlocked operations with the locking of your choice.

There is lots more documentation and sample code in the DDK about how to use these functions correctly. In a nutshell, the typical way to do this is to define a LIST_ENTRY to act as the head of your list and then to embed a LIST_ENTRY in whatever you’re trying to make a list of. Look at some common system data structures in the DDK header files for examples of structures with embedded LIST_ENTRY structures.

The only tricky part about linked lists is that everyone does them slightly differently. Well, the best news about using LIST_ENTRY and its associated APIs is that now everyone can do linked lists the same way. That makes code review easier, which makes code quality improve.

So, do yourself a favor – next time you think you need a list of something, use a LIST_ENTRY. You’ll thank yourself in a year when you have to go back and maintain your code.

Friday night music blogging

Saturday, July 8th, 2006

I guess it’s technically Saturday at this point, but it’s close enough. In keeping with Bruce Schneier’s habit of off-topic blogging on Fridays, I am unable to resist talking about music for a second.

The night started off with the purchase of an Airport Express to replace a slightly flaky and recently regifted wi-fi router. I’m currently listening to Dylan on the only good non-headphone speakers I own and wondering what the heck took me so long. The speakers are finally going to get used again in this post-CD world.

Then I headed over to watch Evan, our VP of Sales, play a rip-roarin’ acoustic set at a local bar. I arrived to Highway to Hell and departed on Margaritaville, if that tells you how inebriated they were at the time. Two guitars, some bongo drums, and an electric bass, plus about 50 empty cuervo shots, and you have a recipe for fun! I, however, had only cuatro equises.

Now I’m sitting here wondering what exactly I’m going to play on guitar/bass/drums for a few tracks tomorrow on a bit of a demo CD for my brother Scott, who’s just getting out into the music world after finishing his Master’s degree from Juilliard. Oh, and while I’m on the subject, congrats to Scott and his fiancee Heather on their recent engagement, which was announced this weekend.

And finally, I can’t help writing just one more time about how excellent of a song Tangled Up in Blue is. It’s currently playing on AirTunes. What a great way to christen a new music toy.

A new MVP *and* a new blog!

Thursday, July 6th, 2006

I’ve had a feeling this was coming. Ken Johnson has been awarded an MVP award and has started blogging, all at the same time. Ken is the guy responsible for the previous post (which I am told looks terrible in IE – sorry about that; I use Firefox), and has been hanging out on the driver-related message boards for quite a while now. He knows everything; what more can I say? :-)

Congratulations, Ken!