-
Notifications
You must be signed in to change notification settings - Fork 2
/
ram_block.c
273 lines (243 loc) · 6.78 KB
/
ram_block.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/* Disk on RAM Driver */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/genhd.h> // For basic block driver framework
#include <linux/blkdev.h> // For at least, struct block_device_operations
#include <linux/hdreg.h> // For struct hd_geometry
#include <linux/errno.h>
#include "ram_device.h"
#define RB_FIRST_MINOR 0
#define RB_MINOR_CNT 16
static u_int rb_major = 0;
/*
* The internal structure representation of our Device
*/
static struct rb_device
{
/* Size is the size of the device (in sectors) */
unsigned int size;
/* For exclusive access to our request queue */
spinlock_t lock;
/* Our request queue */
struct request_queue *rb_queue;
/* This is kernel's representation of an individual disk device */
struct gendisk *rb_disk;
} rb_dev;
static int rb_open(struct block_device *bdev, fmode_t mode)
{
unsigned unit = iminor(bdev->bd_inode);
printk(KERN_INFO "rb: Device is opened\n");
printk(KERN_INFO "rb: Inode number is %d\n", unit);
if (unit > RB_MINOR_CNT)
return -ENODEV;
return 0;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0))
static int rb_close(struct gendisk *disk, fmode_t mode)
{
printk(KERN_INFO "rb: Device is closed\n");
return 0;
}
#else
static void rb_close(struct gendisk *disk, fmode_t mode)
{
printk(KERN_INFO "rb: Device is closed\n");
}
#endif
static int rb_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
geo->heads = 1;
geo->cylinders = 32;
geo->sectors = 32;
geo->start = 0;
return 0;
}
/*
* Actual Data transfer
*/
static int rb_transfer(struct request *req)
{
//struct rb_device *dev = (struct rb_device *)(req->rq_disk->private_data);
int dir = rq_data_dir(req);
sector_t start_sector = blk_rq_pos(req);
unsigned int sector_cnt = blk_rq_sectors(req);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0))
#define BV_PAGE(bv) ((bv)->bv_page)
#define BV_OFFSET(bv) ((bv)->bv_offset)
#define BV_LEN(bv) ((bv)->bv_len)
struct bio_vec *bv;
#else
#define BV_PAGE(bv) ((bv).bv_page)
#define BV_OFFSET(bv) ((bv).bv_offset)
#define BV_LEN(bv) ((bv).bv_len)
struct bio_vec bv;
#endif
struct req_iterator iter;
sector_t sector_offset;
unsigned int sectors;
u8 *buffer;
int ret = 0;
//printk(KERN_DEBUG "rb: Dir:%d; Sec:%lld; Cnt:%d\n", dir, start_sector, sector_cnt);
sector_offset = 0;
rq_for_each_segment(bv, req, iter)
{
buffer = page_address(BV_PAGE(bv)) + BV_OFFSET(bv);
if (BV_LEN(bv) % RB_SECTOR_SIZE != 0)
{
printk(KERN_ERR "rb: Should never happen: "
"bio size (%d) is not a multiple of RB_SECTOR_SIZE (%d).\n"
"This may lead to data truncation.\n",
BV_LEN(bv), RB_SECTOR_SIZE);
ret = -EIO;
}
sectors = BV_LEN(bv) / RB_SECTOR_SIZE;
printk(KERN_DEBUG "rb: Start Sector: %llu, Sector Offset: %llu; Buffer: %p; Length: %u sectors\n",
(unsigned long long)(start_sector), (unsigned long long)(sector_offset), buffer, sectors);
if (dir == WRITE) /* Write to the device */
{
ramdevice_write(start_sector + sector_offset, buffer, sectors);
}
else /* Read from the device */
{
ramdevice_read(start_sector + sector_offset, buffer, sectors);
}
sector_offset += sectors;
}
if (sector_offset != sector_cnt)
{
printk(KERN_ERR "rb: bio info doesn't match with the request info");
ret = -EIO;
}
return ret;
}
/*
* Represents a block I/O request for us to execute
*/
static void rb_request(struct request_queue *q)
{
struct request *req;
int ret;
/* Gets the current request from the dispatch queue */
while ((req = blk_fetch_request(q)) != NULL)
{
#if 0
/*
* This function tells us whether we are looking at a filesystem request
* - one that moves block of data
*/
if (!blk_fs_request(req))
{
printk(KERN_NOTICE "rb: Skip non-fs request\n");
/* We pass 0 to indicate that we successfully completed the request */
__blk_end_request_all(req, 0);
//__blk_end_request(req, 0, blk_rq_bytes(req));
continue;
}
#endif
ret = rb_transfer(req);
__blk_end_request_all(req, ret);
//__blk_end_request(req, ret, blk_rq_bytes(req));
}
}
/*
* These are the file operations that performed on the ram block device
*/
static struct block_device_operations rb_fops =
{
.owner = THIS_MODULE,
.open = rb_open,
.release = rb_close,
.getgeo = rb_getgeo,
};
/*
* This is the registration and initialization section of the ram block device
* driver
*/
static int __init rb_init(void)
{
int ret;
/* Set up our RAM Device */
if ((ret = ramdevice_init()) < 0)
{
return ret;
}
rb_dev.size = ret;
/* Get Registered */
rb_major = register_blkdev(rb_major, "rb");
if (rb_major <= 0)
{
printk(KERN_ERR "rb: Unable to get Major Number\n");
ramdevice_cleanup();
return -EBUSY;
}
/* Get a request queue (here queue is created) */
spin_lock_init(&rb_dev.lock);
rb_dev.rb_queue = blk_init_queue(rb_request, &rb_dev.lock);
if (rb_dev.rb_queue == NULL)
{
printk(KERN_ERR "rb: blk_init_queue failure\n");
unregister_blkdev(rb_major, "rb");
ramdevice_cleanup();
return -ENOMEM;
}
/*
* Add the gendisk structure
* By using this memory allocation is involved,
* the minor number we need to pass bcz the device
* will support this much partitions
*/
rb_dev.rb_disk = alloc_disk(RB_MINOR_CNT);
if (!rb_dev.rb_disk)
{
printk(KERN_ERR "rb: alloc_disk failure\n");
blk_cleanup_queue(rb_dev.rb_queue);
unregister_blkdev(rb_major, "rb");
ramdevice_cleanup();
return -ENOMEM;
}
/* Setting the major number */
rb_dev.rb_disk->major = rb_major;
/* Setting the first mior number */
rb_dev.rb_disk->first_minor = RB_FIRST_MINOR;
/* Initializing the device operations */
rb_dev.rb_disk->fops = &rb_fops;
/* Driver-specific own internal data */
rb_dev.rb_disk->private_data = &rb_dev;
rb_dev.rb_disk->queue = rb_dev.rb_queue;
/*
* You do not want partition information to show up in
* cat /proc/partitions set this flags
*/
//rb_dev.rb_disk->flags = GENHD_FL_SUPPRESS_PARTITION_INFO;
sprintf(rb_dev.rb_disk->disk_name, "rb");
/* Setting the capacity of the device in its gendisk structure */
set_capacity(rb_dev.rb_disk, rb_dev.size);
/* Adding the disk to the system */
add_disk(rb_dev.rb_disk);
/* Now the disk is "live" */
printk(KERN_INFO "rb: Ram Block driver initialised (%d sectors; %d bytes)\n",
rb_dev.size, rb_dev.size * RB_SECTOR_SIZE);
return 0;
}
/*
* This is the unregistration and uninitialization section of the ram block
* device driver
*/
static void __exit rb_cleanup(void)
{
del_gendisk(rb_dev.rb_disk);
put_disk(rb_dev.rb_disk);
blk_cleanup_queue(rb_dev.rb_queue);
unregister_blkdev(rb_major, "rb");
ramdevice_cleanup();
}
module_init(rb_init);
module_exit(rb_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email@sarika-pugs.com>");
MODULE_DESCRIPTION("Ram Block Driver");
MODULE_ALIAS_BLOCKDEV_MAJOR(rb_major);