blob: 99f9f9605e9c53761ad03774a2e72606254a7cc1 [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
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500478static void ubd_end_request(struct request *req, int bytes, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479{
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500480 blk_end_request(req, error, bytes);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481}
482
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800483/* Callable only from interrupt context - otherwise you need to do
484 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700485static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700487 if(bytes < 0){
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500488 ubd_end_request(req, 0, -EIO);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700489 return;
490 }
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500491 ubd_end_request(req, bytes, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492}
493
Jeff Dikea0044bd2007-05-06 14:51:36 -0700494static LIST_HEAD(restart);
495
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800496/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800497/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400498static void ubd_handler(void)
499{
Jeff Dike2adcec22007-05-06 14:51:37 -0700500 struct io_thread_req *req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800501 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700502 struct ubd *ubd;
503 struct list_head *list, *next_ele;
504 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400505 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Jeff Dikea0044bd2007-05-06 14:51:36 -0700507 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700508 n = os_read_file(thread_fd, &req,
509 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700510 if(n != sizeof(req)){
511 if(n == -EAGAIN)
512 break;
513 printk(KERN_ERR "spurious interrupt in ubd_handler, "
514 "err = %d\n", -n);
515 return;
516 }
517
Jeff Dike2adcec22007-05-06 14:51:37 -0700518 rq = req->req;
519 rq->nr_sectors -= req->length >> 9;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700520 if(rq->nr_sectors == 0)
521 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike2adcec22007-05-06 14:51:37 -0700522 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400523 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800524 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700525
526 list_for_each_safe(list, next_ele, &restart){
527 ubd = container_of(list, struct ubd, restart);
528 list_del_init(&ubd->restart);
529 spin_lock_irqsave(&ubd->lock, flags);
530 do_ubd_request(ubd->queue);
531 spin_unlock_irqrestore(&ubd->lock, flags);
532 }
Jeff Dike91acb212005-10-10 23:10:32 -0400533}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
Al Viro7bea96f2006-10-08 22:49:34 +0100535static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536{
Jeff Dike91acb212005-10-10 23:10:32 -0400537 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700538 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539}
540
Jeff Dike91acb212005-10-10 23:10:32 -0400541/* Only changed by ubd_init, which is an initcall. */
542static int io_pid = -1;
543
544void kill_io_thread(void)
545{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800546 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400547 os_kill_process(io_pid, 1);
548}
549
550__uml_exitcall(kill_io_thread);
551
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800552static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553{
554 char *file;
555
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800556 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700557 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558}
559
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800560static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800562 os_close_file(ubd_dev->fd);
563 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 return;
565
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800566 os_close_file(ubd_dev->cow.fd);
567 vfree(ubd_dev->cow.bitmap);
568 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569}
570
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800571static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572{
573 struct openflags flags;
574 char **back_ptr;
575 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800576 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800578 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800580 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
581 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800582
583 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800584 back_ptr, &ubd_dev->cow.bitmap_offset,
585 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800586 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800588 if((fd == -ENOENT) && create_cow){
589 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800590 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
591 &ubd_dev->cow.bitmap_offset,
592 &ubd_dev->cow.bitmap_len,
593 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800594 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800596 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 }
598 }
599
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800600 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800601 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800602 -fd);
603 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800605 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800607 if(ubd_dev->cow.file != NULL){
Jeff Dikef4768ff2007-08-22 14:01:53 -0700608 blk_queue_max_sectors(ubd_dev->queue, 8 * sizeof(long));
609
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 err = -ENOMEM;
Jesper Juhlda2486b2007-10-16 01:27:19 -0700611 ubd_dev->cow.bitmap = vmalloc(ubd_dev->cow.bitmap_len);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800612 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
614 goto error;
615 }
616 flush_tlb_kernel_vm();
617
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800618 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
619 ubd_dev->cow.bitmap_offset,
620 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 if(err < 0)
622 goto error;
623
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800624 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800626 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800627 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800629 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700631 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800633 os_close_file(ubd_dev->fd);
Jeff Dikedc764e52007-05-06 14:51:41 -0700634 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635}
636
Jeff Dike2e3f5252007-05-06 14:51:29 -0700637static void ubd_device_release(struct device *dev)
638{
639 struct ubd *ubd_dev = dev->driver_data;
640
641 blk_cleanup_queue(ubd_dev->queue);
642 *ubd_dev = ((struct ubd) DEFAULT_UBD);
643}
644
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800645static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800646 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647{
648 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
650 disk = alloc_disk(1 << UBD_SHIFT);
651 if(disk == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -0700652 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
654 disk->major = major;
655 disk->first_minor = unit << UBD_SHIFT;
656 disk->fops = &ubd_blops;
657 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700658 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700660 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662
663 /* sysfs register (not for ide fake devices) */
664 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800665 ubd_devs[unit].pdev.id = unit;
666 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700667 ubd_devs[unit].pdev.dev.release = ubd_device_release;
668 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800669 platform_device_register(&ubd_devs[unit].pdev);
670 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 }
672
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800673 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800674 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 add_disk(disk);
676
677 *disk_out = disk;
678 return 0;
679}
680
681#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
682
Jeff Dikef28169d2007-02-10 01:43:53 -0800683static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800685 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800686 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800688 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700689 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800691 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800692 if(err < 0){
693 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700694 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800695 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800697 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Jeff Dikea0044bd2007-05-06 14:51:36 -0700699 INIT_LIST_HEAD(&ubd_dev->restart);
WANG Cong4f40c052007-11-05 14:50:59 -0800700 sg_init_table(ubd_dev->sg, MAX_SG);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700701
Jeff Dike62f96cb2007-02-10 01:44:16 -0800702 err = -ENOMEM;
703 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
704 if (ubd_dev->queue == NULL) {
705 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700706 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800707 }
708 ubd_dev->queue->queuedata = ubd_dev;
709
Jeff Dikea0044bd2007-05-06 14:51:36 -0700710 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800711 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
712 if(err){
713 *error_out = "Failed to register device";
714 goto out_cleanup;
715 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800716
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800718 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800719 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
721 /* perhaps this should also be under the "if (fake_major)" above */
722 /* using the fake_disk->disk_name and also the fakehd_set name */
723 if (fake_ide)
724 make_ide_entries(ubd_gendisk[n]->disk_name);
725
Jeff Dikeec7cf782005-09-03 15:57:29 -0700726 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700727out:
728 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800729
730out_cleanup:
731 blk_cleanup_queue(ubd_dev->queue);
732 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733}
734
Jeff Dikef28169d2007-02-10 01:43:53 -0800735static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800737 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Jeff Dikef28169d2007-02-10 01:43:53 -0800739 /* This string is possibly broken up and stored, so it's only
740 * freed if ubd_setup_common fails, or if only general options
741 * were set.
742 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800743 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800744 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800745 *error_out = "Failed to allocate memory";
746 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800748
749 ret = ubd_setup_common(str, &n, error_out);
750 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800751 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800752
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800753 if (n == -1) {
754 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800755 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800756 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
Jeff Dikedc764e52007-05-06 14:51:41 -0700758 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800759 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800760 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800761 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700762 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800764out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700765 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800766
767err_free:
768 kfree(str);
769 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770}
771
772static int ubd_get_config(char *name, char *str, int size, char **error_out)
773{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800774 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 int n, len = 0;
776
777 n = parse_unit(&name);
778 if((n >= MAX_DEV) || (n < 0)){
779 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700780 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 }
782
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800783 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800784 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800786 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 CONFIG_CHUNK(str, size, len, "", 1);
788 goto out;
789 }
790
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800791 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800793 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800795 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 }
797 else CONFIG_CHUNK(str, size, len, "", 1);
798
799 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800800 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700801 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802}
803
Jeff Dike29d56cf2005-06-25 14:55:25 -0700804static int ubd_id(char **str, int *start_out, int *end_out)
805{
Jeff Dikedc764e52007-05-06 14:51:41 -0700806 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700807
808 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700809 *start_out = 0;
810 *end_out = MAX_DEV - 1;
811 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700812}
813
Jeff Dikef28169d2007-02-10 01:43:53 -0800814static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700816 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800817 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700818 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800820 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800822 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700823
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800824 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700825 goto out;
826
827 /* you cannot remove a open disk */
828 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800829 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700830 goto out;
831
Jeff Dikedc764e52007-05-06 14:51:41 -0700832 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700833 if(disk != NULL){
834 del_gendisk(disk);
835 put_disk(disk);
836 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
838 if(fake_gendisk[n] != NULL){
839 del_gendisk(fake_gendisk[n]);
840 put_disk(fake_gendisk[n]);
841 fake_gendisk[n] = NULL;
842 }
843
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700845 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700846out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800847 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700848 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849}
850
Jeff Dikef28169d2007-02-10 01:43:53 -0800851/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800852 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800853 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800855 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 .name = "ubd",
857 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -0700858 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700859 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 .remove = ubd_remove,
861};
862
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800863static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
865 mconsole_register_dev(&ubd_mc);
866 return 0;
867}
868
869__initcall(ubd_mc_init);
870
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800871static int __init ubd0_init(void)
872{
873 struct ubd *ubd_dev = &ubd_devs[0];
874
Jeff Dikeb8831a12007-02-10 01:44:17 -0800875 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800876 if(ubd_dev->file == NULL)
877 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -0800878 mutex_unlock(&ubd_lock);
879
Jeff Dikedc764e52007-05-06 14:51:41 -0700880 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800881}
882
883__initcall(ubd0_init);
884
Jeff Dikeb8831a12007-02-10 01:44:17 -0800885/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +0000886static struct platform_driver ubd_driver = {
887 .driver = {
888 .name = DRIVER_NAME,
889 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890};
891
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800892static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893{
Jeff Dikef28169d2007-02-10 01:43:53 -0800894 char *error;
895 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 if (register_blkdev(MAJOR_NR, "ubd"))
898 return -1;
899
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 if (fake_major != MAJOR_NR) {
901 char name[sizeof("ubd_nnn\0")];
902
903 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 if (register_blkdev(fake_major, "ubd"))
905 return -1;
906 }
Russell King3ae5eae2005-11-09 22:32:44 +0000907 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -0700908 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800909 for (i = 0; i < MAX_DEV; i++){
910 err = ubd_add(i, &error);
911 if(err)
912 printk(KERN_ERR "Failed to initialize ubd device %d :"
913 "%s\n", i, error);
914 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700915 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 return 0;
917}
918
919late_initcall(ubd_init);
920
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800921static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400922 unsigned long stack;
923 int err;
924
925 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
926 if(global_openflags.s){
927 printk(KERN_INFO "ubd: Synchronous mode\n");
928 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
929 * enough. So use anyway the io thread. */
930 }
931 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800932 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400933 &thread_fd);
934 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800935 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400936 "ubd : Failed to start I/O thread (errno = %d) - "
937 "falling back to synchronous I/O\n", -io_pid);
938 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700939 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400940 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800941 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800942 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400943 if(err != 0)
944 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800945 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400946}
947
948device_initcall(ubd_driver_init);
949
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950static int ubd_open(struct inode *inode, struct file *filp)
951{
952 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800953 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 int err = 0;
955
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800956 if(ubd_dev->count == 0){
957 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 if(err){
959 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800960 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 goto out;
962 }
963 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800964 ubd_dev->count++;
965 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700966
967 /* This should no more be needed. And it didn't work anyway to exclude
968 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800969 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800970 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700972 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700974 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975}
976
977static int ubd_release(struct inode * inode, struct file * file)
978{
979 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800980 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800982 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800983 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -0700984 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985}
986
Jeff Dike91acb212005-10-10 23:10:32 -0400987static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
988 __u64 *cow_offset, unsigned long *bitmap,
989 __u64 bitmap_offset, unsigned long *bitmap_words,
990 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991{
Jeff Dike91acb212005-10-10 23:10:32 -0400992 __u64 sector = io_offset >> 9;
993 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994
Jeff Dike91acb212005-10-10 23:10:32 -0400995 for(i = 0; i < length >> 9; i++){
996 if(cow_mask != NULL)
997 ubd_set_bit(i, (unsigned char *) cow_mask);
998 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
999 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000
Jeff Dike91acb212005-10-10 23:10:32 -04001001 update_bitmap = 1;
1002 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1003 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
Jeff Dike91acb212005-10-10 23:10:32 -04001005 if(!update_bitmap)
1006 return;
1007
1008 *cow_offset = sector / (sizeof(unsigned long) * 8);
1009
1010 /* This takes care of the case where we're exactly at the end of the
1011 * device, and *cow_offset + 1 is off the end. So, just back it up
1012 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1013 * for the original diagnosis.
1014 */
1015 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1016 sizeof(unsigned long) - 1))
1017 (*cow_offset)--;
1018
1019 bitmap_words[0] = bitmap[*cow_offset];
1020 bitmap_words[1] = bitmap[*cow_offset + 1];
1021
1022 *cow_offset *= sizeof(unsigned long);
1023 *cow_offset += bitmap_offset;
1024}
1025
1026static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1027 __u64 bitmap_offset, __u64 bitmap_len)
1028{
1029 __u64 sector = req->offset >> 9;
1030 int i;
1031
1032 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1033 panic("Operation too long");
1034
1035 if(req->op == UBD_READ) {
1036 for(i = 0; i < req->length >> 9; i++){
1037 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001038 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001039 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001040 }
Jeff Dike91acb212005-10-10 23:10:32 -04001041 }
1042 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1043 &req->cow_offset, bitmap, bitmap_offset,
1044 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045}
1046
Jeff Dike62f96cb2007-02-10 01:44:16 -08001047/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001048static void prepare_request(struct request *req, struct io_thread_req *io_req,
1049 unsigned long long offset, int page_offset,
1050 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051{
1052 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001053 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001054
Jeff Dike62f96cb2007-02-10 01:44:16 -08001055 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001056 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1057 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001058 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001059 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 io_req->offset = offset;
1061 io_req->length = len;
1062 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001063 io_req->sector_mask = 0;
1064
1065 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001067 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001068 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 io_req->sectorsize = 1 << 9;
1070
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001071 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001072 cowify_req(io_req, ubd_dev->cow.bitmap,
1073 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001074
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075}
1076
Jeff Dike62f96cb2007-02-10 01:44:16 -08001077/* Called with dev->lock held */
Jens Axboe165125e2007-07-24 09:28:11 +02001078static void do_ubd_request(struct request_queue *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079{
Jeff Dike2adcec22007-05-06 14:51:37 -07001080 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 struct request *req;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001082 int n, last_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083
Jeff Dikea0044bd2007-05-06 14:51:36 -07001084 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001085 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001086 if(dev->end_sg == 0){
1087 struct request *req = elv_next_request(q);
1088 if(req == NULL)
1089 return;
1090
1091 dev->request = req;
1092 blkdev_dequeue_request(req);
1093 dev->start_sg = 0;
1094 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001095 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001096
1097 req = dev->request;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001098 last_sectors = 0;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001099 while(dev->start_sg < dev->end_sg){
1100 struct scatterlist *sg = &dev->sg[dev->start_sg];
1101
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001102 req->sector += last_sectors;
Jeff Dike2adcec22007-05-06 14:51:37 -07001103 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001104 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001105 if(io_req == NULL){
1106 if(list_empty(&dev->restart))
1107 list_add(&dev->restart, &restart);
1108 return;
1109 }
1110 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001111 (unsigned long long) req->sector << 9,
Jens Axboe45711f12007-10-22 21:19:53 +02001112 sg->offset, sg->length, sg_page(sg));
Jeff Dikea0044bd2007-05-06 14:51:36 -07001113
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001114 last_sectors = sg->length >> 9;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001115 n = os_write_file(thread_fd, &io_req,
1116 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001117 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001118 if(n != -EAGAIN)
1119 printk("write to io thread failed, "
1120 "errno = %d\n", -n);
1121 else if(list_empty(&dev->restart))
1122 list_add(&dev->restart, &restart);
Miklos Szeredi12429bf2007-11-28 16:21:52 -08001123 kfree(io_req);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001124 return;
1125 }
1126
Jeff Dikea0044bd2007-05-06 14:51:36 -07001127 dev->start_sg++;
1128 }
1129 dev->end_sg = 0;
1130 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001131 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132}
1133
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001134static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1135{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001136 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001137
1138 geo->heads = 128;
1139 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001140 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001141 return 0;
1142}
1143
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144static int ubd_ioctl(struct inode * inode, struct file * file,
1145 unsigned int cmd, unsigned long arg)
1146{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001147 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 struct hd_driveid ubd_id = {
1149 .cyls = 0,
1150 .heads = 128,
1151 .sectors = 32,
1152 };
1153
1154 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001157 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1159 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001160 return -EFAULT;
1161 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001162
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 case CDROMVOLREAD:
1164 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001165 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 volume.channel0 = 255;
1167 volume.channel1 = 255;
1168 volume.channel2 = 255;
1169 volume.channel3 = 255;
1170 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001171 return -EFAULT;
1172 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001174 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175}
1176
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001177static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178{
1179 struct uml_stat buf1, buf2;
1180 int err;
1181
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001182 if(from_cmdline == NULL)
1183 return 0;
1184 if(!strcmp(from_cmdline, from_cow))
1185 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186
1187 err = os_stat_file(from_cmdline, &buf1);
1188 if(err < 0){
1189 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001190 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 }
1192 err = os_stat_file(from_cow, &buf2);
1193 if(err < 0){
1194 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001195 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 }
1197 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001198 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
1200 printk("Backing file mismatch - \"%s\" requested,\n"
1201 "\"%s\" specified in COW header of \"%s\"\n",
1202 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001203 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204}
1205
1206static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1207{
1208 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001209 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 int err;
1211
1212 err = os_file_modtime(file, &modtime);
1213 if(err < 0){
1214 printk("Failed to get modification time of backing file "
1215 "\"%s\", err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001216 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 }
1218
1219 err = os_file_size(file, &actual);
1220 if(err < 0){
1221 printk("Failed to get size of backing file \"%s\", "
1222 "err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001223 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 }
1225
Jeff Dikedc764e52007-05-06 14:51:41 -07001226 if(actual != size){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1228 * the typecast.*/
1229 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1230 "file\n", (unsigned long long) size, actual);
Jeff Dikedc764e52007-05-06 14:51:41 -07001231 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 }
1233 if(modtime != mtime){
1234 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1235 "file\n", mtime, modtime);
Jeff Dikedc764e52007-05-06 14:51:41 -07001236 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001238 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239}
1240
1241int read_cow_bitmap(int fd, void *buf, int offset, int len)
1242{
1243 int err;
1244
1245 err = os_seek_file(fd, offset);
1246 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001247 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248
Jeff Dikedc764e52007-05-06 14:51:41 -07001249 err = os_read_file(fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001251 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252
Jeff Dikedc764e52007-05-06 14:51:41 -07001253 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254}
1255
Jeff Dike6c29256c2006-03-27 01:14:37 -08001256int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 char **backing_file_out, int *bitmap_offset_out,
1258 unsigned long *bitmap_len_out, int *data_offset_out,
1259 int *create_cow_out)
1260{
1261 time_t mtime;
1262 unsigned long long size;
1263 __u32 version, align;
1264 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001265 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266
1267 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001268 if (fd < 0) {
1269 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 *create_cow_out = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001271 if (!openflags->w ||
1272 ((fd != -EROFS) && (fd != -EACCES)))
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001273 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 openflags->w = 0;
1275 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001276 if (fd < 0)
1277 return fd;
Jeff Dikedc764e52007-05-06 14:51:41 -07001278 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
Jeff Dike6c29256c2006-03-27 01:14:37 -08001280 if(shared)
1281 printk("Not locking \"%s\" on the host\n", file);
1282 else {
1283 err = os_lock_file(fd, openflags->w);
1284 if(err < 0){
1285 printk("Failed to lock '%s', err = %d\n", file, -err);
1286 goto out_close;
1287 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 }
1289
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001290 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001291 if(backing_file_out == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -07001292 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293
1294 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1295 &size, &sectorsize, &align, bitmap_offset_out);
1296 if(err && (*backing_file_out != NULL)){
1297 printk("Failed to read COW header from COW file \"%s\", "
1298 "errno = %d\n", file, -err);
1299 goto out_close;
1300 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001301 if(err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001302 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001304 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001306 /* Allow switching only if no mismatch. */
1307 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 printk("Switching backing file to '%s'\n", *backing_file_out);
1309 err = write_cow_header(file, fd, *backing_file_out,
1310 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001311 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001313 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001315 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 *backing_file_out = backing_file;
1317 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001318 if (err)
1319 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 }
1321
1322 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1323 bitmap_len_out, data_offset_out);
1324
Jeff Dikedc764e52007-05-06 14:51:41 -07001325 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 out_close:
1327 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001328 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329}
1330
1331int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1332 int sectorsize, int alignment, int *bitmap_offset_out,
1333 unsigned long *bitmap_len_out, int *data_offset_out)
1334{
1335 int err, fd;
1336
1337 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001338 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 if(fd < 0){
1340 err = fd;
1341 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1342 -err);
1343 goto out;
1344 }
1345
1346 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1347 bitmap_offset_out, bitmap_len_out,
1348 data_offset_out);
1349 if(!err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001350 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 os_close_file(fd);
1352 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001353 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354}
1355
Jeff Dike91acb212005-10-10 23:10:32 -04001356static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357{
Jeff Dike91acb212005-10-10 23:10:32 -04001358 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359
Jeff Dike91acb212005-10-10 23:10:32 -04001360 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001361 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
Jeff Dike91acb212005-10-10 23:10:32 -04001363 n = os_seek_file(req->fds[1], req->cow_offset);
1364 if(n < 0){
1365 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001366 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001367 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001369 n = os_write_file(req->fds[1], &req->bitmap_words,
1370 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001371 if(n != sizeof(req->bitmap_words)){
1372 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1373 req->fds[1]);
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 Dikedc764e52007-05-06 14:51:41 -07001377 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378}
Jeff Dike91acb212005-10-10 23:10:32 -04001379
1380void do_io(struct io_thread_req *req)
1381{
1382 char *buf;
1383 unsigned long len;
1384 int n, nsectors, start, end, bit;
1385 int err;
1386 __u64 off;
1387
1388 nsectors = req->length / req->sectorsize;
1389 start = 0;
1390 do {
1391 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1392 end = start;
1393 while((end < nsectors) &&
1394 (ubd_test_bit(end, (unsigned char *)
1395 &req->sector_mask) == bit))
1396 end++;
1397
1398 off = req->offset + req->offsets[bit] +
1399 start * req->sectorsize;
1400 len = (end - start) * req->sectorsize;
1401 buf = &req->buffer[start * req->sectorsize];
1402
1403 err = os_seek_file(req->fds[bit], off);
1404 if(err < 0){
1405 printk("do_io - lseek failed : err = %d\n", -err);
1406 req->error = 1;
1407 return;
1408 }
1409 if(req->op == UBD_READ){
1410 n = 0;
1411 do {
1412 buf = &buf[n];
1413 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001414 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001415 if (n < 0) {
1416 printk("do_io - read failed, err = %d "
1417 "fd = %d\n", -n, req->fds[bit]);
1418 req->error = 1;
1419 return;
1420 }
1421 } while((n < len) && (n != 0));
1422 if (n < len) memset(&buf[n], 0, len - n);
1423 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001424 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001425 if(n != len){
1426 printk("do_io - write failed err = %d "
1427 "fd = %d\n", -n, req->fds[bit]);
1428 req->error = 1;
1429 return;
1430 }
1431 }
1432
1433 start = end;
1434 } while(start < nsectors);
1435
1436 req->error = update_bitmap(req);
1437}
1438
1439/* Changed in start_io_thread, which is serialized by being called only
1440 * from ubd_init, which is an initcall.
1441 */
1442int kernel_fd = -1;
1443
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001444/* Only changed by the io thread. XXX: currently unused. */
1445static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001446
1447int io_thread(void *arg)
1448{
Jeff Dike2adcec22007-05-06 14:51:37 -07001449 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001450 int n;
1451
1452 ignore_sigwinch_sig();
1453 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001454 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001455 sizeof(struct io_thread_req *));
1456 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001457 if(n < 0)
1458 printk("io_thread - read failed, fd = %d, "
1459 "err = %d\n", kernel_fd, -n);
1460 else {
1461 printk("io_thread - short read, fd = %d, "
1462 "length = %d\n", kernel_fd, n);
1463 }
1464 continue;
1465 }
1466 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001467 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001468 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001469 sizeof(struct io_thread_req *));
1470 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001471 printk("io_thread - write failed, fd = %d, err = %d\n",
1472 kernel_fd, -n);
1473 }
Jeff Dike91acb212005-10-10 23:10:32 -04001474
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001475 return 0;
1476}