How to unsafely remove blockdevice driver in Linux

2.9k views Asked by At

I am writing a block device driver for linux. It is crucial to support unsafe removal (like usb unplug). In other words, I want to be able to shut down the block device without creating memory leaks / crashes even while applications hold open files or performing IO on my device or if it is mounted with file system. Surely unsafe removal would possibly corrupt the data which is stored on the device, but that is something the customers are willing to accept.

Here is the basics steps I have done:

  1. Upon unsafe removal, block device spawns a zombie which will automatically fail all new IO requests, ioctls, etc. The zombie substitutes make_request function and changes other function pointers so kernel would not need the original block device.
  2. Block device waits for all IO which is running now (and use my internal resources) to complete
  3. It does del_gendisk(); however this does not really free's kernel resources because they are still used.
  4. Block device frees itself.
  5. The zombie keeps track of the amount of opens() and close() on the block device and when last close() occurs it automatically free() itself
    1. Result - I am not leaking the blockdevice, request queue, gen disk, etc.

However this is a very difficult mechanism which requires a lot of code and is extremely prone to race conditions. I am still struggling with corner cases, per_cpu counting of io's and occasional crashes

My questions: Is there a mechanism in the kernel which already does that? I searched manuals, literature, and countless source code examples of block device drivers, ram disks and USB drivers but could not find a solution. I am sure, that I am not the first one to encounter this problem.

Edited: I learned from the answer below, by Dave S about the hot-plug mechanism but it does not help me. I need a solution of how to safely shut down the driver and not how to notify the kernel that driver was shut down.

Example of one problem: blk_queue_make_request() registers a function through which my block devices serves IO. In that function I increment per_cpu counters to know how many IO's are in flight by each cpu. However there is a race condition of function being called but counter was not increased yet, so my device thinks there are 0 IO's, releases the resources and then IO comes and crashes the system. Hotplug will not assist me with this problem as far as I understand

2

There are 2 answers

4
Dave S On

About a decade ago I used hotplugging on a software driver project to safely add/remove an external USB disk drive which interfaced to an embedded Linux driven Set-top Box.

For your project you will also need to write a hot plug. A hotplug is a program which is used by the kernel to notify user mode software when some significant (usually hardware-related) events take place. An example is when a USB device has just been plugged in or removed.

From Linux 2.6 kernel onwards, hotplugging has been integrated with the driver model core so that any bus or class can report hotplug events when devices are added or removed.

In the kernel tree, /usr/src/linux/Documentation/usb/hotplug.txt has basic information about USB Device Driver API support for hotplugging. See also this link, and GOOGLE as well for examples and documentation.

http://linux-hotplug.sourceforge.net/

Another very helpful document which discusses hotplugging with block devices can be found here:

https://www.kernel.org/doc/pending/hotplug.txt

This document also gives a good example of illustrating hotplug events handling:

Below is a table of the main variables you should be aware of:

Hotplug event variables:

Every hotplug event should provide at least the following variables:

ACTION
The current hotplug action: "add" to add the device, "remove" to remove it.
The 2.6.22 kernel can also generate "change", "online", "offline", and
"move" actions.

DEVPATH
Path under /sys at which this device's sysfs directory can be found.

SUBSYSTEM
If this is "block", it's a block device.  Anything other subsystem is
either a char device or does not have an associated device node.

The following variables are also provided for some devices:

MAJOR and MINOR
If these are present, a device node can be created in /dev for this device.
Some devices (such as network cards) don't generate a /dev node.

DRIVER
If present, a suggested driver (module) for handling this device.  No
relation to whether or not a driver is currently handling the device.

INTERFACE and IFINDEX
When SUBSYSTEM=net, these variables indicate the name of the interface
and a unique integer for the interface.  (Note that "INTERFACE=eth0" could
be paired with "IFINDEX=2" because eth0 isn't guaranteed to come before lo
and the count doesn't start at 0.)

FIRMWARE
The system is requesting firmware for the device. 
1
aNeutrino On

If the driver is creating device it could be possible to suddenly delete it:

  1. echo 1 > /sys/block/device-name/device/delete where device-name may be sde, for example,

or

  1. echo 1 > /sys/class/scsi_device/h:c:t:l/device/delete, where h is the HBA number, c is the channel on the HBA, t is the SCSI target ID, and l is the LUN.

In my case, it perfectly simulates scenarios for crushing writes and recovery of data from journaling.

Normally to safely remove device more steps is needed so deleting device is a pretty drastic event for data and could be useful for testing :)

please consider this:

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/online_storage_reconfiguration_guide/removing_devices

http://www.sysadminshare.com/2012/09/add-remove-single-disk-device-in-linux.html