blob: 0eabe73c964dc04732baa35cfaec864e34b41599 [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
Jeff Dikee16f5352007-06-08 13:46:54 -070023#include "linux/kernel.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include "linux/module.h"
25#include "linux/blkdev.h"
26#include "linux/hdreg.h"
27#include "linux/init.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include "linux/cdrom.h"
29#include "linux/proc_fs.h"
30#include "linux/ctype.h"
31#include "linux/capability.h"
32#include "linux/mm.h"
33#include "linux/vmalloc.h"
34#include "linux/blkpg.h"
35#include "linux/genhd.h"
36#include "linux/spinlock.h"
Russell Kingd052d1b2005-10-29 19:07:23 +010037#include "linux/platform_device.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include "asm/segment.h"
39#include "asm/uaccess.h"
40#include "asm/irq.h"
41#include "asm/types.h"
42#include "asm/tlbflush.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include "mem_user.h"
44#include "kern_util.h"
45#include "kern.h"
46#include "mconsole_kern.h"
47#include "init.h"
48#include "irq_user.h"
49#include "irq_kern.h"
50#include "ubd_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#include "os.h"
52#include "mem.h"
53#include "mem_kern.h"
54#include "cow.h"
55
Jeff Dike7b9014c2005-05-20 13:59:11 -070056enum ubd_req { UBD_READ, UBD_WRITE };
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
58struct io_thread_req {
Jeff Dike62f96cb2007-02-10 01:44:16 -080059 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -040060 enum ubd_req op;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 int fds[2];
62 unsigned long offsets[2];
63 unsigned long long offset;
64 unsigned long length;
65 char *buffer;
66 int sectorsize;
Jeff Dike91acb212005-10-10 23:10:32 -040067 unsigned long sector_mask;
68 unsigned long long cow_offset;
69 unsigned long bitmap_words[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 int error;
71};
72
Jeff Dike6c29256c2006-03-27 01:14:37 -080073extern int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 char **backing_file_out, int *bitmap_offset_out,
75 unsigned long *bitmap_len_out, int *data_offset_out,
76 int *create_cow_out);
77extern int create_cow_file(char *cow_file, char *backing_file,
78 struct openflags flags, int sectorsize,
79 int alignment, int *bitmap_offset_out,
80 unsigned long *bitmap_len_out,
81 int *data_offset_out);
82extern int read_cow_bitmap(int fd, void *buf, int offset, int len);
Jeff Dike91acb212005-10-10 23:10:32 -040083extern void do_io(struct io_thread_req *req);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
Jeff Dike91acb212005-10-10 23:10:32 -040085static inline int ubd_test_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070086{
87 __u64 n;
88 int bits, off;
89
Jeff Dike91acb212005-10-10 23:10:32 -040090 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 n = bit / bits;
92 off = bit % bits;
Jeff Dikedc764e52007-05-06 14:51:41 -070093 return (data[n] & (1 << off)) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094}
95
Jeff Dike91acb212005-10-10 23:10:32 -040096static inline void ubd_set_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070097{
98 __u64 n;
99 int bits, off;
100
Jeff Dike91acb212005-10-10 23:10:32 -0400101 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 n = bit / bits;
103 off = bit % bits;
Jeff Dike91acb212005-10-10 23:10:32 -0400104 data[n] |= (1 << off);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105}
106/*End stuff from ubd_user.h*/
107
108#define DRIVER_NAME "uml-blkdev"
109
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800110static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112static int ubd_open(struct inode * inode, struct file * filp);
113static int ubd_release(struct inode * inode, struct file * file);
114static int ubd_ioctl(struct inode * inode, struct file * file,
115 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800116static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800118#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120static struct block_device_operations ubd_blops = {
121 .owner = THIS_MODULE,
122 .open = ubd_open,
123 .release = ubd_release,
124 .ioctl = ubd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800125 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126};
127
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128/* Protected by ubd_lock */
129static int fake_major = MAJOR_NR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130static struct gendisk *ubd_gendisk[MAX_DEV];
131static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800132
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133#ifdef CONFIG_BLK_DEV_UBD_SYNC
134#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
135 .cl = 1 })
136#else
137#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
138 .cl = 1 })
139#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140static struct openflags global_openflags = OPEN_FLAGS;
141
142struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800143 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800145 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 int fd;
147 unsigned long *bitmap;
148 unsigned long bitmap_len;
149 int bitmap_offset;
Jeff Dikedc764e52007-05-06 14:51:41 -0700150 int data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151};
152
Jeff Dikea0044bd2007-05-06 14:51:36 -0700153#define MAX_SG 64
154
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155struct ubd {
Jeff Dikea0044bd2007-05-06 14:51:36 -0700156 struct list_head restart;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800157 /* name (and fd, below) of the file opened for writing, either the
158 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 char *file;
160 int count;
161 int fd;
162 __u64 size;
163 struct openflags boot_openflags;
164 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800165 unsigned shared:1;
166 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 struct cow cow;
168 struct platform_device pdev;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800169 struct request_queue *queue;
170 spinlock_t lock;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700171 struct scatterlist sg[MAX_SG];
172 struct request *request;
173 int start_sg, end_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174};
175
176#define DEFAULT_COW { \
177 .file = NULL, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700178 .fd = -1, \
179 .bitmap = NULL, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 .bitmap_offset = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700181 .data_offset = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182}
183
184#define DEFAULT_UBD { \
185 .file = NULL, \
186 .count = 0, \
187 .fd = -1, \
188 .size = -1, \
189 .boot_openflags = OPEN_FLAGS, \
190 .openflags = OPEN_FLAGS, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700191 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800192 .shared = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700193 .cow = DEFAULT_COW, \
Jeff Dike62f96cb2007-02-10 01:44:16 -0800194 .lock = SPIN_LOCK_UNLOCKED, \
Jeff Dikea0044bd2007-05-06 14:51:36 -0700195 .request = NULL, \
196 .start_sg = 0, \
197 .end_sg = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198}
199
Jeff Dikeb8831a12007-02-10 01:44:17 -0800200/* Protected by ubd_lock */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800201struct ubd ubd_devs[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203/* Only changed by fake_ide_setup which is a setup */
204static int fake_ide = 0;
205static struct proc_dir_entry *proc_ide_root = NULL;
206static struct proc_dir_entry *proc_ide = NULL;
207
208static void make_proc_ide(void)
209{
210 proc_ide_root = proc_mkdir("ide", NULL);
211 proc_ide = proc_mkdir("ide0", proc_ide_root);
212}
213
214static int proc_ide_read_media(char *page, char **start, off_t off, int count,
215 int *eof, void *data)
216{
217 int len;
218
219 strcpy(page, "disk\n");
220 len = strlen("disk\n");
221 len -= off;
222 if (len < count){
223 *eof = 1;
224 if (len <= 0) return 0;
225 }
226 else len = count;
227 *start = page + off;
228 return len;
229}
230
231static void make_ide_entries(char *dev_name)
232{
233 struct proc_dir_entry *dir, *ent;
234 char name[64];
235
236 if(proc_ide_root == NULL) make_proc_ide();
237
238 dir = proc_mkdir(dev_name, proc_ide);
239 if(!dir) return;
240
241 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
242 if(!ent) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 ent->data = NULL;
244 ent->read_proc = proc_ide_read_media;
245 ent->write_proc = NULL;
246 sprintf(name,"ide0/%s", dev_name);
247 proc_symlink(dev_name, proc_ide_root, name);
248}
249
250static int fake_ide_setup(char *str)
251{
252 fake_ide = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700253 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254}
255
256__setup("fake_ide", fake_ide_setup);
257
258__uml_help(fake_ide_setup,
259"fake_ide\n"
260" Create ide0 entries that map onto ubd devices.\n\n"
261);
262
263static int parse_unit(char **ptr)
264{
265 char *str = *ptr, *end;
266 int n = -1;
267
268 if(isdigit(*str)) {
269 n = simple_strtoul(str, &end, 0);
270 if(end == str)
Jeff Dikedc764e52007-05-06 14:51:41 -0700271 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 *ptr = end;
273 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800274 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 n = *str - 'a';
276 str++;
277 *ptr = str;
278 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700279 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280}
281
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800282/* If *index_out == -1 at exit, the passed option was a general one;
283 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
284 * should not be freed on exit.
285 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800286static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800288 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 struct openflags flags = global_openflags;
290 char *backing_file;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800291 int n, err = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
293 if(index_out) *index_out = -1;
294 n = *str;
295 if(n == '='){
296 char *end;
297 int major;
298
299 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 if(!strcmp(str, "sync")){
301 global_openflags = of_sync(global_openflags);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800302 goto out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 }
304
Jeff Dikef28169d2007-02-10 01:43:53 -0800305 err = -EINVAL;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800306 major = simple_strtoul(str, &end, 0);
307 if((*end != '\0') || (end == str)){
308 *error_out = "Didn't parse major number";
309 goto out1;
310 }
311
Jeff Dikef28169d2007-02-10 01:43:53 -0800312 mutex_lock(&ubd_lock);
313 if(fake_major != MAJOR_NR){
314 *error_out = "Can't assign a fake major twice";
315 goto out1;
316 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800317
Jeff Dikef28169d2007-02-10 01:43:53 -0800318 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
320 printk(KERN_INFO "Setting extra ubd major number to %d\n",
321 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800322 err = 0;
323 out1:
324 mutex_unlock(&ubd_lock);
325 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
327
328 n = parse_unit(&str);
329 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800330 *error_out = "Couldn't parse device number";
331 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 }
333 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800334 *error_out = "Device number out of range";
335 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 }
337
Jeff Dikef28169d2007-02-10 01:43:53 -0800338 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800339 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800341 ubd_dev = &ubd_devs[n];
342 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800343 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 goto out;
345 }
346
347 if (index_out)
348 *index_out = n;
349
Jeff Dikef28169d2007-02-10 01:43:53 -0800350 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800351 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 switch (*str) {
353 case 'r':
354 flags.w = 0;
355 break;
356 case 's':
357 flags.s = 1;
358 break;
359 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800360 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800362 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800363 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800364 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 case '=':
366 str++;
367 goto break_loop;
368 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800369 *error_out = "Expected '=' or flag letter "
370 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 goto out;
372 }
373 str++;
374 }
375
Jeff Dikef28169d2007-02-10 01:43:53 -0800376 if (*str == '=')
377 *error_out = "Too many flags specified";
378 else
379 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 goto out;
381
382break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 backing_file = strchr(str, ',');
384
Jeff Dikef28169d2007-02-10 01:43:53 -0800385 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387
Jeff Dikef28169d2007-02-10 01:43:53 -0800388 if(backing_file != NULL){
389 if(ubd_dev->no_cow){
390 *error_out = "Can't specify both 'd' and a cow file";
391 goto out;
392 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 else {
394 *backing_file = '\0';
395 backing_file++;
396 }
397 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800398 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800399 ubd_dev->file = str;
400 ubd_dev->cow.file = backing_file;
401 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800403 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800404 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405}
406
407static int ubd_setup(char *str)
408{
Jeff Dikef28169d2007-02-10 01:43:53 -0800409 char *error;
410 int err;
411
412 err = ubd_setup_common(str, NULL, &error);
413 if(err)
414 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
415 "%s\n", str, error);
416 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417}
418
419__setup("ubd", ubd_setup);
420__uml_help(ubd_setup,
421"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
422" This is used to associate a device with a file in the underlying\n"
423" filesystem. When specifying two filenames, the first one is the\n"
424" COW name and the second is the backing file name. As separator you can\n"
425" use either a ':' or a ',': the first one allows writing things like;\n"
426" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
427" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800428" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429" a COW file or a backing file. To override this detection, add the 'd'\n"
430" flag:\n"
431" ubd0d=BackingFile\n"
432" Usually, there is a filesystem in the file, but \n"
433" that's not required. Swap devices containing swap files can be\n"
434" specified like this. Also, a file which doesn't contain a\n"
435" filesystem can have its contents read in the virtual \n"
436" machine by running 'dd' on the device. <n> must be in the range\n"
437" 0 to 7. Appending an 'r' to the number will cause that device\n"
438" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
439" an 's' will cause data to be written to disk on the host immediately.\n\n"
440);
441
442static int udb_setup(char *str)
443{
444 printk("udb%s specified on command line is almost certainly a ubd -> "
445 "udb TYPO\n", str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700446 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447}
448
449__setup("udb", udb_setup);
450__uml_help(udb_setup,
451"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700452" This option is here solely to catch ubd -> udb typos, which can be\n"
453" to impossible to catch visually unless you specifically look for\n"
454" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455" in the boot output.\n\n"
456);
457
458static int fakehd_set = 0;
459static int fakehd(char *str)
460{
461 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
462 fakehd_set = 1;
463 return 1;
464}
465
466__setup("fakehd", fakehd);
467__uml_help(fakehd,
468"fakehd\n"
469" Change the ubd device name to \"hd\".\n\n"
470);
471
Jens Axboe165125e2007-07-24 09:28:11 +0200472static void do_ubd_request(struct request_queue * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400473
474/* Only changed by ubd_init, which is an initcall. */
475int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
Jeff Dikea0044bd2007-05-06 14:51:36 -0700477static void ubd_end_request(struct request *req, int bytes, int uptodate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700479 if (!end_that_request_first(req, uptodate, bytes >> 9)) {
480 struct ubd *dev = req->rq_disk->private_data;
481 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
Jeff Dikea0044bd2007-05-06 14:51:36 -0700483 add_disk_randomness(req->rq_disk);
484 spin_lock_irqsave(&dev->lock, flags);
485 end_that_request_last(req, uptodate);
486 spin_unlock_irqrestore(&dev->lock, flags);
Jeff Dike91acb212005-10-10 23:10:32 -0400487 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488}
489
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800490/* Callable only from interrupt context - otherwise you need to do
491 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700492static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700494 if(bytes < 0){
495 ubd_end_request(req, 0, 0);
496 return;
497 }
498 ubd_end_request(req, bytes, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499}
500
Jeff Dikea0044bd2007-05-06 14:51:36 -0700501static LIST_HEAD(restart);
502
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800503/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800504/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400505static void ubd_handler(void)
506{
Jeff Dike2adcec22007-05-06 14:51:37 -0700507 struct io_thread_req *req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800508 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700509 struct ubd *ubd;
510 struct list_head *list, *next_ele;
511 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400512 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
Jeff Dikea0044bd2007-05-06 14:51:36 -0700514 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700515 n = os_read_file(thread_fd, &req,
516 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700517 if(n != sizeof(req)){
518 if(n == -EAGAIN)
519 break;
520 printk(KERN_ERR "spurious interrupt in ubd_handler, "
521 "err = %d\n", -n);
522 return;
523 }
524
Jeff Dike2adcec22007-05-06 14:51:37 -0700525 rq = req->req;
526 rq->nr_sectors -= req->length >> 9;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700527 if(rq->nr_sectors == 0)
528 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike2adcec22007-05-06 14:51:37 -0700529 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400530 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800531 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700532
533 list_for_each_safe(list, next_ele, &restart){
534 ubd = container_of(list, struct ubd, restart);
535 list_del_init(&ubd->restart);
536 spin_lock_irqsave(&ubd->lock, flags);
537 do_ubd_request(ubd->queue);
538 spin_unlock_irqrestore(&ubd->lock, flags);
539 }
Jeff Dike91acb212005-10-10 23:10:32 -0400540}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
Al Viro7bea96f2006-10-08 22:49:34 +0100542static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
Jeff Dike91acb212005-10-10 23:10:32 -0400544 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700545 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546}
547
Jeff Dike91acb212005-10-10 23:10:32 -0400548/* Only changed by ubd_init, which is an initcall. */
549static int io_pid = -1;
550
551void kill_io_thread(void)
552{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800553 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400554 os_kill_process(io_pid, 1);
555}
556
557__uml_exitcall(kill_io_thread);
558
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800559static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
561 char *file;
562
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800563 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700564 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800567static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800569 os_close_file(ubd_dev->fd);
570 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 return;
572
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800573 os_close_file(ubd_dev->cow.fd);
574 vfree(ubd_dev->cow.bitmap);
575 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576}
577
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800578static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579{
580 struct openflags flags;
581 char **back_ptr;
582 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800583 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800585 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800587 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
588 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800589
590 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800591 back_ptr, &ubd_dev->cow.bitmap_offset,
592 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800593 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800595 if((fd == -ENOENT) && create_cow){
596 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800597 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
598 &ubd_dev->cow.bitmap_offset,
599 &ubd_dev->cow.bitmap_len,
600 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800601 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800603 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 }
605 }
606
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800607 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800608 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800609 -fd);
610 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800612 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800614 if(ubd_dev->cow.file != NULL){
Jeff Dikef4768ff2007-08-22 14:01:53 -0700615 blk_queue_max_sectors(ubd_dev->queue, 8 * sizeof(long));
616
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 err = -ENOMEM;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800618 ubd_dev->cow.bitmap = (void *) vmalloc(ubd_dev->cow.bitmap_len);
619 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
621 goto error;
622 }
623 flush_tlb_kernel_vm();
624
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800625 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
626 ubd_dev->cow.bitmap_offset,
627 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 if(err < 0)
629 goto error;
630
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800631 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800633 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800634 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800636 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700638 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800640 os_close_file(ubd_dev->fd);
Jeff Dikedc764e52007-05-06 14:51:41 -0700641 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642}
643
Jeff Dike2e3f5252007-05-06 14:51:29 -0700644static void ubd_device_release(struct device *dev)
645{
646 struct ubd *ubd_dev = dev->driver_data;
647
648 blk_cleanup_queue(ubd_dev->queue);
649 *ubd_dev = ((struct ubd) DEFAULT_UBD);
650}
651
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800652static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800653 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654{
655 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
657 disk = alloc_disk(1 << UBD_SHIFT);
658 if(disk == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -0700659 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660
661 disk->major = major;
662 disk->first_minor = unit << UBD_SHIFT;
663 disk->fops = &ubd_blops;
664 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700665 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700667 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
670 /* sysfs register (not for ide fake devices) */
671 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800672 ubd_devs[unit].pdev.id = unit;
673 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700674 ubd_devs[unit].pdev.dev.release = ubd_device_release;
675 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800676 platform_device_register(&ubd_devs[unit].pdev);
677 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 }
679
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800680 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800681 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 add_disk(disk);
683
684 *disk_out = disk;
685 return 0;
686}
687
688#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
689
Jeff Dikef28169d2007-02-10 01:43:53 -0800690static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800692 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800693 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800695 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700696 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800698 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800699 if(err < 0){
700 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700701 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800702 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800704 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
Jeff Dikea0044bd2007-05-06 14:51:36 -0700706 INIT_LIST_HEAD(&ubd_dev->restart);
707
Jeff Dike62f96cb2007-02-10 01:44:16 -0800708 err = -ENOMEM;
709 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
710 if (ubd_dev->queue == NULL) {
711 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700712 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800713 }
714 ubd_dev->queue->queuedata = ubd_dev;
715
Jeff Dikea0044bd2007-05-06 14:51:36 -0700716 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800717 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
718 if(err){
719 *error_out = "Failed to register device";
720 goto out_cleanup;
721 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800722
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800724 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800725 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726
727 /* perhaps this should also be under the "if (fake_major)" above */
728 /* using the fake_disk->disk_name and also the fakehd_set name */
729 if (fake_ide)
730 make_ide_entries(ubd_gendisk[n]->disk_name);
731
Jeff Dikeec7cf782005-09-03 15:57:29 -0700732 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700733out:
734 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800735
736out_cleanup:
737 blk_cleanup_queue(ubd_dev->queue);
738 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739}
740
Jeff Dikef28169d2007-02-10 01:43:53 -0800741static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800743 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
Jeff Dikef28169d2007-02-10 01:43:53 -0800745 /* This string is possibly broken up and stored, so it's only
746 * freed if ubd_setup_common fails, or if only general options
747 * were set.
748 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800749 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800750 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800751 *error_out = "Failed to allocate memory";
752 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800754
755 ret = ubd_setup_common(str, &n, error_out);
756 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800757 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800758
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800759 if (n == -1) {
760 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800761 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800762 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
Jeff Dikedc764e52007-05-06 14:51:41 -0700764 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800765 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800766 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800767 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700768 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800770out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700771 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800772
773err_free:
774 kfree(str);
775 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776}
777
778static int ubd_get_config(char *name, char *str, int size, char **error_out)
779{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800780 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 int n, len = 0;
782
783 n = parse_unit(&name);
784 if((n >= MAX_DEV) || (n < 0)){
785 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700786 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 }
788
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800789 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800790 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800792 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 CONFIG_CHUNK(str, size, len, "", 1);
794 goto out;
795 }
796
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800797 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800799 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800801 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 }
803 else CONFIG_CHUNK(str, size, len, "", 1);
804
805 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800806 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700807 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808}
809
Jeff Dike29d56cf2005-06-25 14:55:25 -0700810static int ubd_id(char **str, int *start_out, int *end_out)
811{
Jeff Dikedc764e52007-05-06 14:51:41 -0700812 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700813
814 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700815 *start_out = 0;
816 *end_out = MAX_DEV - 1;
817 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700818}
819
Jeff Dikef28169d2007-02-10 01:43:53 -0800820static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700822 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800823 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700824 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800826 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800828 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700829
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800830 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700831 goto out;
832
833 /* you cannot remove a open disk */
834 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800835 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700836 goto out;
837
Jeff Dikedc764e52007-05-06 14:51:41 -0700838 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700839 if(disk != NULL){
840 del_gendisk(disk);
841 put_disk(disk);
842 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
844 if(fake_gendisk[n] != NULL){
845 del_gendisk(fake_gendisk[n]);
846 put_disk(fake_gendisk[n]);
847 fake_gendisk[n] = NULL;
848 }
849
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700851 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700852out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800853 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700854 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855}
856
Jeff Dikef28169d2007-02-10 01:43:53 -0800857/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800858 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800859 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800861 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 .name = "ubd",
863 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -0700864 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700865 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 .remove = ubd_remove,
867};
868
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800869static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870{
871 mconsole_register_dev(&ubd_mc);
872 return 0;
873}
874
875__initcall(ubd_mc_init);
876
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800877static int __init ubd0_init(void)
878{
879 struct ubd *ubd_dev = &ubd_devs[0];
880
Jeff Dikeb8831a12007-02-10 01:44:17 -0800881 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800882 if(ubd_dev->file == NULL)
883 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -0800884 mutex_unlock(&ubd_lock);
885
Jeff Dikedc764e52007-05-06 14:51:41 -0700886 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800887}
888
889__initcall(ubd0_init);
890
Jeff Dikeb8831a12007-02-10 01:44:17 -0800891/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +0000892static struct platform_driver ubd_driver = {
893 .driver = {
894 .name = DRIVER_NAME,
895 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896};
897
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800898static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899{
Jeff Dikef28169d2007-02-10 01:43:53 -0800900 char *error;
901 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 if (register_blkdev(MAJOR_NR, "ubd"))
904 return -1;
905
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 if (fake_major != MAJOR_NR) {
907 char name[sizeof("ubd_nnn\0")];
908
909 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 if (register_blkdev(fake_major, "ubd"))
911 return -1;
912 }
Russell King3ae5eae2005-11-09 22:32:44 +0000913 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -0700914 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800915 for (i = 0; i < MAX_DEV; i++){
916 err = ubd_add(i, &error);
917 if(err)
918 printk(KERN_ERR "Failed to initialize ubd device %d :"
919 "%s\n", i, error);
920 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700921 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 return 0;
923}
924
925late_initcall(ubd_init);
926
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800927static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400928 unsigned long stack;
929 int err;
930
931 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
932 if(global_openflags.s){
933 printk(KERN_INFO "ubd: Synchronous mode\n");
934 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
935 * enough. So use anyway the io thread. */
936 }
937 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800938 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400939 &thread_fd);
940 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800941 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400942 "ubd : Failed to start I/O thread (errno = %d) - "
943 "falling back to synchronous I/O\n", -io_pid);
944 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700945 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400946 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800947 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800948 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400949 if(err != 0)
950 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800951 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400952}
953
954device_initcall(ubd_driver_init);
955
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956static int ubd_open(struct inode *inode, struct file *filp)
957{
958 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800959 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 int err = 0;
961
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800962 if(ubd_dev->count == 0){
963 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 if(err){
965 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800966 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 goto out;
968 }
969 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800970 ubd_dev->count++;
971 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700972
973 /* This should no more be needed. And it didn't work anyway to exclude
974 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800975 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800976 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700978 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700980 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981}
982
983static int ubd_release(struct inode * inode, struct file * file)
984{
985 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800986 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800988 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800989 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -0700990 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991}
992
Jeff Dike91acb212005-10-10 23:10:32 -0400993static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
994 __u64 *cow_offset, unsigned long *bitmap,
995 __u64 bitmap_offset, unsigned long *bitmap_words,
996 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997{
Jeff Dike91acb212005-10-10 23:10:32 -0400998 __u64 sector = io_offset >> 9;
999 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000
Jeff Dike91acb212005-10-10 23:10:32 -04001001 for(i = 0; i < length >> 9; i++){
1002 if(cow_mask != NULL)
1003 ubd_set_bit(i, (unsigned char *) cow_mask);
1004 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1005 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006
Jeff Dike91acb212005-10-10 23:10:32 -04001007 update_bitmap = 1;
1008 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1009 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010
Jeff Dike91acb212005-10-10 23:10:32 -04001011 if(!update_bitmap)
1012 return;
1013
1014 *cow_offset = sector / (sizeof(unsigned long) * 8);
1015
1016 /* This takes care of the case where we're exactly at the end of the
1017 * device, and *cow_offset + 1 is off the end. So, just back it up
1018 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1019 * for the original diagnosis.
1020 */
1021 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1022 sizeof(unsigned long) - 1))
1023 (*cow_offset)--;
1024
1025 bitmap_words[0] = bitmap[*cow_offset];
1026 bitmap_words[1] = bitmap[*cow_offset + 1];
1027
1028 *cow_offset *= sizeof(unsigned long);
1029 *cow_offset += bitmap_offset;
1030}
1031
1032static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1033 __u64 bitmap_offset, __u64 bitmap_len)
1034{
1035 __u64 sector = req->offset >> 9;
1036 int i;
1037
1038 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1039 panic("Operation too long");
1040
1041 if(req->op == UBD_READ) {
1042 for(i = 0; i < req->length >> 9; i++){
1043 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001044 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001045 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001046 }
Jeff Dike91acb212005-10-10 23:10:32 -04001047 }
1048 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1049 &req->cow_offset, bitmap, bitmap_offset,
1050 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051}
1052
Jeff Dike62f96cb2007-02-10 01:44:16 -08001053/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001054static void prepare_request(struct request *req, struct io_thread_req *io_req,
1055 unsigned long long offset, int page_offset,
1056 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057{
1058 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001059 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001060
Jeff Dike62f96cb2007-02-10 01:44:16 -08001061 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001062 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1063 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001064 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001065 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 io_req->offset = offset;
1067 io_req->length = len;
1068 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001069 io_req->sector_mask = 0;
1070
1071 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001073 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001074 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 io_req->sectorsize = 1 << 9;
1076
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001077 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001078 cowify_req(io_req, ubd_dev->cow.bitmap,
1079 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001080
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081}
1082
Jeff Dike62f96cb2007-02-10 01:44:16 -08001083/* Called with dev->lock held */
Jens Axboe165125e2007-07-24 09:28:11 +02001084static void do_ubd_request(struct request_queue *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085{
Jeff Dike2adcec22007-05-06 14:51:37 -07001086 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 struct request *req;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001088 int n, last_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
Jeff Dikea0044bd2007-05-06 14:51:36 -07001090 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001091 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001092 if(dev->end_sg == 0){
1093 struct request *req = elv_next_request(q);
1094 if(req == NULL)
1095 return;
1096
1097 dev->request = req;
1098 blkdev_dequeue_request(req);
1099 dev->start_sg = 0;
1100 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001101 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001102
1103 req = dev->request;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001104 last_sectors = 0;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001105 while(dev->start_sg < dev->end_sg){
1106 struct scatterlist *sg = &dev->sg[dev->start_sg];
1107
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001108 req->sector += last_sectors;
Jeff Dike2adcec22007-05-06 14:51:37 -07001109 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001110 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001111 if(io_req == NULL){
1112 if(list_empty(&dev->restart))
1113 list_add(&dev->restart, &restart);
1114 return;
1115 }
1116 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001117 (unsigned long long) req->sector << 9,
1118 sg->offset, sg->length, sg->page);
1119
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001120 last_sectors = sg->length >> 9;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001121 n = os_write_file(thread_fd, &io_req,
1122 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001123 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001124 if(n != -EAGAIN)
1125 printk("write to io thread failed, "
1126 "errno = %d\n", -n);
1127 else if(list_empty(&dev->restart))
1128 list_add(&dev->restart, &restart);
1129 return;
1130 }
1131
Jeff Dikea0044bd2007-05-06 14:51:36 -07001132 dev->start_sg++;
1133 }
1134 dev->end_sg = 0;
1135 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001136 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137}
1138
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001139static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1140{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001141 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001142
1143 geo->heads = 128;
1144 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001145 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001146 return 0;
1147}
1148
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149static int ubd_ioctl(struct inode * inode, struct file * file,
1150 unsigned int cmd, unsigned long arg)
1151{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001152 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 struct hd_driveid ubd_id = {
1154 .cyls = 0,
1155 .heads = 128,
1156 .sectors = 32,
1157 };
1158
1159 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001162 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1164 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001165 return -EFAULT;
1166 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001167
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 case CDROMVOLREAD:
1169 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001170 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 volume.channel0 = 255;
1172 volume.channel1 = 255;
1173 volume.channel2 = 255;
1174 volume.channel3 = 255;
1175 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001176 return -EFAULT;
1177 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001179 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180}
1181
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001182static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183{
1184 struct uml_stat buf1, buf2;
1185 int err;
1186
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001187 if(from_cmdline == NULL)
1188 return 0;
1189 if(!strcmp(from_cmdline, from_cow))
1190 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191
1192 err = os_stat_file(from_cmdline, &buf1);
1193 if(err < 0){
1194 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001195 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 }
1197 err = os_stat_file(from_cow, &buf2);
1198 if(err < 0){
1199 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001200 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 }
1202 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001203 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204
1205 printk("Backing file mismatch - \"%s\" requested,\n"
1206 "\"%s\" specified in COW header of \"%s\"\n",
1207 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001208 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209}
1210
1211static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1212{
1213 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001214 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 int err;
1216
1217 err = os_file_modtime(file, &modtime);
1218 if(err < 0){
1219 printk("Failed to get modification time of backing file "
1220 "\"%s\", err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001221 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 }
1223
1224 err = os_file_size(file, &actual);
1225 if(err < 0){
1226 printk("Failed to get size of backing file \"%s\", "
1227 "err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001228 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 }
1230
Jeff Dikedc764e52007-05-06 14:51:41 -07001231 if(actual != size){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1233 * the typecast.*/
1234 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1235 "file\n", (unsigned long long) size, actual);
Jeff Dikedc764e52007-05-06 14:51:41 -07001236 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 }
1238 if(modtime != mtime){
1239 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1240 "file\n", mtime, modtime);
Jeff Dikedc764e52007-05-06 14:51:41 -07001241 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001243 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244}
1245
1246int read_cow_bitmap(int fd, void *buf, int offset, int len)
1247{
1248 int err;
1249
1250 err = os_seek_file(fd, offset);
1251 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001252 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
Jeff Dikedc764e52007-05-06 14:51:41 -07001254 err = os_read_file(fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001256 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257
Jeff Dikedc764e52007-05-06 14:51:41 -07001258 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259}
1260
Jeff Dike6c29256c2006-03-27 01:14:37 -08001261int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 char **backing_file_out, int *bitmap_offset_out,
1263 unsigned long *bitmap_len_out, int *data_offset_out,
1264 int *create_cow_out)
1265{
1266 time_t mtime;
1267 unsigned long long size;
1268 __u32 version, align;
1269 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001270 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271
1272 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001273 if (fd < 0) {
1274 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 *create_cow_out = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001276 if (!openflags->w ||
1277 ((fd != -EROFS) && (fd != -EACCES)))
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001278 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 openflags->w = 0;
1280 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001281 if (fd < 0)
1282 return fd;
Jeff Dikedc764e52007-05-06 14:51:41 -07001283 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
Jeff Dike6c29256c2006-03-27 01:14:37 -08001285 if(shared)
1286 printk("Not locking \"%s\" on the host\n", file);
1287 else {
1288 err = os_lock_file(fd, openflags->w);
1289 if(err < 0){
1290 printk("Failed to lock '%s', err = %d\n", file, -err);
1291 goto out_close;
1292 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 }
1294
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001295 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001296 if(backing_file_out == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -07001297 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298
1299 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1300 &size, &sectorsize, &align, bitmap_offset_out);
1301 if(err && (*backing_file_out != NULL)){
1302 printk("Failed to read COW header from COW file \"%s\", "
1303 "errno = %d\n", file, -err);
1304 goto out_close;
1305 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001306 if(err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001307 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001309 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001311 /* Allow switching only if no mismatch. */
1312 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 printk("Switching backing file to '%s'\n", *backing_file_out);
1314 err = write_cow_header(file, fd, *backing_file_out,
1315 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001316 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001318 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001320 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 *backing_file_out = backing_file;
1322 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001323 if (err)
1324 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 }
1326
1327 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1328 bitmap_len_out, data_offset_out);
1329
Jeff Dikedc764e52007-05-06 14:51:41 -07001330 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 out_close:
1332 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001333 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334}
1335
1336int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1337 int sectorsize, int alignment, int *bitmap_offset_out,
1338 unsigned long *bitmap_len_out, int *data_offset_out)
1339{
1340 int err, fd;
1341
1342 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001343 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 if(fd < 0){
1345 err = fd;
1346 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1347 -err);
1348 goto out;
1349 }
1350
1351 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1352 bitmap_offset_out, bitmap_len_out,
1353 data_offset_out);
1354 if(!err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001355 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 os_close_file(fd);
1357 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001358 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359}
1360
Jeff Dike91acb212005-10-10 23:10:32 -04001361static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362{
Jeff Dike91acb212005-10-10 23:10:32 -04001363 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
Jeff Dike91acb212005-10-10 23:10:32 -04001365 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001366 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Jeff Dike91acb212005-10-10 23:10:32 -04001368 n = os_seek_file(req->fds[1], req->cow_offset);
1369 if(n < 0){
1370 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001371 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001372 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001374 n = os_write_file(req->fds[1], &req->bitmap_words,
1375 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001376 if(n != sizeof(req->bitmap_words)){
1377 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1378 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001379 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001380 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381
Jeff Dikedc764e52007-05-06 14:51:41 -07001382 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383}
Jeff Dike91acb212005-10-10 23:10:32 -04001384
1385void do_io(struct io_thread_req *req)
1386{
1387 char *buf;
1388 unsigned long len;
1389 int n, nsectors, start, end, bit;
1390 int err;
1391 __u64 off;
1392
1393 nsectors = req->length / req->sectorsize;
1394 start = 0;
1395 do {
1396 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1397 end = start;
1398 while((end < nsectors) &&
1399 (ubd_test_bit(end, (unsigned char *)
1400 &req->sector_mask) == bit))
1401 end++;
1402
1403 off = req->offset + req->offsets[bit] +
1404 start * req->sectorsize;
1405 len = (end - start) * req->sectorsize;
1406 buf = &req->buffer[start * req->sectorsize];
1407
1408 err = os_seek_file(req->fds[bit], off);
1409 if(err < 0){
1410 printk("do_io - lseek failed : err = %d\n", -err);
1411 req->error = 1;
1412 return;
1413 }
1414 if(req->op == UBD_READ){
1415 n = 0;
1416 do {
1417 buf = &buf[n];
1418 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001419 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001420 if (n < 0) {
1421 printk("do_io - read failed, err = %d "
1422 "fd = %d\n", -n, req->fds[bit]);
1423 req->error = 1;
1424 return;
1425 }
1426 } while((n < len) && (n != 0));
1427 if (n < len) memset(&buf[n], 0, len - n);
1428 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001429 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001430 if(n != len){
1431 printk("do_io - write failed err = %d "
1432 "fd = %d\n", -n, req->fds[bit]);
1433 req->error = 1;
1434 return;
1435 }
1436 }
1437
1438 start = end;
1439 } while(start < nsectors);
1440
1441 req->error = update_bitmap(req);
1442}
1443
1444/* Changed in start_io_thread, which is serialized by being called only
1445 * from ubd_init, which is an initcall.
1446 */
1447int kernel_fd = -1;
1448
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001449/* Only changed by the io thread. XXX: currently unused. */
1450static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001451
1452int io_thread(void *arg)
1453{
Jeff Dike2adcec22007-05-06 14:51:37 -07001454 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001455 int n;
1456
1457 ignore_sigwinch_sig();
1458 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001459 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001460 sizeof(struct io_thread_req *));
1461 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001462 if(n < 0)
1463 printk("io_thread - read failed, fd = %d, "
1464 "err = %d\n", kernel_fd, -n);
1465 else {
1466 printk("io_thread - short read, fd = %d, "
1467 "length = %d\n", kernel_fd, n);
1468 }
1469 continue;
1470 }
1471 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001472 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001473 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001474 sizeof(struct io_thread_req *));
1475 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001476 printk("io_thread - write failed, fd = %d, err = %d\n",
1477 kernel_fd, -n);
1478 }
Jeff Dike91acb212005-10-10 23:10:32 -04001479
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001480 return 0;
1481}