blob: c1d40fb738e6b55b666baac46803a8a8df1934a5 [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 = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800831 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 .name = "ubd",
833 .config = ubd_config,
834 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700835 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 .remove = ubd_remove,
837};
838
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800839static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840{
841 mconsole_register_dev(&ubd_mc);
842 return 0;
843}
844
845__initcall(ubd_mc_init);
846
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800847static int __init ubd0_init(void)
848{
849 struct ubd *ubd_dev = &ubd_devs[0];
850
851 if(ubd_dev->file == NULL)
852 ubd_dev->file = "root_fs";
853 return(0);
854}
855
856__initcall(ubd0_init);
857
Russell King3ae5eae2005-11-09 22:32:44 +0000858static struct platform_driver ubd_driver = {
859 .driver = {
860 .name = DRIVER_NAME,
861 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862};
863
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800864static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865{
Jeff Dikef28169d2007-02-10 01:43:53 -0800866 char *error;
867 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 if (register_blkdev(MAJOR_NR, "ubd"))
870 return -1;
871
872 ubd_queue = blk_init_queue(do_ubd_request, &ubd_io_lock);
873 if (!ubd_queue) {
874 unregister_blkdev(MAJOR_NR, "ubd");
875 return -1;
876 }
877
878 if (fake_major != MAJOR_NR) {
879 char name[sizeof("ubd_nnn\0")];
880
881 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 if (register_blkdev(fake_major, "ubd"))
883 return -1;
884 }
Russell King3ae5eae2005-11-09 22:32:44 +0000885 platform_driver_register(&ubd_driver);
Jeff Dikef28169d2007-02-10 01:43:53 -0800886 for (i = 0; i < MAX_DEV; i++){
887 err = ubd_add(i, &error);
888 if(err)
889 printk(KERN_ERR "Failed to initialize ubd device %d :"
890 "%s\n", i, error);
891 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 return 0;
893}
894
895late_initcall(ubd_init);
896
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800897static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400898 unsigned long stack;
899 int err;
900
901 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
902 if(global_openflags.s){
903 printk(KERN_INFO "ubd: Synchronous mode\n");
904 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
905 * enough. So use anyway the io thread. */
906 }
907 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800908 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400909 &thread_fd);
910 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800911 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400912 "ubd : Failed to start I/O thread (errno = %d) - "
913 "falling back to synchronous I/O\n", -io_pid);
914 io_pid = -1;
915 return(0);
916 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800917 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800918 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400919 if(err != 0)
920 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800921 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400922}
923
924device_initcall(ubd_driver_init);
925
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926static int ubd_open(struct inode *inode, struct file *filp)
927{
928 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800929 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 int err = 0;
931
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800932 if(ubd_dev->count == 0){
933 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 if(err){
935 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800936 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 goto out;
938 }
939 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800940 ubd_dev->count++;
941 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700942
943 /* This should no more be needed. And it didn't work anyway to exclude
944 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800945 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800946 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700948 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 out:
950 return(err);
951}
952
953static int ubd_release(struct inode * inode, struct file * file)
954{
955 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800956 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800958 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800959 ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 return(0);
961}
962
Jeff Dike91acb212005-10-10 23:10:32 -0400963static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
964 __u64 *cow_offset, unsigned long *bitmap,
965 __u64 bitmap_offset, unsigned long *bitmap_words,
966 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967{
Jeff Dike91acb212005-10-10 23:10:32 -0400968 __u64 sector = io_offset >> 9;
969 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
Jeff Dike91acb212005-10-10 23:10:32 -0400971 for(i = 0; i < length >> 9; i++){
972 if(cow_mask != NULL)
973 ubd_set_bit(i, (unsigned char *) cow_mask);
974 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
975 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
Jeff Dike91acb212005-10-10 23:10:32 -0400977 update_bitmap = 1;
978 ubd_set_bit(sector + i, (unsigned char *) bitmap);
979 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980
Jeff Dike91acb212005-10-10 23:10:32 -0400981 if(!update_bitmap)
982 return;
983
984 *cow_offset = sector / (sizeof(unsigned long) * 8);
985
986 /* This takes care of the case where we're exactly at the end of the
987 * device, and *cow_offset + 1 is off the end. So, just back it up
988 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
989 * for the original diagnosis.
990 */
991 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
992 sizeof(unsigned long) - 1))
993 (*cow_offset)--;
994
995 bitmap_words[0] = bitmap[*cow_offset];
996 bitmap_words[1] = bitmap[*cow_offset + 1];
997
998 *cow_offset *= sizeof(unsigned long);
999 *cow_offset += bitmap_offset;
1000}
1001
1002static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1003 __u64 bitmap_offset, __u64 bitmap_len)
1004{
1005 __u64 sector = req->offset >> 9;
1006 int i;
1007
1008 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1009 panic("Operation too long");
1010
1011 if(req->op == UBD_READ) {
1012 for(i = 0; i < req->length >> 9; i++){
1013 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001014 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001015 &req->sector_mask);
1016 }
1017 }
1018 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1019 &req->cow_offset, bitmap, bitmap_offset,
1020 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021}
1022
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023/* Called with ubd_io_lock held */
Jeff Dike91acb212005-10-10 23:10:32 -04001024static int prepare_request(struct request *req, struct io_thread_req *io_req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025{
1026 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001027 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001028 __u64 offset;
1029 int len;
1030
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -07001031 /* This should be impossible now */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001032 if((rq_data_dir(req) == WRITE) && !ubd_dev->openflags.w){
Jeff Dike6c29256c2006-03-27 01:14:37 -08001033 printk("Write attempted on readonly ubd device %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 disk->disk_name);
Jeff Dike91acb212005-10-10 23:10:32 -04001035 end_request(req, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 return(1);
1037 }
1038
Jeff Dike91acb212005-10-10 23:10:32 -04001039 offset = ((__u64) req->sector) << 9;
1040 len = req->current_nr_sectors << 9;
1041
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001042 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd : ubd_dev->fd;
1043 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001044 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 io_req->offset = offset;
1046 io_req->length = len;
1047 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001048 io_req->sector_mask = 0;
1049
1050 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001052 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dike91acb212005-10-10 23:10:32 -04001053 io_req->buffer = req->buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 io_req->sectorsize = 1 << 9;
1055
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001056 if(ubd_dev->cow.file != NULL)
1057 cowify_req(io_req, ubd_dev->cow.bitmap, ubd_dev->cow.bitmap_offset,
1058 ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001059
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 return(0);
1061}
1062
1063/* Called with ubd_io_lock held */
1064static void do_ubd_request(request_queue_t *q)
1065{
1066 struct io_thread_req io_req;
1067 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001068 int err, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069
Jeff Dike91acb212005-10-10 23:10:32 -04001070 if(thread_fd == -1){
1071 while((req = elv_next_request(q)) != NULL){
1072 err = prepare_request(req, &io_req);
1073 if(!err){
1074 do_io(&io_req);
1075 __ubd_finish(req, io_req.error);
1076 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 }
1078 }
Jeff Dike91acb212005-10-10 23:10:32 -04001079 else {
1080 if(do_ubd || (req = elv_next_request(q)) == NULL)
1081 return;
1082 err = prepare_request(req, &io_req);
1083 if(!err){
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -08001084 do_ubd = 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001085 n = os_write_file(thread_fd, (char *) &io_req,
1086 sizeof(io_req));
1087 if(n != sizeof(io_req))
1088 printk("write to io thread failed, "
1089 "errno = %d\n", -n);
1090 }
1091 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092}
1093
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001094static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1095{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001096 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001097
1098 geo->heads = 128;
1099 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001100 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001101 return 0;
1102}
1103
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104static int ubd_ioctl(struct inode * inode, struct file * file,
1105 unsigned int cmd, unsigned long arg)
1106{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001107 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 struct hd_driveid ubd_id = {
1109 .cyls = 0,
1110 .heads = 128,
1111 .sectors = 32,
1112 };
1113
1114 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001117 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1119 sizeof(ubd_id)))
1120 return(-EFAULT);
1121 return(0);
1122
1123 case CDROMVOLREAD:
1124 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
1125 return(-EFAULT);
1126 volume.channel0 = 255;
1127 volume.channel1 = 255;
1128 volume.channel2 = 255;
1129 volume.channel3 = 255;
1130 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
1131 return(-EFAULT);
1132 return(0);
1133 }
1134 return(-EINVAL);
1135}
1136
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001137static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138{
1139 struct uml_stat buf1, buf2;
1140 int err;
1141
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001142 if(from_cmdline == NULL)
1143 return 0;
1144 if(!strcmp(from_cmdline, from_cow))
1145 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146
1147 err = os_stat_file(from_cmdline, &buf1);
1148 if(err < 0){
1149 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001150 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 }
1152 err = os_stat_file(from_cow, &buf2);
1153 if(err < 0){
1154 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001155 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 }
1157 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001158 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159
1160 printk("Backing file mismatch - \"%s\" requested,\n"
1161 "\"%s\" specified in COW header of \"%s\"\n",
1162 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001163 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164}
1165
1166static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1167{
1168 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001169 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 int err;
1171
1172 err = os_file_modtime(file, &modtime);
1173 if(err < 0){
1174 printk("Failed to get modification time of backing file "
1175 "\"%s\", err = %d\n", file, -err);
1176 return(err);
1177 }
1178
1179 err = os_file_size(file, &actual);
1180 if(err < 0){
1181 printk("Failed to get size of backing file \"%s\", "
1182 "err = %d\n", file, -err);
1183 return(err);
1184 }
1185
1186 if(actual != size){
1187 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1188 * the typecast.*/
1189 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1190 "file\n", (unsigned long long) size, actual);
1191 return(-EINVAL);
1192 }
1193 if(modtime != mtime){
1194 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1195 "file\n", mtime, modtime);
1196 return(-EINVAL);
1197 }
1198 return(0);
1199}
1200
1201int read_cow_bitmap(int fd, void *buf, int offset, int len)
1202{
1203 int err;
1204
1205 err = os_seek_file(fd, offset);
1206 if(err < 0)
1207 return(err);
1208
1209 err = os_read_file(fd, buf, len);
1210 if(err < 0)
1211 return(err);
1212
1213 return(0);
1214}
1215
Jeff Dike6c29256c2006-03-27 01:14:37 -08001216int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 char **backing_file_out, int *bitmap_offset_out,
1218 unsigned long *bitmap_len_out, int *data_offset_out,
1219 int *create_cow_out)
1220{
1221 time_t mtime;
1222 unsigned long long size;
1223 __u32 version, align;
1224 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001225 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226
1227 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001228 if (fd < 0) {
1229 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 *create_cow_out = 1;
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001231 if (!openflags->w ||
1232 ((fd != -EROFS) && (fd != -EACCES)))
1233 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 openflags->w = 0;
1235 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001236 if (fd < 0)
1237 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 }
1239
Jeff Dike6c29256c2006-03-27 01:14:37 -08001240 if(shared)
1241 printk("Not locking \"%s\" on the host\n", file);
1242 else {
1243 err = os_lock_file(fd, openflags->w);
1244 if(err < 0){
1245 printk("Failed to lock '%s', err = %d\n", file, -err);
1246 goto out_close;
1247 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 }
1249
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001250 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001251 if(backing_file_out == NULL)
1252 return(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
1254 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1255 &size, &sectorsize, &align, bitmap_offset_out);
1256 if(err && (*backing_file_out != NULL)){
1257 printk("Failed to read COW header from COW file \"%s\", "
1258 "errno = %d\n", file, -err);
1259 goto out_close;
1260 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001261 if(err)
1262 return(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001264 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001266 /* Allow switching only if no mismatch. */
1267 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 printk("Switching backing file to '%s'\n", *backing_file_out);
1269 err = write_cow_header(file, fd, *backing_file_out,
1270 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001271 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001273 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001275 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 *backing_file_out = backing_file;
1277 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001278 if (err)
1279 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 }
1281
1282 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1283 bitmap_len_out, data_offset_out);
1284
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001285 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 out_close:
1287 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001288 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289}
1290
1291int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1292 int sectorsize, int alignment, int *bitmap_offset_out,
1293 unsigned long *bitmap_len_out, int *data_offset_out)
1294{
1295 int err, fd;
1296
1297 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001298 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 if(fd < 0){
1300 err = fd;
1301 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1302 -err);
1303 goto out;
1304 }
1305
1306 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1307 bitmap_offset_out, bitmap_len_out,
1308 data_offset_out);
1309 if(!err)
1310 return(fd);
1311 os_close_file(fd);
1312 out:
1313 return(err);
1314}
1315
Jeff Dike91acb212005-10-10 23:10:32 -04001316static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317{
Jeff Dike91acb212005-10-10 23:10:32 -04001318 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319
Jeff Dike91acb212005-10-10 23:10:32 -04001320 if(req->cow_offset == -1)
1321 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
Jeff Dike91acb212005-10-10 23:10:32 -04001323 n = os_seek_file(req->fds[1], req->cow_offset);
1324 if(n < 0){
1325 printk("do_io - bitmap lseek failed : err = %d\n", -n);
1326 return(1);
1327 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
Jeff Dike91acb212005-10-10 23:10:32 -04001329 n = os_write_file(req->fds[1], &req->bitmap_words,
1330 sizeof(req->bitmap_words));
1331 if(n != sizeof(req->bitmap_words)){
1332 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1333 req->fds[1]);
1334 return(1);
1335 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336
Jeff Dike91acb212005-10-10 23:10:32 -04001337 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338}
Jeff Dike91acb212005-10-10 23:10:32 -04001339
1340void do_io(struct io_thread_req *req)
1341{
1342 char *buf;
1343 unsigned long len;
1344 int n, nsectors, start, end, bit;
1345 int err;
1346 __u64 off;
1347
1348 nsectors = req->length / req->sectorsize;
1349 start = 0;
1350 do {
1351 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1352 end = start;
1353 while((end < nsectors) &&
1354 (ubd_test_bit(end, (unsigned char *)
1355 &req->sector_mask) == bit))
1356 end++;
1357
1358 off = req->offset + req->offsets[bit] +
1359 start * req->sectorsize;
1360 len = (end - start) * req->sectorsize;
1361 buf = &req->buffer[start * req->sectorsize];
1362
1363 err = os_seek_file(req->fds[bit], off);
1364 if(err < 0){
1365 printk("do_io - lseek failed : err = %d\n", -err);
1366 req->error = 1;
1367 return;
1368 }
1369 if(req->op == UBD_READ){
1370 n = 0;
1371 do {
1372 buf = &buf[n];
1373 len -= n;
1374 n = os_read_file(req->fds[bit], buf, len);
1375 if (n < 0) {
1376 printk("do_io - read failed, err = %d "
1377 "fd = %d\n", -n, req->fds[bit]);
1378 req->error = 1;
1379 return;
1380 }
1381 } while((n < len) && (n != 0));
1382 if (n < len) memset(&buf[n], 0, len - n);
1383 } else {
1384 n = os_write_file(req->fds[bit], buf, len);
1385 if(n != len){
1386 printk("do_io - write failed err = %d "
1387 "fd = %d\n", -n, req->fds[bit]);
1388 req->error = 1;
1389 return;
1390 }
1391 }
1392
1393 start = end;
1394 } while(start < nsectors);
1395
1396 req->error = update_bitmap(req);
1397}
1398
1399/* Changed in start_io_thread, which is serialized by being called only
1400 * from ubd_init, which is an initcall.
1401 */
1402int kernel_fd = -1;
1403
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001404/* Only changed by the io thread. XXX: currently unused. */
1405static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001406
1407int io_thread(void *arg)
1408{
1409 struct io_thread_req req;
1410 int n;
1411
1412 ignore_sigwinch_sig();
1413 while(1){
1414 n = os_read_file(kernel_fd, &req, sizeof(req));
1415 if(n != sizeof(req)){
1416 if(n < 0)
1417 printk("io_thread - read failed, fd = %d, "
1418 "err = %d\n", kernel_fd, -n);
1419 else {
1420 printk("io_thread - short read, fd = %d, "
1421 "length = %d\n", kernel_fd, n);
1422 }
1423 continue;
1424 }
1425 io_count++;
1426 do_io(&req);
1427 n = os_write_file(kernel_fd, &req, sizeof(req));
1428 if(n != sizeof(req))
1429 printk("io_thread - write failed, fd = %d, err = %d\n",
1430 kernel_fd, -n);
1431 }
Jeff Dike91acb212005-10-10 23:10:32 -04001432
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001433 return 0;
1434}