天天看點

device mapper驅動

dm(device mapper)驅動實際做的一件事就是将多個裝置映射成一個塊裝置顯示

在上層,LVM和multipath等工具都是基于這個驅動,将多個裝置虛拟成一個裝置

的。

dm驅動位于linux/driver/md/目錄下

由dm.c dm-target.c dm-tabel.c dm-ioctl.c dm-io.c等幾個檔案組成架構。

因為它對上層顯示的是一個虛拟的塊裝置,是以會接收到來自通用block層的bio

,dm架構将該bio轉發給dm-multipath.c等target driver(負責向具體的裝置發

送bio),進而實作從虛拟裝置到具體裝置的映射。

dm架構的出現相當于在通用block層(發出bio)和Io Schedule層(将接收的bio

組裝成request并将request按照排程算法發給下層)插入了一個中間層,完成對

bio的複制和分發。

dm驅動對上層顯示為一個塊裝置,實質上它是由幾個target 裝置組成的,是以當

上層向一個dm驅動的塊裝置發bio時,dm驅動将該bio轉發給target driver,

target driver複制出多個bio發給它負責對應的target,target傳回結果後,

target driver再将結果合并,傳回給dm,dm再傳回給上層。

dm驅動對上層有兩個接口:

使用者程式使用ioctl接口:LVM,multipath等使用者态工具都是通過ioctl接口來向

dm驅動發指令建立相應的dm裝置的,關鍵代碼定義在dm-target.c中的

lookup_ioctl函數

使用者使用接口:由dm驅動虛拟的裝置會在/dev/目錄下顯示dm-0,1,2等裝置,lvm

,multipath等程式通過該接口來使用dm虛拟塊裝置。

因為dm對上層是一個虛拟的塊裝置,是以要申請gendisk結構并且有一個磁盤的請

求隊列

磁盤操作:

md->disk->fops = &dm_blk_dops;

queue的各種回調函數:

md->queue->backing_dev_info.congested_fn = dm_any_congested;

将bio轉化為request

md->queue->make_request_fn = dm_request

合并bio到一個request

md->queue->merge_bvec_fn = dm_merge_bvec

處理request之前的準備工作

md->queue->prep_rq_fn = dm_prep_fn

處理queue中的request

md->queue->request_fn = dm_request_fn

md->queue->lld_busy_fn = dm_lld_busy

request處理完成時的回調函數

md->queue->softirq_done_fn = dm_softirq_done

1.驅動可以使用自己定義的bio轉化函數,未定義的話,通用block層提供了将bio

轉化為request的函數:blk_queue_bio

定義方式:

blk_queue_make_request(md->queue, dm_request);

 實際操作:

 md->queue->make_request_fn = dm_request

struct request_queue *

blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn,

    spinlock_t *lock)

{

 ...

 blk_queue_make_request(q, blk_queue_bio);

 ...

}

2.驅動可以定義request準備函數,該函數會在request從隊列取出時執行,如果

未定義,則不執行直接取出request。

定義方式:

blk_queue_prep_rq(md->queue, dm_prep_fn);

 實際操作:

 md->queue->prep_rq_fn = dm_prep_fn

3.驅動定義request處理方式

定義方式:

q = blk_init_allocated_queue(md->queue, dm_request_fn, NULL);

 實際操作:

 md->queue->request_fn = dm_request_fn

使用DM驅動前提:

想要同DM驅動溝通,使用者空間首先要安裝device-mapper-libs-1.02.77-9.el6.x86_64,這個包裡包含的庫檔案提供同DM驅動的ioctl接口互動的接口。

multipath使用者空間的庫檔案包含在device-mapper-multipath-libs-0.4.9-64.el6.x86_64中。

multipath的指令:

device-mapper-multipath包含兩個指令:multipath和multipathd,它們運作時都需要device-mapper-multipath-libs庫的支援。

1.multipath指令主要作用是根據/etc/multipath.conf的設定收集multipath裝置的資訊,比如mpatha由那幾個磁盤構成(即包含幾條路徑),然後将這些資訊通過device-mapper庫發給DM驅動,DM驅動調用依次dev_create和table_load,在DM驅動中建立這個mapped_device裝置,并根據使用者空間來的資訊建立和填充映射表,即該mapped_device裝置同幾個target的映射關系。

是以,multipath指令主要作用就是在使用者空間通過/sys等接口收集資訊,然後将資訊發給DM驅動,讓DM驅動建立對應的DM裝置,因為DM驅動處于驅動的上層,在SCSI層之上block層之下,是以它本身擷取資訊不便,并且秉承機制與政策分離的理念,DM驅動隻實作了各種接口供使用者空間調用,建立DM裝置的資訊都是使用者空間提供給它的。

multipath指令還可以用-F參數全部删除它在DM驅動中對應的DM裝置或者用-l參數顯示DM裝置的多路徑資訊。

2.multipathd指令以守護程序方式運作在背景,它的作用是實時同步DM驅動中mapped_device裝置同使用者空間的資訊。因為DM驅動是高層驅動,它完全是被動的,被動的建立,被動的删除,是以它不知道它的target裝置的狀态,它僅僅是實作了映射以及使用,而使用者空間可以通過/sys接口擷取裝置的實時狀态資訊,是以當裝置發生故障時,使用者空間通知DM驅動做出反應,删除那個故障的target裝置,當故障裝置恢複時,使用者空間又通知DM驅動添加回這個target裝置。

同時,當DM驅動使用target裝置發生問題時,它也會向使用者空間發送消息,使用者空間再去檢測問題原因。

以上這些使用者空間的工作就是由multipathd完成的,它總體的任務就是動态地檢測多路徑裝置的狀态,然後動态地更新DM驅動中mapped_device裝置的資訊以及對DM驅動發來的消息做反應。

DM裝置建立過程:

DM驅動根據multipath指令傳來的資訊建立起DM裝置後,該mapped_device裝置隻是提供給上層block層執行IO的對象,它同多個multipath裝置關聯,一個multipath就是一條路徑。

例dm-0,它是一個mapped_device裝置,但它可能有多條路徑,即多個target,是以每條路徑都要有一個結構體來控制,這個結構體就是multipath,而這個target_type就是multipath_target,在table_load時,會為每個mapped_device裝置建立映射表以及相應的target,建立target調用的就是multipath_target中定義的ctr函數,而映射IO時調用的就是multipath_target中的map_rq函數。

ioctl通信方法

DM驅動端:

static const struct file_operations _ctl_fops = {

.open = nonseekable_open,

.unlocked_ioctl= dm_ctl_ioctl,

.compat_ioctl = dm_compat_ctl_ioctl,

.owner= THIS_MODULE,

.llseek  = noop_llseek,

};

static long dm_ctl_ioctl(struct file *file, uint command, ulong u)

{

return (long)ctl_ioctl(command, (struct dm_ioctl __user *)u);

}

static int ctl_ioctl(uint command, struct dm_ioctl __user *user)

{

fn = lookup_ioctl(cmd, &ioctl_flags);

if (!fn) {

DMWARN("dm_ctl_ioctl: unknown command 0x%x", command);

return -ENOTTY;

}

...

}

static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags)

{

static struct {

int cmd;

int flags;

ioctl_fn fn;

} _ioctls[] = {

{DM_VERSION_CMD, 0, NULL},

{DM_REMOVE_ALL_CMD, IOCTL_FLAGS_NO_PARAMS, remove_all},

{DM_LIST_DEVICES_CMD, 0, list_devices},

{DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS, dev_create},

{DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS, dev_remove},

{DM_DEV_RENAME_CMD, 0, dev_rename},

{DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend},

{DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status},

{DM_DEV_WAIT_CMD, 0, dev_wait},

{DM_TABLE_LOAD_CMD, 0, table_load},

{DM_TABLE_CLEAR_CMD, IOCTL_FLAGS_NO_PARAMS, table_clear},

{DM_TABLE_DEPS_CMD, 0, table_deps},

{DM_TABLE_STATUS_CMD, 0, table_status},

{DM_LIST_VERSIONS_CMD, 0, list_versions},

{DM_TARGET_MSG_CMD, 0, target_message},

{DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry}

};

if (unlikely(cmd >= ARRAY_SIZE(_ioctls)))

return NULL;

*ioctl_flags = _ioctls[cmd].flags;

return _ioctls[cmd].fn;

}

使用者空間device-mapper庫:

int dm_task_run(struct dm_task *dmt)

{

...

repeat_ioctl:

if (!(dmi = _do_dm_ioctl(dmt, command, _ioctl_buffer_double_factor,

ioctl_retry, &retryable))) {

...

}

static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command,

    unsigned buffer_repeat_count,

    unsigned retry_repeat_count,

    int *retryable)

{

struct dm_ioctl *dmi;

int ioctl_with_uevent;

...

#ifdef DM_IOCTLS

if (ioctl(_control_fd, command, dmi) < 0 && //調用/dev/mapper/control的ioctl接口,cmd,dmi就傳遞給DM驅動的ioctl函數。

dmt->expected_errno != errno) {

...

}

...

}

DM與MD的差別:

md is multiple devices. It's a RAID implementation in Linux kernel available since kernel release 2.0. 

It allows you to create RAID level 0, 10, 4, 5 and 6. It has various optimizations like utilizing SSE and MMX instructions. It's a standart software RAID in Linux.

dm is device mapper. It's Linux kernel framework that allows you to, well, map one device on another devices (one or many). This works as creating virtual device (mapped device) that you can access in /dev/mapper directory. All I/O to that device will be mapped to other devices. Reasons for device mapper is that there are many cases where you need to map devices, but you would like to reuse code.

There are several drivers (called dm targets) utilizing device mapper, for example:

  • dm-linear - map one device to another linearly. That means your new device will translate I/O requests to underlying device with some LBA shift. For example, when you read at LBA 1000 from /dev/mapper/linear your read will be done at LBA 1050 from /dev/sdd.
  • dm-stripe - map one device to multiple devices as in RAID 0.

Difference between dm-stripe and md RAID level 0 is not that big - it's just different implementations, but I believe that md RAID has better performance.

And finally, lvm is userspace toolset that provide logical volume management facilities on linux. It uses device mapper to map volume groups and logical volumes to physical devices.

And there is a special confusing case - 

dm-raid

, you can read about it here

繼續閱讀