Block Device
Driver
The Linux Kernel
NỘI DUNG TRÌNH BÀY:
1.
2.
3.
4.
5.
6.
7.
8.
9.
Tổng quan
Đăng ký một block I/O device
Đăng ký một ổ đĩa – Register a disk
Cấu trúc ‘‘Struct gendisk’’
Cấu trúc ‘‘Struct block_device_operations’’
Yêu cầu hàng đợi
Yêu cầu đối với block device
Cấu trúc ‘‘Struct bio”
Demo
Mục tiêu bài học:
01
02
03
Thu thập kiến thức về
hoạt động của hệ
thống I/O trên Linux
Thực hành sử dụng
cấu trúc và hàm của
các Block Devices
Có các kỹ năng cơ bản
để sử dụng API cho
các block device, bằng
cách giải các bài tập
01
OVER VIEW
Tổng quan
1.1.
Basic Knowledge
2.2.
Block device
Kiến thức nền
Đặc điểm Block device,
hệ thống con Block I/O,…
TỔNG QUAN:
1. Kiến thức nền
Device driver là một
chương trình phần
mềm nằm dưới tầng
kernel, nhiệm vụ của nó
là nhận các chỉ thị từ
user space để điều
khiển hardware hoạt
động.
TỔNG QUAN:
1. Kiến thức nền
Có 3 loại device cơ bản trong hệ thống
Linux
+ Character Devices (Byte-oriented:
RS232, PS/2, VGA, I2C, SPI, Keyboard,
...)
+ Block Devices (Block-oriented: IDE,
SCSI, MTD, …)
+ Network Devices (Packet-oriented:
Ethernet, Wi-fi, ...)
TỔNG QUAN:
2. Block device
- Các Block Device đặc trưng bởi khả năng truy cập ngẫu nhiên vào dữ liệu được tổ chức
trong các block có kích thước cố định.
Ví dụ về các thiết bị như vậy là ổ cứng, ổ CD-ROM, đĩa RAM, v.v.
- BlockDevice có thể phải di chuyển đến bất kỳ vị trí nào trong thiết bị để cung cấp quyền
truy cập ngẫu nhiên vào dữ liệu. Để đơn giản hóa cơng việc với các Block Device, Linux
kernel cung cấp một hệ thống con được gọi là Block I / O (hoặc block layer).
- Kích thước của block có thể khác nhau tùy thuộc vào hệ thống tệp được sử dụng, các giá
trị phổ biến nhất là 512 byte, 1 kilobyte và 4 kilobyte.
02
Register a
block I/O device
Đăng ký một Block I/O device
Đăng ký một block I/O device:
#include <linux/fs.h>
• Để đăng ký một block
I / O device, hàm
register_blkdev()
được sử dụng.
• Để hủy đăng ký một
block I / O device, hàm
unregister_blkdev()
được sử dụng.
#define MY_BLOCK_MAJOR
#define MY_BLKDEV_NAME
240
"mybdev"
static int my_block_init(void)
{
int status;
status = register_blkdev(MY_BLOCK_MAJOR, MY_BLKDEV_NAME);
if (status < 0) {
printk(KERN_ERR "unable to register mybdev block
device\n");
return -EBUSY;
}
//...
}
static void my_block_exit(void)
{
//...
unregister_blkdev(MY_BLOCK_MAJOR, MY_BLKDEV_NAME);
}
03
Register a disk
Đăng ký một ổ đĩa
Đăng ký một ổ đĩa:
•
Để tạo và sử dụng block device (disk), một giao diện chuyên biệt được định nghĩa trong
linux/genhd.h
•
Các chức năng hữu ích được định nghĩa trong linux/genhd.h là register /allocate a disk
(đăng ký / cấp phát đĩa), thêm nó vào hệ thống và de-register /unmount the disk (hủy đăng
ký / ngắt kết nối đĩa).
•
Các hàm alloc_disk() được sử dụng để phân bổ một đĩa, và các hàm del_gendisk()được
sử dụng để giải phóng nó. Thêm đĩa vào hệ thống được thực hiện bằng cách sử dụng hàm
add_disk().
•
Các hàm alloc_disk() và add_disk() thường được sử dụng trong chức năng khởi tạo mơđun và hàm del_gendisk() trong chức năng thốt mơ-đun.
•
Cấu trúc cơ bản trong làm việc với các block device (disk) là cấu trúc struct gendisk
(Xem ví dụ)
04
Struct gendisk structure
Cấu trúc Struct gendisk
Struct gendisk structure:
Các cấu trúc struct gendisk có các trường quan trọng sau đây:
• major, first_minor, minor, Mơ tả định danh được sử dụng bởi các đĩa; một đĩa phải có ít
nhất một minor; nếu đĩa cho phép hoạt động phân vùng, một minor phải được phân bổ
cho mỗi phân vùng có thể
• disk_name, đại diện cho tên đĩa khi nó xuất hiện trong /proc/partitions và trong
sysfs(/sys/block)
• fops, đại diện cho các hoạt động liên quan đến đĩa
• queue, đại diện cho hàng đợi yêu cầu
• capacity, là dung lượng đĩa trong 512 byte sectors; nó được khởi tạo bằng cách sử dụng
hàm set_capacity()
• private_data, là một con trỏ đến dữ liệu cá nhân
(Xem ví dụ về việc điền cấu trúc struct gendisk)
05
Struct block_device_operations
structure
Cấu trúc
Struct block_device_operations
Struct block_device_operations structure:
Cũng như đối với character device, các hoạt động trong struct file_operations phải được hồn
thành, vì vậy đối với block device, các hoạt động trong struct
block_device_operations phải được hoàn thành. Việc liên kết các hoạt động được thực
hiện thông qua trường fops trong cấu trúc struct gendisk.
Một số trường của cấu trúc struct block_device_operations được trình bày dưới đây:
struct block_device_operations {
int (*open) (struct block_device *, fmode_t);
int (*release) (struct gendisk *, fmode_t);
int (*locked_ioctl) (struct block_device *, fmode_t, unsigned,
unsigned long);
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*compat_ioctl) (struct block_device *, fmode_t, unsigned,
unsigned long);
int (*direct_access) (struct block_device *, sector_t,
void **, unsigned long *);
int (*media_changed) (struct gendisk *);
int (*revalidate_disk) (struct gendisk *);
int (*getgeo)(struct block_device *, struct hd_geometry *);
struct module *owner;
}
06
1.1.
Create and delete a
request queue
2.2.
Useful functions for
processing request queues
Request queues
Yêu cầu hàng đợi
Tạo và xóa yêu cầu hàng đợi
Các chức năng hữu ích để xử lý
yêu cầu hàng đợi
“
Drivers cho block device sử dụng hàng đợi để lưu trữ các yêu cầu block
I / O sẽ được xử lý. Một hàng đợi yêu cầu được đại diện bởi cấu trúc
struct request_queue .
Hàng đợi yêu cầu được tạo thành từ một danh sách các yêu cầu được
liên kết kép và kiểm sốt thơng tin liên quan của chúng. Các yêu cầu
được thêm vào hàng đợi bằng mã kernel cấp cao hơn (ví dụ: tệp hệ
thống).
Miễn là hàng đợi yêu cầu không trống, driver được liên kết của hàng
đợi sẽ phải truy xuất yêu cầu đầu tiên từ hàng đợi và chuyển nó đến
block device được liên kết. Mỗi mục trong hàng đợi yêu cầu là một yêu
cầu được đại diện bởi cấu trúc struct request
”
Request queues:
1. Tạo và xóa yêu cầu hàng đợi
Hàng đợi yêu cầu được tạo bằng hàm blk_init_queue() và bị xóa bằng hàm blk_cleanup_queue().
(Xem ví dụ về việc sử dụng các hàm này)
2. Các chức năng hữu ích để xử lý yêu cầu hàng đợi
Các chức năng được sử dụng để xử lý các yêu cầu từ hàng đợi yêu cầu được mơ tả dưới đây:
•
•
•
•
blk_peek_request()- truy xuất một tham chiếu đến yêu cầu đầu tiên từ hàng đợi; yêu cầu
tương ứng phải được bắt đầu sử dụng blk_start_request();
blk_start_request()- trích xuất yêu cầu từ hàng đợi và bắt đầu nó để xử lý; nói chung, hàm
nhận dưới dạng một tham chiếu con trỏ đến một yêu cầu được trả về bởi blk_peek_request();
blk_fetch_request()- lấy yêu cầu đầu tiên từ hàng đợi (sử dụng blk_peek_request()) và bắt
đầu nó (sử dụng blk_start_request());
blk_requeue_request() - để nhập lại hàng đợi.
Trước khi gọi bất kỳ hàm nào ở trên, phải có được spinlock liên quan đến hàng đợi. Nếu hàm được gọi từ hàm kiểu
request_fn_proc, thì spinlock đã được giữ.
07
Requests for block
devices
Yêu cầu đối với
block device
1.1.
Create a request
2.2.
Finish a request
2.3.
Process a request
Tạo một yêu cầu
Hoàn thành một yêu cầu
Xử lý một yêu cầu
“
Yêu cầu đối với thiết bị khối được mô tả theo cấu trúc struct request
Các lĩnh vực struct request cấu trúc bao gồm:
cmd_flags: một loạt cờ bao gồm hướng (đọc hoặc ghi); để tìm ra hướng, định nghĩa macro
rq_data_dir được sử dụng, trả về 0 cho một yêu cầu đọc và 1 cho một yêu cầu ghi trên thiết
bị;
__sector: khu vực đầu tiên của yêu cầu chuyển giao; nếu khu vực thiết bị có kích thước khác,
cần thực hiện chuyển đổi thích hợp. Để truy cập trường này, hãy sử dụng blk_rq_pos macro;
__data_len: tổng số byte được chuyển; để truy cập trường này, blk_rq_bytes macro được sử
dụng;
nói chung, dữ liệu từ struct bio hiện tại sẽ được chuyển giao; kích thước dữ liệu được lấy
bằng macro blk_rq_cur_bytes;
bio, một danh sách động các cấu trúc struct bio là một tập hợp các bộ đệm được liên kết với
yêu cầu; trường này được truy cập bằng cách định nghĩa macro rq_for_each_segment nếu
có nhiều vùng đệm hoặc bằng bio_data trong trường hợp chỉ có một vùng đệm được liên
kết;
”
Requests for block devices:
1. Tạo một yêu cầu
-
Yêu cầu đọc / ghi được tạo bởi các lớp mã cao hơn hệ thống con I / O kernel.
Hệ thống con I / O hoạt động như một giao diện giữa hệ thống con quản lý tệp và block device
driver. Các hoạt động chính thuộc trách nhiệm của hệ thống con I / O là thêm các yêu cầu vào
hàng đợi của block device cụ thể và sắp xếp và hợp nhất các yêu cầu theo cân nhắc về hiệu
suất.
2. Hoàn thành một u cầu
-
-
Khi trình điều khiển đã hồn tất việc chuyển tất cả các sector của một yêu cầu to/from
thiết bị, nó phải thơng báo cho hệ thống con I / O bằng cách gọi hàm blk_end_request().
Nếu khóa được liên kết với hàng đợi yêu cầu đã được mua, hàm __blk_end_request() có
thể được sử dụng.
Nếu trình điều khiển muốn đóng u cầu ngay cả khi nó khơng chuyển tất cả các sector
liên quan, nó có thể gọi hàm blk_end_request_all() hoặc __blk_end_request_all(). Các
hàm __blk_end_request_all() được gọi nếu khóa liên quan đến hàng đợi yêu cầu đã được
mua lại.
Requests for block devices:
3. Xử lý một yêu cầu
-
-
Phần trung tâm của trình điều khiển thiết bị khối là loại hàm request_fn_proc. Hàm này
được liên kết với driver bằng hàm blk_init_queue().
Hàm này được gọi khi kernel cho rằng driver nên xử lý các yêu cầu I / O. Hàm phải bắt đầu
xử lý các yêu cầu từ hàng đợi, nhưng khơng bắt buộc phải hồn thành chúng, vì các u
cầu có thể được hồn thành bởi các phần khác của driver..
Các lock tham số, gửi khi tạo một hàng đợi yêu cầu, là một spinlock rằng kernel nắm giữ
khi thực hiện phương pháp yêu cầu. Khóa này cũng đảm bảo rằng khơng có u cầu nào
khác cho device sẽ được thêm vào hàng đợi trong khi chức năng yêu cầu đang chạy.
(Xem ví dụ)
- Các hàm my_block_request()có chứa một vịng lặp while() cho iterating qua hàng đợi
yêu cầu gửi như là đối số.
08
struct bio
structure
Cấu trúc
struct bio
1
Tạo cấu trúc struct bio
2
Gửi cấu trúc struct bio
3
Chờ hoàn thành một cấu trúc
struct bio
4
Khởi tạo cấu trúc struct bio
5
Cách sử dụng nội dung của
một cấu trúc struct bio
6
Giải phóng một cấu trúc struct bio
7
Thiết lập yêu cầu hàng đợi
“
Một cấu trúc struct request được thực hiện dưới dạng danh sách liên kết các struct bio
cùng với thông tin cho phép driver giữ nguyên vị trí hiện tại của nó trong khi xử lý yêu cầu.
Các cấu trúc struct bio là một mô tả ở mức độ thấp của một I / O block yêu cầu:
struct bio {
//...
struct gendisk
unsigned int
*/
//...
struct bio_vec
//...
struct bvec_iter
/...
void
//...
};
*bi_disk;
bi_opf;
*bi_io_vec;
/* bottom bits req flags, top bits REQ_OP. Use accessors.
/* the actual vec list */
bi_iter;
*bi_private;
Cấu trúc struct bio chứa một bi_io_vec vector của cấu trúc struct bio_vec . Nó bao gồm
các trang riêng lẻ trong bộ nhớ vật lý sẽ được chuyển, độ lệch trong trang và kích thước của
bộ đệm.
Để lặp qua một cấu trúc struct bio, chúng ta cần lặp qua vectơ struct bio_vec và chuyển
dữ liệu từ mọi trang vật lý. Để đơn giản hóa việc lặp vectơ, cấu trúc struct bvec_iter được sử
dụng. Cấu trúc này duy trì thơng tin về số lượng bộ đệm và các cung đã được sử dụng trong
quá trình lặp lại.
”
Struct bio structure:
1. Tạo cấu trúc struct bio
Hai hàm có thể được sử dụng để tạo cấu trúc struct bio:
• bio_alloc(): phân bổ không gian cho một cấu trúc mới; cấu trúc phải được khởi tạo;
• bio_clone(): tạo một bản sao của một cấu trúc struct bio hiện có ; cấu trúc mới thu được được
khởi tạo với các giá trị của các trường cấu trúc được nhân bản; bộ đệm được chia sẻ với cấu
trúc struct bio đã được nhân bản do đó việc truy cập vào bộ đệm phải được thực hiện cẩn
thận để tránh truy cập vào cùng một vùng bộ nhớ từ hai bản sao;
Cả hai hàm đều trả về một cấu trúc struct bio mới.
2. Gửi cấu trúc struct bio
Để gửi cấu trúc struct bio tới I / O device driver được liên kết, hàm submit_bio() này sẽ
được sử dụng. Hàm nhận dưới dạng đối số một cấu trúc struct bio đã khởi tạo sẽ được
thêm vào một yêu cầu từ hàng đợi yêu cầu của I / O device. Từ hàng đợi đó, nó có thể
được xử lý bởi I / O device driver bằng một chức năng chuyên biệt.