blob: 73f9652b2ee9000aab4243c50db4650d7974ea67 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL
4 */
5
6/* 2001-09-28...2002-04-17
7 * Partition stuff by James_McMechan@hotmail.com
8 * old style ubd by setting UBD_SHIFT to 0
9 * 2002-09-27...2002-10-18 massive tinkering for 2.5
10 * partitions have changed in 2.5
11 * 2003-01-29 more tinkering for 2.5.59-1
12 * This should now address the sysfs problems and has
13 * the symlink for devfs to allow for booting with
14 * the common /dev/ubd/discX/... names rather than
15 * only /dev/ubdN/discN this version also has lots of
16 * clean ups preparing for ubd-many.
17 * James McMechan
18 */
19
20#define MAJOR_NR UBD_MAJOR
21#define UBD_SHIFT 4
22
23#include "linux/config.h"
24#include "linux/module.h"
25#include "linux/blkdev.h"
26#include "linux/hdreg.h"
27#include "linux/init.h"
28#include "linux/devfs_fs_kernel.h"
29#include "linux/cdrom.h"
30#include "linux/proc_fs.h"
31#include "linux/ctype.h"
32#include "linux/capability.h"
33#include "linux/mm.h"
34#include "linux/vmalloc.h"
35#include "linux/blkpg.h"
36#include "linux/genhd.h"
37#include "linux/spinlock.h"
Russell Kingd052d1b2005-10-29 19:07:23 +010038#include "linux/platform_device.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include "asm/segment.h"
40#include "asm/uaccess.h"
41#include "asm/irq.h"
42#include "asm/types.h"
43#include "asm/tlbflush.h"
44#include "user_util.h"
45#include "mem_user.h"
46#include "kern_util.h"
47#include "kern.h"
48#include "mconsole_kern.h"
49#include "init.h"
50#include "irq_user.h"
51#include "irq_kern.h"
52#include "ubd_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#include "os.h"
54#include "mem.h"
55#include "mem_kern.h"
56#include "cow.h"
57
Jeff Dike7b9014c2005-05-20 13:59:11 -070058enum ubd_req { UBD_READ, UBD_WRITE };
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
60struct io_thread_req {
Jeff Dike91acb212005-10-10 23:10:32 -040061 enum ubd_req op;
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 int fds[2];
63 unsigned long offsets[2];
64 unsigned long long offset;
65 unsigned long length;
66 char *buffer;
67 int sectorsize;
Jeff Dike91acb212005-10-10 23:10:32 -040068 unsigned long sector_mask;
69 unsigned long long cow_offset;
70 unsigned long bitmap_words[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 int error;
72};
73
74extern int open_ubd_file(char *file, struct openflags *openflags,
75 char **backing_file_out, int *bitmap_offset_out,
76 unsigned long *bitmap_len_out, int *data_offset_out,
77 int *create_cow_out);
78extern int create_cow_file(char *cow_file, char *backing_file,
79 struct openflags flags, int sectorsize,
80 int alignment, int *bitmap_offset_out,
81 unsigned long *bitmap_len_out,
82 int *data_offset_out);
83extern int read_cow_bitmap(int fd, void *buf, int offset, int len);
Jeff Dike91acb212005-10-10 23:10:32 -040084extern void do_io(struct io_thread_req *req);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
Jeff Dike91acb212005-10-10 23:10:32 -040086static inline int ubd_test_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070087{
88 __u64 n;
89 int bits, off;
90
Jeff Dike91acb212005-10-10 23:10:32 -040091 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 n = bit / bits;
93 off = bit % bits;
Jeff Dike91acb212005-10-10 23:10:32 -040094 return((data[n] & (1 << off)) != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095}
96
Jeff Dike91acb212005-10-10 23:10:32 -040097static inline void ubd_set_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070098{
99 __u64 n;
100 int bits, off;
101
Jeff Dike91acb212005-10-10 23:10:32 -0400102 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 n = bit / bits;
104 off = bit % bits;
Jeff Dike91acb212005-10-10 23:10:32 -0400105 data[n] |= (1 << off);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106}
107/*End stuff from ubd_user.h*/
108
109#define DRIVER_NAME "uml-blkdev"
110
111static DEFINE_SPINLOCK(ubd_io_lock);
112static DEFINE_SPINLOCK(ubd_lock);
113
Jeff Dike91acb212005-10-10 23:10:32 -0400114static void (*do_ubd)(void);
115
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116static int ubd_open(struct inode * inode, struct file * filp);
117static int ubd_release(struct inode * inode, struct file * file);
118static int ubd_ioctl(struct inode * inode, struct file * file,
119 unsigned int cmd, unsigned long arg);
120
121#define MAX_DEV (8)
122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123static struct block_device_operations ubd_blops = {
124 .owner = THIS_MODULE,
125 .open = ubd_open,
126 .release = ubd_release,
127 .ioctl = ubd_ioctl,
128};
129
130/* Protected by the queue_lock */
131static request_queue_t *ubd_queue;
132
133/* Protected by ubd_lock */
134static int fake_major = MAJOR_NR;
135
136static struct gendisk *ubd_gendisk[MAX_DEV];
137static struct gendisk *fake_gendisk[MAX_DEV];
138
139#ifdef CONFIG_BLK_DEV_UBD_SYNC
140#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
141 .cl = 1 })
142#else
143#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
144 .cl = 1 })
145#endif
146
147/* Not protected - changed only in ubd_setup_common and then only to
148 * to enable O_SYNC.
149 */
150static struct openflags global_openflags = OPEN_FLAGS;
151
152struct cow {
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700153 /* This is the backing file, actually */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 char *file;
155 int fd;
156 unsigned long *bitmap;
157 unsigned long bitmap_len;
158 int bitmap_offset;
159 int data_offset;
160};
161
162struct ubd {
163 char *file;
164 int count;
165 int fd;
166 __u64 size;
167 struct openflags boot_openflags;
168 struct openflags openflags;
169 int no_cow;
170 struct cow cow;
171 struct platform_device pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172};
173
174#define DEFAULT_COW { \
175 .file = NULL, \
176 .fd = -1, \
177 .bitmap = NULL, \
178 .bitmap_offset = 0, \
179 .data_offset = 0, \
180}
181
182#define DEFAULT_UBD { \
183 .file = NULL, \
184 .count = 0, \
185 .fd = -1, \
186 .size = -1, \
187 .boot_openflags = OPEN_FLAGS, \
188 .openflags = OPEN_FLAGS, \
189 .no_cow = 0, \
190 .cow = DEFAULT_COW, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191}
192
193struct ubd ubd_dev[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
194
195static int ubd0_init(void)
196{
197 struct ubd *dev = &ubd_dev[0];
198
199 if(dev->file == NULL)
200 dev->file = "root_fs";
201 return(0);
202}
203
204__initcall(ubd0_init);
205
206/* Only changed by fake_ide_setup which is a setup */
207static int fake_ide = 0;
208static struct proc_dir_entry *proc_ide_root = NULL;
209static struct proc_dir_entry *proc_ide = NULL;
210
211static void make_proc_ide(void)
212{
213 proc_ide_root = proc_mkdir("ide", NULL);
214 proc_ide = proc_mkdir("ide0", proc_ide_root);
215}
216
217static int proc_ide_read_media(char *page, char **start, off_t off, int count,
218 int *eof, void *data)
219{
220 int len;
221
222 strcpy(page, "disk\n");
223 len = strlen("disk\n");
224 len -= off;
225 if (len < count){
226 *eof = 1;
227 if (len <= 0) return 0;
228 }
229 else len = count;
230 *start = page + off;
231 return len;
232}
233
234static void make_ide_entries(char *dev_name)
235{
236 struct proc_dir_entry *dir, *ent;
237 char name[64];
238
239 if(proc_ide_root == NULL) make_proc_ide();
240
241 dir = proc_mkdir(dev_name, proc_ide);
242 if(!dir) return;
243
244 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
245 if(!ent) return;
246 ent->nlink = 1;
247 ent->data = NULL;
248 ent->read_proc = proc_ide_read_media;
249 ent->write_proc = NULL;
250 sprintf(name,"ide0/%s", dev_name);
251 proc_symlink(dev_name, proc_ide_root, name);
252}
253
254static int fake_ide_setup(char *str)
255{
256 fake_ide = 1;
257 return(1);
258}
259
260__setup("fake_ide", fake_ide_setup);
261
262__uml_help(fake_ide_setup,
263"fake_ide\n"
264" Create ide0 entries that map onto ubd devices.\n\n"
265);
266
267static int parse_unit(char **ptr)
268{
269 char *str = *ptr, *end;
270 int n = -1;
271
272 if(isdigit(*str)) {
273 n = simple_strtoul(str, &end, 0);
274 if(end == str)
275 return(-1);
276 *ptr = end;
277 }
278 else if (('a' <= *str) && (*str <= 'h')) {
279 n = *str - 'a';
280 str++;
281 *ptr = str;
282 }
283 return(n);
284}
285
286static int ubd_setup_common(char *str, int *index_out)
287{
288 struct ubd *dev;
289 struct openflags flags = global_openflags;
290 char *backing_file;
291 int n, err, i;
292
293 if(index_out) *index_out = -1;
294 n = *str;
295 if(n == '='){
296 char *end;
297 int major;
298
299 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 if(!strcmp(str, "sync")){
301 global_openflags = of_sync(global_openflags);
302 return(0);
303 }
304 major = simple_strtoul(str, &end, 0);
305 if((*end != '\0') || (end == str)){
306 printk(KERN_ERR
307 "ubd_setup : didn't parse major number\n");
308 return(1);
309 }
310
311 err = 1;
312 spin_lock(&ubd_lock);
313 if(fake_major != MAJOR_NR){
314 printk(KERN_ERR "Can't assign a fake major twice\n");
315 goto out1;
316 }
317
318 fake_major = major;
319
320 printk(KERN_INFO "Setting extra ubd major number to %d\n",
321 major);
322 err = 0;
323 out1:
324 spin_unlock(&ubd_lock);
325 return(err);
326 }
327
328 n = parse_unit(&str);
329 if(n < 0){
330 printk(KERN_ERR "ubd_setup : couldn't parse unit number "
331 "'%s'\n", str);
332 return(1);
333 }
334 if(n >= MAX_DEV){
335 printk(KERN_ERR "ubd_setup : index %d out of range "
336 "(%d devices, from 0 to %d)\n", n, MAX_DEV, MAX_DEV - 1);
337 return(1);
338 }
339
340 err = 1;
341 spin_lock(&ubd_lock);
342
343 dev = &ubd_dev[n];
344 if(dev->file != NULL){
345 printk(KERN_ERR "ubd_setup : device already configured\n");
346 goto out;
347 }
348
349 if (index_out)
350 *index_out = n;
351
352 for (i = 0; i < 4; i++) {
353 switch (*str) {
354 case 'r':
355 flags.w = 0;
356 break;
357 case 's':
358 flags.s = 1;
359 break;
360 case 'd':
361 dev->no_cow = 1;
362 break;
363 case '=':
364 str++;
365 goto break_loop;
366 default:
367 printk(KERN_ERR "ubd_setup : Expected '=' or flag letter (r,s or d)\n");
368 goto out;
369 }
370 str++;
371 }
372
373 if (*str == '=')
374 printk(KERN_ERR "ubd_setup : Too many flags specified\n");
375 else
376 printk(KERN_ERR "ubd_setup : Expected '='\n");
377 goto out;
378
379break_loop:
380 err = 0;
381 backing_file = strchr(str, ',');
382
383 if (!backing_file) {
384 backing_file = strchr(str, ':');
385 }
386
387 if(backing_file){
388 if(dev->no_cow)
389 printk(KERN_ERR "Can't specify both 'd' and a "
390 "cow file\n");
391 else {
392 *backing_file = '\0';
393 backing_file++;
394 }
395 }
396 dev->file = str;
397 dev->cow.file = backing_file;
398 dev->boot_openflags = flags;
399out:
400 spin_unlock(&ubd_lock);
401 return(err);
402}
403
404static int ubd_setup(char *str)
405{
406 ubd_setup_common(str, NULL);
407 return(1);
408}
409
410__setup("ubd", ubd_setup);
411__uml_help(ubd_setup,
412"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
413" This is used to associate a device with a file in the underlying\n"
414" filesystem. When specifying two filenames, the first one is the\n"
415" COW name and the second is the backing file name. As separator you can\n"
416" use either a ':' or a ',': the first one allows writing things like;\n"
417" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
418" while with a ',' the shell would not expand the 2nd '~'.\n"
419" When using only one filename, UML will detect whether to thread it like\n"
420" a COW file or a backing file. To override this detection, add the 'd'\n"
421" flag:\n"
422" ubd0d=BackingFile\n"
423" Usually, there is a filesystem in the file, but \n"
424" that's not required. Swap devices containing swap files can be\n"
425" specified like this. Also, a file which doesn't contain a\n"
426" filesystem can have its contents read in the virtual \n"
427" machine by running 'dd' on the device. <n> must be in the range\n"
428" 0 to 7. Appending an 'r' to the number will cause that device\n"
429" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
430" an 's' will cause data to be written to disk on the host immediately.\n\n"
431);
432
433static int udb_setup(char *str)
434{
435 printk("udb%s specified on command line is almost certainly a ubd -> "
436 "udb TYPO\n", str);
437 return(1);
438}
439
440__setup("udb", udb_setup);
441__uml_help(udb_setup,
442"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700443" This option is here solely to catch ubd -> udb typos, which can be\n"
444" to impossible to catch visually unless you specifically look for\n"
445" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446" in the boot output.\n\n"
447);
448
449static int fakehd_set = 0;
450static int fakehd(char *str)
451{
452 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
453 fakehd_set = 1;
454 return 1;
455}
456
457__setup("fakehd", fakehd);
458__uml_help(fakehd,
459"fakehd\n"
460" Change the ubd device name to \"hd\".\n\n"
461);
462
463static void do_ubd_request(request_queue_t * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400464
465/* Only changed by ubd_init, which is an initcall. */
466int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
468/* Changed by ubd_handler, which is serialized because interrupts only
469 * happen on CPU 0.
470 */
471int intr_count = 0;
472
473/* call ubd_finish if you need to serialize */
Jeff Dike91acb212005-10-10 23:10:32 -0400474static void __ubd_finish(struct request *req, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475{
Jeff Dike91acb212005-10-10 23:10:32 -0400476 int nsect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
Jeff Dike91acb212005-10-10 23:10:32 -0400478 if(error){
479 end_request(req, 0);
480 return;
481 }
482 nsect = req->current_nr_sectors;
483 req->sector += nsect;
484 req->buffer += nsect << 9;
485 req->errors = 0;
486 req->nr_sectors -= nsect;
487 req->current_nr_sectors = 0;
488 end_request(req, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489}
490
Jeff Dike91acb212005-10-10 23:10:32 -0400491static inline void ubd_finish(struct request *req, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
Jeff Dike91acb212005-10-10 23:10:32 -0400493 spin_lock(&ubd_io_lock);
494 __ubd_finish(req, error);
495 spin_unlock(&ubd_io_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496}
497
Jeff Dike91acb212005-10-10 23:10:32 -0400498/* Called without ubd_io_lock held */
499static void ubd_handler(void)
500{
501 struct io_thread_req req;
502 struct request *rq = elv_next_request(ubd_queue);
503 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
Jeff Dike91acb212005-10-10 23:10:32 -0400505 do_ubd = NULL;
506 intr_count++;
507 n = os_read_file(thread_fd, &req, sizeof(req));
508 if(n != sizeof(req)){
509 printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, "
510 "err = %d\n", os_getpid(), -n);
511 spin_lock(&ubd_io_lock);
512 end_request(rq, 0);
513 spin_unlock(&ubd_io_lock);
514 return;
515 }
516
517 ubd_finish(rq, req.error);
518 reactivate_fd(thread_fd, UBD_IRQ);
519 do_ubd_request(ubd_queue);
520}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
522static irqreturn_t ubd_intr(int irq, void *dev, struct pt_regs *unused)
523{
Jeff Dike91acb212005-10-10 23:10:32 -0400524 ubd_handler();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 return(IRQ_HANDLED);
526}
527
Jeff Dike91acb212005-10-10 23:10:32 -0400528/* Only changed by ubd_init, which is an initcall. */
529static int io_pid = -1;
530
531void kill_io_thread(void)
532{
533 if(io_pid != -1)
534 os_kill_process(io_pid, 1);
535}
536
537__uml_exitcall(kill_io_thread);
538
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539static int ubd_file_size(struct ubd *dev, __u64 *size_out)
540{
541 char *file;
542
543 file = dev->cow.file ? dev->cow.file : dev->file;
544 return(os_file_size(file, size_out));
545}
546
547static void ubd_close(struct ubd *dev)
548{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 os_close_file(dev->fd);
550 if(dev->cow.file == NULL)
551 return;
552
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 os_close_file(dev->cow.fd);
554 vfree(dev->cow.bitmap);
555 dev->cow.bitmap = NULL;
556}
557
558static int ubd_open_dev(struct ubd *dev)
559{
560 struct openflags flags;
561 char **back_ptr;
562 int err, create_cow, *create_ptr;
563
564 dev->openflags = dev->boot_openflags;
565 create_cow = 0;
566 create_ptr = (dev->cow.file != NULL) ? &create_cow : NULL;
567 back_ptr = dev->no_cow ? NULL : &dev->cow.file;
568 dev->fd = open_ubd_file(dev->file, &dev->openflags, back_ptr,
569 &dev->cow.bitmap_offset, &dev->cow.bitmap_len,
570 &dev->cow.data_offset, create_ptr);
571
572 if((dev->fd == -ENOENT) && create_cow){
Jeff Dike91acb212005-10-10 23:10:32 -0400573 dev->fd = create_cow_file(dev->file, dev->cow.file,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 dev->openflags, 1 << 9, PAGE_SIZE,
575 &dev->cow.bitmap_offset,
576 &dev->cow.bitmap_len,
577 &dev->cow.data_offset);
578 if(dev->fd >= 0){
579 printk(KERN_INFO "Creating \"%s\" as COW file for "
580 "\"%s\"\n", dev->file, dev->cow.file);
581 }
582 }
583
584 if(dev->fd < 0){
585 printk("Failed to open '%s', errno = %d\n", dev->file,
586 -dev->fd);
587 return(dev->fd);
588 }
589
590 if(dev->cow.file != NULL){
591 err = -ENOMEM;
592 dev->cow.bitmap = (void *) vmalloc(dev->cow.bitmap_len);
593 if(dev->cow.bitmap == NULL){
594 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
595 goto error;
596 }
597 flush_tlb_kernel_vm();
598
599 err = read_cow_bitmap(dev->fd, dev->cow.bitmap,
600 dev->cow.bitmap_offset,
601 dev->cow.bitmap_len);
602 if(err < 0)
603 goto error;
604
605 flags = dev->openflags;
606 flags.w = 0;
607 err = open_ubd_file(dev->cow.file, &flags, NULL, NULL, NULL,
608 NULL, NULL);
609 if(err < 0) goto error;
610 dev->cow.fd = err;
611 }
612 return(0);
613 error:
614 os_close_file(dev->fd);
615 return(err);
616}
617
618static int ubd_new_disk(int major, u64 size, int unit,
619 struct gendisk **disk_out)
620
621{
622 struct gendisk *disk;
623 char from[sizeof("ubd/nnnnn\0")], to[sizeof("discnnnnn/disc\0")];
624 int err;
625
626 disk = alloc_disk(1 << UBD_SHIFT);
627 if(disk == NULL)
628 return(-ENOMEM);
629
630 disk->major = major;
631 disk->first_minor = unit << UBD_SHIFT;
632 disk->fops = &ubd_blops;
633 set_capacity(disk, size / 512);
634 if(major == MAJOR_NR){
635 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
636 sprintf(disk->devfs_name, "ubd/disc%d", unit);
637 sprintf(from, "ubd/%d", unit);
638 sprintf(to, "disc%d/disc", unit);
639 err = devfs_mk_symlink(from, to);
640 if(err)
641 printk("ubd_new_disk failed to make link from %s to "
642 "%s, error = %d\n", from, to, err);
643 }
644 else {
645 sprintf(disk->disk_name, "ubd_fake%d", unit);
646 sprintf(disk->devfs_name, "ubd_fake/disc%d", unit);
647 }
648
649 /* sysfs register (not for ide fake devices) */
650 if (major == MAJOR_NR) {
651 ubd_dev[unit].pdev.id = unit;
652 ubd_dev[unit].pdev.name = DRIVER_NAME;
653 platform_device_register(&ubd_dev[unit].pdev);
654 disk->driverfs_dev = &ubd_dev[unit].pdev.dev;
655 }
656
657 disk->private_data = &ubd_dev[unit];
658 disk->queue = ubd_queue;
659 add_disk(disk);
660
661 *disk_out = disk;
662 return 0;
663}
664
665#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
666
667static int ubd_add(int n)
668{
669 struct ubd *dev = &ubd_dev[n];
670 int err;
671
Jeff Dikeec7cf782005-09-03 15:57:29 -0700672 err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 if(dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700674 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
676 if (ubd_open_dev(dev))
Jeff Dikeec7cf782005-09-03 15:57:29 -0700677 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
679 err = ubd_file_size(dev, &dev->size);
680 if(err < 0)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700681 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
683 dev->size = ROUND_BLOCK(dev->size);
684
685 err = ubd_new_disk(MAJOR_NR, dev->size, n, &ubd_gendisk[n]);
686 if(err)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700687 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688
689 if(fake_major != MAJOR_NR)
690 ubd_new_disk(fake_major, dev->size, n,
691 &fake_gendisk[n]);
692
693 /* perhaps this should also be under the "if (fake_major)" above */
694 /* using the fake_disk->disk_name and also the fakehd_set name */
695 if (fake_ide)
696 make_ide_entries(ubd_gendisk[n]->disk_name);
697
Jeff Dikeec7cf782005-09-03 15:57:29 -0700698 err = 0;
699out_close:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 ubd_close(dev);
Jeff Dikeec7cf782005-09-03 15:57:29 -0700701out:
702 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703}
704
705static int ubd_config(char *str)
706{
707 int n, err;
708
Jeff Dike970d6e32006-01-06 00:18:48 -0800709 str = kstrdup(str, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 if(str == NULL){
711 printk(KERN_ERR "ubd_config failed to strdup string\n");
712 return(1);
713 }
714 err = ubd_setup_common(str, &n);
715 if(err){
716 kfree(str);
717 return(-1);
718 }
719 if(n == -1) return(0);
720
721 spin_lock(&ubd_lock);
722 err = ubd_add(n);
723 if(err)
724 ubd_dev[n].file = NULL;
725 spin_unlock(&ubd_lock);
726
727 return(err);
728}
729
730static int ubd_get_config(char *name, char *str, int size, char **error_out)
731{
732 struct ubd *dev;
733 int n, len = 0;
734
735 n = parse_unit(&name);
736 if((n >= MAX_DEV) || (n < 0)){
737 *error_out = "ubd_get_config : device number out of range";
738 return(-1);
739 }
740
741 dev = &ubd_dev[n];
742 spin_lock(&ubd_lock);
743
744 if(dev->file == NULL){
745 CONFIG_CHUNK(str, size, len, "", 1);
746 goto out;
747 }
748
749 CONFIG_CHUNK(str, size, len, dev->file, 0);
750
751 if(dev->cow.file != NULL){
752 CONFIG_CHUNK(str, size, len, ",", 0);
753 CONFIG_CHUNK(str, size, len, dev->cow.file, 1);
754 }
755 else CONFIG_CHUNK(str, size, len, "", 1);
756
757 out:
758 spin_unlock(&ubd_lock);
759 return(len);
760}
761
Jeff Dike29d56cf2005-06-25 14:55:25 -0700762static int ubd_id(char **str, int *start_out, int *end_out)
763{
764 int n;
765
766 n = parse_unit(str);
767 *start_out = 0;
768 *end_out = MAX_DEV - 1;
769 return n;
770}
771
772static int ubd_remove(int n)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773{
774 struct ubd *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700775 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Jeff Dike29d56cf2005-06-25 14:55:25 -0700777 spin_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778
779 if(ubd_gendisk[n] == NULL)
780 goto out;
781
Jeff Dike29d56cf2005-06-25 14:55:25 -0700782 dev = &ubd_dev[n];
783
784 if(dev->file == NULL)
785 goto out;
786
787 /* you cannot remove a open disk */
788 err = -EBUSY;
789 if(dev->count > 0)
790 goto out;
791
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 del_gendisk(ubd_gendisk[n]);
793 put_disk(ubd_gendisk[n]);
794 ubd_gendisk[n] = NULL;
795
796 if(fake_gendisk[n] != NULL){
797 del_gendisk(fake_gendisk[n]);
798 put_disk(fake_gendisk[n]);
799 fake_gendisk[n] = NULL;
800 }
801
802 platform_device_unregister(&dev->pdev);
803 *dev = ((struct ubd) DEFAULT_UBD);
804 err = 0;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700805out:
806 spin_unlock(&ubd_lock);
807 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808}
809
810static struct mc_device ubd_mc = {
811 .name = "ubd",
812 .config = ubd_config,
813 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700814 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 .remove = ubd_remove,
816};
817
818static int ubd_mc_init(void)
819{
820 mconsole_register_dev(&ubd_mc);
821 return 0;
822}
823
824__initcall(ubd_mc_init);
825
Russell King3ae5eae2005-11-09 22:32:44 +0000826static struct platform_driver ubd_driver = {
827 .driver = {
828 .name = DRIVER_NAME,
829 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830};
831
832int ubd_init(void)
833{
834 int i;
835
836 devfs_mk_dir("ubd");
837 if (register_blkdev(MAJOR_NR, "ubd"))
838 return -1;
839
840 ubd_queue = blk_init_queue(do_ubd_request, &ubd_io_lock);
841 if (!ubd_queue) {
842 unregister_blkdev(MAJOR_NR, "ubd");
843 return -1;
844 }
845
846 if (fake_major != MAJOR_NR) {
847 char name[sizeof("ubd_nnn\0")];
848
849 snprintf(name, sizeof(name), "ubd_%d", fake_major);
850 devfs_mk_dir(name);
851 if (register_blkdev(fake_major, "ubd"))
852 return -1;
853 }
Russell King3ae5eae2005-11-09 22:32:44 +0000854 platform_driver_register(&ubd_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 for (i = 0; i < MAX_DEV; i++)
856 ubd_add(i);
857 return 0;
858}
859
860late_initcall(ubd_init);
861
Jeff Dike91acb212005-10-10 23:10:32 -0400862int ubd_driver_init(void){
863 unsigned long stack;
864 int err;
865
866 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
867 if(global_openflags.s){
868 printk(KERN_INFO "ubd: Synchronous mode\n");
869 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
870 * enough. So use anyway the io thread. */
871 }
872 stack = alloc_stack(0, 0);
873 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
874 &thread_fd);
875 if(io_pid < 0){
876 printk(KERN_ERR
877 "ubd : Failed to start I/O thread (errno = %d) - "
878 "falling back to synchronous I/O\n", -io_pid);
879 io_pid = -1;
880 return(0);
881 }
882 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
883 SA_INTERRUPT, "ubd", ubd_dev);
884 if(err != 0)
885 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
886 return(err);
887}
888
889device_initcall(ubd_driver_init);
890
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891static int ubd_open(struct inode *inode, struct file *filp)
892{
893 struct gendisk *disk = inode->i_bdev->bd_disk;
894 struct ubd *dev = disk->private_data;
895 int err = 0;
896
897 if(dev->count == 0){
898 err = ubd_open_dev(dev);
899 if(err){
900 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
901 disk->disk_name, dev->file, -err);
902 goto out;
903 }
904 }
905 dev->count++;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700906 set_disk_ro(disk, !dev->openflags.w);
907
908 /* This should no more be needed. And it didn't work anyway to exclude
909 * read-write remounting of filesystems.*/
910 /*if((filp->f_mode & FMODE_WRITE) && !dev->openflags.w){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 if(--dev->count == 0) ubd_close(dev);
912 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700913 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 out:
915 return(err);
916}
917
918static int ubd_release(struct inode * inode, struct file * file)
919{
920 struct gendisk *disk = inode->i_bdev->bd_disk;
921 struct ubd *dev = disk->private_data;
922
923 if(--dev->count == 0)
924 ubd_close(dev);
925 return(0);
926}
927
Jeff Dike91acb212005-10-10 23:10:32 -0400928static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
929 __u64 *cow_offset, unsigned long *bitmap,
930 __u64 bitmap_offset, unsigned long *bitmap_words,
931 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932{
Jeff Dike91acb212005-10-10 23:10:32 -0400933 __u64 sector = io_offset >> 9;
934 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
Jeff Dike91acb212005-10-10 23:10:32 -0400936 for(i = 0; i < length >> 9; i++){
937 if(cow_mask != NULL)
938 ubd_set_bit(i, (unsigned char *) cow_mask);
939 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
940 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941
Jeff Dike91acb212005-10-10 23:10:32 -0400942 update_bitmap = 1;
943 ubd_set_bit(sector + i, (unsigned char *) bitmap);
944 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945
Jeff Dike91acb212005-10-10 23:10:32 -0400946 if(!update_bitmap)
947 return;
948
949 *cow_offset = sector / (sizeof(unsigned long) * 8);
950
951 /* This takes care of the case where we're exactly at the end of the
952 * device, and *cow_offset + 1 is off the end. So, just back it up
953 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
954 * for the original diagnosis.
955 */
956 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
957 sizeof(unsigned long) - 1))
958 (*cow_offset)--;
959
960 bitmap_words[0] = bitmap[*cow_offset];
961 bitmap_words[1] = bitmap[*cow_offset + 1];
962
963 *cow_offset *= sizeof(unsigned long);
964 *cow_offset += bitmap_offset;
965}
966
967static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
968 __u64 bitmap_offset, __u64 bitmap_len)
969{
970 __u64 sector = req->offset >> 9;
971 int i;
972
973 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
974 panic("Operation too long");
975
976 if(req->op == UBD_READ) {
977 for(i = 0; i < req->length >> 9; i++){
978 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
979 ubd_set_bit(i, (unsigned char *)
980 &req->sector_mask);
981 }
982 }
983 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
984 &req->cow_offset, bitmap, bitmap_offset,
985 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986}
987
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988/* Called with ubd_io_lock held */
Jeff Dike91acb212005-10-10 23:10:32 -0400989static int prepare_request(struct request *req, struct io_thread_req *io_req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990{
991 struct gendisk *disk = req->rq_disk;
992 struct ubd *dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -0400993 __u64 offset;
994 int len;
995
996 if(req->rq_status == RQ_INACTIVE) return(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700998 /* This should be impossible now */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 if((rq_data_dir(req) == WRITE) && !dev->openflags.w){
1000 printk("Write attempted on readonly ubd device %s\n",
1001 disk->disk_name);
Jeff Dike91acb212005-10-10 23:10:32 -04001002 end_request(req, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 return(1);
1004 }
1005
Jeff Dike91acb212005-10-10 23:10:32 -04001006 offset = ((__u64) req->sector) << 9;
1007 len = req->current_nr_sectors << 9;
1008
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 io_req->fds[0] = (dev->cow.file != NULL) ? dev->cow.fd : dev->fd;
1010 io_req->fds[1] = dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001011 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 io_req->offset = offset;
1013 io_req->length = len;
1014 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001015 io_req->sector_mask = 0;
1016
1017 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 io_req->offsets[0] = 0;
1019 io_req->offsets[1] = dev->cow.data_offset;
Jeff Dike91acb212005-10-10 23:10:32 -04001020 io_req->buffer = req->buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 io_req->sectorsize = 1 << 9;
1022
Jeff Dike91acb212005-10-10 23:10:32 -04001023 if(dev->cow.file != NULL)
1024 cowify_req(io_req, dev->cow.bitmap, dev->cow.bitmap_offset,
1025 dev->cow.bitmap_len);
1026
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 return(0);
1028}
1029
1030/* Called with ubd_io_lock held */
1031static void do_ubd_request(request_queue_t *q)
1032{
1033 struct io_thread_req io_req;
1034 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001035 int err, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
Jeff Dike91acb212005-10-10 23:10:32 -04001037 if(thread_fd == -1){
1038 while((req = elv_next_request(q)) != NULL){
1039 err = prepare_request(req, &io_req);
1040 if(!err){
1041 do_io(&io_req);
1042 __ubd_finish(req, io_req.error);
1043 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 }
1045 }
Jeff Dike91acb212005-10-10 23:10:32 -04001046 else {
1047 if(do_ubd || (req = elv_next_request(q)) == NULL)
1048 return;
1049 err = prepare_request(req, &io_req);
1050 if(!err){
1051 do_ubd = ubd_handler;
1052 n = os_write_file(thread_fd, (char *) &io_req,
1053 sizeof(io_req));
1054 if(n != sizeof(io_req))
1055 printk("write to io thread failed, "
1056 "errno = %d\n", -n);
1057 }
1058 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059}
1060
1061static int ubd_ioctl(struct inode * inode, struct file * file,
1062 unsigned int cmd, unsigned long arg)
1063{
1064 struct hd_geometry __user *loc = (struct hd_geometry __user *) arg;
1065 struct ubd *dev = inode->i_bdev->bd_disk->private_data;
1066 struct hd_driveid ubd_id = {
1067 .cyls = 0,
1068 .heads = 128,
1069 .sectors = 32,
1070 };
1071
1072 switch (cmd) {
1073 struct hd_geometry g;
1074 struct cdrom_volctrl volume;
1075 case HDIO_GETGEO:
1076 if(!loc) return(-EINVAL);
1077 g.heads = 128;
1078 g.sectors = 32;
1079 g.cylinders = dev->size / (128 * 32 * 512);
1080 g.start = get_start_sect(inode->i_bdev);
1081 return(copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0);
1082
1083 case HDIO_GET_IDENTITY:
1084 ubd_id.cyls = dev->size / (128 * 32 * 512);
1085 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1086 sizeof(ubd_id)))
1087 return(-EFAULT);
1088 return(0);
1089
1090 case CDROMVOLREAD:
1091 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
1092 return(-EFAULT);
1093 volume.channel0 = 255;
1094 volume.channel1 = 255;
1095 volume.channel2 = 255;
1096 volume.channel3 = 255;
1097 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
1098 return(-EFAULT);
1099 return(0);
1100 }
1101 return(-EINVAL);
1102}
1103
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104static int same_backing_files(char *from_cmdline, char *from_cow, char *cow)
1105{
1106 struct uml_stat buf1, buf2;
1107 int err;
1108
1109 if(from_cmdline == NULL) return(1);
1110 if(!strcmp(from_cmdline, from_cow)) return(1);
1111
1112 err = os_stat_file(from_cmdline, &buf1);
1113 if(err < 0){
1114 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
1115 return(1);
1116 }
1117 err = os_stat_file(from_cow, &buf2);
1118 if(err < 0){
1119 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
1120 return(1);
1121 }
1122 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
1123 return(1);
1124
1125 printk("Backing file mismatch - \"%s\" requested,\n"
1126 "\"%s\" specified in COW header of \"%s\"\n",
1127 from_cmdline, from_cow, cow);
1128 return(0);
1129}
1130
1131static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1132{
1133 unsigned long modtime;
1134 long long actual;
1135 int err;
1136
1137 err = os_file_modtime(file, &modtime);
1138 if(err < 0){
1139 printk("Failed to get modification time of backing file "
1140 "\"%s\", err = %d\n", file, -err);
1141 return(err);
1142 }
1143
1144 err = os_file_size(file, &actual);
1145 if(err < 0){
1146 printk("Failed to get size of backing file \"%s\", "
1147 "err = %d\n", file, -err);
1148 return(err);
1149 }
1150
1151 if(actual != size){
1152 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1153 * the typecast.*/
1154 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1155 "file\n", (unsigned long long) size, actual);
1156 return(-EINVAL);
1157 }
1158 if(modtime != mtime){
1159 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1160 "file\n", mtime, modtime);
1161 return(-EINVAL);
1162 }
1163 return(0);
1164}
1165
1166int read_cow_bitmap(int fd, void *buf, int offset, int len)
1167{
1168 int err;
1169
1170 err = os_seek_file(fd, offset);
1171 if(err < 0)
1172 return(err);
1173
1174 err = os_read_file(fd, buf, len);
1175 if(err < 0)
1176 return(err);
1177
1178 return(0);
1179}
1180
1181int open_ubd_file(char *file, struct openflags *openflags,
1182 char **backing_file_out, int *bitmap_offset_out,
1183 unsigned long *bitmap_len_out, int *data_offset_out,
1184 int *create_cow_out)
1185{
1186 time_t mtime;
1187 unsigned long long size;
1188 __u32 version, align;
1189 char *backing_file;
1190 int fd, err, sectorsize, same, mode = 0644;
1191
1192 fd = os_open_file(file, *openflags, mode);
1193 if(fd < 0){
1194 if((fd == -ENOENT) && (create_cow_out != NULL))
1195 *create_cow_out = 1;
1196 if(!openflags->w ||
1197 ((fd != -EROFS) && (fd != -EACCES))) return(fd);
1198 openflags->w = 0;
1199 fd = os_open_file(file, *openflags, mode);
1200 if(fd < 0)
1201 return(fd);
1202 }
1203
1204 err = os_lock_file(fd, openflags->w);
1205 if(err < 0){
1206 printk("Failed to lock '%s', err = %d\n", file, -err);
1207 goto out_close;
1208 }
1209
1210 if(backing_file_out == NULL) return(fd);
1211
1212 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1213 &size, &sectorsize, &align, bitmap_offset_out);
1214 if(err && (*backing_file_out != NULL)){
1215 printk("Failed to read COW header from COW file \"%s\", "
1216 "errno = %d\n", file, -err);
1217 goto out_close;
1218 }
1219 if(err) return(fd);
1220
1221 if(backing_file_out == NULL) return(fd);
1222
1223 same = same_backing_files(*backing_file_out, backing_file, file);
1224
1225 if(!same && !backing_file_mismatch(*backing_file_out, size, mtime)){
1226 printk("Switching backing file to '%s'\n", *backing_file_out);
1227 err = write_cow_header(file, fd, *backing_file_out,
1228 sectorsize, align, &size);
1229 if(err){
1230 printk("Switch failed, errno = %d\n", -err);
1231 return(err);
1232 }
1233 }
1234 else {
1235 *backing_file_out = backing_file;
1236 err = backing_file_mismatch(*backing_file_out, size, mtime);
1237 if(err) goto out_close;
1238 }
1239
1240 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1241 bitmap_len_out, data_offset_out);
1242
1243 return(fd);
1244 out_close:
1245 os_close_file(fd);
1246 return(err);
1247}
1248
1249int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1250 int sectorsize, int alignment, int *bitmap_offset_out,
1251 unsigned long *bitmap_len_out, int *data_offset_out)
1252{
1253 int err, fd;
1254
1255 flags.c = 1;
1256 fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL, NULL);
1257 if(fd < 0){
1258 err = fd;
1259 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1260 -err);
1261 goto out;
1262 }
1263
1264 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1265 bitmap_offset_out, bitmap_len_out,
1266 data_offset_out);
1267 if(!err)
1268 return(fd);
1269 os_close_file(fd);
1270 out:
1271 return(err);
1272}
1273
Jeff Dike91acb212005-10-10 23:10:32 -04001274static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275{
Jeff Dike91acb212005-10-10 23:10:32 -04001276 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277
Jeff Dike91acb212005-10-10 23:10:32 -04001278 if(req->cow_offset == -1)
1279 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280
Jeff Dike91acb212005-10-10 23:10:32 -04001281 n = os_seek_file(req->fds[1], req->cow_offset);
1282 if(n < 0){
1283 printk("do_io - bitmap lseek failed : err = %d\n", -n);
1284 return(1);
1285 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286
Jeff Dike91acb212005-10-10 23:10:32 -04001287 n = os_write_file(req->fds[1], &req->bitmap_words,
1288 sizeof(req->bitmap_words));
1289 if(n != sizeof(req->bitmap_words)){
1290 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1291 req->fds[1]);
1292 return(1);
1293 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
Jeff Dike91acb212005-10-10 23:10:32 -04001295 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296}
Jeff Dike91acb212005-10-10 23:10:32 -04001297
1298void do_io(struct io_thread_req *req)
1299{
1300 char *buf;
1301 unsigned long len;
1302 int n, nsectors, start, end, bit;
1303 int err;
1304 __u64 off;
1305
1306 nsectors = req->length / req->sectorsize;
1307 start = 0;
1308 do {
1309 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1310 end = start;
1311 while((end < nsectors) &&
1312 (ubd_test_bit(end, (unsigned char *)
1313 &req->sector_mask) == bit))
1314 end++;
1315
1316 off = req->offset + req->offsets[bit] +
1317 start * req->sectorsize;
1318 len = (end - start) * req->sectorsize;
1319 buf = &req->buffer[start * req->sectorsize];
1320
1321 err = os_seek_file(req->fds[bit], off);
1322 if(err < 0){
1323 printk("do_io - lseek failed : err = %d\n", -err);
1324 req->error = 1;
1325 return;
1326 }
1327 if(req->op == UBD_READ){
1328 n = 0;
1329 do {
1330 buf = &buf[n];
1331 len -= n;
1332 n = os_read_file(req->fds[bit], buf, len);
1333 if (n < 0) {
1334 printk("do_io - read failed, err = %d "
1335 "fd = %d\n", -n, req->fds[bit]);
1336 req->error = 1;
1337 return;
1338 }
1339 } while((n < len) && (n != 0));
1340 if (n < len) memset(&buf[n], 0, len - n);
1341 } else {
1342 n = os_write_file(req->fds[bit], buf, len);
1343 if(n != len){
1344 printk("do_io - write failed err = %d "
1345 "fd = %d\n", -n, req->fds[bit]);
1346 req->error = 1;
1347 return;
1348 }
1349 }
1350
1351 start = end;
1352 } while(start < nsectors);
1353
1354 req->error = update_bitmap(req);
1355}
1356
1357/* Changed in start_io_thread, which is serialized by being called only
1358 * from ubd_init, which is an initcall.
1359 */
1360int kernel_fd = -1;
1361
1362/* Only changed by the io thread */
1363int io_count = 0;
1364
1365int io_thread(void *arg)
1366{
1367 struct io_thread_req req;
1368 int n;
1369
1370 ignore_sigwinch_sig();
1371 while(1){
1372 n = os_read_file(kernel_fd, &req, sizeof(req));
1373 if(n != sizeof(req)){
1374 if(n < 0)
1375 printk("io_thread - read failed, fd = %d, "
1376 "err = %d\n", kernel_fd, -n);
1377 else {
1378 printk("io_thread - short read, fd = %d, "
1379 "length = %d\n", kernel_fd, n);
1380 }
1381 continue;
1382 }
1383 io_count++;
1384 do_io(&req);
1385 n = os_write_file(kernel_fd, &req, sizeof(req));
1386 if(n != sizeof(req))
1387 printk("io_thread - write failed, fd = %d, err = %d\n",
1388 kernel_fd, -n);
1389 }
Jeff Dike91acb212005-10-10 23:10:32 -04001390
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001391 return 0;
1392}