Loopback Device를 이용한 가상 SSD 생성이 완료되었으므로, 이제 Linux 커널의 블록 디바이스 프레임워크를 활용해 vSSD Driver를 개발합니다. 이 드라이버는 RAM 디스크를 활용하여 데이터를 저장하여, Loopback Device의 역할을 대신합니다.
touch devicedriver.c Makefile app.c
1) devicedriver.c: vSSD Driver 모듈 코드
2) app.c: vSSD Driver를 테스트하는 사용자 애플리케이션
3) Makefile: 빌드 파일
다음은 vSSD Driver의 주요 코드입니다.
RAM 디스크를 생성하고, 블록 디바이스 연산을 구현합니다.
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/blk-mq.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#define VSSD_NAME "vssd"
#define RAMDISK_SIZE (1024 * 1024) // 1GB
static struct gendisk *my_disk;
static struct request_queue *my_queue;
static struct blk_mq_tag_set *tag_set;
static int major_num;
static char *ramdisk;
static blk_status_t my_request(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) {
struct request *req = bd->rq;
blk_mq_start_request(req);
pr_info("Handling request: cmd_flags=%x\n", req->cmd_flags);
blk_mq_end_request(req, BLK_STS_OK);
return BLK_STS_OK;
}
static struct blk_mq_ops mq_ops = {
.queue_rq = my_request,
};
static struct block_device_operations my_block_ops = {
.owner = THIS_MODULE,
};
static int __init my_block_init(void) {
ramdisk = vmalloc(RAMDISK_SIZE);
if (!ramdisk) {
pr_err("Failed to allocate ramdisk\n");
return -ENOMEM;
}
memset(ramdisk, 0, RAMDISK_SIZE);
major_num = register_blkdev(0, VSSD_NAME);
if (major_num < 0) {
pr_err("register_blkdev failed\n");
vfree(ramdisk);
return major_num;
}
tag_set = kzalloc(sizeof(*tag_set), GFP_KERNEL);
if (!tag_set) {
unregister_blkdev(major_num, VSSD_NAME);
vfree(ramdisk);
return -ENOMEM;
}
tag_set->ops = &mq_ops;
tag_set->nr_hw_queues = 1;
tag_set->queue_depth = 128;
if (blk_mq_alloc_tag_set(tag_set)) {
kfree(tag_set);
unregister_blkdev(major_num, VSSD_NAME);
vfree(ramdisk);
return -ENOMEM;
}
my_queue = blk_mq_init_queue(tag_set);
if (IS_ERR(my_queue)) {
blk_mq_free_tag_set(tag_set);
unregister_blkdev(major_num, VSSD_NAME);
vfree(ramdisk);
return PTR_ERR(my_queue);
}
my_disk = blk_mq_alloc_disk(tag_set, NULL);
if (!my_disk) {
blk_cleanup_queue(my_queue);
blk_mq_free_tag_set(tag_set);
unregister_blkdev(major_num, VSSD_NAME);
vfree(ramdisk);
return -ENOMEM;
}
my_disk->major = major_num;
my_disk->first_minor = 0;
my_disk->fops = &my_block_ops;
my_disk->queue = my_queue;
snprintf(my_disk->disk_name, 32, VSSD_NAME);
set_capacity(my_disk, RAMDISK_SIZE / 512);
add_disk(my_disk);
pr_info("Module loaded: /dev/%s\n", VSSD_NAME);
return 0;
}
static void __exit my_block_exit(void) {
del_gendisk(my_disk);
put_disk(my_disk);
blk_cleanup_queue(my_queue);
blk_mq_free_tag_set(tag_set);
unregister_blkdev(major_num, VSSD_NAME);
vfree(ramdisk);
pr_info("Module unloaded\n");
}
module_init(my_block_init);
module_exit(my_block_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("임채진");
MODULE_DESCRIPTION("Virtual SSD Block Device");
다음은 vSSD Driver를 테스트하는 app.c 코드입니다.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define DEVICE_PATH "/dev/vssd"
int main() {
int fd;
char write_buf[] = "Hello, vSSD!";
char read_buf[512];
fd = open(DEVICE_PATH, O_RDWR);
if (fd < 0) {
perror("Failed to open device");
return EXIT_FAILURE;
}
write(fd, write_buf, strlen(write_buf));
lseek(fd, 0, SEEK_SET);
read(fd, read_buf, sizeof(read_buf));
printf("Read from device: %s\n", read_buf);
close(fd);
return EXIT_SUCCESS;
}
Loopback Device를 대신하는 RAM 디스크 기반 드라이버를 로드합니다.
$ sudo insmod devicedriver.ko
주의: <major_number>는 드라이버 로드 후 dmesg에서 확인할 수 있습니다.
$ sudo mknod /dev/vssd b <major_number> 0
$ sudo chmod 666 /dev/vssd
첫 번째 실습과 동일한 방식으로 파일 시스템 생성 및 테스트를 진행합니다.
# ext4 파일 시스템 생성
$ sudo mkfs.ext4 /dev/vssd
# 가상 SSD 마운트
$ sudo mount /dev/vssd /mnt/virtual_ssd
# 테스트
$ echo "Hello, vSSD!" > /mnt/virtual_ssd/testfile
$ cat /mnt/virtual_ssd/testfile
결론
1. 첫 번째 실습: Loopback Device를 활용해 가상 SSD 환경을 설정하고 테스트합니다.
2. 두 번째 실습: vSSD Driver를 구현하여 RAM 디스크를 활용한 블록 디바이스 드라이버를 개발합니다.
이 두 실습을 통해 Linux 블록 디바이스의 기본 구조를 이해하고, Loopback Device와 커널 드라이버의 차이점을 배울 수 있습니다.