blob: a3061ae39b3b1b8f1be5caa97d13eb13abddbd29 [file] [log] [blame]
Jeff Dike6c29256c2006-03-27 01:14:37 -08001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * 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
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include "linux/module.h"
24#include "linux/blkdev.h"
25#include "linux/hdreg.h"
26#include "linux/init.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include "linux/cdrom.h"
28#include "linux/proc_fs.h"
29#include "linux/ctype.h"
30#include "linux/capability.h"
31#include "linux/mm.h"
32#include "linux/vmalloc.h"
33#include "linux/blkpg.h"
34#include "linux/genhd.h"
35#include "linux/spinlock.h"
Russell Kingd052d1b2005-10-29 19:07:23 +010036#include "linux/platform_device.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include "asm/segment.h"
38#include "asm/uaccess.h"
39#include "asm/irq.h"
40#include "asm/types.h"
41#include "asm/tlbflush.h"
42#include "user_util.h"
43#include "mem_user.h"
44#include "kern_util.h"
45#include "kern.h"
46#include "mconsole_kern.h"
47#include "init.h"
48#include "irq_user.h"
49#include "irq_kern.h"
50#include "ubd_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#include "os.h"
52#include "mem.h"
53#include "mem_kern.h"
54#include "cow.h"
55
Jeff Dike7b9014c2005-05-20 13:59:11 -070056enum ubd_req { UBD_READ, UBD_WRITE };
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
58struct io_thread_req {
Jeff Dike91acb212005-10-10 23:10:32 -040059 enum ubd_req op;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 int fds[2];
61 unsigned long offsets[2];
62 unsigned long long offset;
63 unsigned long length;
64 char *buffer;
65 int sectorsize;
Jeff Dike91acb212005-10-10 23:10:32 -040066 unsigned long sector_mask;
67 unsigned long long cow_offset;
68 unsigned long bitmap_words[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 int error;
70};
71
Jeff Dike6c29256c2006-03-27 01:14:37 -080072extern int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 char **backing_file_out, int *bitmap_offset_out,
74 unsigned long *bitmap_len_out, int *data_offset_out,
75 int *create_cow_out);
76extern int create_cow_file(char *cow_file, char *backing_file,
77 struct openflags flags, int sectorsize,
78 int alignment, int *bitmap_offset_out,
79 unsigned long *bitmap_len_out,
80 int *data_offset_out);
81extern int read_cow_bitmap(int fd, void *buf, int offset, int len);
Jeff Dike91acb212005-10-10 23:10:32 -040082extern void do_io(struct io_thread_req *req);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
Jeff Dike91acb212005-10-10 23:10:32 -040084static inline int ubd_test_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085{
86 __u64 n;
87 int bits, off;
88
Jeff Dike91acb212005-10-10 23:10:32 -040089 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 n = bit / bits;
91 off = bit % bits;
Jeff Dike91acb212005-10-10 23:10:32 -040092 return((data[n] & (1 << off)) != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093}
94
Jeff Dike91acb212005-10-10 23:10:32 -040095static inline void ubd_set_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070096{
97 __u64 n;
98 int bits, off;
99
Jeff Dike91acb212005-10-10 23:10:32 -0400100 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 n = bit / bits;
102 off = bit % bits;
Jeff Dike91acb212005-10-10 23:10:32 -0400103 data[n] |= (1 << off);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104}
105/*End stuff from ubd_user.h*/
106
107#define DRIVER_NAME "uml-blkdev"
108
109static DEFINE_SPINLOCK(ubd_io_lock);
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800110
111static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112
Jeff Dike91acb212005-10-10 23:10:32 -0400113static void (*do_ubd)(void);
114
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115static int ubd_open(struct inode * inode, struct file * filp);
116static int ubd_release(struct inode * inode, struct file * file);
117static int ubd_ioctl(struct inode * inode, struct file * file,
118 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800119static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800121#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122
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,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800128 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129};
130
131/* Protected by the queue_lock */
132static request_queue_t *ubd_queue;
133
134/* Protected by ubd_lock */
135static int fake_major = MAJOR_NR;
136
137static struct gendisk *ubd_gendisk[MAX_DEV];
138static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800139
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140#ifdef CONFIG_BLK_DEV_UBD_SYNC
141#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
142 .cl = 1 })
143#else
144#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
145 .cl = 1 })
146#endif
147
148/* Not protected - changed only in ubd_setup_common and then only to
149 * to enable O_SYNC.
150 */
151static struct openflags global_openflags = OPEN_FLAGS;
152
153struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800154 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800156 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 int fd;
158 unsigned long *bitmap;
159 unsigned long bitmap_len;
160 int bitmap_offset;
161 int data_offset;
162};
163
164struct ubd {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800165 /* name (and fd, below) of the file opened for writing, either the
166 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 char *file;
168 int count;
169 int fd;
170 __u64 size;
171 struct openflags boot_openflags;
172 struct openflags openflags;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800173 int shared;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 int no_cow;
175 struct cow cow;
176 struct platform_device pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177};
178
179#define DEFAULT_COW { \
180 .file = NULL, \
181 .fd = -1, \
182 .bitmap = NULL, \
183 .bitmap_offset = 0, \
184 .data_offset = 0, \
185}
186
187#define DEFAULT_UBD { \
188 .file = NULL, \
189 .count = 0, \
190 .fd = -1, \
191 .size = -1, \
192 .boot_openflags = OPEN_FLAGS, \
193 .openflags = OPEN_FLAGS, \
194 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800195 .shared = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 .cow = DEFAULT_COW, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197}
198
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800199struct ubd ubd_devs[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200
201static int ubd0_init(void)
202{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800203 struct ubd *ubd_dev = &ubd_devs[0];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800205 if(ubd_dev->file == NULL)
206 ubd_dev->file = "root_fs";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 return(0);
208}
209
210__initcall(ubd0_init);
211
212/* Only changed by fake_ide_setup which is a setup */
213static int fake_ide = 0;
214static struct proc_dir_entry *proc_ide_root = NULL;
215static struct proc_dir_entry *proc_ide = NULL;
216
217static void make_proc_ide(void)
218{
219 proc_ide_root = proc_mkdir("ide", NULL);
220 proc_ide = proc_mkdir("ide0", proc_ide_root);
221}
222
223static int proc_ide_read_media(char *page, char **start, off_t off, int count,
224 int *eof, void *data)
225{
226 int len;
227
228 strcpy(page, "disk\n");
229 len = strlen("disk\n");
230 len -= off;
231 if (len < count){
232 *eof = 1;
233 if (len <= 0) return 0;
234 }
235 else len = count;
236 *start = page + off;
237 return len;
238}
239
240static void make_ide_entries(char *dev_name)
241{
242 struct proc_dir_entry *dir, *ent;
243 char name[64];
244
245 if(proc_ide_root == NULL) make_proc_ide();
246
247 dir = proc_mkdir(dev_name, proc_ide);
248 if(!dir) return;
249
250 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
251 if(!ent) return;
252 ent->nlink = 1;
253 ent->data = NULL;
254 ent->read_proc = proc_ide_read_media;
255 ent->write_proc = NULL;
256 sprintf(name,"ide0/%s", dev_name);
257 proc_symlink(dev_name, proc_ide_root, name);
258}
259
260static int fake_ide_setup(char *str)
261{
262 fake_ide = 1;
263 return(1);
264}
265
266__setup("fake_ide", fake_ide_setup);
267
268__uml_help(fake_ide_setup,
269"fake_ide\n"
270" Create ide0 entries that map onto ubd devices.\n\n"
271);
272
273static int parse_unit(char **ptr)
274{
275 char *str = *ptr, *end;
276 int n = -1;
277
278 if(isdigit(*str)) {
279 n = simple_strtoul(str, &end, 0);
280 if(end == str)
281 return(-1);
282 *ptr = end;
283 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800284 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 n = *str - 'a';
286 str++;
287 *ptr = str;
288 }
289 return(n);
290}
291
292static int ubd_setup_common(char *str, int *index_out)
293{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800294 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 struct openflags flags = global_openflags;
296 char *backing_file;
297 int n, err, i;
298
299 if(index_out) *index_out = -1;
300 n = *str;
301 if(n == '='){
302 char *end;
303 int major;
304
305 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 if(!strcmp(str, "sync")){
307 global_openflags = of_sync(global_openflags);
308 return(0);
309 }
310 major = simple_strtoul(str, &end, 0);
311 if((*end != '\0') || (end == str)){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800312 printk(KERN_ERR
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 "ubd_setup : didn't parse major number\n");
314 return(1);
315 }
316
317 err = 1;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800318 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 if(fake_major != MAJOR_NR){
320 printk(KERN_ERR "Can't assign a fake major twice\n");
321 goto out1;
322 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800323
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 fake_major = major;
325
326 printk(KERN_INFO "Setting extra ubd major number to %d\n",
327 major);
328 err = 0;
329 out1:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800330 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 return(err);
332 }
333
334 n = parse_unit(&str);
335 if(n < 0){
336 printk(KERN_ERR "ubd_setup : couldn't parse unit number "
337 "'%s'\n", str);
338 return(1);
339 }
340 if(n >= MAX_DEV){
341 printk(KERN_ERR "ubd_setup : index %d out of range "
342 "(%d devices, from 0 to %d)\n", n, MAX_DEV, MAX_DEV - 1);
343 return(1);
344 }
345
346 err = 1;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800347 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800349 ubd_dev = &ubd_devs[n];
350 if(ubd_dev->file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 printk(KERN_ERR "ubd_setup : device already configured\n");
352 goto out;
353 }
354
355 if (index_out)
356 *index_out = n;
357
Jeff Dike6c29256c2006-03-27 01:14:37 -0800358 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 switch (*str) {
360 case 'r':
361 flags.w = 0;
362 break;
363 case 's':
364 flags.s = 1;
365 break;
366 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800367 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800369 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800370 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800371 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 case '=':
373 str++;
374 goto break_loop;
375 default:
Jeff Dike6c29256c2006-03-27 01:14:37 -0800376 printk(KERN_ERR "ubd_setup : Expected '=' or flag letter (r, s, c, or d)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 goto out;
378 }
379 str++;
380 }
381
382 if (*str == '=')
383 printk(KERN_ERR "ubd_setup : Too many flags specified\n");
384 else
385 printk(KERN_ERR "ubd_setup : Expected '='\n");
386 goto out;
387
388break_loop:
389 err = 0;
390 backing_file = strchr(str, ',');
391
392 if (!backing_file) {
393 backing_file = strchr(str, ':');
394 }
395
396 if(backing_file){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800397 if(ubd_dev->no_cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 printk(KERN_ERR "Can't specify both 'd' and a "
399 "cow file\n");
400 else {
401 *backing_file = '\0';
402 backing_file++;
403 }
404 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800405 ubd_dev->file = str;
406 ubd_dev->cow.file = backing_file;
407 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800409 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 return(err);
411}
412
413static int ubd_setup(char *str)
414{
415 ubd_setup_common(str, NULL);
416 return(1);
417}
418
419__setup("ubd", ubd_setup);
420__uml_help(ubd_setup,
421"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
422" This is used to associate a device with a file in the underlying\n"
423" filesystem. When specifying two filenames, the first one is the\n"
424" COW name and the second is the backing file name. As separator you can\n"
425" use either a ':' or a ',': the first one allows writing things like;\n"
426" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
427" while with a ',' the shell would not expand the 2nd '~'.\n"
428" When using only one filename, UML will detect whether to thread it like\n"
429" a COW file or a backing file. To override this detection, add the 'd'\n"
430" flag:\n"
431" ubd0d=BackingFile\n"
432" Usually, there is a filesystem in the file, but \n"
433" that's not required. Swap devices containing swap files can be\n"
434" specified like this. Also, a file which doesn't contain a\n"
435" filesystem can have its contents read in the virtual \n"
436" machine by running 'dd' on the device. <n> must be in the range\n"
437" 0 to 7. Appending an 'r' to the number will cause that device\n"
438" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
439" an 's' will cause data to be written to disk on the host immediately.\n\n"
440);
441
442static int udb_setup(char *str)
443{
444 printk("udb%s specified on command line is almost certainly a ubd -> "
445 "udb TYPO\n", str);
446 return(1);
447}
448
449__setup("udb", udb_setup);
450__uml_help(udb_setup,
451"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700452" This option is here solely to catch ubd -> udb typos, which can be\n"
453" to impossible to catch visually unless you specifically look for\n"
454" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455" in the boot output.\n\n"
456);
457
458static int fakehd_set = 0;
459static int fakehd(char *str)
460{
461 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
462 fakehd_set = 1;
463 return 1;
464}
465
466__setup("fakehd", fakehd);
467__uml_help(fakehd,
468"fakehd\n"
469" Change the ubd device name to \"hd\".\n\n"
470);
471
472static void do_ubd_request(request_queue_t * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400473
474/* Only changed by ubd_init, which is an initcall. */
475int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
477/* Changed by ubd_handler, which is serialized because interrupts only
478 * happen on CPU 0.
479 */
480int intr_count = 0;
481
482/* call ubd_finish if you need to serialize */
Jeff Dike91acb212005-10-10 23:10:32 -0400483static void __ubd_finish(struct request *req, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484{
Jeff Dike91acb212005-10-10 23:10:32 -0400485 int nsect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
Jeff Dike91acb212005-10-10 23:10:32 -0400487 if(error){
488 end_request(req, 0);
489 return;
490 }
491 nsect = req->current_nr_sectors;
492 req->sector += nsect;
493 req->buffer += nsect << 9;
494 req->errors = 0;
495 req->nr_sectors -= nsect;
496 req->current_nr_sectors = 0;
497 end_request(req, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498}
499
Jeff Dike91acb212005-10-10 23:10:32 -0400500static inline void ubd_finish(struct request *req, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501{
Jeff Dike91acb212005-10-10 23:10:32 -0400502 spin_lock(&ubd_io_lock);
503 __ubd_finish(req, error);
504 spin_unlock(&ubd_io_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505}
506
Jeff Dike91acb212005-10-10 23:10:32 -0400507/* Called without ubd_io_lock held */
508static void ubd_handler(void)
509{
510 struct io_thread_req req;
511 struct request *rq = elv_next_request(ubd_queue);
512 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
Jeff Dike91acb212005-10-10 23:10:32 -0400514 do_ubd = NULL;
515 intr_count++;
516 n = os_read_file(thread_fd, &req, sizeof(req));
517 if(n != sizeof(req)){
518 printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, "
519 "err = %d\n", os_getpid(), -n);
520 spin_lock(&ubd_io_lock);
521 end_request(rq, 0);
522 spin_unlock(&ubd_io_lock);
523 return;
524 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800525
Jeff Dike91acb212005-10-10 23:10:32 -0400526 ubd_finish(rq, req.error);
527 reactivate_fd(thread_fd, UBD_IRQ);
528 do_ubd_request(ubd_queue);
529}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
Al Viro7bea96f2006-10-08 22:49:34 +0100531static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532{
Jeff Dike91acb212005-10-10 23:10:32 -0400533 ubd_handler();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 return(IRQ_HANDLED);
535}
536
Jeff Dike91acb212005-10-10 23:10:32 -0400537/* Only changed by ubd_init, which is an initcall. */
538static int io_pid = -1;
539
540void kill_io_thread(void)
541{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800542 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400543 os_kill_process(io_pid, 1);
544}
545
546__uml_exitcall(kill_io_thread);
547
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800548static int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549{
550 char *file;
551
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800552 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 return(os_file_size(file, size_out));
554}
555
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800556static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800558 os_close_file(ubd_dev->fd);
559 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 return;
561
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800562 os_close_file(ubd_dev->cow.fd);
563 vfree(ubd_dev->cow.bitmap);
564 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800567static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
569 struct openflags flags;
570 char **back_ptr;
571 int err, create_cow, *create_ptr;
572
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800573 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800575 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
576 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
577 ubd_dev->fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
578 back_ptr, &ubd_dev->cow.bitmap_offset,
579 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800580 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800582 if((ubd_dev->fd == -ENOENT) && create_cow){
583 ubd_dev->fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
584 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
585 &ubd_dev->cow.bitmap_offset,
586 &ubd_dev->cow.bitmap_len,
587 &ubd_dev->cow.data_offset);
588 if(ubd_dev->fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800590 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 }
592 }
593
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800594 if(ubd_dev->fd < 0){
595 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
596 -ubd_dev->fd);
597 return(ubd_dev->fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 }
599
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800600 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 err = -ENOMEM;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800602 ubd_dev->cow.bitmap = (void *) vmalloc(ubd_dev->cow.bitmap_len);
603 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
605 goto error;
606 }
607 flush_tlb_kernel_vm();
608
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800609 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
610 ubd_dev->cow.bitmap_offset,
611 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 if(err < 0)
613 goto error;
614
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800615 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800617 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800618 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800620 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 }
622 return(0);
623 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800624 os_close_file(ubd_dev->fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 return(err);
626}
627
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800628static int ubd_disk_register(int major, u64 size, int unit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 struct gendisk **disk_out)
630
631{
632 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
634 disk = alloc_disk(1 << UBD_SHIFT);
635 if(disk == NULL)
636 return(-ENOMEM);
637
638 disk->major = major;
639 disk->first_minor = unit << UBD_SHIFT;
640 disk->fops = &ubd_blops;
641 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700642 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700644 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
647 /* sysfs register (not for ide fake devices) */
648 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800649 ubd_devs[unit].pdev.id = unit;
650 ubd_devs[unit].pdev.name = DRIVER_NAME;
651 platform_device_register(&ubd_devs[unit].pdev);
652 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 }
654
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800655 disk->private_data = &ubd_devs[unit];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 disk->queue = ubd_queue;
657 add_disk(disk);
658
659 *disk_out = disk;
660 return 0;
661}
662
663#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
664
665static int ubd_add(int n)
666{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800667 struct ubd *ubd_dev = &ubd_devs[n];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 int err;
669
Jeff Dikeec7cf782005-09-03 15:57:29 -0700670 err = -ENODEV;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800671 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700672 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800674 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 if(err < 0)
Jeff Dike80c13742006-09-29 01:58:51 -0700676 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800678 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800680 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800681 if(err)
Jeff Dike80c13742006-09-29 01:58:51 -0700682 goto out;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800683
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800685 ubd_disk_register(fake_major, ubd_dev->size, n,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 &fake_gendisk[n]);
687
688 /* perhaps this should also be under the "if (fake_major)" above */
689 /* using the fake_disk->disk_name and also the fakehd_set name */
690 if (fake_ide)
691 make_ide_entries(ubd_gendisk[n]->disk_name);
692
Jeff Dikeec7cf782005-09-03 15:57:29 -0700693 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700694out:
695 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696}
697
698static int ubd_config(char *str)
699{
700 int n, err;
701
Jeff Dike970d6e32006-01-06 00:18:48 -0800702 str = kstrdup(str, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 if(str == NULL){
704 printk(KERN_ERR "ubd_config failed to strdup string\n");
705 return(1);
706 }
707 err = ubd_setup_common(str, &n);
708 if(err){
709 kfree(str);
710 return(-1);
711 }
712 if(n == -1) return(0);
713
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800714 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 err = ubd_add(n);
716 if(err)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800717 ubd_devs[n].file = NULL;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800718 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719
720 return(err);
721}
722
723static int ubd_get_config(char *name, char *str, int size, char **error_out)
724{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800725 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 int n, len = 0;
727
728 n = parse_unit(&name);
729 if((n >= MAX_DEV) || (n < 0)){
730 *error_out = "ubd_get_config : device number out of range";
731 return(-1);
732 }
733
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800734 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800735 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800737 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 CONFIG_CHUNK(str, size, len, "", 1);
739 goto out;
740 }
741
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800742 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800744 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800746 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 }
748 else CONFIG_CHUNK(str, size, len, "", 1);
749
750 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800751 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 return(len);
753}
754
Jeff Dike29d56cf2005-06-25 14:55:25 -0700755static int ubd_id(char **str, int *start_out, int *end_out)
756{
757 int n;
758
759 n = parse_unit(str);
760 *start_out = 0;
761 *end_out = MAX_DEV - 1;
762 return n;
763}
764
765static int ubd_remove(int n)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800767 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700768 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800770 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
772 if(ubd_gendisk[n] == NULL)
773 goto out;
774
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800775 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700776
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800777 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700778 goto out;
779
780 /* you cannot remove a open disk */
781 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800782 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700783 goto out;
784
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 del_gendisk(ubd_gendisk[n]);
786 put_disk(ubd_gendisk[n]);
787 ubd_gendisk[n] = NULL;
788
789 if(fake_gendisk[n] != NULL){
790 del_gendisk(fake_gendisk[n]);
791 put_disk(fake_gendisk[n]);
792 fake_gendisk[n] = NULL;
793 }
794
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800795 platform_device_unregister(&ubd_dev->pdev);
796 *ubd_dev = ((struct ubd) DEFAULT_UBD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 err = 0;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700798out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800799 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700800 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801}
802
803static struct mc_device ubd_mc = {
804 .name = "ubd",
805 .config = ubd_config,
806 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700807 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 .remove = ubd_remove,
809};
810
811static int ubd_mc_init(void)
812{
813 mconsole_register_dev(&ubd_mc);
814 return 0;
815}
816
817__initcall(ubd_mc_init);
818
Russell King3ae5eae2005-11-09 22:32:44 +0000819static struct platform_driver ubd_driver = {
820 .driver = {
821 .name = DRIVER_NAME,
822 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823};
824
825int ubd_init(void)
826{
827 int i;
828
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 if (register_blkdev(MAJOR_NR, "ubd"))
830 return -1;
831
832 ubd_queue = blk_init_queue(do_ubd_request, &ubd_io_lock);
833 if (!ubd_queue) {
834 unregister_blkdev(MAJOR_NR, "ubd");
835 return -1;
836 }
837
838 if (fake_major != MAJOR_NR) {
839 char name[sizeof("ubd_nnn\0")];
840
841 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 if (register_blkdev(fake_major, "ubd"))
843 return -1;
844 }
Russell King3ae5eae2005-11-09 22:32:44 +0000845 platform_driver_register(&ubd_driver);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800846 for (i = 0; i < MAX_DEV; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 ubd_add(i);
848 return 0;
849}
850
851late_initcall(ubd_init);
852
Jeff Dike91acb212005-10-10 23:10:32 -0400853int ubd_driver_init(void){
854 unsigned long stack;
855 int err;
856
857 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
858 if(global_openflags.s){
859 printk(KERN_INFO "ubd: Synchronous mode\n");
860 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
861 * enough. So use anyway the io thread. */
862 }
863 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800864 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400865 &thread_fd);
866 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800867 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400868 "ubd : Failed to start I/O thread (errno = %d) - "
869 "falling back to synchronous I/O\n", -io_pid);
870 io_pid = -1;
871 return(0);
872 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800873 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800874 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400875 if(err != 0)
876 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800877 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400878}
879
880device_initcall(ubd_driver_init);
881
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882static int ubd_open(struct inode *inode, struct file *filp)
883{
884 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800885 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 int err = 0;
887
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800888 if(ubd_dev->count == 0){
889 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 if(err){
891 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800892 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 goto out;
894 }
895 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800896 ubd_dev->count++;
897 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700898
899 /* This should no more be needed. And it didn't work anyway to exclude
900 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800901 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800902 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700904 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 out:
906 return(err);
907}
908
909static int ubd_release(struct inode * inode, struct file * file)
910{
911 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800912 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800914 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800915 ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 return(0);
917}
918
Jeff Dike91acb212005-10-10 23:10:32 -0400919static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
920 __u64 *cow_offset, unsigned long *bitmap,
921 __u64 bitmap_offset, unsigned long *bitmap_words,
922 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923{
Jeff Dike91acb212005-10-10 23:10:32 -0400924 __u64 sector = io_offset >> 9;
925 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
Jeff Dike91acb212005-10-10 23:10:32 -0400927 for(i = 0; i < length >> 9; i++){
928 if(cow_mask != NULL)
929 ubd_set_bit(i, (unsigned char *) cow_mask);
930 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
931 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
Jeff Dike91acb212005-10-10 23:10:32 -0400933 update_bitmap = 1;
934 ubd_set_bit(sector + i, (unsigned char *) bitmap);
935 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
Jeff Dike91acb212005-10-10 23:10:32 -0400937 if(!update_bitmap)
938 return;
939
940 *cow_offset = sector / (sizeof(unsigned long) * 8);
941
942 /* This takes care of the case where we're exactly at the end of the
943 * device, and *cow_offset + 1 is off the end. So, just back it up
944 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
945 * for the original diagnosis.
946 */
947 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
948 sizeof(unsigned long) - 1))
949 (*cow_offset)--;
950
951 bitmap_words[0] = bitmap[*cow_offset];
952 bitmap_words[1] = bitmap[*cow_offset + 1];
953
954 *cow_offset *= sizeof(unsigned long);
955 *cow_offset += bitmap_offset;
956}
957
958static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
959 __u64 bitmap_offset, __u64 bitmap_len)
960{
961 __u64 sector = req->offset >> 9;
962 int i;
963
964 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
965 panic("Operation too long");
966
967 if(req->op == UBD_READ) {
968 for(i = 0; i < req->length >> 9; i++){
969 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -0800970 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -0400971 &req->sector_mask);
972 }
973 }
974 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
975 &req->cow_offset, bitmap, bitmap_offset,
976 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977}
978
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979/* Called with ubd_io_lock held */
Jeff Dike91acb212005-10-10 23:10:32 -0400980static int prepare_request(struct request *req, struct io_thread_req *io_req)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981{
982 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800983 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -0400984 __u64 offset;
985 int len;
986
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700987 /* This should be impossible now */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800988 if((rq_data_dir(req) == WRITE) && !ubd_dev->openflags.w){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800989 printk("Write attempted on readonly ubd device %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 disk->disk_name);
Jeff Dike91acb212005-10-10 23:10:32 -0400991 end_request(req, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 return(1);
993 }
994
Jeff Dike91acb212005-10-10 23:10:32 -0400995 offset = ((__u64) req->sector) << 9;
996 len = req->current_nr_sectors << 9;
997
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800998 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd : ubd_dev->fd;
999 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001000 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 io_req->offset = offset;
1002 io_req->length = len;
1003 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001004 io_req->sector_mask = 0;
1005
1006 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001008 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dike91acb212005-10-10 23:10:32 -04001009 io_req->buffer = req->buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 io_req->sectorsize = 1 << 9;
1011
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001012 if(ubd_dev->cow.file != NULL)
1013 cowify_req(io_req, ubd_dev->cow.bitmap, ubd_dev->cow.bitmap_offset,
1014 ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001015
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 return(0);
1017}
1018
1019/* Called with ubd_io_lock held */
1020static void do_ubd_request(request_queue_t *q)
1021{
1022 struct io_thread_req io_req;
1023 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001024 int err, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025
Jeff Dike91acb212005-10-10 23:10:32 -04001026 if(thread_fd == -1){
1027 while((req = elv_next_request(q)) != NULL){
1028 err = prepare_request(req, &io_req);
1029 if(!err){
1030 do_io(&io_req);
1031 __ubd_finish(req, io_req.error);
1032 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 }
1034 }
Jeff Dike91acb212005-10-10 23:10:32 -04001035 else {
1036 if(do_ubd || (req = elv_next_request(q)) == NULL)
1037 return;
1038 err = prepare_request(req, &io_req);
1039 if(!err){
1040 do_ubd = ubd_handler;
1041 n = os_write_file(thread_fd, (char *) &io_req,
1042 sizeof(io_req));
1043 if(n != sizeof(io_req))
1044 printk("write to io thread failed, "
1045 "errno = %d\n", -n);
1046 }
1047 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048}
1049
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001050static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1051{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001052 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001053
1054 geo->heads = 128;
1055 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001056 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001057 return 0;
1058}
1059
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060static int ubd_ioctl(struct inode * inode, struct file * file,
1061 unsigned int cmd, unsigned long arg)
1062{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001063 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 struct hd_driveid ubd_id = {
1065 .cyls = 0,
1066 .heads = 128,
1067 .sectors = 32,
1068 };
1069
1070 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001073 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1075 sizeof(ubd_id)))
1076 return(-EFAULT);
1077 return(0);
1078
1079 case CDROMVOLREAD:
1080 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
1081 return(-EFAULT);
1082 volume.channel0 = 255;
1083 volume.channel1 = 255;
1084 volume.channel2 = 255;
1085 volume.channel3 = 255;
1086 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
1087 return(-EFAULT);
1088 return(0);
1089 }
1090 return(-EINVAL);
1091}
1092
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001093static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094{
1095 struct uml_stat buf1, buf2;
1096 int err;
1097
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001098 if(from_cmdline == NULL)
1099 return 0;
1100 if(!strcmp(from_cmdline, from_cow))
1101 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102
1103 err = os_stat_file(from_cmdline, &buf1);
1104 if(err < 0){
1105 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001106 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 }
1108 err = os_stat_file(from_cow, &buf2);
1109 if(err < 0){
1110 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001111 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 }
1113 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001114 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115
1116 printk("Backing file mismatch - \"%s\" requested,\n"
1117 "\"%s\" specified in COW header of \"%s\"\n",
1118 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001119 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120}
1121
1122static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1123{
1124 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001125 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 int err;
1127
1128 err = os_file_modtime(file, &modtime);
1129 if(err < 0){
1130 printk("Failed to get modification time of backing file "
1131 "\"%s\", err = %d\n", file, -err);
1132 return(err);
1133 }
1134
1135 err = os_file_size(file, &actual);
1136 if(err < 0){
1137 printk("Failed to get size of backing file \"%s\", "
1138 "err = %d\n", file, -err);
1139 return(err);
1140 }
1141
1142 if(actual != size){
1143 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1144 * the typecast.*/
1145 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1146 "file\n", (unsigned long long) size, actual);
1147 return(-EINVAL);
1148 }
1149 if(modtime != mtime){
1150 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1151 "file\n", mtime, modtime);
1152 return(-EINVAL);
1153 }
1154 return(0);
1155}
1156
1157int read_cow_bitmap(int fd, void *buf, int offset, int len)
1158{
1159 int err;
1160
1161 err = os_seek_file(fd, offset);
1162 if(err < 0)
1163 return(err);
1164
1165 err = os_read_file(fd, buf, len);
1166 if(err < 0)
1167 return(err);
1168
1169 return(0);
1170}
1171
Jeff Dike6c29256c2006-03-27 01:14:37 -08001172int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 char **backing_file_out, int *bitmap_offset_out,
1174 unsigned long *bitmap_len_out, int *data_offset_out,
1175 int *create_cow_out)
1176{
1177 time_t mtime;
1178 unsigned long long size;
1179 __u32 version, align;
1180 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001181 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182
1183 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001184 if (fd < 0) {
1185 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 *create_cow_out = 1;
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001187 if (!openflags->w ||
1188 ((fd != -EROFS) && (fd != -EACCES)))
1189 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 openflags->w = 0;
1191 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001192 if (fd < 0)
1193 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 }
1195
Jeff Dike6c29256c2006-03-27 01:14:37 -08001196 if(shared)
1197 printk("Not locking \"%s\" on the host\n", file);
1198 else {
1199 err = os_lock_file(fd, openflags->w);
1200 if(err < 0){
1201 printk("Failed to lock '%s', err = %d\n", file, -err);
1202 goto out_close;
1203 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 }
1205
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001206 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001207 if(backing_file_out == NULL)
1208 return(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209
1210 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1211 &size, &sectorsize, &align, bitmap_offset_out);
1212 if(err && (*backing_file_out != NULL)){
1213 printk("Failed to read COW header from COW file \"%s\", "
1214 "errno = %d\n", file, -err);
1215 goto out_close;
1216 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001217 if(err)
1218 return(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001220 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001222 /* Allow switching only if no mismatch. */
1223 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 printk("Switching backing file to '%s'\n", *backing_file_out);
1225 err = write_cow_header(file, fd, *backing_file_out,
1226 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001227 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001229 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001231 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 *backing_file_out = backing_file;
1233 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001234 if (err)
1235 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 }
1237
1238 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1239 bitmap_len_out, data_offset_out);
1240
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001241 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 out_close:
1243 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001244 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245}
1246
1247int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1248 int sectorsize, int alignment, int *bitmap_offset_out,
1249 unsigned long *bitmap_len_out, int *data_offset_out)
1250{
1251 int err, fd;
1252
1253 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001254 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 if(fd < 0){
1256 err = fd;
1257 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1258 -err);
1259 goto out;
1260 }
1261
1262 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1263 bitmap_offset_out, bitmap_len_out,
1264 data_offset_out);
1265 if(!err)
1266 return(fd);
1267 os_close_file(fd);
1268 out:
1269 return(err);
1270}
1271
Jeff Dike91acb212005-10-10 23:10:32 -04001272static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273{
Jeff Dike91acb212005-10-10 23:10:32 -04001274 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
Jeff Dike91acb212005-10-10 23:10:32 -04001276 if(req->cow_offset == -1)
1277 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278
Jeff Dike91acb212005-10-10 23:10:32 -04001279 n = os_seek_file(req->fds[1], req->cow_offset);
1280 if(n < 0){
1281 printk("do_io - bitmap lseek failed : err = %d\n", -n);
1282 return(1);
1283 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
Jeff Dike91acb212005-10-10 23:10:32 -04001285 n = os_write_file(req->fds[1], &req->bitmap_words,
1286 sizeof(req->bitmap_words));
1287 if(n != sizeof(req->bitmap_words)){
1288 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1289 req->fds[1]);
1290 return(1);
1291 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292
Jeff Dike91acb212005-10-10 23:10:32 -04001293 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294}
Jeff Dike91acb212005-10-10 23:10:32 -04001295
1296void do_io(struct io_thread_req *req)
1297{
1298 char *buf;
1299 unsigned long len;
1300 int n, nsectors, start, end, bit;
1301 int err;
1302 __u64 off;
1303
1304 nsectors = req->length / req->sectorsize;
1305 start = 0;
1306 do {
1307 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1308 end = start;
1309 while((end < nsectors) &&
1310 (ubd_test_bit(end, (unsigned char *)
1311 &req->sector_mask) == bit))
1312 end++;
1313
1314 off = req->offset + req->offsets[bit] +
1315 start * req->sectorsize;
1316 len = (end - start) * req->sectorsize;
1317 buf = &req->buffer[start * req->sectorsize];
1318
1319 err = os_seek_file(req->fds[bit], off);
1320 if(err < 0){
1321 printk("do_io - lseek failed : err = %d\n", -err);
1322 req->error = 1;
1323 return;
1324 }
1325 if(req->op == UBD_READ){
1326 n = 0;
1327 do {
1328 buf = &buf[n];
1329 len -= n;
1330 n = os_read_file(req->fds[bit], buf, len);
1331 if (n < 0) {
1332 printk("do_io - read failed, err = %d "
1333 "fd = %d\n", -n, req->fds[bit]);
1334 req->error = 1;
1335 return;
1336 }
1337 } while((n < len) && (n != 0));
1338 if (n < len) memset(&buf[n], 0, len - n);
1339 } else {
1340 n = os_write_file(req->fds[bit], buf, len);
1341 if(n != len){
1342 printk("do_io - write failed err = %d "
1343 "fd = %d\n", -n, req->fds[bit]);
1344 req->error = 1;
1345 return;
1346 }
1347 }
1348
1349 start = end;
1350 } while(start < nsectors);
1351
1352 req->error = update_bitmap(req);
1353}
1354
1355/* Changed in start_io_thread, which is serialized by being called only
1356 * from ubd_init, which is an initcall.
1357 */
1358int kernel_fd = -1;
1359
1360/* Only changed by the io thread */
1361int io_count = 0;
1362
1363int io_thread(void *arg)
1364{
1365 struct io_thread_req req;
1366 int n;
1367
1368 ignore_sigwinch_sig();
1369 while(1){
1370 n = os_read_file(kernel_fd, &req, sizeof(req));
1371 if(n != sizeof(req)){
1372 if(n < 0)
1373 printk("io_thread - read failed, fd = %d, "
1374 "err = %d\n", kernel_fd, -n);
1375 else {
1376 printk("io_thread - short read, fd = %d, "
1377 "length = %d\n", kernel_fd, n);
1378 }
1379 continue;
1380 }
1381 io_count++;
1382 do_io(&req);
1383 n = os_write_file(kernel_fd, &req, sizeof(req));
1384 if(n != sizeof(req))
1385 printk("io_thread - write failed, fd = %d, err = %d\n",
1386 kernel_fd, -n);
1387 }
Jeff Dike91acb212005-10-10 23:10:32 -04001388
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001389 return 0;
1390}