How can I check if a given block device is in use (i.e., mounted, used as swap, used as a software RAID device etc.)?
One could technicaly check /proc/swaps, /proc/mdstat, /etc/mtab, etc., but that's just checking in lots of places. And it doesn't cover everything (i.e. LVM, dm-crypt etc.).
Is there a generic way to check if a given block device is in use?
block device in use check
I don't think general solution exists.
On a case by case basis, as sometimes these are hardware specific (esp when we are dealing with hardware):
all the different examples can be found in kernel source drivers/block (grep for register_blkdev() for all block devices), for example:
1. xen-blkfront.c - checking if still mounted:
static int blkif_release(struct inode *inode, struct file *filep)
{
struct blkfront_info *info = inode->i_bdev->bd_disk->private_data;
info->users--;
if (info->users == 0) {
/* Check whether we have been instructed to close. We will
have ignored this request initially, as the device was
still mounted. */
struct xenbus_device *dev = info->xbdev;
enum xenbus_state state = xenbus_read_driver_state(dev->otherend);
2. z2ram.c - bit testing:
static void
get_z2ram( void )
{
int i;
for ( i = 0; i < Z2RAM_SIZE / Z2RAM_CHUNKSIZE; i++ )
{
if ( test_bit( i, zorro_unused_z2ram ) )
{
3. sx8.c: CARM_RME and CARM_CME bit testing. And I don't know what is that. But the use of the device is halted till the status turned otherwise.
tmp = readl(mmio + CARM_HMUC);
if (tmp & CARM_CME) {
DPRINTK("CME bit present, waiting\n");
rc = carm_init_wait(mmio, CARM_CME, 1);
if (rc) {
DPRINTK("EXIT, carm_init_wait 1 failed\n");
return rc;
}
}
if (tmp & CARM_RME) {
DPRINTK("RME bit present, waiting\n");
rc = carm_init_wait(mmio, CARM_RME, 1);
if (rc) {
DPRINTK("EXIT, carm_init_wait 2 failed\n");
return rc;
}
}
tmp &= ~(CARM_RME | CARM_CME);
writel(tmp, mmio + CARM_HMUC);
So many possibility exists.
DRBD tests it quite well
I see DRBD[1] tests it quite well if we want to use a device which is already used:
"Lower device is already claimed. This usually means it is mounted."
However, I don't fully understand how it is done when looking at the code.
[1] http://www.drbd.org
look for bd_claim
AFAIK, Linux uses bd_claim to claim exclusive use of a block dev. I only know what some docs about evms said, though... (evms has to hack around bd_claim, because it wants to have multiple dm devices on the same real block dev, or something nasty.)
so check out bd_claim and see if you can use it.
but how to use it from userspace?
Grepping kernel sources for bd_claim indeed shows this is perhaps what I'm looking for.
But how to use it from userspace?
fs/block_dev.c:
int bd_claim(struct block_device *bdev, void *holder)
{
int res;
spin_lock(&bdev_lock);
/* first decide result */
if (bdev->bd_holder == holder)
res = 0; /* already a holder */
else if (bdev->bd_holder != NULL)
res = -EBUSY; /* held by someone else */
else if (bdev->bd_contains == bdev)
res = 0; /* is a whole device which isn't held */
else if (bdev->bd_contains->bd_holder == bd_claim)
res = 0; /* is a partition of a device that is being partitioned */
else if (bdev->bd_contains->bd_holder != NULL)
res = -EBUSY; /* is a partition of a held device */
else
res = 0; /* is a partition of an un-held device */
> But how to use it
> But how to use it [bd_claim] from userspace?
sorry, no idea. It doesn't export info to sysfs, does it?
Maybe if you're feeling ambitious you could add some sysfs code to bd_claim...
here is a solution
Here's a quick solution in Perl:
#!/usr/bin/perl
use Fcntl;
my $path = "/dev/sda3";
sysopen(FH, $path, O_RDWR|O_EXCL) or die $!;
locking block device buffer
THank you for the hints and discussion. I think u all are right....bd_claim and O_EXCL is the answer.
First, when u open a device with O_EXCL flag, u lock the buffer as the API blkdev_open() shows:
static int blkdev_open(struct inode * inode, struct file * filp)
{
struct block_device *bdev;
int res;
/*
* Preserve backwards compatibility and allow large file access
* even if userspace doesn't ask for it explicitly. Some mkfs
* binary needs it. We might want to drop this workaround
* during an unstable branch.
*/
filp->f_flags |= O_LARGEFILE;
bdev = bd_acquire(inode);
if (bdev == NULL)
return -ENOMEM;
res = do_open(bdev, filp, 0);
if (res)
return res;
if (!(filp->f_flags & O_EXCL) )
return 0;
if (!(res = bd_claim(bdev, filp)))==================> buffer is locked, at the VFS level.
return 0;
blkdev_put(bdev);
return res;
}
Since it worked at the VFS level, it is the same for all the different filesystem like ext3 or ext4 etc.
There goes your userspace solution as well.