blob: 83189e188c3f21f6f3eabc011f6dfb91cd38b74e [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
152struct ubd {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800153 /* name (and fd, below) of the file opened for writing, either the
154 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 char *file;
156 int count;
157 int fd;
158 __u64 size;
159 struct openflags boot_openflags;
160 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800161 unsigned shared:1;
162 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 struct cow cow;
164 struct platform_device pdev;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800165 struct request_queue *queue;
166 spinlock_t lock;
Jeff Dike2a9529a2007-03-29 01:20:27 -0700167 int active;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168};
169
170#define DEFAULT_COW { \
171 .file = NULL, \
172 .fd = -1, \
173 .bitmap = NULL, \
174 .bitmap_offset = 0, \
175 .data_offset = 0, \
176}
177
178#define DEFAULT_UBD { \
179 .file = NULL, \
180 .count = 0, \
181 .fd = -1, \
182 .size = -1, \
183 .boot_openflags = OPEN_FLAGS, \
184 .openflags = OPEN_FLAGS, \
185 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800186 .shared = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 .cow = DEFAULT_COW, \
Jeff Dike62f96cb2007-02-10 01:44:16 -0800188 .lock = SPIN_LOCK_UNLOCKED, \
Jeff Dike2a9529a2007-03-29 01:20:27 -0700189 .active = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190}
191
Jeff Dikeb8831a12007-02-10 01:44:17 -0800192/* Protected by ubd_lock */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800193struct ubd ubd_devs[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195/* Only changed by fake_ide_setup which is a setup */
196static int fake_ide = 0;
197static struct proc_dir_entry *proc_ide_root = NULL;
198static struct proc_dir_entry *proc_ide = NULL;
199
200static void make_proc_ide(void)
201{
202 proc_ide_root = proc_mkdir("ide", NULL);
203 proc_ide = proc_mkdir("ide0", proc_ide_root);
204}
205
206static int proc_ide_read_media(char *page, char **start, off_t off, int count,
207 int *eof, void *data)
208{
209 int len;
210
211 strcpy(page, "disk\n");
212 len = strlen("disk\n");
213 len -= off;
214 if (len < count){
215 *eof = 1;
216 if (len <= 0) return 0;
217 }
218 else len = count;
219 *start = page + off;
220 return len;
221}
222
223static void make_ide_entries(char *dev_name)
224{
225 struct proc_dir_entry *dir, *ent;
226 char name[64];
227
228 if(proc_ide_root == NULL) make_proc_ide();
229
230 dir = proc_mkdir(dev_name, proc_ide);
231 if(!dir) return;
232
233 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
234 if(!ent) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 ent->data = NULL;
236 ent->read_proc = proc_ide_read_media;
237 ent->write_proc = NULL;
238 sprintf(name,"ide0/%s", dev_name);
239 proc_symlink(dev_name, proc_ide_root, name);
240}
241
242static int fake_ide_setup(char *str)
243{
244 fake_ide = 1;
245 return(1);
246}
247
248__setup("fake_ide", fake_ide_setup);
249
250__uml_help(fake_ide_setup,
251"fake_ide\n"
252" Create ide0 entries that map onto ubd devices.\n\n"
253);
254
255static int parse_unit(char **ptr)
256{
257 char *str = *ptr, *end;
258 int n = -1;
259
260 if(isdigit(*str)) {
261 n = simple_strtoul(str, &end, 0);
262 if(end == str)
263 return(-1);
264 *ptr = end;
265 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800266 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 n = *str - 'a';
268 str++;
269 *ptr = str;
270 }
271 return(n);
272}
273
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800274/* If *index_out == -1 at exit, the passed option was a general one;
275 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
276 * should not be freed on exit.
277 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800278static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800280 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 struct openflags flags = global_openflags;
282 char *backing_file;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800283 int n, err = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
285 if(index_out) *index_out = -1;
286 n = *str;
287 if(n == '='){
288 char *end;
289 int major;
290
291 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 if(!strcmp(str, "sync")){
293 global_openflags = of_sync(global_openflags);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800294 goto out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 }
296
Jeff Dikef28169d2007-02-10 01:43:53 -0800297 err = -EINVAL;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800298 major = simple_strtoul(str, &end, 0);
299 if((*end != '\0') || (end == str)){
300 *error_out = "Didn't parse major number";
301 goto out1;
302 }
303
Jeff Dikef28169d2007-02-10 01:43:53 -0800304 mutex_lock(&ubd_lock);
305 if(fake_major != MAJOR_NR){
306 *error_out = "Can't assign a fake major twice";
307 goto out1;
308 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800309
Jeff Dikef28169d2007-02-10 01:43:53 -0800310 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311
312 printk(KERN_INFO "Setting extra ubd major number to %d\n",
313 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800314 err = 0;
315 out1:
316 mutex_unlock(&ubd_lock);
317 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 }
319
320 n = parse_unit(&str);
321 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800322 *error_out = "Couldn't parse device number";
323 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 }
325 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800326 *error_out = "Device number out of range";
327 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 }
329
Jeff Dikef28169d2007-02-10 01:43:53 -0800330 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800331 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800333 ubd_dev = &ubd_devs[n];
334 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800335 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 goto out;
337 }
338
339 if (index_out)
340 *index_out = n;
341
Jeff Dikef28169d2007-02-10 01:43:53 -0800342 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800343 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 switch (*str) {
345 case 'r':
346 flags.w = 0;
347 break;
348 case 's':
349 flags.s = 1;
350 break;
351 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800352 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800354 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800355 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800356 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 case '=':
358 str++;
359 goto break_loop;
360 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800361 *error_out = "Expected '=' or flag letter "
362 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 goto out;
364 }
365 str++;
366 }
367
Jeff Dikef28169d2007-02-10 01:43:53 -0800368 if (*str == '=')
369 *error_out = "Too many flags specified";
370 else
371 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 goto out;
373
374break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 backing_file = strchr(str, ',');
376
Jeff Dikef28169d2007-02-10 01:43:53 -0800377 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
Jeff Dikef28169d2007-02-10 01:43:53 -0800380 if(backing_file != NULL){
381 if(ubd_dev->no_cow){
382 *error_out = "Can't specify both 'd' and a cow file";
383 goto out;
384 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 else {
386 *backing_file = '\0';
387 backing_file++;
388 }
389 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800390 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800391 ubd_dev->file = str;
392 ubd_dev->cow.file = backing_file;
393 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800395 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800396 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397}
398
399static int ubd_setup(char *str)
400{
Jeff Dikef28169d2007-02-10 01:43:53 -0800401 char *error;
402 int err;
403
404 err = ubd_setup_common(str, NULL, &error);
405 if(err)
406 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
407 "%s\n", str, error);
408 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409}
410
411__setup("ubd", ubd_setup);
412__uml_help(ubd_setup,
413"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
414" This is used to associate a device with a file in the underlying\n"
415" filesystem. When specifying two filenames, the first one is the\n"
416" COW name and the second is the backing file name. As separator you can\n"
417" use either a ':' or a ',': the first one allows writing things like;\n"
418" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
419" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800420" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421" a COW file or a backing file. To override this detection, add the 'd'\n"
422" flag:\n"
423" ubd0d=BackingFile\n"
424" Usually, there is a filesystem in the file, but \n"
425" that's not required. Swap devices containing swap files can be\n"
426" specified like this. Also, a file which doesn't contain a\n"
427" filesystem can have its contents read in the virtual \n"
428" machine by running 'dd' on the device. <n> must be in the range\n"
429" 0 to 7. Appending an 'r' to the number will cause that device\n"
430" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
431" an 's' will cause data to be written to disk on the host immediately.\n\n"
432);
433
434static int udb_setup(char *str)
435{
436 printk("udb%s specified on command line is almost certainly a ubd -> "
437 "udb TYPO\n", str);
438 return(1);
439}
440
441__setup("udb", udb_setup);
442__uml_help(udb_setup,
443"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700444" This option is here solely to catch ubd -> udb typos, which can be\n"
445" to impossible to catch visually unless you specifically look for\n"
446" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447" in the boot output.\n\n"
448);
449
450static int fakehd_set = 0;
451static int fakehd(char *str)
452{
453 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
454 fakehd_set = 1;
455 return 1;
456}
457
458__setup("fakehd", fakehd);
459__uml_help(fakehd,
460"fakehd\n"
461" Change the ubd device name to \"hd\".\n\n"
462);
463
464static void do_ubd_request(request_queue_t * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400465
466/* Only changed by ubd_init, which is an initcall. */
467int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469/* call ubd_finish if you need to serialize */
Jeff Dike91acb212005-10-10 23:10:32 -0400470static void __ubd_finish(struct request *req, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471{
Jeff Dike91acb212005-10-10 23:10:32 -0400472 int nsect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
Jeff Dike91acb212005-10-10 23:10:32 -0400474 if(error){
475 end_request(req, 0);
476 return;
477 }
478 nsect = req->current_nr_sectors;
479 req->sector += nsect;
480 req->buffer += nsect << 9;
481 req->errors = 0;
482 req->nr_sectors -= nsect;
483 req->current_nr_sectors = 0;
484 end_request(req, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485}
486
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800487/* Callable only from interrupt context - otherwise you need to do
488 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dike91acb212005-10-10 23:10:32 -0400489static inline void ubd_finish(struct request *req, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490{
Jeff Dike62f96cb2007-02-10 01:44:16 -0800491 struct ubd *dev = req->rq_disk->private_data;
492
493 spin_lock(&dev->lock);
Jeff Dike91acb212005-10-10 23:10:32 -0400494 __ubd_finish(req, error);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800495 spin_unlock(&dev->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496}
497
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800498/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800499/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400500static void ubd_handler(void)
501{
502 struct io_thread_req req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800503 struct request *rq;
504 struct ubd *dev;
Jeff Dike91acb212005-10-10 23:10:32 -0400505 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Jeff Dike91acb212005-10-10 23:10:32 -0400507 n = os_read_file(thread_fd, &req, sizeof(req));
508 if(n != sizeof(req)){
509 printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, "
510 "err = %d\n", os_getpid(), -n);
Jeff Dike91acb212005-10-10 23:10:32 -0400511 return;
512 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800513
Jeff Dike62f96cb2007-02-10 01:44:16 -0800514 rq = req.req;
515 dev = rq->rq_disk->private_data;
Jeff Dike2a9529a2007-03-29 01:20:27 -0700516 dev->active = 0;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800517
Jeff Dike91acb212005-10-10 23:10:32 -0400518 ubd_finish(rq, req.error);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800519 reactivate_fd(thread_fd, UBD_IRQ);
520 spin_lock(&dev->lock);
521 do_ubd_request(dev->queue);
522 spin_unlock(&dev->lock);
Jeff Dike91acb212005-10-10 23:10:32 -0400523}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524
Al Viro7bea96f2006-10-08 22:49:34 +0100525static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526{
Jeff Dike91acb212005-10-10 23:10:32 -0400527 ubd_handler();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 return(IRQ_HANDLED);
529}
530
Jeff Dike91acb212005-10-10 23:10:32 -0400531/* Only changed by ubd_init, which is an initcall. */
532static int io_pid = -1;
533
534void kill_io_thread(void)
535{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800536 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400537 os_kill_process(io_pid, 1);
538}
539
540__uml_exitcall(kill_io_thread);
541
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800542static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
544 char *file;
545
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800546 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 return(os_file_size(file, size_out));
548}
549
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800550static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800552 os_close_file(ubd_dev->fd);
553 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 return;
555
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800556 os_close_file(ubd_dev->cow.fd);
557 vfree(ubd_dev->cow.bitmap);
558 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559}
560
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800561static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562{
563 struct openflags flags;
564 char **back_ptr;
565 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800566 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800568 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800570 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
571 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800572
573 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800574 back_ptr, &ubd_dev->cow.bitmap_offset,
575 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800576 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800578 if((fd == -ENOENT) && create_cow){
579 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800580 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
581 &ubd_dev->cow.bitmap_offset,
582 &ubd_dev->cow.bitmap_len,
583 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800584 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800586 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 }
588 }
589
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800590 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800591 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800592 -fd);
593 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800595 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800597 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 err = -ENOMEM;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800599 ubd_dev->cow.bitmap = (void *) vmalloc(ubd_dev->cow.bitmap_len);
600 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
602 goto error;
603 }
604 flush_tlb_kernel_vm();
605
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800606 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
607 ubd_dev->cow.bitmap_offset,
608 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 if(err < 0)
610 goto error;
611
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800612 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800614 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800615 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800617 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 }
619 return(0);
620 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800621 os_close_file(ubd_dev->fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 return(err);
623}
624
Jeff Dike2e3f5252007-05-06 14:51:29 -0700625static void ubd_device_release(struct device *dev)
626{
627 struct ubd *ubd_dev = dev->driver_data;
628
629 blk_cleanup_queue(ubd_dev->queue);
630 *ubd_dev = ((struct ubd) DEFAULT_UBD);
631}
632
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800633static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800634 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635{
636 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
638 disk = alloc_disk(1 << UBD_SHIFT);
639 if(disk == NULL)
640 return(-ENOMEM);
641
642 disk->major = major;
643 disk->first_minor = unit << UBD_SHIFT;
644 disk->fops = &ubd_blops;
645 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700646 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700648 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
651 /* sysfs register (not for ide fake devices) */
652 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800653 ubd_devs[unit].pdev.id = unit;
654 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700655 ubd_devs[unit].pdev.dev.release = ubd_device_release;
656 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800657 platform_device_register(&ubd_devs[unit].pdev);
658 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 }
660
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800661 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800662 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 add_disk(disk);
664
665 *disk_out = disk;
666 return 0;
667}
668
669#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
670
Jeff Dikef28169d2007-02-10 01:43:53 -0800671static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800673 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800674 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800676 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700677 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800679 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800680 if(err < 0){
681 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700682 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800683 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800685 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
Jeff Dike62f96cb2007-02-10 01:44:16 -0800687 err = -ENOMEM;
688 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
689 if (ubd_dev->queue == NULL) {
690 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700691 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800692 }
693 ubd_dev->queue->queuedata = ubd_dev;
694
695 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
696 if(err){
697 *error_out = "Failed to register device";
698 goto out_cleanup;
699 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800700
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800702 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800703 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
705 /* perhaps this should also be under the "if (fake_major)" above */
706 /* using the fake_disk->disk_name and also the fakehd_set name */
707 if (fake_ide)
708 make_ide_entries(ubd_gendisk[n]->disk_name);
709
Jeff Dikeec7cf782005-09-03 15:57:29 -0700710 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700711out:
712 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800713
714out_cleanup:
715 blk_cleanup_queue(ubd_dev->queue);
716 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717}
718
Jeff Dikef28169d2007-02-10 01:43:53 -0800719static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800721 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
Jeff Dikef28169d2007-02-10 01:43:53 -0800723 /* This string is possibly broken up and stored, so it's only
724 * freed if ubd_setup_common fails, or if only general options
725 * were set.
726 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800727 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800728 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800729 *error_out = "Failed to allocate memory";
730 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800732
733 ret = ubd_setup_common(str, &n, error_out);
734 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800735 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800736
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800737 if (n == -1) {
738 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800739 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800740 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800742 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800743 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800744 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800745 ubd_devs[n].file = NULL;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800746 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800748out:
749 return ret;
750
751err_free:
752 kfree(str);
753 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754}
755
756static int ubd_get_config(char *name, char *str, int size, char **error_out)
757{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800758 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 int n, len = 0;
760
761 n = parse_unit(&name);
762 if((n >= MAX_DEV) || (n < 0)){
763 *error_out = "ubd_get_config : device number out of range";
764 return(-1);
765 }
766
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800767 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800768 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800770 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 CONFIG_CHUNK(str, size, len, "", 1);
772 goto out;
773 }
774
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800775 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800777 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800779 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 }
781 else CONFIG_CHUNK(str, size, len, "", 1);
782
783 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800784 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 return(len);
786}
787
Jeff Dike29d56cf2005-06-25 14:55:25 -0700788static int ubd_id(char **str, int *start_out, int *end_out)
789{
790 int n;
791
792 n = parse_unit(str);
793 *start_out = 0;
794 *end_out = MAX_DEV - 1;
795 return n;
796}
797
Jeff Dikef28169d2007-02-10 01:43:53 -0800798static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700800 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800801 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700802 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800804 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800806 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700807
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800808 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700809 goto out;
810
811 /* you cannot remove a open disk */
812 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800813 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700814 goto out;
815
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700816 ubd_gendisk[n] = NULL;
817 if(disk != NULL){
818 del_gendisk(disk);
819 put_disk(disk);
820 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
822 if(fake_gendisk[n] != NULL){
823 del_gendisk(fake_gendisk[n]);
824 put_disk(fake_gendisk[n]);
825 fake_gendisk[n] = NULL;
826 }
827
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700829 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700830out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800831 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700832 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833}
834
Jeff Dikef28169d2007-02-10 01:43:53 -0800835/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800836 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800837 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800839 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 .name = "ubd",
841 .config = ubd_config,
842 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700843 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 .remove = ubd_remove,
845};
846
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800847static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848{
849 mconsole_register_dev(&ubd_mc);
850 return 0;
851}
852
853__initcall(ubd_mc_init);
854
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800855static int __init ubd0_init(void)
856{
857 struct ubd *ubd_dev = &ubd_devs[0];
858
Jeff Dikeb8831a12007-02-10 01:44:17 -0800859 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800860 if(ubd_dev->file == NULL)
861 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -0800862 mutex_unlock(&ubd_lock);
863
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800864 return(0);
865}
866
867__initcall(ubd0_init);
868
Jeff Dikeb8831a12007-02-10 01:44:17 -0800869/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +0000870static struct platform_driver ubd_driver = {
871 .driver = {
872 .name = DRIVER_NAME,
873 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874};
875
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800876static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877{
Jeff Dikef28169d2007-02-10 01:43:53 -0800878 char *error;
879 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 if (register_blkdev(MAJOR_NR, "ubd"))
882 return -1;
883
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 if (fake_major != MAJOR_NR) {
885 char name[sizeof("ubd_nnn\0")];
886
887 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 if (register_blkdev(fake_major, "ubd"))
889 return -1;
890 }
Russell King3ae5eae2005-11-09 22:32:44 +0000891 platform_driver_register(&ubd_driver);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800892 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800893 for (i = 0; i < MAX_DEV; i++){
894 err = ubd_add(i, &error);
895 if(err)
896 printk(KERN_ERR "Failed to initialize ubd device %d :"
897 "%s\n", i, error);
898 }
Jeff Dikeb8831a12007-02-10 01:44:17 -0800899 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 return 0;
901}
902
903late_initcall(ubd_init);
904
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800905static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400906 unsigned long stack;
907 int err;
908
909 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
910 if(global_openflags.s){
911 printk(KERN_INFO "ubd: Synchronous mode\n");
912 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
913 * enough. So use anyway the io thread. */
914 }
915 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800916 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400917 &thread_fd);
918 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800919 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400920 "ubd : Failed to start I/O thread (errno = %d) - "
921 "falling back to synchronous I/O\n", -io_pid);
922 io_pid = -1;
923 return(0);
924 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800925 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800926 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400927 if(err != 0)
928 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800929 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400930}
931
932device_initcall(ubd_driver_init);
933
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934static int ubd_open(struct inode *inode, struct file *filp)
935{
936 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800937 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 int err = 0;
939
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800940 if(ubd_dev->count == 0){
941 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 if(err){
943 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800944 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 goto out;
946 }
947 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800948 ubd_dev->count++;
949 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700950
951 /* This should no more be needed. And it didn't work anyway to exclude
952 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800953 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800954 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700956 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 out:
958 return(err);
959}
960
961static int ubd_release(struct inode * inode, struct file * file)
962{
963 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800964 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800966 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800967 ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 return(0);
969}
970
Jeff Dike91acb212005-10-10 23:10:32 -0400971static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
972 __u64 *cow_offset, unsigned long *bitmap,
973 __u64 bitmap_offset, unsigned long *bitmap_words,
974 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975{
Jeff Dike91acb212005-10-10 23:10:32 -0400976 __u64 sector = io_offset >> 9;
977 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978
Jeff Dike91acb212005-10-10 23:10:32 -0400979 for(i = 0; i < length >> 9; i++){
980 if(cow_mask != NULL)
981 ubd_set_bit(i, (unsigned char *) cow_mask);
982 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
983 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
Jeff Dike91acb212005-10-10 23:10:32 -0400985 update_bitmap = 1;
986 ubd_set_bit(sector + i, (unsigned char *) bitmap);
987 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988
Jeff Dike91acb212005-10-10 23:10:32 -0400989 if(!update_bitmap)
990 return;
991
992 *cow_offset = sector / (sizeof(unsigned long) * 8);
993
994 /* This takes care of the case where we're exactly at the end of the
995 * device, and *cow_offset + 1 is off the end. So, just back it up
996 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
997 * for the original diagnosis.
998 */
999 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1000 sizeof(unsigned long) - 1))
1001 (*cow_offset)--;
1002
1003 bitmap_words[0] = bitmap[*cow_offset];
1004 bitmap_words[1] = bitmap[*cow_offset + 1];
1005
1006 *cow_offset *= sizeof(unsigned long);
1007 *cow_offset += bitmap_offset;
1008}
1009
1010static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1011 __u64 bitmap_offset, __u64 bitmap_len)
1012{
1013 __u64 sector = req->offset >> 9;
1014 int i;
1015
1016 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1017 panic("Operation too long");
1018
1019 if(req->op == UBD_READ) {
1020 for(i = 0; i < req->length >> 9; i++){
1021 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001022 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001023 &req->sector_mask);
1024 }
1025 }
1026 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1027 &req->cow_offset, bitmap, bitmap_offset,
1028 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029}
1030
Jeff Dike62f96cb2007-02-10 01:44:16 -08001031/* Called with dev->lock held */
Jeff Dike91acb212005-10-10 23:10:32 -04001032static int prepare_request(struct request *req, struct io_thread_req *io_req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033{
1034 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001035 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001036 __u64 offset;
1037 int len;
1038
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -07001039 /* This should be impossible now */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001040 if((rq_data_dir(req) == WRITE) && !ubd_dev->openflags.w){
Jeff Dike6c29256c2006-03-27 01:14:37 -08001041 printk("Write attempted on readonly ubd device %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 disk->disk_name);
Jeff Dike91acb212005-10-10 23:10:32 -04001043 end_request(req, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 return(1);
1045 }
1046
Jeff Dike91acb212005-10-10 23:10:32 -04001047 offset = ((__u64) req->sector) << 9;
1048 len = req->current_nr_sectors << 9;
1049
Jeff Dike62f96cb2007-02-10 01:44:16 -08001050 io_req->req = req;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001051 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd : ubd_dev->fd;
1052 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001053 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 io_req->offset = offset;
1055 io_req->length = len;
1056 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001057 io_req->sector_mask = 0;
1058
1059 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001061 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dike91acb212005-10-10 23:10:32 -04001062 io_req->buffer = req->buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 io_req->sectorsize = 1 << 9;
1064
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001065 if(ubd_dev->cow.file != NULL)
1066 cowify_req(io_req, ubd_dev->cow.bitmap, ubd_dev->cow.bitmap_offset,
1067 ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001068
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 return(0);
1070}
1071
Jeff Dike62f96cb2007-02-10 01:44:16 -08001072/* Called with dev->lock held */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073static void do_ubd_request(request_queue_t *q)
1074{
1075 struct io_thread_req io_req;
1076 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001077 int err, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078
Jeff Dike91acb212005-10-10 23:10:32 -04001079 if(thread_fd == -1){
1080 while((req = elv_next_request(q)) != NULL){
1081 err = prepare_request(req, &io_req);
1082 if(!err){
1083 do_io(&io_req);
1084 __ubd_finish(req, io_req.error);
1085 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 }
1087 }
Jeff Dike91acb212005-10-10 23:10:32 -04001088 else {
Jeff Dike2a9529a2007-03-29 01:20:27 -07001089 struct ubd *dev = q->queuedata;
1090 if(dev->active || (req = elv_next_request(q)) == NULL)
Jeff Dike91acb212005-10-10 23:10:32 -04001091 return;
1092 err = prepare_request(req, &io_req);
1093 if(!err){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001094 dev->active = 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001095 n = os_write_file(thread_fd, (char *) &io_req,
1096 sizeof(io_req));
1097 if(n != sizeof(io_req))
1098 printk("write to io thread failed, "
1099 "errno = %d\n", -n);
1100 }
1101 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102}
1103
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001104static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1105{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001106 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001107
1108 geo->heads = 128;
1109 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001110 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001111 return 0;
1112}
1113
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114static int ubd_ioctl(struct inode * inode, struct file * file,
1115 unsigned int cmd, unsigned long arg)
1116{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001117 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 struct hd_driveid ubd_id = {
1119 .cyls = 0,
1120 .heads = 128,
1121 .sectors = 32,
1122 };
1123
1124 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001127 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1129 sizeof(ubd_id)))
1130 return(-EFAULT);
1131 return(0);
Jeff Dikeb8831a12007-02-10 01:44:17 -08001132
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 case CDROMVOLREAD:
1134 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
1135 return(-EFAULT);
1136 volume.channel0 = 255;
1137 volume.channel1 = 255;
1138 volume.channel2 = 255;
1139 volume.channel3 = 255;
1140 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
1141 return(-EFAULT);
1142 return(0);
1143 }
1144 return(-EINVAL);
1145}
1146
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001147static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148{
1149 struct uml_stat buf1, buf2;
1150 int err;
1151
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001152 if(from_cmdline == NULL)
1153 return 0;
1154 if(!strcmp(from_cmdline, from_cow))
1155 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156
1157 err = os_stat_file(from_cmdline, &buf1);
1158 if(err < 0){
1159 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001160 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 }
1162 err = os_stat_file(from_cow, &buf2);
1163 if(err < 0){
1164 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001165 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 }
1167 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001168 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169
1170 printk("Backing file mismatch - \"%s\" requested,\n"
1171 "\"%s\" specified in COW header of \"%s\"\n",
1172 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001173 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174}
1175
1176static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1177{
1178 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001179 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 int err;
1181
1182 err = os_file_modtime(file, &modtime);
1183 if(err < 0){
1184 printk("Failed to get modification time of backing file "
1185 "\"%s\", err = %d\n", file, -err);
1186 return(err);
1187 }
1188
1189 err = os_file_size(file, &actual);
1190 if(err < 0){
1191 printk("Failed to get size of backing file \"%s\", "
1192 "err = %d\n", file, -err);
1193 return(err);
1194 }
1195
1196 if(actual != size){
1197 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1198 * the typecast.*/
1199 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1200 "file\n", (unsigned long long) size, actual);
1201 return(-EINVAL);
1202 }
1203 if(modtime != mtime){
1204 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1205 "file\n", mtime, modtime);
1206 return(-EINVAL);
1207 }
1208 return(0);
1209}
1210
1211int read_cow_bitmap(int fd, void *buf, int offset, int len)
1212{
1213 int err;
1214
1215 err = os_seek_file(fd, offset);
1216 if(err < 0)
1217 return(err);
1218
1219 err = os_read_file(fd, buf, len);
1220 if(err < 0)
1221 return(err);
1222
1223 return(0);
1224}
1225
Jeff Dike6c29256c2006-03-27 01:14:37 -08001226int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 char **backing_file_out, int *bitmap_offset_out,
1228 unsigned long *bitmap_len_out, int *data_offset_out,
1229 int *create_cow_out)
1230{
1231 time_t mtime;
1232 unsigned long long size;
1233 __u32 version, align;
1234 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001235 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
1237 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001238 if (fd < 0) {
1239 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 *create_cow_out = 1;
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001241 if (!openflags->w ||
1242 ((fd != -EROFS) && (fd != -EACCES)))
1243 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 openflags->w = 0;
1245 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001246 if (fd < 0)
1247 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 }
1249
Jeff Dike6c29256c2006-03-27 01:14:37 -08001250 if(shared)
1251 printk("Not locking \"%s\" on the host\n", file);
1252 else {
1253 err = os_lock_file(fd, openflags->w);
1254 if(err < 0){
1255 printk("Failed to lock '%s', err = %d\n", file, -err);
1256 goto out_close;
1257 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 }
1259
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001260 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001261 if(backing_file_out == NULL)
1262 return(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
1264 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1265 &size, &sectorsize, &align, bitmap_offset_out);
1266 if(err && (*backing_file_out != NULL)){
1267 printk("Failed to read COW header from COW file \"%s\", "
1268 "errno = %d\n", file, -err);
1269 goto out_close;
1270 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001271 if(err)
1272 return(fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001274 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001276 /* Allow switching only if no mismatch. */
1277 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 printk("Switching backing file to '%s'\n", *backing_file_out);
1279 err = write_cow_header(file, fd, *backing_file_out,
1280 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001281 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001283 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001285 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 *backing_file_out = backing_file;
1287 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001288 if (err)
1289 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 }
1291
1292 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1293 bitmap_len_out, data_offset_out);
1294
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001295 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 out_close:
1297 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001298 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299}
1300
1301int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1302 int sectorsize, int alignment, int *bitmap_offset_out,
1303 unsigned long *bitmap_len_out, int *data_offset_out)
1304{
1305 int err, fd;
1306
1307 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001308 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 if(fd < 0){
1310 err = fd;
1311 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1312 -err);
1313 goto out;
1314 }
1315
1316 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1317 bitmap_offset_out, bitmap_len_out,
1318 data_offset_out);
1319 if(!err)
1320 return(fd);
1321 os_close_file(fd);
1322 out:
1323 return(err);
1324}
1325
Jeff Dike91acb212005-10-10 23:10:32 -04001326static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327{
Jeff Dike91acb212005-10-10 23:10:32 -04001328 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329
Jeff Dike91acb212005-10-10 23:10:32 -04001330 if(req->cow_offset == -1)
1331 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332
Jeff Dike91acb212005-10-10 23:10:32 -04001333 n = os_seek_file(req->fds[1], req->cow_offset);
1334 if(n < 0){
1335 printk("do_io - bitmap lseek failed : err = %d\n", -n);
1336 return(1);
1337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338
Jeff Dike91acb212005-10-10 23:10:32 -04001339 n = os_write_file(req->fds[1], &req->bitmap_words,
1340 sizeof(req->bitmap_words));
1341 if(n != sizeof(req->bitmap_words)){
1342 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1343 req->fds[1]);
1344 return(1);
1345 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346
Jeff Dike91acb212005-10-10 23:10:32 -04001347 return(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348}
Jeff Dike91acb212005-10-10 23:10:32 -04001349
1350void do_io(struct io_thread_req *req)
1351{
1352 char *buf;
1353 unsigned long len;
1354 int n, nsectors, start, end, bit;
1355 int err;
1356 __u64 off;
1357
1358 nsectors = req->length / req->sectorsize;
1359 start = 0;
1360 do {
1361 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1362 end = start;
1363 while((end < nsectors) &&
1364 (ubd_test_bit(end, (unsigned char *)
1365 &req->sector_mask) == bit))
1366 end++;
1367
1368 off = req->offset + req->offsets[bit] +
1369 start * req->sectorsize;
1370 len = (end - start) * req->sectorsize;
1371 buf = &req->buffer[start * req->sectorsize];
1372
1373 err = os_seek_file(req->fds[bit], off);
1374 if(err < 0){
1375 printk("do_io - lseek failed : err = %d\n", -err);
1376 req->error = 1;
1377 return;
1378 }
1379 if(req->op == UBD_READ){
1380 n = 0;
1381 do {
1382 buf = &buf[n];
1383 len -= n;
1384 n = os_read_file(req->fds[bit], buf, len);
1385 if (n < 0) {
1386 printk("do_io - read failed, err = %d "
1387 "fd = %d\n", -n, req->fds[bit]);
1388 req->error = 1;
1389 return;
1390 }
1391 } while((n < len) && (n != 0));
1392 if (n < len) memset(&buf[n], 0, len - n);
1393 } else {
1394 n = os_write_file(req->fds[bit], buf, len);
1395 if(n != len){
1396 printk("do_io - write failed err = %d "
1397 "fd = %d\n", -n, req->fds[bit]);
1398 req->error = 1;
1399 return;
1400 }
1401 }
1402
1403 start = end;
1404 } while(start < nsectors);
1405
1406 req->error = update_bitmap(req);
1407}
1408
1409/* Changed in start_io_thread, which is serialized by being called only
1410 * from ubd_init, which is an initcall.
1411 */
1412int kernel_fd = -1;
1413
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001414/* Only changed by the io thread. XXX: currently unused. */
1415static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001416
1417int io_thread(void *arg)
1418{
1419 struct io_thread_req req;
1420 int n;
1421
1422 ignore_sigwinch_sig();
1423 while(1){
1424 n = os_read_file(kernel_fd, &req, sizeof(req));
1425 if(n != sizeof(req)){
1426 if(n < 0)
1427 printk("io_thread - read failed, fd = %d, "
1428 "err = %d\n", kernel_fd, -n);
1429 else {
1430 printk("io_thread - short read, fd = %d, "
1431 "length = %d\n", kernel_fd, n);
1432 }
1433 continue;
1434 }
1435 io_count++;
1436 do_io(&req);
1437 n = os_write_file(kernel_fd, &req, sizeof(req));
1438 if(n != sizeof(req))
1439 printk("io_thread - write failed, fd = %d, err = %d\n",
1440 kernel_fd, -n);
1441 }
Jeff Dike91acb212005-10-10 23:10:32 -04001442
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001443 return 0;
1444}