blob: 7e6cdde62ead261e6140c30cc51d8517e0b65aac [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"
WANG Cong23464ff2007-10-24 13:07:11 +020038#include "linux/scatterlist.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include "asm/segment.h"
40#include "asm/uaccess.h"
41#include "asm/irq.h"
42#include "asm/types.h"
43#include "asm/tlbflush.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include "mem_user.h"
45#include "kern_util.h"
46#include "kern.h"
47#include "mconsole_kern.h"
48#include "init.h"
49#include "irq_user.h"
50#include "irq_kern.h"
51#include "ubd_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#include "os.h"
53#include "mem.h"
54#include "mem_kern.h"
55#include "cow.h"
56
Jeff Dike7b9014c2005-05-20 13:59:11 -070057enum ubd_req { UBD_READ, UBD_WRITE };
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
59struct io_thread_req {
Jeff Dike62f96cb2007-02-10 01:44:16 -080060 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -040061 enum ubd_req op;
Linus Torvalds1da177e2005-04-16 15:20:36 -070062 int fds[2];
63 unsigned long offsets[2];
64 unsigned long long offset;
65 unsigned long length;
66 char *buffer;
67 int sectorsize;
Jeff Dike91acb212005-10-10 23:10:32 -040068 unsigned long sector_mask;
69 unsigned long long cow_offset;
70 unsigned long bitmap_words[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 int error;
72};
73
Jeff Dike6c29256c2006-03-27 01:14:37 -080074extern int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 char **backing_file_out, int *bitmap_offset_out,
76 unsigned long *bitmap_len_out, int *data_offset_out,
77 int *create_cow_out);
78extern int create_cow_file(char *cow_file, char *backing_file,
79 struct openflags flags, int sectorsize,
80 int alignment, int *bitmap_offset_out,
81 unsigned long *bitmap_len_out,
82 int *data_offset_out);
83extern int read_cow_bitmap(int fd, void *buf, int offset, int len);
Jeff Dike91acb212005-10-10 23:10:32 -040084extern void do_io(struct io_thread_req *req);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
Jeff Dike91acb212005-10-10 23:10:32 -040086static inline int ubd_test_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070087{
88 __u64 n;
89 int bits, off;
90
Jeff Dike91acb212005-10-10 23:10:32 -040091 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 n = bit / bits;
93 off = bit % bits;
Jeff Dikedc764e52007-05-06 14:51:41 -070094 return (data[n] & (1 << off)) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095}
96
Jeff Dike91acb212005-10-10 23:10:32 -040097static inline void ubd_set_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070098{
99 __u64 n;
100 int bits, off;
101
Jeff Dike91acb212005-10-10 23:10:32 -0400102 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 n = bit / bits;
104 off = bit % bits;
Jeff Dike91acb212005-10-10 23:10:32 -0400105 data[n] |= (1 << off);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106}
107/*End stuff from ubd_user.h*/
108
109#define DRIVER_NAME "uml-blkdev"
110
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800111static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113static int ubd_open(struct inode * inode, struct file * filp);
114static int ubd_release(struct inode * inode, struct file * file);
115static int ubd_ioctl(struct inode * inode, struct file * file,
116 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800117static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800119#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121static struct block_device_operations ubd_blops = {
122 .owner = THIS_MODULE,
123 .open = ubd_open,
124 .release = ubd_release,
125 .ioctl = ubd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800126 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127};
128
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129/* Protected by ubd_lock */
130static int fake_major = MAJOR_NR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131static struct gendisk *ubd_gendisk[MAX_DEV];
132static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800133
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134#ifdef CONFIG_BLK_DEV_UBD_SYNC
135#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
136 .cl = 1 })
137#else
138#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
139 .cl = 1 })
140#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141static struct openflags global_openflags = OPEN_FLAGS;
142
143struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800144 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800146 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 int fd;
148 unsigned long *bitmap;
149 unsigned long bitmap_len;
150 int bitmap_offset;
Jeff Dikedc764e52007-05-06 14:51:41 -0700151 int data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152};
153
Jeff Dikea0044bd2007-05-06 14:51:36 -0700154#define MAX_SG 64
155
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156struct ubd {
Jeff Dikea0044bd2007-05-06 14:51:36 -0700157 struct list_head restart;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800158 /* name (and fd, below) of the file opened for writing, either the
159 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 char *file;
161 int count;
162 int fd;
163 __u64 size;
164 struct openflags boot_openflags;
165 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800166 unsigned shared:1;
167 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 struct cow cow;
169 struct platform_device pdev;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800170 struct request_queue *queue;
171 spinlock_t lock;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700172 struct scatterlist sg[MAX_SG];
173 struct request *request;
174 int start_sg, end_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175};
176
177#define DEFAULT_COW { \
178 .file = NULL, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700179 .fd = -1, \
180 .bitmap = NULL, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 .bitmap_offset = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700182 .data_offset = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183}
184
185#define DEFAULT_UBD { \
186 .file = NULL, \
187 .count = 0, \
188 .fd = -1, \
189 .size = -1, \
190 .boot_openflags = OPEN_FLAGS, \
191 .openflags = OPEN_FLAGS, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700192 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800193 .shared = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700194 .cow = DEFAULT_COW, \
Jeff Dike62f96cb2007-02-10 01:44:16 -0800195 .lock = SPIN_LOCK_UNLOCKED, \
Jeff Dikea0044bd2007-05-06 14:51:36 -0700196 .request = NULL, \
197 .start_sg = 0, \
198 .end_sg = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199}
200
Jeff Dikeb8831a12007-02-10 01:44:17 -0800201/* Protected by ubd_lock */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800202struct ubd ubd_devs[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204/* Only changed by fake_ide_setup which is a setup */
205static int fake_ide = 0;
206static struct proc_dir_entry *proc_ide_root = NULL;
207static struct proc_dir_entry *proc_ide = NULL;
208
209static void make_proc_ide(void)
210{
211 proc_ide_root = proc_mkdir("ide", NULL);
212 proc_ide = proc_mkdir("ide0", proc_ide_root);
213}
214
215static int proc_ide_read_media(char *page, char **start, off_t off, int count,
216 int *eof, void *data)
217{
218 int len;
219
220 strcpy(page, "disk\n");
221 len = strlen("disk\n");
222 len -= off;
223 if (len < count){
224 *eof = 1;
225 if (len <= 0) return 0;
226 }
227 else len = count;
228 *start = page + off;
229 return len;
230}
231
232static void make_ide_entries(char *dev_name)
233{
234 struct proc_dir_entry *dir, *ent;
235 char name[64];
236
237 if(proc_ide_root == NULL) make_proc_ide();
238
239 dir = proc_mkdir(dev_name, proc_ide);
240 if(!dir) return;
241
242 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
243 if(!ent) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 ent->data = NULL;
245 ent->read_proc = proc_ide_read_media;
246 ent->write_proc = NULL;
247 sprintf(name,"ide0/%s", dev_name);
248 proc_symlink(dev_name, proc_ide_root, name);
249}
250
251static int fake_ide_setup(char *str)
252{
253 fake_ide = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700254 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255}
256
257__setup("fake_ide", fake_ide_setup);
258
259__uml_help(fake_ide_setup,
260"fake_ide\n"
261" Create ide0 entries that map onto ubd devices.\n\n"
262);
263
264static int parse_unit(char **ptr)
265{
266 char *str = *ptr, *end;
267 int n = -1;
268
269 if(isdigit(*str)) {
270 n = simple_strtoul(str, &end, 0);
271 if(end == str)
Jeff Dikedc764e52007-05-06 14:51:41 -0700272 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 *ptr = end;
274 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800275 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 n = *str - 'a';
277 str++;
278 *ptr = str;
279 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700280 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281}
282
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800283/* If *index_out == -1 at exit, the passed option was a general one;
284 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
285 * should not be freed on exit.
286 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800287static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800289 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 struct openflags flags = global_openflags;
291 char *backing_file;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800292 int n, err = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
294 if(index_out) *index_out = -1;
295 n = *str;
296 if(n == '='){
297 char *end;
298 int major;
299
300 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 if(!strcmp(str, "sync")){
302 global_openflags = of_sync(global_openflags);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800303 goto out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 }
305
Jeff Dikef28169d2007-02-10 01:43:53 -0800306 err = -EINVAL;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800307 major = simple_strtoul(str, &end, 0);
308 if((*end != '\0') || (end == str)){
309 *error_out = "Didn't parse major number";
310 goto out1;
311 }
312
Jeff Dikef28169d2007-02-10 01:43:53 -0800313 mutex_lock(&ubd_lock);
314 if(fake_major != MAJOR_NR){
315 *error_out = "Can't assign a fake major twice";
316 goto out1;
317 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800318
Jeff Dikef28169d2007-02-10 01:43:53 -0800319 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320
321 printk(KERN_INFO "Setting extra ubd major number to %d\n",
322 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800323 err = 0;
324 out1:
325 mutex_unlock(&ubd_lock);
326 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 }
328
329 n = parse_unit(&str);
330 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800331 *error_out = "Couldn't parse device number";
332 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 }
334 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800335 *error_out = "Device number out of range";
336 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 }
338
Jeff Dikef28169d2007-02-10 01:43:53 -0800339 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800340 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800342 ubd_dev = &ubd_devs[n];
343 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800344 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 goto out;
346 }
347
348 if (index_out)
349 *index_out = n;
350
Jeff Dikef28169d2007-02-10 01:43:53 -0800351 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800352 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 switch (*str) {
354 case 'r':
355 flags.w = 0;
356 break;
357 case 's':
358 flags.s = 1;
359 break;
360 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800361 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800363 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800364 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800365 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 case '=':
367 str++;
368 goto break_loop;
369 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800370 *error_out = "Expected '=' or flag letter "
371 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 goto out;
373 }
374 str++;
375 }
376
Jeff Dikef28169d2007-02-10 01:43:53 -0800377 if (*str == '=')
378 *error_out = "Too many flags specified";
379 else
380 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 goto out;
382
383break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 backing_file = strchr(str, ',');
385
Jeff Dikef28169d2007-02-10 01:43:53 -0800386 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Jeff Dikef28169d2007-02-10 01:43:53 -0800389 if(backing_file != NULL){
390 if(ubd_dev->no_cow){
391 *error_out = "Can't specify both 'd' and a cow file";
392 goto out;
393 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 else {
395 *backing_file = '\0';
396 backing_file++;
397 }
398 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800399 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800400 ubd_dev->file = str;
401 ubd_dev->cow.file = backing_file;
402 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800404 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800405 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406}
407
408static int ubd_setup(char *str)
409{
Jeff Dikef28169d2007-02-10 01:43:53 -0800410 char *error;
411 int err;
412
413 err = ubd_setup_common(str, NULL, &error);
414 if(err)
415 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
416 "%s\n", str, error);
417 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418}
419
420__setup("ubd", ubd_setup);
421__uml_help(ubd_setup,
422"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
423" This is used to associate a device with a file in the underlying\n"
424" filesystem. When specifying two filenames, the first one is the\n"
425" COW name and the second is the backing file name. As separator you can\n"
426" use either a ':' or a ',': the first one allows writing things like;\n"
427" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
428" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800429" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430" a COW file or a backing file. To override this detection, add the 'd'\n"
431" flag:\n"
432" ubd0d=BackingFile\n"
433" Usually, there is a filesystem in the file, but \n"
434" that's not required. Swap devices containing swap files can be\n"
435" specified like this. Also, a file which doesn't contain a\n"
436" filesystem can have its contents read in the virtual \n"
437" machine by running 'dd' on the device. <n> must be in the range\n"
438" 0 to 7. Appending an 'r' to the number will cause that device\n"
439" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
440" an 's' will cause data to be written to disk on the host immediately.\n\n"
441);
442
443static int udb_setup(char *str)
444{
445 printk("udb%s specified on command line is almost certainly a ubd -> "
446 "udb TYPO\n", str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700447 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448}
449
450__setup("udb", udb_setup);
451__uml_help(udb_setup,
452"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700453" This option is here solely to catch ubd -> udb typos, which can be\n"
454" to impossible to catch visually unless you specifically look for\n"
455" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456" in the boot output.\n\n"
457);
458
459static int fakehd_set = 0;
460static int fakehd(char *str)
461{
462 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
463 fakehd_set = 1;
464 return 1;
465}
466
467__setup("fakehd", fakehd);
468__uml_help(fakehd,
469"fakehd\n"
470" Change the ubd device name to \"hd\".\n\n"
471);
472
Jens Axboe165125e2007-07-24 09:28:11 +0200473static void do_ubd_request(struct request_queue * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400474
475/* Only changed by ubd_init, which is an initcall. */
476int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
Jeff Dikea0044bd2007-05-06 14:51:36 -0700478static void ubd_end_request(struct request *req, int bytes, int uptodate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700480 if (!end_that_request_first(req, uptodate, bytes >> 9)) {
481 struct ubd *dev = req->rq_disk->private_data;
482 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
Jeff Dikea0044bd2007-05-06 14:51:36 -0700484 add_disk_randomness(req->rq_disk);
485 spin_lock_irqsave(&dev->lock, flags);
486 end_that_request_last(req, uptodate);
487 spin_unlock_irqrestore(&dev->lock, flags);
Jeff Dike91acb212005-10-10 23:10:32 -0400488 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489}
490
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800491/* Callable only from interrupt context - otherwise you need to do
492 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700493static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700495 if(bytes < 0){
496 ubd_end_request(req, 0, 0);
497 return;
498 }
499 ubd_end_request(req, bytes, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500}
501
Jeff Dikea0044bd2007-05-06 14:51:36 -0700502static LIST_HEAD(restart);
503
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800504/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800505/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400506static void ubd_handler(void)
507{
Jeff Dike2adcec22007-05-06 14:51:37 -0700508 struct io_thread_req *req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800509 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700510 struct ubd *ubd;
511 struct list_head *list, *next_ele;
512 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400513 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Jeff Dikea0044bd2007-05-06 14:51:36 -0700515 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700516 n = os_read_file(thread_fd, &req,
517 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700518 if(n != sizeof(req)){
519 if(n == -EAGAIN)
520 break;
521 printk(KERN_ERR "spurious interrupt in ubd_handler, "
522 "err = %d\n", -n);
523 return;
524 }
525
Jeff Dike2adcec22007-05-06 14:51:37 -0700526 rq = req->req;
527 rq->nr_sectors -= req->length >> 9;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700528 if(rq->nr_sectors == 0)
529 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike2adcec22007-05-06 14:51:37 -0700530 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400531 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800532 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700533
534 list_for_each_safe(list, next_ele, &restart){
535 ubd = container_of(list, struct ubd, restart);
536 list_del_init(&ubd->restart);
537 spin_lock_irqsave(&ubd->lock, flags);
538 do_ubd_request(ubd->queue);
539 spin_unlock_irqrestore(&ubd->lock, flags);
540 }
Jeff Dike91acb212005-10-10 23:10:32 -0400541}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
Al Viro7bea96f2006-10-08 22:49:34 +0100543static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544{
Jeff Dike91acb212005-10-10 23:10:32 -0400545 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700546 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547}
548
Jeff Dike91acb212005-10-10 23:10:32 -0400549/* Only changed by ubd_init, which is an initcall. */
550static int io_pid = -1;
551
552void kill_io_thread(void)
553{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800554 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400555 os_kill_process(io_pid, 1);
556}
557
558__uml_exitcall(kill_io_thread);
559
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800560static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561{
562 char *file;
563
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800564 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700565 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566}
567
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800568static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800570 os_close_file(ubd_dev->fd);
571 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 return;
573
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800574 os_close_file(ubd_dev->cow.fd);
575 vfree(ubd_dev->cow.bitmap);
576 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577}
578
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800579static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580{
581 struct openflags flags;
582 char **back_ptr;
583 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800584 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800586 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800588 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
589 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800590
591 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800592 back_ptr, &ubd_dev->cow.bitmap_offset,
593 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800594 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800596 if((fd == -ENOENT) && create_cow){
597 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800598 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
599 &ubd_dev->cow.bitmap_offset,
600 &ubd_dev->cow.bitmap_len,
601 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800602 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800604 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 }
606 }
607
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800608 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800609 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800610 -fd);
611 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800613 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800615 if(ubd_dev->cow.file != NULL){
Jeff Dikef4768ff2007-08-22 14:01:53 -0700616 blk_queue_max_sectors(ubd_dev->queue, 8 * sizeof(long));
617
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 err = -ENOMEM;
Jesper Juhlda2486b2007-10-16 01:27:19 -0700619 ubd_dev->cow.bitmap = vmalloc(ubd_dev->cow.bitmap_len);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800620 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
622 goto error;
623 }
624 flush_tlb_kernel_vm();
625
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800626 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
627 ubd_dev->cow.bitmap_offset,
628 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 if(err < 0)
630 goto error;
631
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800632 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800634 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800635 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800637 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700639 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800641 os_close_file(ubd_dev->fd);
Jeff Dikedc764e52007-05-06 14:51:41 -0700642 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643}
644
Jeff Dike2e3f5252007-05-06 14:51:29 -0700645static void ubd_device_release(struct device *dev)
646{
647 struct ubd *ubd_dev = dev->driver_data;
648
649 blk_cleanup_queue(ubd_dev->queue);
650 *ubd_dev = ((struct ubd) DEFAULT_UBD);
651}
652
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800653static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800654 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655{
656 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
658 disk = alloc_disk(1 << UBD_SHIFT);
659 if(disk == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -0700660 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
662 disk->major = major;
663 disk->first_minor = unit << UBD_SHIFT;
664 disk->fops = &ubd_blops;
665 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700666 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700668 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
671 /* sysfs register (not for ide fake devices) */
672 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800673 ubd_devs[unit].pdev.id = unit;
674 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700675 ubd_devs[unit].pdev.dev.release = ubd_device_release;
676 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800677 platform_device_register(&ubd_devs[unit].pdev);
678 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 }
680
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800681 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800682 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 add_disk(disk);
684
685 *disk_out = disk;
686 return 0;
687}
688
689#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
690
Jeff Dikef28169d2007-02-10 01:43:53 -0800691static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800693 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800694 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800696 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700697 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800699 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800700 if(err < 0){
701 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700702 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800703 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800705 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706
Jeff Dikea0044bd2007-05-06 14:51:36 -0700707 INIT_LIST_HEAD(&ubd_dev->restart);
WANG Cong4f40c052007-11-05 14:50:59 -0800708 sg_init_table(ubd_dev->sg, MAX_SG);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700709
Jeff Dike62f96cb2007-02-10 01:44:16 -0800710 err = -ENOMEM;
711 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
712 if (ubd_dev->queue == NULL) {
713 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700714 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800715 }
716 ubd_dev->queue->queuedata = ubd_dev;
717
Jeff Dikea0044bd2007-05-06 14:51:36 -0700718 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800719 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
720 if(err){
721 *error_out = "Failed to register device";
722 goto out_cleanup;
723 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800724
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800726 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800727 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
729 /* perhaps this should also be under the "if (fake_major)" above */
730 /* using the fake_disk->disk_name and also the fakehd_set name */
731 if (fake_ide)
732 make_ide_entries(ubd_gendisk[n]->disk_name);
733
Jeff Dikeec7cf782005-09-03 15:57:29 -0700734 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700735out:
736 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800737
738out_cleanup:
739 blk_cleanup_queue(ubd_dev->queue);
740 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741}
742
Jeff Dikef28169d2007-02-10 01:43:53 -0800743static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800745 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746
Jeff Dikef28169d2007-02-10 01:43:53 -0800747 /* This string is possibly broken up and stored, so it's only
748 * freed if ubd_setup_common fails, or if only general options
749 * were set.
750 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800751 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800752 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800753 *error_out = "Failed to allocate memory";
754 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800756
757 ret = ubd_setup_common(str, &n, error_out);
758 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800759 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800760
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800761 if (n == -1) {
762 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800763 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800764 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Jeff Dikedc764e52007-05-06 14:51:41 -0700766 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800767 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800768 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800769 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700770 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800772out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700773 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800774
775err_free:
776 kfree(str);
777 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778}
779
780static int ubd_get_config(char *name, char *str, int size, char **error_out)
781{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800782 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 int n, len = 0;
784
785 n = parse_unit(&name);
786 if((n >= MAX_DEV) || (n < 0)){
787 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700788 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 }
790
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800791 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800792 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800794 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 CONFIG_CHUNK(str, size, len, "", 1);
796 goto out;
797 }
798
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800799 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800801 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800803 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 }
805 else CONFIG_CHUNK(str, size, len, "", 1);
806
807 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800808 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700809 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810}
811
Jeff Dike29d56cf2005-06-25 14:55:25 -0700812static int ubd_id(char **str, int *start_out, int *end_out)
813{
Jeff Dikedc764e52007-05-06 14:51:41 -0700814 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700815
816 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700817 *start_out = 0;
818 *end_out = MAX_DEV - 1;
819 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700820}
821
Jeff Dikef28169d2007-02-10 01:43:53 -0800822static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700824 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800825 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700826 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800828 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800830 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700831
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800832 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700833 goto out;
834
835 /* you cannot remove a open disk */
836 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800837 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700838 goto out;
839
Jeff Dikedc764e52007-05-06 14:51:41 -0700840 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700841 if(disk != NULL){
842 del_gendisk(disk);
843 put_disk(disk);
844 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845
846 if(fake_gendisk[n] != NULL){
847 del_gendisk(fake_gendisk[n]);
848 put_disk(fake_gendisk[n]);
849 fake_gendisk[n] = NULL;
850 }
851
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700853 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700854out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800855 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700856 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857}
858
Jeff Dikef28169d2007-02-10 01:43:53 -0800859/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800860 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800861 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800863 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 .name = "ubd",
865 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -0700866 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700867 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 .remove = ubd_remove,
869};
870
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800871static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872{
873 mconsole_register_dev(&ubd_mc);
874 return 0;
875}
876
877__initcall(ubd_mc_init);
878
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800879static int __init ubd0_init(void)
880{
881 struct ubd *ubd_dev = &ubd_devs[0];
882
Jeff Dikeb8831a12007-02-10 01:44:17 -0800883 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800884 if(ubd_dev->file == NULL)
885 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -0800886 mutex_unlock(&ubd_lock);
887
Jeff Dikedc764e52007-05-06 14:51:41 -0700888 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800889}
890
891__initcall(ubd0_init);
892
Jeff Dikeb8831a12007-02-10 01:44:17 -0800893/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +0000894static struct platform_driver ubd_driver = {
895 .driver = {
896 .name = DRIVER_NAME,
897 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898};
899
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800900static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901{
Jeff Dikef28169d2007-02-10 01:43:53 -0800902 char *error;
903 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 if (register_blkdev(MAJOR_NR, "ubd"))
906 return -1;
907
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 if (fake_major != MAJOR_NR) {
909 char name[sizeof("ubd_nnn\0")];
910
911 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 if (register_blkdev(fake_major, "ubd"))
913 return -1;
914 }
Russell King3ae5eae2005-11-09 22:32:44 +0000915 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -0700916 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800917 for (i = 0; i < MAX_DEV; i++){
918 err = ubd_add(i, &error);
919 if(err)
920 printk(KERN_ERR "Failed to initialize ubd device %d :"
921 "%s\n", i, error);
922 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700923 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 return 0;
925}
926
927late_initcall(ubd_init);
928
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800929static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400930 unsigned long stack;
931 int err;
932
933 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
934 if(global_openflags.s){
935 printk(KERN_INFO "ubd: Synchronous mode\n");
936 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
937 * enough. So use anyway the io thread. */
938 }
939 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800940 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400941 &thread_fd);
942 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800943 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400944 "ubd : Failed to start I/O thread (errno = %d) - "
945 "falling back to synchronous I/O\n", -io_pid);
946 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700947 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400948 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800949 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800950 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400951 if(err != 0)
952 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800953 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400954}
955
956device_initcall(ubd_driver_init);
957
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958static int ubd_open(struct inode *inode, struct file *filp)
959{
960 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800961 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 int err = 0;
963
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800964 if(ubd_dev->count == 0){
965 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 if(err){
967 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800968 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 goto out;
970 }
971 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800972 ubd_dev->count++;
973 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700974
975 /* This should no more be needed. And it didn't work anyway to exclude
976 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800977 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800978 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700980 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700982 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983}
984
985static int ubd_release(struct inode * inode, struct file * file)
986{
987 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800988 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800990 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800991 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -0700992 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993}
994
Jeff Dike91acb212005-10-10 23:10:32 -0400995static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
996 __u64 *cow_offset, unsigned long *bitmap,
997 __u64 bitmap_offset, unsigned long *bitmap_words,
998 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999{
Jeff Dike91acb212005-10-10 23:10:32 -04001000 __u64 sector = io_offset >> 9;
1001 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
Jeff Dike91acb212005-10-10 23:10:32 -04001003 for(i = 0; i < length >> 9; i++){
1004 if(cow_mask != NULL)
1005 ubd_set_bit(i, (unsigned char *) cow_mask);
1006 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1007 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
Jeff Dike91acb212005-10-10 23:10:32 -04001009 update_bitmap = 1;
1010 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1011 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012
Jeff Dike91acb212005-10-10 23:10:32 -04001013 if(!update_bitmap)
1014 return;
1015
1016 *cow_offset = sector / (sizeof(unsigned long) * 8);
1017
1018 /* This takes care of the case where we're exactly at the end of the
1019 * device, and *cow_offset + 1 is off the end. So, just back it up
1020 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1021 * for the original diagnosis.
1022 */
1023 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1024 sizeof(unsigned long) - 1))
1025 (*cow_offset)--;
1026
1027 bitmap_words[0] = bitmap[*cow_offset];
1028 bitmap_words[1] = bitmap[*cow_offset + 1];
1029
1030 *cow_offset *= sizeof(unsigned long);
1031 *cow_offset += bitmap_offset;
1032}
1033
1034static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1035 __u64 bitmap_offset, __u64 bitmap_len)
1036{
1037 __u64 sector = req->offset >> 9;
1038 int i;
1039
1040 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1041 panic("Operation too long");
1042
1043 if(req->op == UBD_READ) {
1044 for(i = 0; i < req->length >> 9; i++){
1045 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001046 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001047 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001048 }
Jeff Dike91acb212005-10-10 23:10:32 -04001049 }
1050 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1051 &req->cow_offset, bitmap, bitmap_offset,
1052 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053}
1054
Jeff Dike62f96cb2007-02-10 01:44:16 -08001055/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001056static void prepare_request(struct request *req, struct io_thread_req *io_req,
1057 unsigned long long offset, int page_offset,
1058 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059{
1060 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001061 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001062
Jeff Dike62f96cb2007-02-10 01:44:16 -08001063 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001064 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1065 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001066 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001067 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 io_req->offset = offset;
1069 io_req->length = len;
1070 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001071 io_req->sector_mask = 0;
1072
1073 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001075 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001076 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 io_req->sectorsize = 1 << 9;
1078
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001079 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001080 cowify_req(io_req, ubd_dev->cow.bitmap,
1081 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001082
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083}
1084
Jeff Dike62f96cb2007-02-10 01:44:16 -08001085/* Called with dev->lock held */
Jens Axboe165125e2007-07-24 09:28:11 +02001086static void do_ubd_request(struct request_queue *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087{
Jeff Dike2adcec22007-05-06 14:51:37 -07001088 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 struct request *req;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001090 int n, last_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091
Jeff Dikea0044bd2007-05-06 14:51:36 -07001092 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001093 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001094 if(dev->end_sg == 0){
1095 struct request *req = elv_next_request(q);
1096 if(req == NULL)
1097 return;
1098
1099 dev->request = req;
1100 blkdev_dequeue_request(req);
1101 dev->start_sg = 0;
1102 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001103 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001104
1105 req = dev->request;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001106 last_sectors = 0;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001107 while(dev->start_sg < dev->end_sg){
1108 struct scatterlist *sg = &dev->sg[dev->start_sg];
1109
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001110 req->sector += last_sectors;
Jeff Dike2adcec22007-05-06 14:51:37 -07001111 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001112 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001113 if(io_req == NULL){
1114 if(list_empty(&dev->restart))
1115 list_add(&dev->restart, &restart);
1116 return;
1117 }
1118 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001119 (unsigned long long) req->sector << 9,
Jens Axboe45711f12007-10-22 21:19:53 +02001120 sg->offset, sg->length, sg_page(sg));
Jeff Dikea0044bd2007-05-06 14:51:36 -07001121
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001122 last_sectors = sg->length >> 9;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001123 n = os_write_file(thread_fd, &io_req,
1124 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001125 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001126 if(n != -EAGAIN)
1127 printk("write to io thread failed, "
1128 "errno = %d\n", -n);
1129 else if(list_empty(&dev->restart))
1130 list_add(&dev->restart, &restart);
1131 return;
1132 }
1133
Jeff Dikea0044bd2007-05-06 14:51:36 -07001134 dev->start_sg++;
1135 }
1136 dev->end_sg = 0;
1137 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001138 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139}
1140
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001141static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1142{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001143 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001144
1145 geo->heads = 128;
1146 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001147 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001148 return 0;
1149}
1150
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151static int ubd_ioctl(struct inode * inode, struct file * file,
1152 unsigned int cmd, unsigned long arg)
1153{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001154 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 struct hd_driveid ubd_id = {
1156 .cyls = 0,
1157 .heads = 128,
1158 .sectors = 32,
1159 };
1160
1161 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001164 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1166 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001167 return -EFAULT;
1168 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001169
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 case CDROMVOLREAD:
1171 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001172 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 volume.channel0 = 255;
1174 volume.channel1 = 255;
1175 volume.channel2 = 255;
1176 volume.channel3 = 255;
1177 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001178 return -EFAULT;
1179 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001181 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182}
1183
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001184static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185{
1186 struct uml_stat buf1, buf2;
1187 int err;
1188
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001189 if(from_cmdline == NULL)
1190 return 0;
1191 if(!strcmp(from_cmdline, from_cow))
1192 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193
1194 err = os_stat_file(from_cmdline, &buf1);
1195 if(err < 0){
1196 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001197 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 }
1199 err = os_stat_file(from_cow, &buf2);
1200 if(err < 0){
1201 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001202 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 }
1204 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001205 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206
1207 printk("Backing file mismatch - \"%s\" requested,\n"
1208 "\"%s\" specified in COW header of \"%s\"\n",
1209 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001210 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211}
1212
1213static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1214{
1215 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001216 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 int err;
1218
1219 err = os_file_modtime(file, &modtime);
1220 if(err < 0){
1221 printk("Failed to get modification time of backing file "
1222 "\"%s\", err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001223 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 }
1225
1226 err = os_file_size(file, &actual);
1227 if(err < 0){
1228 printk("Failed to get size of backing file \"%s\", "
1229 "err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001230 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 }
1232
Jeff Dikedc764e52007-05-06 14:51:41 -07001233 if(actual != size){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1235 * the typecast.*/
1236 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1237 "file\n", (unsigned long long) size, actual);
Jeff Dikedc764e52007-05-06 14:51:41 -07001238 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 }
1240 if(modtime != mtime){
1241 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1242 "file\n", mtime, modtime);
Jeff Dikedc764e52007-05-06 14:51:41 -07001243 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001245 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246}
1247
1248int read_cow_bitmap(int fd, void *buf, int offset, int len)
1249{
1250 int err;
1251
1252 err = os_seek_file(fd, offset);
1253 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001254 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255
Jeff Dikedc764e52007-05-06 14:51:41 -07001256 err = os_read_file(fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001258 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259
Jeff Dikedc764e52007-05-06 14:51:41 -07001260 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261}
1262
Jeff Dike6c29256c2006-03-27 01:14:37 -08001263int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 char **backing_file_out, int *bitmap_offset_out,
1265 unsigned long *bitmap_len_out, int *data_offset_out,
1266 int *create_cow_out)
1267{
1268 time_t mtime;
1269 unsigned long long size;
1270 __u32 version, align;
1271 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001272 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
1274 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001275 if (fd < 0) {
1276 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 *create_cow_out = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001278 if (!openflags->w ||
1279 ((fd != -EROFS) && (fd != -EACCES)))
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001280 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 openflags->w = 0;
1282 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001283 if (fd < 0)
1284 return fd;
Jeff Dikedc764e52007-05-06 14:51:41 -07001285 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286
Jeff Dike6c29256c2006-03-27 01:14:37 -08001287 if(shared)
1288 printk("Not locking \"%s\" on the host\n", file);
1289 else {
1290 err = os_lock_file(fd, openflags->w);
1291 if(err < 0){
1292 printk("Failed to lock '%s', err = %d\n", file, -err);
1293 goto out_close;
1294 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 }
1296
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001297 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001298 if(backing_file_out == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -07001299 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300
1301 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1302 &size, &sectorsize, &align, bitmap_offset_out);
1303 if(err && (*backing_file_out != NULL)){
1304 printk("Failed to read COW header from COW file \"%s\", "
1305 "errno = %d\n", file, -err);
1306 goto out_close;
1307 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001308 if(err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001309 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001311 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001313 /* Allow switching only if no mismatch. */
1314 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 printk("Switching backing file to '%s'\n", *backing_file_out);
1316 err = write_cow_header(file, fd, *backing_file_out,
1317 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001318 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001320 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001322 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 *backing_file_out = backing_file;
1324 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001325 if (err)
1326 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 }
1328
1329 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1330 bitmap_len_out, data_offset_out);
1331
Jeff Dikedc764e52007-05-06 14:51:41 -07001332 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 out_close:
1334 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001335 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336}
1337
1338int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1339 int sectorsize, int alignment, int *bitmap_offset_out,
1340 unsigned long *bitmap_len_out, int *data_offset_out)
1341{
1342 int err, fd;
1343
1344 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001345 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 if(fd < 0){
1347 err = fd;
1348 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1349 -err);
1350 goto out;
1351 }
1352
1353 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1354 bitmap_offset_out, bitmap_len_out,
1355 data_offset_out);
1356 if(!err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001357 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 os_close_file(fd);
1359 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001360 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361}
1362
Jeff Dike91acb212005-10-10 23:10:32 -04001363static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364{
Jeff Dike91acb212005-10-10 23:10:32 -04001365 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
Jeff Dike91acb212005-10-10 23:10:32 -04001367 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001368 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
Jeff Dike91acb212005-10-10 23:10:32 -04001370 n = os_seek_file(req->fds[1], req->cow_offset);
1371 if(n < 0){
1372 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001373 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001374 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001376 n = os_write_file(req->fds[1], &req->bitmap_words,
1377 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001378 if(n != sizeof(req->bitmap_words)){
1379 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1380 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001381 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001382 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383
Jeff Dikedc764e52007-05-06 14:51:41 -07001384 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385}
Jeff Dike91acb212005-10-10 23:10:32 -04001386
1387void do_io(struct io_thread_req *req)
1388{
1389 char *buf;
1390 unsigned long len;
1391 int n, nsectors, start, end, bit;
1392 int err;
1393 __u64 off;
1394
1395 nsectors = req->length / req->sectorsize;
1396 start = 0;
1397 do {
1398 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1399 end = start;
1400 while((end < nsectors) &&
1401 (ubd_test_bit(end, (unsigned char *)
1402 &req->sector_mask) == bit))
1403 end++;
1404
1405 off = req->offset + req->offsets[bit] +
1406 start * req->sectorsize;
1407 len = (end - start) * req->sectorsize;
1408 buf = &req->buffer[start * req->sectorsize];
1409
1410 err = os_seek_file(req->fds[bit], off);
1411 if(err < 0){
1412 printk("do_io - lseek failed : err = %d\n", -err);
1413 req->error = 1;
1414 return;
1415 }
1416 if(req->op == UBD_READ){
1417 n = 0;
1418 do {
1419 buf = &buf[n];
1420 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001421 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001422 if (n < 0) {
1423 printk("do_io - read failed, err = %d "
1424 "fd = %d\n", -n, req->fds[bit]);
1425 req->error = 1;
1426 return;
1427 }
1428 } while((n < len) && (n != 0));
1429 if (n < len) memset(&buf[n], 0, len - n);
1430 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001431 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001432 if(n != len){
1433 printk("do_io - write failed err = %d "
1434 "fd = %d\n", -n, req->fds[bit]);
1435 req->error = 1;
1436 return;
1437 }
1438 }
1439
1440 start = end;
1441 } while(start < nsectors);
1442
1443 req->error = update_bitmap(req);
1444}
1445
1446/* Changed in start_io_thread, which is serialized by being called only
1447 * from ubd_init, which is an initcall.
1448 */
1449int kernel_fd = -1;
1450
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001451/* Only changed by the io thread. XXX: currently unused. */
1452static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001453
1454int io_thread(void *arg)
1455{
Jeff Dike2adcec22007-05-06 14:51:37 -07001456 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001457 int n;
1458
1459 ignore_sigwinch_sig();
1460 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001461 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001462 sizeof(struct io_thread_req *));
1463 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001464 if(n < 0)
1465 printk("io_thread - read failed, fd = %d, "
1466 "err = %d\n", kernel_fd, -n);
1467 else {
1468 printk("io_thread - short read, fd = %d, "
1469 "length = %d\n", kernel_fd, n);
1470 }
1471 continue;
1472 }
1473 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001474 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001475 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001476 sizeof(struct io_thread_req *));
1477 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001478 printk("io_thread - write failed, fd = %d, err = %d\n",
1479 kernel_fd, -n);
1480 }
Jeff Dike91acb212005-10-10 23:10:32 -04001481
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001482 return 0;
1483}