Skip to content

Commit 10b4869

Browse files
authored
Merge pull request #370 from sysprog21/extended
Add DMA, block, and network driver examples
2 parents aa65a39 + 563b6db commit 10b4869

5 files changed

Lines changed: 1340 additions & 2 deletions

File tree

examples/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ obj-m += static_key.o
3737
obj-m += led.o
3838
obj-m += dht11.o
3939
obj-m += devicetree.o
40+
obj-m += dma.o
41+
obj-m += blkram.o
42+
obj-m += vnetloop.o
4043

4144
KDIR ?= /lib/modules/$(shell uname -r)/build
4245
PWD := $(CURDIR)

examples/blkram.c

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* blkram.c - Tiny RAM-backed block device using blk-mq.
4+
*
5+
* Covers Linux 5.10 through 6.17+ by using version guards at the three
6+
* major block-layer API transitions:
7+
*
8+
* 5.10-5.14 alloc_disk() + blk_mq_init_queue() + blk_cleanup_queue()
9+
* 5.15-6.8 blk_mq_alloc_disk(set, queuedata) [2-arg macro]
10+
* 6.9+ blk_mq_alloc_disk(set, lim, queuedata) [3-arg macro]
11+
*
12+
* Teardown likewise varies: blk_cleanup_queue() was removed in 5.15,
13+
* blk_cleanup_disk() was removed in 5.18; modern kernels use
14+
* del_gendisk() + put_disk().
15+
*/
16+
17+
#include <linux/blk-mq.h>
18+
#include <linux/blkdev.h>
19+
#include <linux/errno.h>
20+
#include <linux/highmem.h>
21+
#include <linux/init.h>
22+
#include <linux/kernel.h>
23+
#include <linux/module.h>
24+
#include <linux/slab.h>
25+
#include <linux/version.h>
26+
#include <linux/vmalloc.h>
27+
28+
/* genhd.h was removed in 5.18; its content lives in blkdev.h since 5.17. */
29+
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0)
30+
#include <linux/genhd.h>
31+
#endif
32+
33+
#define BLKRAM_SECTOR_SIZE 512
34+
35+
static unsigned long blkram_mb = 8;
36+
module_param(blkram_mb, ulong, 0444);
37+
MODULE_PARM_DESC(blkram_mb, "Size of the RAM disk in MiB");
38+
39+
struct blkram_dev {
40+
struct blk_mq_tag_set tag_set;
41+
struct gendisk *disk;
42+
struct request_queue *queue;
43+
u8 *data;
44+
size_t size;
45+
};
46+
47+
static struct blkram_dev *blkram;
48+
static int blkram_major;
49+
50+
static blk_status_t blkram_transfer(struct blkram_dev *dev, struct request *rq)
51+
{
52+
struct req_iterator iter;
53+
struct bio_vec bvec;
54+
sector_t sector = blk_rq_pos(rq);
55+
unsigned long offset = (unsigned long)sector * BLKRAM_SECTOR_SIZE;
56+
57+
rq_for_each_segment(bvec, rq, iter)
58+
{
59+
unsigned int len = bvec.bv_len;
60+
void *iobuf;
61+
62+
if (offset + len > dev->size)
63+
return BLK_STS_IOERR;
64+
65+
/* kmap_local_page() appeared in 5.11; fall back to kmap_atomic() on 5.10. */
66+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
67+
iobuf = kmap_local_page(bvec.bv_page) + bvec.bv_offset;
68+
#else
69+
iobuf = kmap_atomic(bvec.bv_page) + bvec.bv_offset;
70+
#endif
71+
72+
if (rq_data_dir(rq) == WRITE)
73+
memcpy(dev->data + offset, iobuf, len);
74+
else
75+
memcpy(iobuf, dev->data + offset, len);
76+
77+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
78+
kunmap_local(iobuf);
79+
#else
80+
kunmap_atomic(iobuf);
81+
#endif
82+
offset += len;
83+
}
84+
85+
return BLK_STS_OK;
86+
}
87+
88+
static blk_status_t blkram_queue_rq(struct blk_mq_hw_ctx *hctx,
89+
const struct blk_mq_queue_data *bd)
90+
{
91+
struct request *rq = bd->rq;
92+
struct blkram_dev *dev = rq->q->queuedata;
93+
blk_status_t status;
94+
95+
blk_mq_start_request(rq);
96+
97+
if (blk_rq_is_passthrough(rq))
98+
status = BLK_STS_IOERR;
99+
else
100+
status = blkram_transfer(dev, rq);
101+
102+
blk_mq_end_request(rq, status);
103+
104+
return BLK_STS_OK;
105+
}
106+
107+
static const struct blk_mq_ops blkram_mq_ops = {
108+
.queue_rq = blkram_queue_rq,
109+
};
110+
111+
static const struct block_device_operations blkram_fops = {
112+
.owner = THIS_MODULE,
113+
};
114+
115+
static int __init blkram_init(void)
116+
{
117+
unsigned long sectors;
118+
int ret;
119+
120+
blkram_major = register_blkdev(0, "blkram");
121+
if (blkram_major < 0)
122+
return blkram_major;
123+
124+
blkram = kzalloc(sizeof(*blkram), GFP_KERNEL);
125+
if (!blkram) {
126+
ret = -ENOMEM;
127+
goto err_unreg;
128+
}
129+
130+
blkram->size = blkram_mb * 1024 * 1024;
131+
sectors = blkram->size / BLKRAM_SECTOR_SIZE;
132+
133+
blkram->data = vzalloc(blkram->size);
134+
if (!blkram->data) {
135+
ret = -ENOMEM;
136+
goto err_free_dev;
137+
}
138+
139+
blkram->tag_set.ops = &blkram_mq_ops;
140+
blkram->tag_set.nr_hw_queues = 1;
141+
blkram->tag_set.queue_depth = 64;
142+
blkram->tag_set.numa_node = NUMA_NO_NODE;
143+
blkram->tag_set.cmd_size = 0;
144+
/* BLK_MQ_F_SHOULD_MERGE was removed in 6.6+; merging is always on. */
145+
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
146+
blkram->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
147+
#else
148+
blkram->tag_set.flags = 0;
149+
#endif
150+
blkram->tag_set.driver_data = blkram;
151+
152+
ret = blk_mq_alloc_tag_set(&blkram->tag_set);
153+
if (ret)
154+
goto err_free_data;
155+
156+
/* Three eras of block-device creation:
157+
* 6.9+ blk_mq_alloc_disk(set, lim, queuedata) -- 3-arg form.
158+
* 5.15-6.8 blk_mq_alloc_disk(set, queuedata) -- 2-arg form.
159+
* 5.10-5.14 alloc_disk() + blk_mq_init_queue() -- separate objects.
160+
*/
161+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 9, 0)
162+
{
163+
struct queue_limits lim = {
164+
.logical_block_size = BLKRAM_SECTOR_SIZE,
165+
};
166+
blkram->disk = blk_mq_alloc_disk(&blkram->tag_set, &lim, blkram);
167+
}
168+
if (IS_ERR(blkram->disk)) {
169+
ret = PTR_ERR(blkram->disk);
170+
goto err_tag_set;
171+
}
172+
blkram->queue = blkram->disk->queue;
173+
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
174+
blkram->disk = blk_mq_alloc_disk(&blkram->tag_set, blkram);
175+
if (IS_ERR(blkram->disk)) {
176+
ret = PTR_ERR(blkram->disk);
177+
goto err_tag_set;
178+
}
179+
blkram->queue = blkram->disk->queue;
180+
#else
181+
blkram->queue = blk_mq_init_queue(&blkram->tag_set);
182+
if (IS_ERR(blkram->queue)) {
183+
ret = PTR_ERR(blkram->queue);
184+
goto err_tag_set;
185+
}
186+
187+
blkram->disk = alloc_disk(1);
188+
if (!blkram->disk) {
189+
ret = -ENOMEM;
190+
goto err_cleanup_queue;
191+
}
192+
193+
blkram->disk->queue = blkram->queue;
194+
#endif
195+
196+
blkram->queue->queuedata = blkram;
197+
blkram->disk->major = blkram_major;
198+
blkram->disk->first_minor = 0;
199+
blkram->disk->minors = 1;
200+
blkram->disk->fops = &blkram_fops;
201+
blkram->disk->private_data = blkram;
202+
203+
snprintf(blkram->disk->disk_name, DISK_NAME_LEN, "blkram0");
204+
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 9, 0)
205+
blk_queue_logical_block_size(blkram->queue, BLKRAM_SECTOR_SIZE);
206+
#endif
207+
set_capacity(blkram->disk, sectors);
208+
209+
/* add_disk() returns int since 5.16; earlier kernels return void. */
210+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)
211+
ret = add_disk(blkram->disk);
212+
if (ret)
213+
goto err_put_disk;
214+
#else
215+
add_disk(blkram->disk);
216+
#endif
217+
218+
pr_info("blkram: registered /dev/%s (%lu MiB)\n", blkram->disk->disk_name,
219+
blkram_mb);
220+
221+
return 0;
222+
223+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)
224+
err_put_disk:
225+
put_disk(blkram->disk);
226+
#endif
227+
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)
228+
err_cleanup_queue:
229+
blk_cleanup_queue(blkram->queue);
230+
#endif
231+
err_tag_set:
232+
blk_mq_free_tag_set(&blkram->tag_set);
233+
err_free_data:
234+
vfree(blkram->data);
235+
err_free_dev:
236+
kfree(blkram);
237+
err_unreg:
238+
unregister_blkdev(blkram_major, "blkram");
239+
return ret;
240+
}
241+
242+
static void __exit blkram_exit(void)
243+
{
244+
del_gendisk(blkram->disk);
245+
put_disk(blkram->disk);
246+
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)
247+
blk_cleanup_queue(blkram->queue);
248+
#endif
249+
blk_mq_free_tag_set(&blkram->tag_set);
250+
vfree(blkram->data);
251+
kfree(blkram);
252+
unregister_blkdev(blkram_major, "blkram");
253+
}
254+
255+
module_init(blkram_init);
256+
module_exit(blkram_exit);
257+
258+
MODULE_DESCRIPTION("LKMPG blk-mq RAM disk example");
259+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)