blob: b2c86257b0f83cc0f8f254d286db9bf76e4c5e86 [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
709 str = uml_strdup(str);
710 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
826static struct device_driver ubd_driver = {
827 .name = DRIVER_NAME,
828 .bus = &platform_bus_type,
829};
830
831int ubd_init(void)
832{
833 int i;
834
835 devfs_mk_dir("ubd");
836 if (register_blkdev(MAJOR_NR, "ubd"))
837 return -1;
838
839 ubd_queue = blk_init_queue(do_ubd_request, &ubd_io_lock);
840 if (!ubd_queue) {
841 unregister_blkdev(MAJOR_NR, "ubd");
842 return -1;
843 }
844
845 if (fake_major != MAJOR_NR) {
846 char name[sizeof("ubd_nnn\0")];
847
848 snprintf(name, sizeof(name), "ubd_%d", fake_major);
849 devfs_mk_dir(name);
850 if (register_blkdev(fake_major, "ubd"))
851 return -1;
852 }
853 driver_register(&ubd_driver);
854 for (i = 0; i < MAX_DEV; i++)
855 ubd_add(i);
856 return 0;
857}
858
859late_initcall(ubd_init);
860
Jeff Dike91acb212005-10-10 23:10:32 -0400861int ubd_driver_init(void){
862 unsigned long stack;
863 int err;
864
865 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
866 if(global_openflags.s){
867 printk(KERN_INFO "ubd: Synchronous mode\n");
868 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
869 * enough. So use anyway the io thread. */
870 }
871 stack = alloc_stack(0, 0);
872 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
873 &thread_fd);
874 if(io_pid < 0){
875 printk(KERN_ERR
876 "ubd : Failed to start I/O thread (errno = %d) - "
877 "falling back to synchronous I/O\n", -io_pid);
878 io_pid = -1;
879 return(0);
880 }
881 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
882 SA_INTERRUPT, "ubd", ubd_dev);
883 if(err != 0)
884 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
885 return(err);
886}
887
888device_initcall(ubd_driver_init);
889
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890static int ubd_open(struct inode *inode, struct file *filp)
891{
892 struct gendisk *disk = inode->i_bdev->bd_disk;
893 struct ubd *dev = disk->private_data;
894 int err = 0;
895
896 if(dev->count == 0){
897 err = ubd_open_dev(dev);
898 if(err){
899 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
900 disk->disk_name, dev->file, -err);
901 goto out;
902 }
903 }
904 dev->count++;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700905 set_disk_ro(disk, !dev->openflags.w);
906
907 /* This should no more be needed. And it didn't work anyway to exclude
908 * read-write remounting of filesystems.*/
909 /*if((filp->f_mode & FMODE_WRITE) && !dev->openflags.w){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 if(--dev->count == 0) ubd_close(dev);
911 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700912 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 out:
914 return(err);
915}
916
917static int ubd_release(struct inode * inode, struct file * file)
918{
919 struct gendisk *disk = inode->i_bdev->bd_disk;
920 struct ubd *dev = disk->private_data;
921
922 if(--dev->count == 0)
923 ubd_close(dev);
924 return(0);
925}
926
Jeff Dike91acb212005-10-10 23:10:32 -0400927static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
928 __u64 *cow_offset, unsigned long *bitmap,
929 __u64 bitmap_offset, unsigned long *bitmap_words,
930 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931{
Jeff Dike91acb212005-10-10 23:10:32 -0400932 __u64 sector = io_offset >> 9;
933 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
Jeff Dike91acb212005-10-10 23:10:32 -0400935 for(i = 0; i < length >> 9; i++){
936 if(cow_mask != NULL)
937 ubd_set_bit(i, (unsigned char *) cow_mask);
938 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
939 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940
Jeff Dike91acb212005-10-10 23:10:32 -0400941 update_bitmap = 1;
942 ubd_set_bit(sector + i, (unsigned char *) bitmap);
943 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
Jeff Dike91acb212005-10-10 23:10:32 -0400945 if(!update_bitmap)
946 return;
947
948 *cow_offset = sector / (sizeof(unsigned long) * 8);
949
950 /* This takes care of the case where we're exactly at the end of the
951 * device, and *cow_offset + 1 is off the end. So, just back it up
952 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
953 * for the original diagnosis.
954 */
955 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
956 sizeof(unsigned long) - 1))
957 (*cow_offset)--;
958
959 bitmap_words[0] = bitmap[*cow_offset];
960 bitmap_words[1] = bitmap[*cow_offset + 1];
961
962 *cow_offset *= sizeof(unsigned long);
963 *cow_offset += bitmap_offset;
964}
965
966static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
967 __u64 bitmap_offset, __u64 bitmap_len)
968{
969 __u64 sector = req->offset >> 9;
970 int i;
971
972 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
973 panic("Operation too long");
974
975 if(req->op == UBD_READ) {
976 for(i = 0; i < req->length >> 9; i++){
977 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
978 ubd_set_bit(i, (unsigned char *)
979 &req->sector_mask);
980 }
981 }
982 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
983 &req->cow_offset, bitmap, bitmap_offset,
984 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985}
986
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987/* Called with ubd_io_lock held */
Jeff Dike91acb212005-10-10 23:10:32 -0400988static int prepare_request(struct request *req, struct io_thread_req *io_req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989{
990 struct gendisk *disk = req->rq_disk;
991 struct ubd *dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -0400992 __u64 offset;
993 int len;
994
995 if(req->rq_status == RQ_INACTIVE) return(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700997 /* This should be impossible now */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 if((rq_data_dir(req) == WRITE) && !dev->openflags.w){
999 printk("Write attempted on readonly ubd device %s\n",
1000 disk->disk_name);
Jeff Dike91acb212005-10-10 23:10:32 -04001001 end_request(req, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 return(1);
1003 }
1004
Jeff Dike91acb212005-10-10 23:10:32 -04001005 offset = ((__u64) req->sector) << 9;
1006 len = req->current_nr_sectors << 9;
1007
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 io_req->fds[0] = (dev->cow.file != NULL) ? dev->cow.fd : dev->fd;
1009 io_req->fds[1] = dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001010 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 io_req->offset = offset;
1012 io_req->length = len;
1013 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001014 io_req->sector_mask = 0;
1015
1016 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 io_req->offsets[0] = 0;
1018 io_req->offsets[1] = dev->cow.data_offset;
Jeff Dike91acb212005-10-10 23:10:32 -04001019 io_req->buffer = req->buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 io_req->sectorsize = 1 << 9;
1021
Jeff Dike91acb212005-10-10 23:10:32 -04001022 if(dev->cow.file != NULL)
1023 cowify_req(io_req, dev->cow.bitmap, dev->cow.bitmap_offset,
1024 dev->cow.bitmap_len);
1025
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 return(0);
1027}
1028
1029/* Called with ubd_io_lock held */
1030static void do_ubd_request(request_queue_t *q)
1031{
1032 struct io_thread_req io_req;
1033 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001034 int err, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035
Jeff Dike91acb212005-10-10 23:10:32 -04001036 if(thread_fd == -1){
1037 while((req = elv_next_request(q)) != NULL){
1038 err = prepare_request(req, &io_req);
1039 if(!err){
1040 do_io(&io_req);
1041 __ubd_finish(req, io_req.error);
1042 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 }
1044 }
Jeff Dike91acb212005-10-10 23:10:32 -04001045 else {
1046 if(do_ubd || (req = elv_next_request(q)) == NULL)
1047 return;
1048 err = prepare_request(req, &io_req);
1049 if(!err){
1050 do_ubd = ubd_handler;
1051 n = os_write_file(thread_fd, (char *) &io_req,
1052 sizeof(io_req));
1053 if(n != sizeof(io_req))
1054 printk("write to io thread failed, "
1055 "errno = %d\n", -n);
1056 }
1057 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058}
1059
1060static int ubd_ioctl(struct inode * inode, struct file * file,
1061 unsigned int cmd, unsigned long arg)
1062{
1063 struct hd_geometry __user *loc = (struct hd_geometry __user *) arg;
1064 struct ubd *dev = inode->i_bdev->bd_disk->private_data;
1065 struct hd_driveid ubd_id = {
1066 .cyls = 0,
1067 .heads = 128,
1068 .sectors = 32,
1069 };
1070
1071 switch (cmd) {
1072 struct hd_geometry g;
1073 struct cdrom_volctrl volume;
1074 case HDIO_GETGEO:
1075 if(!loc) return(-EINVAL);
1076 g.heads = 128;
1077 g.sectors = 32;
1078 g.cylinders = dev->size / (128 * 32 * 512);
1079 g.start = get_start_sect(inode->i_bdev);
1080 return(copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0);
1081
1082 case HDIO_GET_IDENTITY:
1083 ubd_id.cyls = dev->size / (128 * 32 * 512);
1084 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1085 sizeof(ubd_id)))
1086 return(-EFAULT);
1087 return(0);
1088
1089 case CDROMVOLREAD:
1090 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
1091 return(-EFAULT);
1092 volume.channel0 = 255;
1093 volume.channel1 = 255;
1094 volume.channel2 = 255;
1095 volume.channel3 = 255;
1096 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
1097 return(-EFAULT);
1098 return(0);
1099 }
1100 return(-EINVAL);
1101}
1102
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103static int same_backing_files(char *from_cmdline, char *from_cow, char *cow)
1104{
1105 struct uml_stat buf1, buf2;
1106 int err;
1107
1108 if(from_cmdline == NULL) return(1);
1109 if(!strcmp(from_cmdline, from_cow)) return(1);
1110
1111 err = os_stat_file(from_cmdline, &buf1);
1112 if(err < 0){
1113 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
1114 return(1);
1115 }
1116 err = os_stat_file(from_cow, &buf2);
1117 if(err < 0){
1118 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
1119 return(1);
1120 }
1121 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
1122 return(1);
1123
1124 printk("Backing file mismatch - \"%s\" requested,\n"
1125 "\"%s\" specified in COW header of \"%s\"\n",
1126 from_cmdline, from_cow, cow);
1127 return(0);
1128}
1129
1130static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1131{
1132 unsigned long modtime;
1133 long long actual;
1134 int err;
1135
1136 err = os_file_modtime(file, &modtime);
1137 if(err < 0){
1138 printk("Failed to get modification time of backing file "
1139 "\"%s\", err = %d\n", file, -err);
1140 return(err);
1141 }
1142
1143 err = os_file_size(file, &actual);
1144 if(err < 0){
1145 printk("Failed to get size of backing file \"%s\", "
1146 "err = %d\n", file, -err);
1147 return(err);
1148 }
1149
1150 if(actual != size){
1151 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1152 * the typecast.*/
1153 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1154 "file\n", (unsigned long long) size, actual);
1155 return(-EINVAL);
1156 }
1157 if(modtime != mtime){
1158 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1159 "file\n", mtime, modtime);
1160 return(-EINVAL);
1161 }
1162 return(0);
1163}
1164
1165int read_cow_bitmap(int fd, void *buf, int offset, int len)
1166{
1167 int err;
1168
1169 err = os_seek_file(fd, offset);
1170 if(err < 0)
1171 return(err);
1172
1173 err = os_read_file(fd, buf, len);
1174 if(err < 0)
1175 return(err);
1176
1177 return(0);
1178}
1179
1180int open_ubd_file(char *file, struct openflags *openflags,
1181 char **backing_file_out, int *bitmap_offset_out,
1182 unsigned long *bitmap_len_out, int *data_offset_out,
1183 int *create_cow_out)
1184{
1185 time_t mtime;
1186 unsigned long long size;
1187 __u32 version, align;
1188 char *backing_file;
1189 int fd, err, sectorsize, same, mode = 0644;
1190
1191 fd = os_open_file(file, *openflags, mode);
1192 if(fd < 0){
1193 if((fd == -ENOENT) && (create_cow_out != NULL))
1194 *create_cow_out = 1;
1195 if(!openflags->w ||
1196 ((fd != -EROFS) && (fd != -EACCES))) return(fd);
1197 openflags->w = 0;
1198 fd = os_open_file(file, *openflags, mode);
1199 if(fd < 0)
1200 return(fd);
1201 }
1202
1203 err = os_lock_file(fd, openflags->w);
1204 if(err < 0){
1205 printk("Failed to lock '%s', err = %d\n", file, -err);
1206 goto out_close;
1207 }
1208
1209 if(backing_file_out == NULL) return(fd);
1210
1211 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1212 &size, &sectorsize, &align, bitmap_offset_out);
1213 if(err && (*backing_file_out != NULL)){
1214 printk("Failed to read COW header from COW file \"%s\", "
1215 "errno = %d\n", file, -err);
1216 goto out_close;
1217 }
1218 if(err) return(fd);
1219
1220 if(backing_file_out == NULL) return(fd);
1221
1222 same = same_backing_files(*backing_file_out, backing_file, file);
1223
1224 if(!same && !backing_file_mismatch(*backing_file_out, size, mtime)){
1225 printk("Switching backing file to '%s'\n", *backing_file_out);
1226 err = write_cow_header(file, fd, *backing_file_out,
1227 sectorsize, align, &size);
1228 if(err){
1229 printk("Switch failed, errno = %d\n", -err);
1230 return(err);
1231 }
1232 }
1233 else {
1234 *backing_file_out = backing_file;
1235 err = backing_file_mismatch(*backing_file_out, size, mtime);
1236 if(err) goto out_close;
1237 }
1238
1239 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1240 bitmap_len_out, data_offset_out);
1241
1242 return(fd);
1243 out_close:
1244 os_close_file(fd);
1245 return(err);
1246}
1247
1248int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1249 int sectorsize, int alignment, int *bitmap_offset_out,
1250 unsigned long *bitmap_len_out, int *data_offset_out)
1251{
1252 int err, fd;
1253
1254 flags.c = 1;
1255 fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL, NULL);
1256 if(fd < 0){
1257 err = fd;
1258 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1259 -err);
1260 goto out;
1261 }
1262
1263 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1264 bitmap_offset_out, bitmap_len_out,
1265 data_offset_out);
1266 if(!err)
1267 return(fd);
1268 os_close_file(fd);
1269 out:
1270 return(err);
1271}
1272
Jeff Dike91acb212005-10-10 23:10:32 -04001273static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274{
Jeff Dike91acb212005-10-10 23:10:32 -04001275 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276
Jeff Dike91acb212005-10-10 23:10:32 -04001277 if(req->cow_offset == -1)
1278 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
Jeff Dike91acb212005-10-10 23:10:32 -04001280 n = os_seek_file(req->fds[1], req->cow_offset);
1281 if(n < 0){
1282 printk("do_io - bitmap lseek failed : err = %d\n", -n);
1283 return(1);
1284 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
Jeff Dike91acb212005-10-10 23:10:32 -04001286 n = os_write_file(req->fds[1], &req->bitmap_words,
1287 sizeof(req->bitmap_words));
1288 if(n != sizeof(req->bitmap_words)){
1289 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1290 req->fds[1]);
1291 return(1);
1292 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293
Jeff Dike91acb212005-10-10 23:10:32 -04001294 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295}
Jeff Dike91acb212005-10-10 23:10:32 -04001296
1297void do_io(struct io_thread_req *req)
1298{
1299 char *buf;
1300 unsigned long len;
1301 int n, nsectors, start, end, bit;
1302 int err;
1303 __u64 off;
1304
1305 nsectors = req->length / req->sectorsize;
1306 start = 0;
1307 do {
1308 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1309 end = start;
1310 while((end < nsectors) &&
1311 (ubd_test_bit(end, (unsigned char *)
1312 &req->sector_mask) == bit))
1313 end++;
1314
1315 off = req->offset + req->offsets[bit] +
1316 start * req->sectorsize;
1317 len = (end - start) * req->sectorsize;
1318 buf = &req->buffer[start * req->sectorsize];
1319
1320 err = os_seek_file(req->fds[bit], off);
1321 if(err < 0){
1322 printk("do_io - lseek failed : err = %d\n", -err);
1323 req->error = 1;
1324 return;
1325 }
1326 if(req->op == UBD_READ){
1327 n = 0;
1328 do {
1329 buf = &buf[n];
1330 len -= n;
1331 n = os_read_file(req->fds[bit], buf, len);
1332 if (n < 0) {
1333 printk("do_io - read failed, err = %d "
1334 "fd = %d\n", -n, req->fds[bit]);
1335 req->error = 1;
1336 return;
1337 }
1338 } while((n < len) && (n != 0));
1339 if (n < len) memset(&buf[n], 0, len - n);
1340 } else {
1341 n = os_write_file(req->fds[bit], buf, len);
1342 if(n != len){
1343 printk("do_io - write failed err = %d "
1344 "fd = %d\n", -n, req->fds[bit]);
1345 req->error = 1;
1346 return;
1347 }
1348 }
1349
1350 start = end;
1351 } while(start < nsectors);
1352
1353 req->error = update_bitmap(req);
1354}
1355
1356/* Changed in start_io_thread, which is serialized by being called only
1357 * from ubd_init, which is an initcall.
1358 */
1359int kernel_fd = -1;
1360
1361/* Only changed by the io thread */
1362int io_count = 0;
1363
1364int io_thread(void *arg)
1365{
1366 struct io_thread_req req;
1367 int n;
1368
1369 ignore_sigwinch_sig();
1370 while(1){
1371 n = os_read_file(kernel_fd, &req, sizeof(req));
1372 if(n != sizeof(req)){
1373 if(n < 0)
1374 printk("io_thread - read failed, fd = %d, "
1375 "err = %d\n", kernel_fd, -n);
1376 else {
1377 printk("io_thread - short read, fd = %d, "
1378 "length = %d\n", kernel_fd, n);
1379 }
1380 continue;
1381 }
1382 io_count++;
1383 do_io(&req);
1384 n = os_write_file(kernel_fd, &req, sizeof(req));
1385 if(n != sizeof(req))
1386 printk("io_thread - write failed, fd = %d, err = %d\n",
1387 kernel_fd, -n);
1388 }
1389}
1390
1391/*
1392 * Overrides for Emacs so that we follow Linus's tabbing style.
1393 * Emacs will notice this stuff at the end of the file and automatically
1394 * adjust the settings for this buffer only. This must remain at the end
1395 * of the file.
1396 * ---------------------------------------------------------------------------
1397 * Local variables:
1398 * c-file-style: "linux"
1399 * End:
1400 */