Can't Unload Kernel Extension; Classes Have Instances

12,842

Running kextunload for an IOKit kext will (if no other kexts depend on it) cause the kernel to attempt to terminate() any instances of classes in that kext which are in the I/O Kit registry. It will then wait a bit and check if any of that kext's classes still have instances. If not, it will unload the kext. If instances remain, kextunload fails (the terminated instances stay terminated, though; by this I mean that I/O kit matching is not re-run on their providers).

So somehow, you're still ending up with live instances.

  • One possibility is that your objects are refusing to terminate(). This can happen if they have clients that won't give up control, e.g. you can't unload the driver for a disk with a mounted file system on top. Userspace clients that don't respond to termination messages are another example.

  • Otherwise, the instances terminate, but are not freed. Since they seem to be of two of your main driver classes, if you don't have any user clients that won't give up their claim, I'm going to go out on a limb and suggest that you might have a circular reference. If that's not it, you'll just have to hunt for retain()s which are not matched by a release(). I give some tips on how to track these down in this answer.

If the instances terminate and are deregistered, they will no longer appear in the output of the ioreg commandline tool, so that's an easy way of checking which of the two cases applies here.

Share:
12,842
pje
Author by

pje

Updated on June 04, 2022

Comments

  • pje
    pje almost 2 years

    I'm writing an OSX kernel extension for an audio device driver (it's software, but emulates a hardware device).

    During development, it'd be convenient to completely uninstall existing old versions and then build and install the new version from scratch. However, this occasionally seems to not be possible without a system restart.

    The program itself is not running and the source files have been deleted from the /System/Library/Extensions/ dir.

    But kextstat reveals a single instance:

    $ kextstat | grep 'com.foo.driver.bar'
    219 0 0xfff123 0x5000 0x5000 com.foo.driver.bar (0.0.1) <102 5 4 3>
    

    (...meaning:)

    Index Refs Address Size Wired Name (Version) <Linked Against>
    

    So there are 0 Refs to my driver instance, but kextunload will sometimes fail, complaining of existing instances:

    $ sudo kextunload -b com.foo.driver.bar
    (kernel) Can't unload kext com.foo.driver.bar; classes have instances:
    (kernel)     Kext com.foo.driver.bar class FooBarDriver has 1 instance.
    (kernel)     Kext com.foo.driver.bar class com_foo_driver_bar has 1 instance.
    Failed to unload com.foo.driver.bar - (libkern/kext) kext is in use or retained (cannot unload).
    

    When this happens, there's no way to "force" unload the kext (that I know of).

    Am I right in guessing that this single instance still exists because of a reference held in memory by the running OS kernel? That doesn't seem right, because then kextunload would always fail. So why does kextunload only sometimes require a system restart to "fully" unload all driver instances?