blob: f4db97efc014d9ddbc214c6ed0d6f736462b6870 [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
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800109/* Can be taken in interrupt context, and is passed to the block layer to lock
110 * the request queue. Kernel side code knows that. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111static DEFINE_SPINLOCK(ubd_io_lock);
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800112
113static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800115/* XXX - this made sense in 2.4 days, now it's only used as a boolean, and
116 * probably it doesn't make sense even for that. */
117static int do_ubd;
Jeff Dike91acb212005-10-10 23:10:32 -0400118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119static int ubd_open(struct inode * inode, struct file * filp);
120static int ubd_release(struct inode * inode, struct file * file);
121static int ubd_ioctl(struct inode * inode, struct file * file,
122 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800123static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800125#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127static struct block_device_operations ubd_blops = {
128 .owner = THIS_MODULE,
129 .open = ubd_open,
130 .release = ubd_release,
131 .ioctl = ubd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800132 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133};
134
135/* Protected by the queue_lock */
136static request_queue_t *ubd_queue;
137
138/* Protected by ubd_lock */
139static int fake_major = MAJOR_NR;
140
141static struct gendisk *ubd_gendisk[MAX_DEV];
142static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800143
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144#ifdef CONFIG_BLK_DEV_UBD_SYNC
145#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
146 .cl = 1 })
147#else
148#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
149 .cl = 1 })
150#endif
151
152/* Not protected - changed only in ubd_setup_common and then only to
153 * to enable O_SYNC.
154 */
155static struct openflags global_openflags = OPEN_FLAGS;
156
157struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800158 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800160 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 int fd;
162 unsigned long *bitmap;
163 unsigned long bitmap_len;
164 int bitmap_offset;
165 int data_offset;
166};
167
168struct ubd {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800169 /* name (and fd, below) of the file opened for writing, either the
170 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 char *file;
172 int count;
173 int fd;
174 __u64 size;
175 struct openflags boot_openflags;
176 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800177 unsigned shared:1;
178 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 struct cow cow;
180 struct platform_device pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181};
182
183#define DEFAULT_COW { \
184 .file = NULL, \
185 .fd = -1, \
186 .bitmap = NULL, \
187 .bitmap_offset = 0, \
188 .data_offset = 0, \
189}
190
191#define DEFAULT_UBD { \
192 .file = NULL, \
193 .count = 0, \
194 .fd = -1, \
195 .size = -1, \
196 .boot_openflags = OPEN_FLAGS, \
197 .openflags = OPEN_FLAGS, \
198 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800199 .shared = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .cow = DEFAULT_COW, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201}
202
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800203struct ubd ubd_devs[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205/* Only changed by fake_ide_setup which is a setup */
206static int fake_ide = 0;
207static struct proc_dir_entry *proc_ide_root = NULL;
208static struct proc_dir_entry *proc_ide = NULL;
209
210static void make_proc_ide(void)
211{
212 proc_ide_root = proc_mkdir("ide", NULL);
213 proc_ide = proc_mkdir("ide0", proc_ide_root);
214}
215
216static int proc_ide_read_media(char *page, char **start, off_t off, int count,
217 int *eof, void *data)
218{
219 int len;
220
221 strcpy(page, "disk\n");
222 len = strlen("disk\n");
223 len -= off;
224 if (len < count){
225 *eof = 1;
226 if (len <= 0) return 0;
227 }
228 else len = count;
229 *start = page + off;
230 return len;
231}
232
233static void make_ide_entries(char *dev_name)
234{
235 struct proc_dir_entry *dir, *ent;
236 char name[64];
237
238 if(proc_ide_root == NULL) make_proc_ide();
239
240 dir = proc_mkdir(dev_name, proc_ide);
241 if(!dir) return;
242
243 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
244 if(!ent) return;
245 ent->nlink = 1;
246 ent->data = NULL;
247 ent->read_proc = proc_ide_read_media;
248 ent->write_proc = NULL;
249 sprintf(name,"ide0/%s", dev_name);
250 proc_symlink(dev_name, proc_ide_root, name);
251}
252
253static int fake_ide_setup(char *str)
254{
255 fake_ide = 1;
256 return(1);
257}
258
259__setup("fake_ide", fake_ide_setup);
260
261__uml_help(fake_ide_setup,
262"fake_ide\n"
263" Create ide0 entries that map onto ubd devices.\n\n"
264);
265
266static int parse_unit(char **ptr)
267{
268 char *str = *ptr, *end;
269 int n = -1;
270
271 if(isdigit(*str)) {
272 n = simple_strtoul(str, &end, 0);
273 if(end == str)
274 return(-1);
275 *ptr = end;
276 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800277 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 n = *str - 'a';
279 str++;
280 *ptr = str;
281 }
282 return(n);
283}
284
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800285/* If *index_out == -1 at exit, the passed option was a general one;
286 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
287 * should not be freed on exit.
288 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800289static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800291 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 struct openflags flags = global_openflags;
293 char *backing_file;
294 int n, err, i;
295
296 if(index_out) *index_out = -1;
297 n = *str;
298 if(n == '='){
299 char *end;
300 int major;
301
302 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 if(!strcmp(str, "sync")){
304 global_openflags = of_sync(global_openflags);
Jeff Dikef28169d2007-02-10 01:43:53 -0800305 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 }
307 major = simple_strtoul(str, &end, 0);
308 if((*end != '\0') || (end == str)){
Jeff Dikef28169d2007-02-10 01:43:53 -0800309 *error_out = "Didn't parse major number";
310 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 }
312
Jeff Dikef28169d2007-02-10 01:43:53 -0800313 err = -EINVAL;
314 mutex_lock(&ubd_lock);
315 if(fake_major != MAJOR_NR){
316 *error_out = "Can't assign a fake major twice";
317 goto out1;
318 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800319
Jeff Dikef28169d2007-02-10 01:43:53 -0800320 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
322 printk(KERN_INFO "Setting extra ubd major number to %d\n",
323 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800324 err = 0;
325 out1:
326 mutex_unlock(&ubd_lock);
327 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 }
329
330 n = parse_unit(&str);
331 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800332 *error_out = "Couldn't parse device number";
333 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 }
335 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800336 *error_out = "Device number out of range";
337 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 }
339
Jeff Dikef28169d2007-02-10 01:43:53 -0800340 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800341 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800343 ubd_dev = &ubd_devs[n];
344 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800345 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 goto out;
347 }
348
349 if (index_out)
350 *index_out = n;
351
Jeff Dikef28169d2007-02-10 01:43:53 -0800352 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800353 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 switch (*str) {
355 case 'r':
356 flags.w = 0;
357 break;
358 case 's':
359 flags.s = 1;
360 break;
361 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800362 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800364 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800365 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800366 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 case '=':
368 str++;
369 goto break_loop;
370 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800371 *error_out = "Expected '=' or flag letter "
372 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 goto out;
374 }
375 str++;
376 }
377
Jeff Dikef28169d2007-02-10 01:43:53 -0800378 if (*str == '=')
379 *error_out = "Too many flags specified";
380 else
381 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 goto out;
383
384break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 backing_file = strchr(str, ',');
386
Jeff Dikef28169d2007-02-10 01:43:53 -0800387 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389
Jeff Dikef28169d2007-02-10 01:43:53 -0800390 if(backing_file != NULL){
391 if(ubd_dev->no_cow){
392 *error_out = "Can't specify both 'd' and a cow file";
393 goto out;
394 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 else {
396 *backing_file = '\0';
397 backing_file++;
398 }
399 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800400 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800401 ubd_dev->file = str;
402 ubd_dev->cow.file = backing_file;
403 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800405 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800406 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407}
408
409static int ubd_setup(char *str)
410{
Jeff Dikef28169d2007-02-10 01:43:53 -0800411 char *error;
412 int err;
413
414 err = ubd_setup_common(str, NULL, &error);
415 if(err)
416 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
417 "%s\n", str, error);
418 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419}
420
421__setup("ubd", ubd_setup);
422__uml_help(ubd_setup,
423"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
424" This is used to associate a device with a file in the underlying\n"
425" filesystem. When specifying two filenames, the first one is the\n"
426" COW name and the second is the backing file name. As separator you can\n"
427" use either a ':' or a ',': the first one allows writing things like;\n"
428" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
429" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800430" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431" a COW file or a backing file. To override this detection, add the 'd'\n"
432" flag:\n"
433" ubd0d=BackingFile\n"
434" Usually, there is a filesystem in the file, but \n"
435" that's not required. Swap devices containing swap files can be\n"
436" specified like this. Also, a file which doesn't contain a\n"
437" filesystem can have its contents read in the virtual \n"
438" machine by running 'dd' on the device. <n> must be in the range\n"
439" 0 to 7. Appending an 'r' to the number will cause that device\n"
440" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
441" an 's' will cause data to be written to disk on the host immediately.\n\n"
442);
443
444static int udb_setup(char *str)
445{
446 printk("udb%s specified on command line is almost certainly a ubd -> "
447 "udb TYPO\n", str);
448 return(1);
449}
450
451__setup("udb", udb_setup);
452__uml_help(udb_setup,
453"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700454" This option is here solely to catch ubd -> udb typos, which can be\n"
455" to impossible to catch visually unless you specifically look for\n"
456" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457" in the boot output.\n\n"
458);
459
460static int fakehd_set = 0;
461static int fakehd(char *str)
462{
463 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
464 fakehd_set = 1;
465 return 1;
466}
467
468__setup("fakehd", fakehd);
469__uml_help(fakehd,
470"fakehd\n"
471" Change the ubd device name to \"hd\".\n\n"
472);
473
474static void do_ubd_request(request_queue_t * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400475
476/* Only changed by ubd_init, which is an initcall. */
477int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
479/* Changed by ubd_handler, which is serialized because interrupts only
480 * happen on CPU 0.
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800481 * XXX: currently unused.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 */
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800483static int intr_count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
485/* call ubd_finish if you need to serialize */
Jeff Dike91acb212005-10-10 23:10:32 -0400486static void __ubd_finish(struct request *req, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
Jeff Dike91acb212005-10-10 23:10:32 -0400488 int nsect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489
Jeff Dike91acb212005-10-10 23:10:32 -0400490 if(error){
491 end_request(req, 0);
492 return;
493 }
494 nsect = req->current_nr_sectors;
495 req->sector += nsect;
496 req->buffer += nsect << 9;
497 req->errors = 0;
498 req->nr_sectors -= nsect;
499 req->current_nr_sectors = 0;
500 end_request(req, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501}
502
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800503/* Callable only from interrupt context - otherwise you need to do
504 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dike91acb212005-10-10 23:10:32 -0400505static inline void ubd_finish(struct request *req, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506{
Jeff Dike91acb212005-10-10 23:10:32 -0400507 spin_lock(&ubd_io_lock);
508 __ubd_finish(req, error);
509 spin_unlock(&ubd_io_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510}
511
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800512/* XXX - move this inside ubd_intr. */
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800513/* Called without ubd_io_lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400514static void ubd_handler(void)
515{
516 struct io_thread_req req;
517 struct request *rq = elv_next_request(ubd_queue);
518 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800520 do_ubd = 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400521 intr_count++;
522 n = os_read_file(thread_fd, &req, sizeof(req));
523 if(n != sizeof(req)){
524 printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, "
525 "err = %d\n", os_getpid(), -n);
526 spin_lock(&ubd_io_lock);
527 end_request(rq, 0);
528 spin_unlock(&ubd_io_lock);
529 return;
530 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800531
Jeff Dike91acb212005-10-10 23:10:32 -0400532 ubd_finish(rq, req.error);
533 reactivate_fd(thread_fd, UBD_IRQ);
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800534 spin_lock(&ubd_io_lock);
Jeff Dike91acb212005-10-10 23:10:32 -0400535 do_ubd_request(ubd_queue);
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800536 spin_unlock(&ubd_io_lock);
Jeff Dike91acb212005-10-10 23:10:32 -0400537}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Al Viro7bea96f2006-10-08 22:49:34 +0100539static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540{
Jeff Dike91acb212005-10-10 23:10:32 -0400541 ubd_handler();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 return(IRQ_HANDLED);
543}
544
Jeff Dike91acb212005-10-10 23:10:32 -0400545/* Only changed by ubd_init, which is an initcall. */
546static int io_pid = -1;
547
548void kill_io_thread(void)
549{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800550 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400551 os_kill_process(io_pid, 1);
552}
553
554__uml_exitcall(kill_io_thread);
555
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800556static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557{
558 char *file;
559
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800560 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 return(os_file_size(file, size_out));
562}
563
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800564static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800566 os_close_file(ubd_dev->fd);
567 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 return;
569
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800570 os_close_file(ubd_dev->cow.fd);
571 vfree(ubd_dev->cow.bitmap);
572 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573}
574
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800575static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576{
577 struct openflags flags;
578 char **back_ptr;
579 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800580 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800582 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800584 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
585 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800586
587 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800588 back_ptr, &ubd_dev->cow.bitmap_offset,
589 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800590 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800592 if((fd == -ENOENT) && create_cow){
593 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800594 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
595 &ubd_dev->cow.bitmap_offset,
596 &ubd_dev->cow.bitmap_len,
597 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800598 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800600 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 }
602 }
603
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800604 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800605 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800606 -fd);
607 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800609 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800611 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 err = -ENOMEM;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800613 ubd_dev->cow.bitmap = (void *) vmalloc(ubd_dev->cow.bitmap_len);
614 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
616 goto error;
617 }
618 flush_tlb_kernel_vm();
619
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800620 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
621 ubd_dev->cow.bitmap_offset,
622 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 if(err < 0)
624 goto error;
625
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800626 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800628 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800629 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800631 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 }
633 return(0);
634 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800635 os_close_file(ubd_dev->fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 return(err);
637}
638
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800639static int ubd_disk_register(int major, u64 size, int unit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 struct gendisk **disk_out)
641
642{
643 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
645 disk = alloc_disk(1 << UBD_SHIFT);
646 if(disk == NULL)
647 return(-ENOMEM);
648
649 disk->major = major;
650 disk->first_minor = unit << UBD_SHIFT;
651 disk->fops = &ubd_blops;
652 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700653 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700655 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
658 /* sysfs register (not for ide fake devices) */
659 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800660 ubd_devs[unit].pdev.id = unit;
661 ubd_devs[unit].pdev.name = DRIVER_NAME;
662 platform_device_register(&ubd_devs[unit].pdev);
663 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 }
665
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800666 disk->private_data = &ubd_devs[unit];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 disk->queue = ubd_queue;
668 add_disk(disk);
669
670 *disk_out = disk;
671 return 0;
672}
673
674#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
675
Jeff Dikef28169d2007-02-10 01:43:53 -0800676static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800678 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800679 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800681 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700682 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800684 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800685 if(err < 0){
686 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700687 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800688 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800690 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800692 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800693 if(err)
Jeff Dike80c13742006-09-29 01:58:51 -0700694 goto out;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800697 ubd_disk_register(fake_major, ubd_dev->size, n,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 &fake_gendisk[n]);
699
700 /* perhaps this should also be under the "if (fake_major)" above */
701 /* using the fake_disk->disk_name and also the fakehd_set name */
702 if (fake_ide)
703 make_ide_entries(ubd_gendisk[n]->disk_name);
704
Jeff Dikeec7cf782005-09-03 15:57:29 -0700705 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700706out:
707 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708}
709
Jeff Dikef28169d2007-02-10 01:43:53 -0800710static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800712 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713
Jeff Dikef28169d2007-02-10 01:43:53 -0800714 /* This string is possibly broken up and stored, so it's only
715 * freed if ubd_setup_common fails, or if only general options
716 * were set.
717 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800718 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800719 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800720 *error_out = "Failed to allocate memory";
721 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800723
724 ret = ubd_setup_common(str, &n, error_out);
725 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800726 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800727
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800728 if (n == -1) {
729 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800730 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800731 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800733 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800734 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800735 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800736 ubd_devs[n].file = NULL;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800737 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800739out:
740 return ret;
741
742err_free:
743 kfree(str);
744 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745}
746
747static int ubd_get_config(char *name, char *str, int size, char **error_out)
748{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800749 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 int n, len = 0;
751
752 n = parse_unit(&name);
753 if((n >= MAX_DEV) || (n < 0)){
754 *error_out = "ubd_get_config : device number out of range";
755 return(-1);
756 }
757
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800758 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800759 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800761 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 CONFIG_CHUNK(str, size, len, "", 1);
763 goto out;
764 }
765
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800766 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800768 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800770 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 }
772 else CONFIG_CHUNK(str, size, len, "", 1);
773
774 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800775 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 return(len);
777}
778
Jeff Dike29d56cf2005-06-25 14:55:25 -0700779static int ubd_id(char **str, int *start_out, int *end_out)
780{
781 int n;
782
783 n = parse_unit(str);
784 *start_out = 0;
785 *end_out = MAX_DEV - 1;
786 return n;
787}
788
Jeff Dikef28169d2007-02-10 01:43:53 -0800789static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800791 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700792 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800794 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
796 if(ubd_gendisk[n] == NULL)
797 goto out;
798
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800799 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700800
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800801 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700802 goto out;
803
804 /* you cannot remove a open disk */
805 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800806 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700807 goto out;
808
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 del_gendisk(ubd_gendisk[n]);
810 put_disk(ubd_gendisk[n]);
811 ubd_gendisk[n] = NULL;
812
813 if(fake_gendisk[n] != NULL){
814 del_gendisk(fake_gendisk[n]);
815 put_disk(fake_gendisk[n]);
816 fake_gendisk[n] = NULL;
817 }
818
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800819 platform_device_unregister(&ubd_dev->pdev);
820 *ubd_dev = ((struct ubd) DEFAULT_UBD);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 err = 0;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700822out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800823 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700824 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825}
826
Jeff Dikef28169d2007-02-10 01:43:53 -0800827/* All these are called by mconsole in process context and without
828 * ubd-specific locks.
829 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830static struct mc_device ubd_mc = {
831 .name = "ubd",
832 .config = ubd_config,
833 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700834 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 .remove = ubd_remove,
836};
837
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800838static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839{
840 mconsole_register_dev(&ubd_mc);
841 return 0;
842}
843
844__initcall(ubd_mc_init);
845
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800846static int __init ubd0_init(void)
847{
848 struct ubd *ubd_dev = &ubd_devs[0];
849
850 if(ubd_dev->file == NULL)
851 ubd_dev->file = "root_fs";
852 return(0);
853}
854
855__initcall(ubd0_init);
856
Russell King3ae5eae2005-11-09 22:32:44 +0000857static struct platform_driver ubd_driver = {
858 .driver = {
859 .name = DRIVER_NAME,
860 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861};
862
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800863static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
Jeff Dikef28169d2007-02-10 01:43:53 -0800865 char *error;
866 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 if (register_blkdev(MAJOR_NR, "ubd"))
869 return -1;
870
871 ubd_queue = blk_init_queue(do_ubd_request, &ubd_io_lock);
872 if (!ubd_queue) {
873 unregister_blkdev(MAJOR_NR, "ubd");
874 return -1;
875 }
876
877 if (fake_major != MAJOR_NR) {
878 char name[sizeof("ubd_nnn\0")];
879
880 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 if (register_blkdev(fake_major, "ubd"))
882 return -1;
883 }
Russell King3ae5eae2005-11-09 22:32:44 +0000884 platform_driver_register(&ubd_driver);
Jeff Dikef28169d2007-02-10 01:43:53 -0800885 for (i = 0; i < MAX_DEV; i++){
886 err = ubd_add(i, &error);
887 if(err)
888 printk(KERN_ERR "Failed to initialize ubd device %d :"
889 "%s\n", i, error);
890 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 return 0;
892}
893
894late_initcall(ubd_init);
895
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800896static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400897 unsigned long stack;
898 int err;
899
900 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
901 if(global_openflags.s){
902 printk(KERN_INFO "ubd: Synchronous mode\n");
903 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
904 * enough. So use anyway the io thread. */
905 }
906 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800907 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400908 &thread_fd);
909 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800910 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400911 "ubd : Failed to start I/O thread (errno = %d) - "
912 "falling back to synchronous I/O\n", -io_pid);
913 io_pid = -1;
914 return(0);
915 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800916 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800917 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400918 if(err != 0)
919 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800920 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400921}
922
923device_initcall(ubd_driver_init);
924
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925static int ubd_open(struct inode *inode, struct file *filp)
926{
927 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800928 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 int err = 0;
930
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800931 if(ubd_dev->count == 0){
932 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 if(err){
934 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800935 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 goto out;
937 }
938 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800939 ubd_dev->count++;
940 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700941
942 /* This should no more be needed. And it didn't work anyway to exclude
943 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800944 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800945 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700947 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 out:
949 return(err);
950}
951
952static int ubd_release(struct inode * inode, struct file * file)
953{
954 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800955 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800957 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800958 ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 return(0);
960}
961
Jeff Dike91acb212005-10-10 23:10:32 -0400962static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
963 __u64 *cow_offset, unsigned long *bitmap,
964 __u64 bitmap_offset, unsigned long *bitmap_words,
965 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966{
Jeff Dike91acb212005-10-10 23:10:32 -0400967 __u64 sector = io_offset >> 9;
968 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969
Jeff Dike91acb212005-10-10 23:10:32 -0400970 for(i = 0; i < length >> 9; i++){
971 if(cow_mask != NULL)
972 ubd_set_bit(i, (unsigned char *) cow_mask);
973 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
974 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
Jeff Dike91acb212005-10-10 23:10:32 -0400976 update_bitmap = 1;
977 ubd_set_bit(sector + i, (unsigned char *) bitmap);
978 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
Jeff Dike91acb212005-10-10 23:10:32 -0400980 if(!update_bitmap)
981 return;
982
983 *cow_offset = sector / (sizeof(unsigned long) * 8);
984
985 /* This takes care of the case where we're exactly at the end of the
986 * device, and *cow_offset + 1 is off the end. So, just back it up
987 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
988 * for the original diagnosis.
989 */
990 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
991 sizeof(unsigned long) - 1))
992 (*cow_offset)--;
993
994 bitmap_words[0] = bitmap[*cow_offset];
995 bitmap_words[1] = bitmap[*cow_offset + 1];
996
997 *cow_offset *= sizeof(unsigned long);
998 *cow_offset += bitmap_offset;
999}
1000
1001static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1002 __u64 bitmap_offset, __u64 bitmap_len)
1003{
1004 __u64 sector = req->offset >> 9;
1005 int i;
1006
1007 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1008 panic("Operation too long");
1009
1010 if(req->op == UBD_READ) {
1011 for(i = 0; i < req->length >> 9; i++){
1012 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001013 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001014 &req->sector_mask);
1015 }
1016 }
1017 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1018 &req->cow_offset, bitmap, bitmap_offset,
1019 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020}
1021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022/* Called with ubd_io_lock held */
Jeff Dike91acb212005-10-10 23:10:32 -04001023static int prepare_request(struct request *req, struct io_thread_req *io_req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024{
1025 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001026 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001027 __u64 offset;
1028 int len;
1029
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -07001030 /* This should be impossible now */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001031 if((rq_data_dir(req) == WRITE) && !ubd_dev->openflags.w){
Jeff Dike6c29256c2006-03-27 01:14:37 -08001032 printk("Write attempted on readonly ubd device %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 disk->disk_name);
Jeff Dike91acb212005-10-10 23:10:32 -04001034 end_request(req, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 return(1);
1036 }
1037
Jeff Dike91acb212005-10-10 23:10:32 -04001038 offset = ((__u64) req->sector) << 9;
1039 len = req->current_nr_sectors << 9;
1040
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001041 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd : ubd_dev->fd;
1042 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001043 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 io_req->offset = offset;
1045 io_req->length = len;
1046 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001047 io_req->sector_mask = 0;
1048
1049 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001051 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dike91acb212005-10-10 23:10:32 -04001052 io_req->buffer = req->buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 io_req->sectorsize = 1 << 9;
1054
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001055 if(ubd_dev->cow.file != NULL)
1056 cowify_req(io_req, ubd_dev->cow.bitmap, ubd_dev->cow.bitmap_offset,
1057 ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001058
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 return(0);
1060}
1061
1062/* Called with ubd_io_lock held */
1063static void do_ubd_request(request_queue_t *q)
1064{
1065 struct io_thread_req io_req;
1066 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001067 int err, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068
Jeff Dike91acb212005-10-10 23:10:32 -04001069 if(thread_fd == -1){
1070 while((req = elv_next_request(q)) != NULL){
1071 err = prepare_request(req, &io_req);
1072 if(!err){
1073 do_io(&io_req);
1074 __ubd_finish(req, io_req.error);
1075 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 }
1077 }
Jeff Dike91acb212005-10-10 23:10:32 -04001078 else {
1079 if(do_ubd || (req = elv_next_request(q)) == NULL)
1080 return;
1081 err = prepare_request(req, &io_req);
1082 if(!err){
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -08001083 do_ubd = 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001084 n = os_write_file(thread_fd, (char *) &io_req,
1085 sizeof(io_req));
1086 if(n != sizeof(io_req))
1087 printk("write to io thread failed, "
1088 "errno = %d\n", -n);
1089 }
1090 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091}
1092
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001093static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1094{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001095 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001096
1097 geo->heads = 128;
1098 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001099 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001100 return 0;
1101}
1102
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103static int ubd_ioctl(struct inode * inode, struct file * file,
1104 unsigned int cmd, unsigned long arg)
1105{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001106 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 struct hd_driveid ubd_id = {
1108 .cyls = 0,
1109 .heads = 128,
1110 .sectors = 32,
1111 };
1112
1113 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001116 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1118 sizeof(ubd_id)))
1119 return(-EFAULT);
1120 return(0);
1121
1122 case CDROMVOLREAD:
1123 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
1124 return(-EFAULT);
1125 volume.channel0 = 255;
1126 volume.channel1 = 255;
1127 volume.channel2 = 255;
1128 volume.channel3 = 255;
1129 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
1130 return(-EFAULT);
1131 return(0);
1132 }
1133 return(-EINVAL);
1134}
1135
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001136static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137{
1138 struct uml_stat buf1, buf2;
1139 int err;
1140
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001141 if(from_cmdline == NULL)
1142 return 0;
1143 if(!strcmp(from_cmdline, from_cow))
1144 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145
1146 err = os_stat_file(from_cmdline, &buf1);
1147 if(err < 0){
1148 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001149 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 }
1151 err = os_stat_file(from_cow, &buf2);
1152 if(err < 0){
1153 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001154 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 }
1156 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001157 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158
1159 printk("Backing file mismatch - \"%s\" requested,\n"
1160 "\"%s\" specified in COW header of \"%s\"\n",
1161 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001162 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163}
1164
1165static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1166{
1167 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001168 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 int err;
1170
1171 err = os_file_modtime(file, &modtime);
1172 if(err < 0){
1173 printk("Failed to get modification time of backing file "
1174 "\"%s\", err = %d\n", file, -err);
1175 return(err);
1176 }
1177
1178 err = os_file_size(file, &actual);
1179 if(err < 0){
1180 printk("Failed to get size of backing file \"%s\", "
1181 "err = %d\n", file, -err);
1182 return(err);
1183 }
1184
1185 if(actual != size){
1186 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1187 * the typecast.*/
1188 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1189 "file\n", (unsigned long long) size, actual);
1190 return(-EINVAL);
1191 }
1192 if(modtime != mtime){
1193 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1194 "file\n", mtime, modtime);
1195 return(-EINVAL);
1196 }
1197 return(0);
1198}
1199
1200int read_cow_bitmap(int fd, void *buf, int offset, int len)
1201{
1202 int err;
1203
1204 err = os_seek_file(fd, offset);
1205 if(err < 0)
1206 return(err);
1207
1208 err = os_read_file(fd, buf, len);
1209 if(err < 0)
1210 return(err);
1211
1212 return(0);
1213}
1214
Jeff Dike6c29256c2006-03-27 01:14:37 -08001215int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 char **backing_file_out, int *bitmap_offset_out,
1217 unsigned long *bitmap_len_out, int *data_offset_out,
1218 int *create_cow_out)
1219{
1220 time_t mtime;
1221 unsigned long long size;
1222 __u32 version, align;
1223 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001224 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225
1226 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001227 if (fd < 0) {
1228 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 *create_cow_out = 1;
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001230 if (!openflags->w ||
1231 ((fd != -EROFS) && (fd != -EACCES)))
1232 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 openflags->w = 0;
1234 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001235 if (fd < 0)
1236 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 }
1238
Jeff Dike6c29256c2006-03-27 01:14:37 -08001239 if(shared)
1240 printk("Not locking \"%s\" on the host\n", file);
1241 else {
1242 err = os_lock_file(fd, openflags->w);
1243 if(err < 0){
1244 printk("Failed to lock '%s', err = %d\n", file, -err);
1245 goto out_close;
1246 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 }
1248
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001249 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001250 if(backing_file_out == NULL)
1251 return(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252
1253 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1254 &size, &sectorsize, &align, bitmap_offset_out);
1255 if(err && (*backing_file_out != NULL)){
1256 printk("Failed to read COW header from COW file \"%s\", "
1257 "errno = %d\n", file, -err);
1258 goto out_close;
1259 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001260 if(err)
1261 return(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001263 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001265 /* Allow switching only if no mismatch. */
1266 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 printk("Switching backing file to '%s'\n", *backing_file_out);
1268 err = write_cow_header(file, fd, *backing_file_out,
1269 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001270 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001272 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001274 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 *backing_file_out = backing_file;
1276 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001277 if (err)
1278 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 }
1280
1281 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1282 bitmap_len_out, data_offset_out);
1283
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001284 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 out_close:
1286 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001287 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288}
1289
1290int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1291 int sectorsize, int alignment, int *bitmap_offset_out,
1292 unsigned long *bitmap_len_out, int *data_offset_out)
1293{
1294 int err, fd;
1295
1296 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001297 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 if(fd < 0){
1299 err = fd;
1300 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1301 -err);
1302 goto out;
1303 }
1304
1305 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1306 bitmap_offset_out, bitmap_len_out,
1307 data_offset_out);
1308 if(!err)
1309 return(fd);
1310 os_close_file(fd);
1311 out:
1312 return(err);
1313}
1314
Jeff Dike91acb212005-10-10 23:10:32 -04001315static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316{
Jeff Dike91acb212005-10-10 23:10:32 -04001317 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318
Jeff Dike91acb212005-10-10 23:10:32 -04001319 if(req->cow_offset == -1)
1320 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321
Jeff Dike91acb212005-10-10 23:10:32 -04001322 n = os_seek_file(req->fds[1], req->cow_offset);
1323 if(n < 0){
1324 printk("do_io - bitmap lseek failed : err = %d\n", -n);
1325 return(1);
1326 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327
Jeff Dike91acb212005-10-10 23:10:32 -04001328 n = os_write_file(req->fds[1], &req->bitmap_words,
1329 sizeof(req->bitmap_words));
1330 if(n != sizeof(req->bitmap_words)){
1331 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1332 req->fds[1]);
1333 return(1);
1334 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335
Jeff Dike91acb212005-10-10 23:10:32 -04001336 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337}
Jeff Dike91acb212005-10-10 23:10:32 -04001338
1339void do_io(struct io_thread_req *req)
1340{
1341 char *buf;
1342 unsigned long len;
1343 int n, nsectors, start, end, bit;
1344 int err;
1345 __u64 off;
1346
1347 nsectors = req->length / req->sectorsize;
1348 start = 0;
1349 do {
1350 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1351 end = start;
1352 while((end < nsectors) &&
1353 (ubd_test_bit(end, (unsigned char *)
1354 &req->sector_mask) == bit))
1355 end++;
1356
1357 off = req->offset + req->offsets[bit] +
1358 start * req->sectorsize;
1359 len = (end - start) * req->sectorsize;
1360 buf = &req->buffer[start * req->sectorsize];
1361
1362 err = os_seek_file(req->fds[bit], off);
1363 if(err < 0){
1364 printk("do_io - lseek failed : err = %d\n", -err);
1365 req->error = 1;
1366 return;
1367 }
1368 if(req->op == UBD_READ){
1369 n = 0;
1370 do {
1371 buf = &buf[n];
1372 len -= n;
1373 n = os_read_file(req->fds[bit], buf, len);
1374 if (n < 0) {
1375 printk("do_io - read failed, err = %d "
1376 "fd = %d\n", -n, req->fds[bit]);
1377 req->error = 1;
1378 return;
1379 }
1380 } while((n < len) && (n != 0));
1381 if (n < len) memset(&buf[n], 0, len - n);
1382 } else {
1383 n = os_write_file(req->fds[bit], buf, len);
1384 if(n != len){
1385 printk("do_io - write failed err = %d "
1386 "fd = %d\n", -n, req->fds[bit]);
1387 req->error = 1;
1388 return;
1389 }
1390 }
1391
1392 start = end;
1393 } while(start < nsectors);
1394
1395 req->error = update_bitmap(req);
1396}
1397
1398/* Changed in start_io_thread, which is serialized by being called only
1399 * from ubd_init, which is an initcall.
1400 */
1401int kernel_fd = -1;
1402
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001403/* Only changed by the io thread. XXX: currently unused. */
1404static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001405
1406int io_thread(void *arg)
1407{
1408 struct io_thread_req req;
1409 int n;
1410
1411 ignore_sigwinch_sig();
1412 while(1){
1413 n = os_read_file(kernel_fd, &req, sizeof(req));
1414 if(n != sizeof(req)){
1415 if(n < 0)
1416 printk("io_thread - read failed, fd = %d, "
1417 "err = %d\n", kernel_fd, -n);
1418 else {
1419 printk("io_thread - short read, fd = %d, "
1420 "length = %d\n", kernel_fd, n);
1421 }
1422 continue;
1423 }
1424 io_count++;
1425 do_io(&req);
1426 n = os_write_file(kernel_fd, &req, sizeof(req));
1427 if(n != sizeof(req))
1428 printk("io_thread - write failed, fd = %d, err = %d\n",
1429 kernel_fd, -n);
1430 }
Jeff Dike91acb212005-10-10 23:10:32 -04001431
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001432 return 0;
1433}