blob: 5ef47b73ce99d3f0f1b4bfba6b088128a0e10746 [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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include "mem_user.h"
43#include "kern_util.h"
44#include "kern.h"
45#include "mconsole_kern.h"
46#include "init.h"
47#include "irq_user.h"
48#include "irq_kern.h"
49#include "ubd_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070050#include "os.h"
51#include "mem.h"
52#include "mem_kern.h"
53#include "cow.h"
54
Jeff Dike7b9014c2005-05-20 13:59:11 -070055enum ubd_req { UBD_READ, UBD_WRITE };
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57struct io_thread_req {
Jeff Dike62f96cb2007-02-10 01:44:16 -080058 struct request *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' Giarrussod7fb2c32006-10-30 22:07:07 -0800109static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111static int ubd_open(struct inode * inode, struct file * filp);
112static int ubd_release(struct inode * inode, struct file * file);
113static int ubd_ioctl(struct inode * inode, struct file * file,
114 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800115static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800117#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119static struct block_device_operations ubd_blops = {
120 .owner = THIS_MODULE,
121 .open = ubd_open,
122 .release = ubd_release,
123 .ioctl = ubd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800124 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125};
126
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127/* Protected by ubd_lock */
128static int fake_major = MAJOR_NR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129static struct gendisk *ubd_gendisk[MAX_DEV];
130static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800131
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132#ifdef CONFIG_BLK_DEV_UBD_SYNC
133#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
134 .cl = 1 })
135#else
136#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
137 .cl = 1 })
138#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139static struct openflags global_openflags = OPEN_FLAGS;
140
141struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800142 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800144 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 int fd;
146 unsigned long *bitmap;
147 unsigned long bitmap_len;
148 int bitmap_offset;
149 int data_offset;
150};
151
Jeff Dikea0044bd2007-05-06 14:51:36 -0700152#define MAX_SG 64
153
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154struct ubd {
Jeff Dikea0044bd2007-05-06 14:51:36 -0700155 struct list_head restart;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800156 /* name (and fd, below) of the file opened for writing, either the
157 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 char *file;
159 int count;
160 int fd;
161 __u64 size;
162 struct openflags boot_openflags;
163 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800164 unsigned shared:1;
165 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 struct cow cow;
167 struct platform_device pdev;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800168 struct request_queue *queue;
169 spinlock_t lock;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700170 struct scatterlist sg[MAX_SG];
171 struct request *request;
172 int start_sg, end_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173};
174
175#define DEFAULT_COW { \
176 .file = NULL, \
177 .fd = -1, \
178 .bitmap = NULL, \
179 .bitmap_offset = 0, \
180 .data_offset = 0, \
181}
182
183#define DEFAULT_UBD { \
184 .file = NULL, \
185 .count = 0, \
186 .fd = -1, \
187 .size = -1, \
188 .boot_openflags = OPEN_FLAGS, \
189 .openflags = OPEN_FLAGS, \
190 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800191 .shared = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 .cow = DEFAULT_COW, \
Jeff Dike62f96cb2007-02-10 01:44:16 -0800193 .lock = SPIN_LOCK_UNLOCKED, \
Jeff Dikea0044bd2007-05-06 14:51:36 -0700194 .request = NULL, \
195 .start_sg = 0, \
196 .end_sg = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197}
198
Jeff Dikeb8831a12007-02-10 01:44:17 -0800199/* Protected by ubd_lock */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800200struct ubd ubd_devs[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202/* Only changed by fake_ide_setup which is a setup */
203static int fake_ide = 0;
204static struct proc_dir_entry *proc_ide_root = NULL;
205static struct proc_dir_entry *proc_ide = NULL;
206
207static void make_proc_ide(void)
208{
209 proc_ide_root = proc_mkdir("ide", NULL);
210 proc_ide = proc_mkdir("ide0", proc_ide_root);
211}
212
213static int proc_ide_read_media(char *page, char **start, off_t off, int count,
214 int *eof, void *data)
215{
216 int len;
217
218 strcpy(page, "disk\n");
219 len = strlen("disk\n");
220 len -= off;
221 if (len < count){
222 *eof = 1;
223 if (len <= 0) return 0;
224 }
225 else len = count;
226 *start = page + off;
227 return len;
228}
229
230static void make_ide_entries(char *dev_name)
231{
232 struct proc_dir_entry *dir, *ent;
233 char name[64];
234
235 if(proc_ide_root == NULL) make_proc_ide();
236
237 dir = proc_mkdir(dev_name, proc_ide);
238 if(!dir) return;
239
240 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
241 if(!ent) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 ent->data = NULL;
243 ent->read_proc = proc_ide_read_media;
244 ent->write_proc = NULL;
245 sprintf(name,"ide0/%s", dev_name);
246 proc_symlink(dev_name, proc_ide_root, name);
247}
248
249static int fake_ide_setup(char *str)
250{
251 fake_ide = 1;
252 return(1);
253}
254
255__setup("fake_ide", fake_ide_setup);
256
257__uml_help(fake_ide_setup,
258"fake_ide\n"
259" Create ide0 entries that map onto ubd devices.\n\n"
260);
261
262static int parse_unit(char **ptr)
263{
264 char *str = *ptr, *end;
265 int n = -1;
266
267 if(isdigit(*str)) {
268 n = simple_strtoul(str, &end, 0);
269 if(end == str)
270 return(-1);
271 *ptr = end;
272 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800273 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 n = *str - 'a';
275 str++;
276 *ptr = str;
277 }
278 return(n);
279}
280
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800281/* If *index_out == -1 at exit, the passed option was a general one;
282 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
283 * should not be freed on exit.
284 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800285static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800287 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 struct openflags flags = global_openflags;
289 char *backing_file;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800290 int n, err = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
292 if(index_out) *index_out = -1;
293 n = *str;
294 if(n == '='){
295 char *end;
296 int major;
297
298 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 if(!strcmp(str, "sync")){
300 global_openflags = of_sync(global_openflags);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800301 goto out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 }
303
Jeff Dikef28169d2007-02-10 01:43:53 -0800304 err = -EINVAL;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800305 major = simple_strtoul(str, &end, 0);
306 if((*end != '\0') || (end == str)){
307 *error_out = "Didn't parse major number";
308 goto out1;
309 }
310
Jeff Dikef28169d2007-02-10 01:43:53 -0800311 mutex_lock(&ubd_lock);
312 if(fake_major != MAJOR_NR){
313 *error_out = "Can't assign a fake major twice";
314 goto out1;
315 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800316
Jeff Dikef28169d2007-02-10 01:43:53 -0800317 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319 printk(KERN_INFO "Setting extra ubd major number to %d\n",
320 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800321 err = 0;
322 out1:
323 mutex_unlock(&ubd_lock);
324 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 }
326
327 n = parse_unit(&str);
328 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800329 *error_out = "Couldn't parse device number";
330 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 }
332 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800333 *error_out = "Device number out of range";
334 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 }
336
Jeff Dikef28169d2007-02-10 01:43:53 -0800337 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800338 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800340 ubd_dev = &ubd_devs[n];
341 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800342 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 goto out;
344 }
345
346 if (index_out)
347 *index_out = n;
348
Jeff Dikef28169d2007-02-10 01:43:53 -0800349 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800350 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 switch (*str) {
352 case 'r':
353 flags.w = 0;
354 break;
355 case 's':
356 flags.s = 1;
357 break;
358 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800359 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800361 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800362 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800363 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 case '=':
365 str++;
366 goto break_loop;
367 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800368 *error_out = "Expected '=' or flag letter "
369 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 goto out;
371 }
372 str++;
373 }
374
Jeff Dikef28169d2007-02-10 01:43:53 -0800375 if (*str == '=')
376 *error_out = "Too many flags specified";
377 else
378 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 goto out;
380
381break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 backing_file = strchr(str, ',');
383
Jeff Dikef28169d2007-02-10 01:43:53 -0800384 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
Jeff Dikef28169d2007-02-10 01:43:53 -0800387 if(backing_file != NULL){
388 if(ubd_dev->no_cow){
389 *error_out = "Can't specify both 'd' and a cow file";
390 goto out;
391 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 else {
393 *backing_file = '\0';
394 backing_file++;
395 }
396 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800397 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800398 ubd_dev->file = str;
399 ubd_dev->cow.file = backing_file;
400 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800402 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800403 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404}
405
406static int ubd_setup(char *str)
407{
Jeff Dikef28169d2007-02-10 01:43:53 -0800408 char *error;
409 int err;
410
411 err = ubd_setup_common(str, NULL, &error);
412 if(err)
413 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
414 "%s\n", str, error);
415 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416}
417
418__setup("ubd", ubd_setup);
419__uml_help(ubd_setup,
420"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
421" This is used to associate a device with a file in the underlying\n"
422" filesystem. When specifying two filenames, the first one is the\n"
423" COW name and the second is the backing file name. As separator you can\n"
424" use either a ':' or a ',': the first one allows writing things like;\n"
425" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
426" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800427" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428" a COW file or a backing file. To override this detection, add the 'd'\n"
429" flag:\n"
430" ubd0d=BackingFile\n"
431" Usually, there is a filesystem in the file, but \n"
432" that's not required. Swap devices containing swap files can be\n"
433" specified like this. Also, a file which doesn't contain a\n"
434" filesystem can have its contents read in the virtual \n"
435" machine by running 'dd' on the device. <n> must be in the range\n"
436" 0 to 7. Appending an 'r' to the number will cause that device\n"
437" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
438" an 's' will cause data to be written to disk on the host immediately.\n\n"
439);
440
441static int udb_setup(char *str)
442{
443 printk("udb%s specified on command line is almost certainly a ubd -> "
444 "udb TYPO\n", str);
445 return(1);
446}
447
448__setup("udb", udb_setup);
449__uml_help(udb_setup,
450"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700451" This option is here solely to catch ubd -> udb typos, which can be\n"
452" to impossible to catch visually unless you specifically look for\n"
453" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454" in the boot output.\n\n"
455);
456
457static int fakehd_set = 0;
458static int fakehd(char *str)
459{
460 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
461 fakehd_set = 1;
462 return 1;
463}
464
465__setup("fakehd", fakehd);
466__uml_help(fakehd,
467"fakehd\n"
468" Change the ubd device name to \"hd\".\n\n"
469);
470
471static void do_ubd_request(request_queue_t * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400472
473/* Only changed by ubd_init, which is an initcall. */
474int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475
Jeff Dikea0044bd2007-05-06 14:51:36 -0700476static void ubd_end_request(struct request *req, int bytes, int uptodate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700478 if (!end_that_request_first(req, uptodate, bytes >> 9)) {
479 struct ubd *dev = req->rq_disk->private_data;
480 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481
Jeff Dikea0044bd2007-05-06 14:51:36 -0700482 add_disk_randomness(req->rq_disk);
483 spin_lock_irqsave(&dev->lock, flags);
484 end_that_request_last(req, uptodate);
485 spin_unlock_irqrestore(&dev->lock, flags);
Jeff Dike91acb212005-10-10 23:10:32 -0400486 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487}
488
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800489/* Callable only from interrupt context - otherwise you need to do
490 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700491static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700493 if(bytes < 0){
494 ubd_end_request(req, 0, 0);
495 return;
496 }
497 ubd_end_request(req, bytes, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498}
499
Jeff Dikea0044bd2007-05-06 14:51:36 -0700500static LIST_HEAD(restart);
501
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800502/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800503/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400504static void ubd_handler(void)
505{
506 struct io_thread_req req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800507 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700508 struct ubd *ubd;
509 struct list_head *list, *next_ele;
510 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400511 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
Jeff Dikea0044bd2007-05-06 14:51:36 -0700513 while(1){
514 n = os_read_file_k(thread_fd, &req, sizeof(req));
515 if(n != sizeof(req)){
516 if(n == -EAGAIN)
517 break;
518 printk(KERN_ERR "spurious interrupt in ubd_handler, "
519 "err = %d\n", -n);
520 return;
521 }
522
523 rq = req.req;
524 rq->nr_sectors -= req.length >> 9;
525 if(rq->nr_sectors == 0)
526 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike91acb212005-10-10 23:10:32 -0400527 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800528 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700529
530 list_for_each_safe(list, next_ele, &restart){
531 ubd = container_of(list, struct ubd, restart);
532 list_del_init(&ubd->restart);
533 spin_lock_irqsave(&ubd->lock, flags);
534 do_ubd_request(ubd->queue);
535 spin_unlock_irqrestore(&ubd->lock, flags);
536 }
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
Jeff Dike2e3f5252007-05-06 14:51:29 -0700639static void ubd_device_release(struct device *dev)
640{
641 struct ubd *ubd_dev = dev->driver_data;
642
643 blk_cleanup_queue(ubd_dev->queue);
644 *ubd_dev = ((struct ubd) DEFAULT_UBD);
645}
646
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800647static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800648 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649{
650 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
652 disk = alloc_disk(1 << UBD_SHIFT);
653 if(disk == NULL)
654 return(-ENOMEM);
655
656 disk->major = major;
657 disk->first_minor = unit << UBD_SHIFT;
658 disk->fops = &ubd_blops;
659 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700660 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700662 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
665 /* sysfs register (not for ide fake devices) */
666 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800667 ubd_devs[unit].pdev.id = unit;
668 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700669 ubd_devs[unit].pdev.dev.release = ubd_device_release;
670 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800671 platform_device_register(&ubd_devs[unit].pdev);
672 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 }
674
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800675 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800676 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 add_disk(disk);
678
679 *disk_out = disk;
680 return 0;
681}
682
683#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
684
Jeff Dikef28169d2007-02-10 01:43:53 -0800685static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800687 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800688 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800690 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700691 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800693 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800694 if(err < 0){
695 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700696 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800697 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800699 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
Jeff Dikea0044bd2007-05-06 14:51:36 -0700701 INIT_LIST_HEAD(&ubd_dev->restart);
702
Jeff Dike62f96cb2007-02-10 01:44:16 -0800703 err = -ENOMEM;
704 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
705 if (ubd_dev->queue == NULL) {
706 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700707 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800708 }
709 ubd_dev->queue->queuedata = ubd_dev;
710
Jeff Dikea0044bd2007-05-06 14:51:36 -0700711 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800712 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
713 if(err){
714 *error_out = "Failed to register device";
715 goto out_cleanup;
716 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800717
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800719 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800720 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
722 /* perhaps this should also be under the "if (fake_major)" above */
723 /* using the fake_disk->disk_name and also the fakehd_set name */
724 if (fake_ide)
725 make_ide_entries(ubd_gendisk[n]->disk_name);
726
Jeff Dikeec7cf782005-09-03 15:57:29 -0700727 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700728out:
729 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800730
731out_cleanup:
732 blk_cleanup_queue(ubd_dev->queue);
733 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734}
735
Jeff Dikef28169d2007-02-10 01:43:53 -0800736static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800738 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739
Jeff Dikef28169d2007-02-10 01:43:53 -0800740 /* This string is possibly broken up and stored, so it's only
741 * freed if ubd_setup_common fails, or if only general options
742 * were set.
743 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800744 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800745 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800746 *error_out = "Failed to allocate memory";
747 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800749
750 ret = ubd_setup_common(str, &n, error_out);
751 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800752 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800753
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800754 if (n == -1) {
755 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800756 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800757 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800759 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800760 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800761 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800762 ubd_devs[n].file = NULL;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800763 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800765out:
766 return ret;
767
768err_free:
769 kfree(str);
770 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771}
772
773static int ubd_get_config(char *name, char *str, int size, char **error_out)
774{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800775 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 int n, len = 0;
777
778 n = parse_unit(&name);
779 if((n >= MAX_DEV) || (n < 0)){
780 *error_out = "ubd_get_config : device number out of range";
781 return(-1);
782 }
783
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800784 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800785 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800787 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 CONFIG_CHUNK(str, size, len, "", 1);
789 goto out;
790 }
791
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800792 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800794 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800796 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 }
798 else CONFIG_CHUNK(str, size, len, "", 1);
799
800 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800801 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 return(len);
803}
804
Jeff Dike29d56cf2005-06-25 14:55:25 -0700805static int ubd_id(char **str, int *start_out, int *end_out)
806{
807 int n;
808
809 n = parse_unit(str);
810 *start_out = 0;
811 *end_out = MAX_DEV - 1;
812 return n;
813}
814
Jeff Dikef28169d2007-02-10 01:43:53 -0800815static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700817 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800818 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700819 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800821 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800823 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700824
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800825 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700826 goto out;
827
828 /* you cannot remove a open disk */
829 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800830 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700831 goto out;
832
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700833 ubd_gendisk[n] = NULL;
834 if(disk != NULL){
835 del_gendisk(disk);
836 put_disk(disk);
837 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
839 if(fake_gendisk[n] != NULL){
840 del_gendisk(fake_gendisk[n]);
841 put_disk(fake_gendisk[n]);
842 fake_gendisk[n] = NULL;
843 }
844
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700846 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700847out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800848 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700849 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850}
851
Jeff Dikef28169d2007-02-10 01:43:53 -0800852/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800853 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800854 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800856 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 .name = "ubd",
858 .config = ubd_config,
859 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700860 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 .remove = ubd_remove,
862};
863
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800864static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865{
866 mconsole_register_dev(&ubd_mc);
867 return 0;
868}
869
870__initcall(ubd_mc_init);
871
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800872static int __init ubd0_init(void)
873{
874 struct ubd *ubd_dev = &ubd_devs[0];
875
Jeff Dikeb8831a12007-02-10 01:44:17 -0800876 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800877 if(ubd_dev->file == NULL)
878 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -0800879 mutex_unlock(&ubd_lock);
880
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800881 return(0);
882}
883
884__initcall(ubd0_init);
885
Jeff Dikeb8831a12007-02-10 01:44:17 -0800886/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +0000887static struct platform_driver ubd_driver = {
888 .driver = {
889 .name = DRIVER_NAME,
890 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891};
892
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800893static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894{
Jeff Dikef28169d2007-02-10 01:43:53 -0800895 char *error;
896 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 if (register_blkdev(MAJOR_NR, "ubd"))
899 return -1;
900
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 if (fake_major != MAJOR_NR) {
902 char name[sizeof("ubd_nnn\0")];
903
904 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 if (register_blkdev(fake_major, "ubd"))
906 return -1;
907 }
Russell King3ae5eae2005-11-09 22:32:44 +0000908 platform_driver_register(&ubd_driver);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800909 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800910 for (i = 0; i < MAX_DEV; i++){
911 err = ubd_add(i, &error);
912 if(err)
913 printk(KERN_ERR "Failed to initialize ubd device %d :"
914 "%s\n", i, error);
915 }
Jeff Dikeb8831a12007-02-10 01:44:17 -0800916 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 return 0;
918}
919
920late_initcall(ubd_init);
921
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800922static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400923 unsigned long stack;
924 int err;
925
926 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
927 if(global_openflags.s){
928 printk(KERN_INFO "ubd: Synchronous mode\n");
929 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
930 * enough. So use anyway the io thread. */
931 }
932 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800933 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400934 &thread_fd);
935 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800936 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400937 "ubd : Failed to start I/O thread (errno = %d) - "
938 "falling back to synchronous I/O\n", -io_pid);
939 io_pid = -1;
940 return(0);
941 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800942 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800943 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400944 if(err != 0)
945 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800946 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400947}
948
949device_initcall(ubd_driver_init);
950
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951static int ubd_open(struct inode *inode, struct file *filp)
952{
953 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800954 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 int err = 0;
956
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800957 if(ubd_dev->count == 0){
958 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 if(err){
960 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800961 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 goto out;
963 }
964 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800965 ubd_dev->count++;
966 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700967
968 /* This should no more be needed. And it didn't work anyway to exclude
969 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800970 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800971 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700973 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 out:
975 return(err);
976}
977
978static int ubd_release(struct inode * inode, struct file * file)
979{
980 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800981 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800983 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800984 ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 return(0);
986}
987
Jeff Dike91acb212005-10-10 23:10:32 -0400988static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
989 __u64 *cow_offset, unsigned long *bitmap,
990 __u64 bitmap_offset, unsigned long *bitmap_words,
991 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992{
Jeff Dike91acb212005-10-10 23:10:32 -0400993 __u64 sector = io_offset >> 9;
994 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
Jeff Dike91acb212005-10-10 23:10:32 -0400996 for(i = 0; i < length >> 9; i++){
997 if(cow_mask != NULL)
998 ubd_set_bit(i, (unsigned char *) cow_mask);
999 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1000 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001
Jeff Dike91acb212005-10-10 23:10:32 -04001002 update_bitmap = 1;
1003 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1004 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
Jeff Dike91acb212005-10-10 23:10:32 -04001006 if(!update_bitmap)
1007 return;
1008
1009 *cow_offset = sector / (sizeof(unsigned long) * 8);
1010
1011 /* This takes care of the case where we're exactly at the end of the
1012 * device, and *cow_offset + 1 is off the end. So, just back it up
1013 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1014 * for the original diagnosis.
1015 */
1016 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1017 sizeof(unsigned long) - 1))
1018 (*cow_offset)--;
1019
1020 bitmap_words[0] = bitmap[*cow_offset];
1021 bitmap_words[1] = bitmap[*cow_offset + 1];
1022
1023 *cow_offset *= sizeof(unsigned long);
1024 *cow_offset += bitmap_offset;
1025}
1026
1027static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1028 __u64 bitmap_offset, __u64 bitmap_len)
1029{
1030 __u64 sector = req->offset >> 9;
1031 int i;
1032
1033 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1034 panic("Operation too long");
1035
1036 if(req->op == UBD_READ) {
1037 for(i = 0; i < req->length >> 9; i++){
1038 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001039 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001040 &req->sector_mask);
1041 }
1042 }
1043 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1044 &req->cow_offset, bitmap, bitmap_offset,
1045 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046}
1047
Jeff Dike62f96cb2007-02-10 01:44:16 -08001048/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001049static void prepare_request(struct request *req, struct io_thread_req *io_req,
1050 unsigned long long offset, int page_offset,
1051 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052{
1053 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001054 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001055
Jeff Dike62f96cb2007-02-10 01:44:16 -08001056 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001057 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1058 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001059 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001060 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 io_req->offset = offset;
1062 io_req->length = len;
1063 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001064 io_req->sector_mask = 0;
1065
1066 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001068 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001069 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 io_req->sectorsize = 1 << 9;
1071
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001072 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001073 cowify_req(io_req, ubd_dev->cow.bitmap,
1074 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001075
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076}
1077
Jeff Dike62f96cb2007-02-10 01:44:16 -08001078/* Called with dev->lock held */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079static void do_ubd_request(request_queue_t *q)
1080{
1081 struct io_thread_req io_req;
1082 struct request *req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001083 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084
Jeff Dikea0044bd2007-05-06 14:51:36 -07001085 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001086 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001087 if(dev->end_sg == 0){
1088 struct request *req = elv_next_request(q);
1089 if(req == NULL)
1090 return;
1091
1092 dev->request = req;
1093 blkdev_dequeue_request(req);
1094 dev->start_sg = 0;
1095 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001096 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001097
1098 req = dev->request;
1099 while(dev->start_sg < dev->end_sg){
1100 struct scatterlist *sg = &dev->sg[dev->start_sg];
1101
1102 prepare_request(req, &io_req,
1103 (unsigned long long) req->sector << 9,
1104 sg->offset, sg->length, sg->page);
1105
1106 n = os_write_file_k(thread_fd, (char *) &io_req,
1107 sizeof(io_req));
1108 if(n != sizeof(io_req)){
1109 if(n != -EAGAIN)
1110 printk("write to io thread failed, "
1111 "errno = %d\n", -n);
1112 else if(list_empty(&dev->restart))
1113 list_add(&dev->restart, &restart);
1114 return;
1115 }
1116
1117 req->sector += sg->length >> 9;
1118 dev->start_sg++;
1119 }
1120 dev->end_sg = 0;
1121 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001122 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123}
1124
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001125static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1126{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001127 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001128
1129 geo->heads = 128;
1130 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001131 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001132 return 0;
1133}
1134
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135static int ubd_ioctl(struct inode * inode, struct file * file,
1136 unsigned int cmd, unsigned long arg)
1137{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001138 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 struct hd_driveid ubd_id = {
1140 .cyls = 0,
1141 .heads = 128,
1142 .sectors = 32,
1143 };
1144
1145 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001148 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1150 sizeof(ubd_id)))
1151 return(-EFAULT);
1152 return(0);
Jeff Dikeb8831a12007-02-10 01:44:17 -08001153
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 case CDROMVOLREAD:
1155 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
1156 return(-EFAULT);
1157 volume.channel0 = 255;
1158 volume.channel1 = 255;
1159 volume.channel2 = 255;
1160 volume.channel3 = 255;
1161 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
1162 return(-EFAULT);
1163 return(0);
1164 }
1165 return(-EINVAL);
1166}
1167
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001168static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169{
1170 struct uml_stat buf1, buf2;
1171 int err;
1172
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001173 if(from_cmdline == NULL)
1174 return 0;
1175 if(!strcmp(from_cmdline, from_cow))
1176 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177
1178 err = os_stat_file(from_cmdline, &buf1);
1179 if(err < 0){
1180 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001181 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 }
1183 err = os_stat_file(from_cow, &buf2);
1184 if(err < 0){
1185 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001186 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 }
1188 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001189 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190
1191 printk("Backing file mismatch - \"%s\" requested,\n"
1192 "\"%s\" specified in COW header of \"%s\"\n",
1193 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001194 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195}
1196
1197static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1198{
1199 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001200 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 int err;
1202
1203 err = os_file_modtime(file, &modtime);
1204 if(err < 0){
1205 printk("Failed to get modification time of backing file "
1206 "\"%s\", err = %d\n", file, -err);
1207 return(err);
1208 }
1209
1210 err = os_file_size(file, &actual);
1211 if(err < 0){
1212 printk("Failed to get size of backing file \"%s\", "
1213 "err = %d\n", file, -err);
1214 return(err);
1215 }
1216
1217 if(actual != size){
1218 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1219 * the typecast.*/
1220 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1221 "file\n", (unsigned long long) size, actual);
1222 return(-EINVAL);
1223 }
1224 if(modtime != mtime){
1225 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1226 "file\n", mtime, modtime);
1227 return(-EINVAL);
1228 }
1229 return(0);
1230}
1231
1232int read_cow_bitmap(int fd, void *buf, int offset, int len)
1233{
1234 int err;
1235
1236 err = os_seek_file(fd, offset);
1237 if(err < 0)
1238 return(err);
1239
1240 err = os_read_file(fd, buf, len);
1241 if(err < 0)
1242 return(err);
1243
1244 return(0);
1245}
1246
Jeff Dike6c29256c2006-03-27 01:14:37 -08001247int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 char **backing_file_out, int *bitmap_offset_out,
1249 unsigned long *bitmap_len_out, int *data_offset_out,
1250 int *create_cow_out)
1251{
1252 time_t mtime;
1253 unsigned long long size;
1254 __u32 version, align;
1255 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001256 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257
1258 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001259 if (fd < 0) {
1260 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 *create_cow_out = 1;
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001262 if (!openflags->w ||
1263 ((fd != -EROFS) && (fd != -EACCES)))
1264 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 openflags->w = 0;
1266 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001267 if (fd < 0)
1268 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 }
1270
Jeff Dike6c29256c2006-03-27 01:14:37 -08001271 if(shared)
1272 printk("Not locking \"%s\" on the host\n", file);
1273 else {
1274 err = os_lock_file(fd, openflags->w);
1275 if(err < 0){
1276 printk("Failed to lock '%s', err = %d\n", file, -err);
1277 goto out_close;
1278 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 }
1280
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001281 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001282 if(backing_file_out == NULL)
1283 return(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
1285 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1286 &size, &sectorsize, &align, bitmap_offset_out);
1287 if(err && (*backing_file_out != NULL)){
1288 printk("Failed to read COW header from COW file \"%s\", "
1289 "errno = %d\n", file, -err);
1290 goto out_close;
1291 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001292 if(err)
1293 return(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001295 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001297 /* Allow switching only if no mismatch. */
1298 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 printk("Switching backing file to '%s'\n", *backing_file_out);
1300 err = write_cow_header(file, fd, *backing_file_out,
1301 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001302 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001304 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001306 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 *backing_file_out = backing_file;
1308 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001309 if (err)
1310 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 }
1312
1313 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1314 bitmap_len_out, data_offset_out);
1315
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001316 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 out_close:
1318 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001319 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320}
1321
1322int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1323 int sectorsize, int alignment, int *bitmap_offset_out,
1324 unsigned long *bitmap_len_out, int *data_offset_out)
1325{
1326 int err, fd;
1327
1328 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001329 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 if(fd < 0){
1331 err = fd;
1332 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1333 -err);
1334 goto out;
1335 }
1336
1337 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1338 bitmap_offset_out, bitmap_len_out,
1339 data_offset_out);
1340 if(!err)
1341 return(fd);
1342 os_close_file(fd);
1343 out:
1344 return(err);
1345}
1346
Jeff Dike91acb212005-10-10 23:10:32 -04001347static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348{
Jeff Dike91acb212005-10-10 23:10:32 -04001349 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350
Jeff Dike91acb212005-10-10 23:10:32 -04001351 if(req->cow_offset == -1)
1352 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353
Jeff Dike91acb212005-10-10 23:10:32 -04001354 n = os_seek_file(req->fds[1], req->cow_offset);
1355 if(n < 0){
1356 printk("do_io - bitmap lseek failed : err = %d\n", -n);
1357 return(1);
1358 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359
Jeff Dike3d564042007-05-06 14:51:32 -07001360 n = os_write_file_k(req->fds[1], &req->bitmap_words,
1361 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001362 if(n != sizeof(req->bitmap_words)){
1363 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1364 req->fds[1]);
1365 return(1);
1366 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Jeff Dike91acb212005-10-10 23:10:32 -04001368 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369}
Jeff Dike91acb212005-10-10 23:10:32 -04001370
1371void do_io(struct io_thread_req *req)
1372{
1373 char *buf;
1374 unsigned long len;
1375 int n, nsectors, start, end, bit;
1376 int err;
1377 __u64 off;
1378
1379 nsectors = req->length / req->sectorsize;
1380 start = 0;
1381 do {
1382 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1383 end = start;
1384 while((end < nsectors) &&
1385 (ubd_test_bit(end, (unsigned char *)
1386 &req->sector_mask) == bit))
1387 end++;
1388
1389 off = req->offset + req->offsets[bit] +
1390 start * req->sectorsize;
1391 len = (end - start) * req->sectorsize;
1392 buf = &req->buffer[start * req->sectorsize];
1393
1394 err = os_seek_file(req->fds[bit], off);
1395 if(err < 0){
1396 printk("do_io - lseek failed : err = %d\n", -err);
1397 req->error = 1;
1398 return;
1399 }
1400 if(req->op == UBD_READ){
1401 n = 0;
1402 do {
1403 buf = &buf[n];
1404 len -= n;
Jeff Dike3d564042007-05-06 14:51:32 -07001405 n = os_read_file_k(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001406 if (n < 0) {
1407 printk("do_io - read failed, err = %d "
1408 "fd = %d\n", -n, req->fds[bit]);
1409 req->error = 1;
1410 return;
1411 }
1412 } while((n < len) && (n != 0));
1413 if (n < len) memset(&buf[n], 0, len - n);
1414 } else {
Jeff Dike3d564042007-05-06 14:51:32 -07001415 n = os_write_file_k(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001416 if(n != len){
1417 printk("do_io - write failed err = %d "
1418 "fd = %d\n", -n, req->fds[bit]);
1419 req->error = 1;
1420 return;
1421 }
1422 }
1423
1424 start = end;
1425 } while(start < nsectors);
1426
1427 req->error = update_bitmap(req);
1428}
1429
1430/* Changed in start_io_thread, which is serialized by being called only
1431 * from ubd_init, which is an initcall.
1432 */
1433int kernel_fd = -1;
1434
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001435/* Only changed by the io thread. XXX: currently unused. */
1436static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001437
1438int io_thread(void *arg)
1439{
1440 struct io_thread_req req;
1441 int n;
1442
1443 ignore_sigwinch_sig();
1444 while(1){
Jeff Dike3d564042007-05-06 14:51:32 -07001445 n = os_read_file_k(kernel_fd, &req, sizeof(req));
Jeff Dike91acb212005-10-10 23:10:32 -04001446 if(n != sizeof(req)){
1447 if(n < 0)
1448 printk("io_thread - read failed, fd = %d, "
1449 "err = %d\n", kernel_fd, -n);
1450 else {
1451 printk("io_thread - short read, fd = %d, "
1452 "length = %d\n", kernel_fd, n);
1453 }
1454 continue;
1455 }
1456 io_count++;
1457 do_io(&req);
Jeff Dike3d564042007-05-06 14:51:32 -07001458 n = os_write_file_k(kernel_fd, &req, sizeof(req));
Jeff Dike91acb212005-10-10 23:10:32 -04001459 if(n != sizeof(req))
1460 printk("io_thread - write failed, fd = %d, err = %d\n",
1461 kernel_fd, -n);
1462 }
Jeff Dike91acb212005-10-10 23:10:32 -04001463
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001464 return 0;
1465}