blob: b1a77b11f08915b01a18bf14d6cb2817160558da [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);
Miklos Szeredi12429bf2007-11-28 16:21:52 -08001131 kfree(io_req);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001132 return;
1133 }
1134
Jeff Dikea0044bd2007-05-06 14:51:36 -07001135 dev->start_sg++;
1136 }
1137 dev->end_sg = 0;
1138 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001139 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140}
1141
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001142static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1143{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001144 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001145
1146 geo->heads = 128;
1147 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001148 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001149 return 0;
1150}
1151
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152static int ubd_ioctl(struct inode * inode, struct file * file,
1153 unsigned int cmd, unsigned long arg)
1154{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001155 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 struct hd_driveid ubd_id = {
1157 .cyls = 0,
1158 .heads = 128,
1159 .sectors = 32,
1160 };
1161
1162 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001165 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1167 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001168 return -EFAULT;
1169 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001170
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 case CDROMVOLREAD:
1172 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001173 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 volume.channel0 = 255;
1175 volume.channel1 = 255;
1176 volume.channel2 = 255;
1177 volume.channel3 = 255;
1178 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001179 return -EFAULT;
1180 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001182 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183}
1184
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001185static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186{
1187 struct uml_stat buf1, buf2;
1188 int err;
1189
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001190 if(from_cmdline == NULL)
1191 return 0;
1192 if(!strcmp(from_cmdline, from_cow))
1193 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194
1195 err = os_stat_file(from_cmdline, &buf1);
1196 if(err < 0){
1197 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001198 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 }
1200 err = os_stat_file(from_cow, &buf2);
1201 if(err < 0){
1202 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001203 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 }
1205 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001206 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207
1208 printk("Backing file mismatch - \"%s\" requested,\n"
1209 "\"%s\" specified in COW header of \"%s\"\n",
1210 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001211 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212}
1213
1214static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1215{
1216 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001217 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 int err;
1219
1220 err = os_file_modtime(file, &modtime);
1221 if(err < 0){
1222 printk("Failed to get modification time of backing file "
1223 "\"%s\", err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001224 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 }
1226
1227 err = os_file_size(file, &actual);
1228 if(err < 0){
1229 printk("Failed to get size of backing file \"%s\", "
1230 "err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001231 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 }
1233
Jeff Dikedc764e52007-05-06 14:51:41 -07001234 if(actual != size){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1236 * the typecast.*/
1237 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1238 "file\n", (unsigned long long) size, actual);
Jeff Dikedc764e52007-05-06 14:51:41 -07001239 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 }
1241 if(modtime != mtime){
1242 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1243 "file\n", mtime, modtime);
Jeff Dikedc764e52007-05-06 14:51:41 -07001244 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001246 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247}
1248
1249int read_cow_bitmap(int fd, void *buf, int offset, int len)
1250{
1251 int err;
1252
1253 err = os_seek_file(fd, offset);
1254 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001255 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256
Jeff Dikedc764e52007-05-06 14:51:41 -07001257 err = os_read_file(fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001259 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260
Jeff Dikedc764e52007-05-06 14:51:41 -07001261 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262}
1263
Jeff Dike6c29256c2006-03-27 01:14:37 -08001264int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 char **backing_file_out, int *bitmap_offset_out,
1266 unsigned long *bitmap_len_out, int *data_offset_out,
1267 int *create_cow_out)
1268{
1269 time_t mtime;
1270 unsigned long long size;
1271 __u32 version, align;
1272 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001273 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
1275 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001276 if (fd < 0) {
1277 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 *create_cow_out = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001279 if (!openflags->w ||
1280 ((fd != -EROFS) && (fd != -EACCES)))
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001281 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 openflags->w = 0;
1283 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001284 if (fd < 0)
1285 return fd;
Jeff Dikedc764e52007-05-06 14:51:41 -07001286 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
Jeff Dike6c29256c2006-03-27 01:14:37 -08001288 if(shared)
1289 printk("Not locking \"%s\" on the host\n", file);
1290 else {
1291 err = os_lock_file(fd, openflags->w);
1292 if(err < 0){
1293 printk("Failed to lock '%s', err = %d\n", file, -err);
1294 goto out_close;
1295 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 }
1297
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001298 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001299 if(backing_file_out == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -07001300 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301
1302 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1303 &size, &sectorsize, &align, bitmap_offset_out);
1304 if(err && (*backing_file_out != NULL)){
1305 printk("Failed to read COW header from COW file \"%s\", "
1306 "errno = %d\n", file, -err);
1307 goto out_close;
1308 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001309 if(err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001310 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001312 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001314 /* Allow switching only if no mismatch. */
1315 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 printk("Switching backing file to '%s'\n", *backing_file_out);
1317 err = write_cow_header(file, fd, *backing_file_out,
1318 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001319 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001321 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001323 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 *backing_file_out = backing_file;
1325 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001326 if (err)
1327 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 }
1329
1330 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1331 bitmap_len_out, data_offset_out);
1332
Jeff Dikedc764e52007-05-06 14:51:41 -07001333 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 out_close:
1335 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001336 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337}
1338
1339int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1340 int sectorsize, int alignment, int *bitmap_offset_out,
1341 unsigned long *bitmap_len_out, int *data_offset_out)
1342{
1343 int err, fd;
1344
1345 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001346 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 if(fd < 0){
1348 err = fd;
1349 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1350 -err);
1351 goto out;
1352 }
1353
1354 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1355 bitmap_offset_out, bitmap_len_out,
1356 data_offset_out);
1357 if(!err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001358 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 os_close_file(fd);
1360 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001361 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362}
1363
Jeff Dike91acb212005-10-10 23:10:32 -04001364static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365{
Jeff Dike91acb212005-10-10 23:10:32 -04001366 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Jeff Dike91acb212005-10-10 23:10:32 -04001368 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001369 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
Jeff Dike91acb212005-10-10 23:10:32 -04001371 n = os_seek_file(req->fds[1], req->cow_offset);
1372 if(n < 0){
1373 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001374 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001375 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001377 n = os_write_file(req->fds[1], &req->bitmap_words,
1378 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001379 if(n != sizeof(req->bitmap_words)){
1380 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1381 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001382 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001383 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384
Jeff Dikedc764e52007-05-06 14:51:41 -07001385 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386}
Jeff Dike91acb212005-10-10 23:10:32 -04001387
1388void do_io(struct io_thread_req *req)
1389{
1390 char *buf;
1391 unsigned long len;
1392 int n, nsectors, start, end, bit;
1393 int err;
1394 __u64 off;
1395
1396 nsectors = req->length / req->sectorsize;
1397 start = 0;
1398 do {
1399 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1400 end = start;
1401 while((end < nsectors) &&
1402 (ubd_test_bit(end, (unsigned char *)
1403 &req->sector_mask) == bit))
1404 end++;
1405
1406 off = req->offset + req->offsets[bit] +
1407 start * req->sectorsize;
1408 len = (end - start) * req->sectorsize;
1409 buf = &req->buffer[start * req->sectorsize];
1410
1411 err = os_seek_file(req->fds[bit], off);
1412 if(err < 0){
1413 printk("do_io - lseek failed : err = %d\n", -err);
1414 req->error = 1;
1415 return;
1416 }
1417 if(req->op == UBD_READ){
1418 n = 0;
1419 do {
1420 buf = &buf[n];
1421 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001422 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001423 if (n < 0) {
1424 printk("do_io - read failed, err = %d "
1425 "fd = %d\n", -n, req->fds[bit]);
1426 req->error = 1;
1427 return;
1428 }
1429 } while((n < len) && (n != 0));
1430 if (n < len) memset(&buf[n], 0, len - n);
1431 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001432 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001433 if(n != len){
1434 printk("do_io - write failed err = %d "
1435 "fd = %d\n", -n, req->fds[bit]);
1436 req->error = 1;
1437 return;
1438 }
1439 }
1440
1441 start = end;
1442 } while(start < nsectors);
1443
1444 req->error = update_bitmap(req);
1445}
1446
1447/* Changed in start_io_thread, which is serialized by being called only
1448 * from ubd_init, which is an initcall.
1449 */
1450int kernel_fd = -1;
1451
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001452/* Only changed by the io thread. XXX: currently unused. */
1453static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001454
1455int io_thread(void *arg)
1456{
Jeff Dike2adcec22007-05-06 14:51:37 -07001457 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001458 int n;
1459
1460 ignore_sigwinch_sig();
1461 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001462 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001463 sizeof(struct io_thread_req *));
1464 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001465 if(n < 0)
1466 printk("io_thread - read failed, fd = %d, "
1467 "err = %d\n", kernel_fd, -n);
1468 else {
1469 printk("io_thread - short read, fd = %d, "
1470 "length = %d\n", kernel_fd, n);
1471 }
1472 continue;
1473 }
1474 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001475 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001476 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001477 sizeof(struct io_thread_req *));
1478 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001479 printk("io_thread - write failed, fd = %d, err = %d\n",
1480 kernel_fd, -n);
1481 }
Jeff Dike91acb212005-10-10 23:10:32 -04001482
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001483 return 0;
1484}