2021-08-04 12:41:40 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2022-01-24 12:39:13 +03:00
# include <linux/blkdev.h>
2021-09-20 15:33:25 +03:00
# include <linux/slab.h>
2021-08-04 12:41:40 +03:00
struct bd_holder_disk {
struct list_head list ;
2022-11-15 17:10:53 +03:00
struct kobject * holder_dir ;
2021-08-04 12:41:40 +03:00
int refcnt ;
} ;
static struct bd_holder_disk * bd_find_holder_disk ( struct block_device * bdev ,
struct gendisk * disk )
{
struct bd_holder_disk * holder ;
2021-08-04 12:41:42 +03:00
list_for_each_entry ( holder , & disk - > slave_bdevs , list )
2022-11-15 17:10:53 +03:00
if ( holder - > holder_dir = = bdev - > bd_holder_dir )
2021-08-04 12:41:40 +03:00
return holder ;
return NULL ;
}
static int add_symlink ( struct kobject * from , struct kobject * to )
{
return sysfs_create_link ( from , to , kobject_name ( to ) ) ;
}
static void del_symlink ( struct kobject * from , struct kobject * to )
{
sysfs_remove_link ( from , kobject_name ( to ) ) ;
}
/**
* bd_link_disk_holder - create symlinks between holding disk and slave bdev
* @ bdev : the claimed slave bdev
* @ disk : the holding disk
*
* DON ' T USE THIS UNLESS YOU ' RE ALREADY USING IT .
*
* This functions creates the following sysfs symlinks .
*
* - from " slaves " directory of the holder @ disk to the claimed @ bdev
* - from " holders " directory of the @ bdev to the holder @ disk
*
* For example , if / dev / dm - 0 maps to / dev / sda and disk for dm - 0 is
* passed to bd_link_disk_holder ( ) , then :
*
* / sys / block / dm - 0 / slaves / sda - - > / sys / block / sda
* / sys / block / sda / holders / dm - 0 - - > / sys / block / dm - 0
*
* The caller must have claimed @ bdev before calling this function and
* ensure that both @ bdev and @ disk are valid during the creation and
* lifetime of these symlinks .
*
* CONTEXT :
* Might sleep .
*
* RETURNS :
* 0 on success , - errno on failure .
*/
int bd_link_disk_holder ( struct block_device * bdev , struct gendisk * disk )
{
struct bd_holder_disk * holder ;
int ret = 0 ;
2022-11-15 17:10:51 +03:00
if ( WARN_ON_ONCE ( ! disk - > slave_dir ) )
return - EINVAL ;
2022-11-15 17:10:54 +03:00
if ( bdev - > bd_disk = = disk )
return - EINVAL ;
2022-11-15 17:10:52 +03:00
/*
* del_gendisk drops the initial reference to bd_holder_dir , so we
* need to keep our own here to allow for cleanup past that point .
*/
mutex_lock ( & bdev - > bd_disk - > open_mutex ) ;
if ( ! disk_live ( bdev - > bd_disk ) ) {
mutex_unlock ( & bdev - > bd_disk - > open_mutex ) ;
return - ENODEV ;
}
kobject_get ( bdev - > bd_holder_dir ) ;
mutex_unlock ( & bdev - > bd_disk - > open_mutex ) ;
2021-08-04 12:41:40 +03:00
2022-11-15 17:10:52 +03:00
mutex_lock ( & disk - > open_mutex ) ;
2021-08-04 12:41:40 +03:00
WARN_ON_ONCE ( ! bdev - > bd_holder ) ;
holder = bd_find_holder_disk ( bdev , disk ) ;
if ( holder ) {
2022-11-15 17:10:52 +03:00
kobject_put ( bdev - > bd_holder_dir ) ;
2021-08-04 12:41:40 +03:00
holder - > refcnt + + ;
goto out_unlock ;
}
holder = kzalloc ( sizeof ( * holder ) , GFP_KERNEL ) ;
if ( ! holder ) {
ret = - ENOMEM ;
goto out_unlock ;
}
INIT_LIST_HEAD ( & holder - > list ) ;
holder - > refcnt = 1 ;
2022-11-15 17:10:53 +03:00
holder - > holder_dir = bdev - > bd_holder_dir ;
2022-11-15 17:10:51 +03:00
ret = add_symlink ( disk - > slave_dir , bdev_kobj ( bdev ) ) ;
if ( ret )
goto out_free_holder ;
ret = add_symlink ( bdev - > bd_holder_dir , & disk_to_dev ( disk ) - > kobj ) ;
if ( ret )
goto out_del_symlink ;
2021-08-04 12:41:42 +03:00
list_add ( & holder - > list , & disk - > slave_bdevs ) ;
2022-11-15 17:10:51 +03:00
mutex_unlock ( & disk - > open_mutex ) ;
return 0 ;
2021-08-20 12:49:29 +03:00
2022-11-15 17:10:51 +03:00
out_del_symlink :
del_symlink ( disk - > slave_dir , bdev_kobj ( bdev ) ) ;
out_free_holder :
kfree ( holder ) ;
2021-08-04 12:41:40 +03:00
out_unlock :
2021-08-04 12:41:42 +03:00
mutex_unlock ( & disk - > open_mutex ) ;
2022-11-15 17:10:52 +03:00
if ( ret )
kobject_put ( bdev - > bd_holder_dir ) ;
2021-08-04 12:41:40 +03:00
return ret ;
}
EXPORT_SYMBOL_GPL ( bd_link_disk_holder ) ;
/**
* bd_unlink_disk_holder - destroy symlinks created by bd_link_disk_holder ( )
* @ bdev : the calimed slave bdev
* @ disk : the holding disk
*
* DON ' T USE THIS UNLESS YOU ' RE ALREADY USING IT .
*
* CONTEXT :
* Might sleep .
*/
void bd_unlink_disk_holder ( struct block_device * bdev , struct gendisk * disk )
{
struct bd_holder_disk * holder ;
2022-11-15 17:10:51 +03:00
if ( WARN_ON_ONCE ( ! disk - > slave_dir ) )
return ;
2021-08-04 12:41:42 +03:00
mutex_lock ( & disk - > open_mutex ) ;
2021-08-04 12:41:40 +03:00
holder = bd_find_holder_disk ( bdev , disk ) ;
if ( ! WARN_ON_ONCE ( holder = = NULL ) & & ! - - holder - > refcnt ) {
2022-11-15 17:10:51 +03:00
del_symlink ( disk - > slave_dir , bdev_kobj ( bdev ) ) ;
2022-11-15 17:10:53 +03:00
del_symlink ( holder - > holder_dir , & disk_to_dev ( disk ) - > kobj ) ;
kobject_put ( holder - > holder_dir ) ;
2021-08-04 12:41:40 +03:00
list_del_init ( & holder - > list ) ;
kfree ( holder ) ;
}
2021-08-04 12:41:42 +03:00
mutex_unlock ( & disk - > open_mutex ) ;
2021-08-04 12:41:40 +03:00
}
EXPORT_SYMBOL_GPL ( bd_unlink_disk_holder ) ;