blob: 7a252abbfeade5711072fc94135010d8f5837355 [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
WANG Congc0a92902008-02-04 22:30:41 -0800232static void make_ide_entries(const char *dev_name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
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;
WANG Congc0a92902008-02-04 22:30:41 -0800247 snprintf(name, sizeof(name), "ide0/%s", dev_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 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"
Jeff Dike20ede452008-02-04 22:30:37 -0800440" an 's' will cause data to be written to disk on the host immediately.\n"
441" 'c' will cause the device to be treated as being shared between multiple\n"
442" UMLs and file locking will be turned off - this is appropriate for a\n"
443" cluster filesystem and inappropriate at almost all other times.\n\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444);
445
WANG Congc0a92902008-02-04 22:30:41 -0800446static int udb_setup(const char *str)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447{
448 printk("udb%s specified on command line is almost certainly a ubd -> "
449 "udb TYPO\n", str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700450 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451}
452
453__setup("udb", udb_setup);
454__uml_help(udb_setup,
455"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700456" This option is here solely to catch ubd -> udb typos, which can be\n"
457" to impossible to catch visually unless you specifically look for\n"
458" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459" in the boot output.\n\n"
460);
461
462static int fakehd_set = 0;
463static int fakehd(char *str)
464{
465 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
466 fakehd_set = 1;
467 return 1;
468}
469
470__setup("fakehd", fakehd);
471__uml_help(fakehd,
472"fakehd\n"
473" Change the ubd device name to \"hd\".\n\n"
474);
475
Jens Axboe165125e2007-07-24 09:28:11 +0200476static void do_ubd_request(struct request_queue * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400477
478/* Only changed by ubd_init, which is an initcall. */
479int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500481static void ubd_end_request(struct request *req, int bytes, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482{
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500483 blk_end_request(req, error, bytes);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484}
485
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800486/* Callable only from interrupt context - otherwise you need to do
487 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700488static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700490 if(bytes < 0){
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500491 ubd_end_request(req, 0, -EIO);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700492 return;
493 }
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500494 ubd_end_request(req, bytes, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495}
496
Jeff Dikea0044bd2007-05-06 14:51:36 -0700497static LIST_HEAD(restart);
498
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800499/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800500/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400501static void ubd_handler(void)
502{
Jeff Dike2adcec22007-05-06 14:51:37 -0700503 struct io_thread_req *req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800504 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700505 struct ubd *ubd;
506 struct list_head *list, *next_ele;
507 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400508 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
Jeff Dikea0044bd2007-05-06 14:51:36 -0700510 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700511 n = os_read_file(thread_fd, &req,
512 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700513 if(n != sizeof(req)){
514 if(n == -EAGAIN)
515 break;
516 printk(KERN_ERR "spurious interrupt in ubd_handler, "
517 "err = %d\n", -n);
518 return;
519 }
520
Jeff Dike2adcec22007-05-06 14:51:37 -0700521 rq = req->req;
522 rq->nr_sectors -= req->length >> 9;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700523 if(rq->nr_sectors == 0)
524 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike2adcec22007-05-06 14:51:37 -0700525 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400526 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800527 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700528
529 list_for_each_safe(list, next_ele, &restart){
530 ubd = container_of(list, struct ubd, restart);
531 list_del_init(&ubd->restart);
532 spin_lock_irqsave(&ubd->lock, flags);
533 do_ubd_request(ubd->queue);
534 spin_unlock_irqrestore(&ubd->lock, flags);
535 }
Jeff Dike91acb212005-10-10 23:10:32 -0400536}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Al Viro7bea96f2006-10-08 22:49:34 +0100538static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539{
Jeff Dike91acb212005-10-10 23:10:32 -0400540 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700541 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542}
543
Jeff Dike91acb212005-10-10 23:10:32 -0400544/* Only changed by ubd_init, which is an initcall. */
545static int io_pid = -1;
546
547void kill_io_thread(void)
548{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800549 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400550 os_kill_process(io_pid, 1);
551}
552
553__uml_exitcall(kill_io_thread);
554
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800555static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556{
557 char *file;
558
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800559 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700560 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561}
562
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800563static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800565 os_close_file(ubd_dev->fd);
566 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 return;
568
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800569 os_close_file(ubd_dev->cow.fd);
570 vfree(ubd_dev->cow.bitmap);
571 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
573
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800574static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575{
576 struct openflags flags;
577 char **back_ptr;
578 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800579 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800581 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800583 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
584 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800585
586 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800587 back_ptr, &ubd_dev->cow.bitmap_offset,
588 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800589 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800591 if((fd == -ENOENT) && create_cow){
592 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800593 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
594 &ubd_dev->cow.bitmap_offset,
595 &ubd_dev->cow.bitmap_len,
596 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800597 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800599 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 }
601 }
602
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800603 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800604 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800605 -fd);
606 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800608 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800610 if(ubd_dev->cow.file != NULL){
Jeff Dikef4768ff2007-08-22 14:01:53 -0700611 blk_queue_max_sectors(ubd_dev->queue, 8 * sizeof(long));
612
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 err = -ENOMEM;
Jesper Juhlda2486b2007-10-16 01:27:19 -0700614 ubd_dev->cow.bitmap = vmalloc(ubd_dev->cow.bitmap_len);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800615 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
617 goto error;
618 }
619 flush_tlb_kernel_vm();
620
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800621 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
622 ubd_dev->cow.bitmap_offset,
623 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 if(err < 0)
625 goto error;
626
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800627 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800629 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800630 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800632 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700634 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800636 os_close_file(ubd_dev->fd);
Jeff Dikedc764e52007-05-06 14:51:41 -0700637 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638}
639
Jeff Dike2e3f5252007-05-06 14:51:29 -0700640static void ubd_device_release(struct device *dev)
641{
642 struct ubd *ubd_dev = dev->driver_data;
643
644 blk_cleanup_queue(ubd_dev->queue);
645 *ubd_dev = ((struct ubd) DEFAULT_UBD);
646}
647
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800648static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800649 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650{
651 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
653 disk = alloc_disk(1 << UBD_SHIFT);
654 if(disk == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -0700655 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
657 disk->major = major;
658 disk->first_minor = unit << UBD_SHIFT;
659 disk->fops = &ubd_blops;
660 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700661 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700663 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
666 /* sysfs register (not for ide fake devices) */
667 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800668 ubd_devs[unit].pdev.id = unit;
669 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700670 ubd_devs[unit].pdev.dev.release = ubd_device_release;
671 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800672 platform_device_register(&ubd_devs[unit].pdev);
673 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 }
675
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800676 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800677 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 add_disk(disk);
679
680 *disk_out = disk;
681 return 0;
682}
683
684#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
685
Jeff Dikef28169d2007-02-10 01:43:53 -0800686static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800688 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800689 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800691 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700692 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800694 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800695 if(err < 0){
696 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700697 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800698 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800700 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
Jeff Dikea0044bd2007-05-06 14:51:36 -0700702 INIT_LIST_HEAD(&ubd_dev->restart);
WANG Cong4f40c052007-11-05 14:50:59 -0800703 sg_init_table(ubd_dev->sg, MAX_SG);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700704
Jeff Dike62f96cb2007-02-10 01:44:16 -0800705 err = -ENOMEM;
706 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
707 if (ubd_dev->queue == NULL) {
708 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700709 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800710 }
711 ubd_dev->queue->queuedata = ubd_dev;
712
Jeff Dikea0044bd2007-05-06 14:51:36 -0700713 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800714 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
715 if(err){
716 *error_out = "Failed to register device";
717 goto out_cleanup;
718 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800719
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800721 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800722 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 /* perhaps this should also be under the "if (fake_major)" above */
725 /* using the fake_disk->disk_name and also the fakehd_set name */
726 if (fake_ide)
727 make_ide_entries(ubd_gendisk[n]->disk_name);
728
Jeff Dikeec7cf782005-09-03 15:57:29 -0700729 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700730out:
731 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800732
733out_cleanup:
734 blk_cleanup_queue(ubd_dev->queue);
735 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736}
737
Jeff Dikef28169d2007-02-10 01:43:53 -0800738static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800740 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Jeff Dikef28169d2007-02-10 01:43:53 -0800742 /* This string is possibly broken up and stored, so it's only
743 * freed if ubd_setup_common fails, or if only general options
744 * were set.
745 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800746 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800747 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800748 *error_out = "Failed to allocate memory";
749 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800751
752 ret = ubd_setup_common(str, &n, error_out);
753 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800754 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800755
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800756 if (n == -1) {
757 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800758 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800759 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Jeff Dikedc764e52007-05-06 14:51:41 -0700761 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800762 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800763 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800764 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700765 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800767out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700768 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800769
770err_free:
771 kfree(str);
772 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773}
774
775static int ubd_get_config(char *name, char *str, int size, char **error_out)
776{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800777 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 int n, len = 0;
779
780 n = parse_unit(&name);
781 if((n >= MAX_DEV) || (n < 0)){
782 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700783 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 }
785
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800786 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800787 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800789 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 CONFIG_CHUNK(str, size, len, "", 1);
791 goto out;
792 }
793
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800794 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800796 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800798 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 }
800 else CONFIG_CHUNK(str, size, len, "", 1);
801
802 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800803 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700804 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805}
806
Jeff Dike29d56cf2005-06-25 14:55:25 -0700807static int ubd_id(char **str, int *start_out, int *end_out)
808{
Jeff Dikedc764e52007-05-06 14:51:41 -0700809 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700810
811 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700812 *start_out = 0;
813 *end_out = MAX_DEV - 1;
814 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700815}
816
Jeff Dikef28169d2007-02-10 01:43:53 -0800817static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700819 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800820 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700821 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800823 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800825 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700826
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800827 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700828 goto out;
829
830 /* you cannot remove a open disk */
831 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800832 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700833 goto out;
834
Jeff Dikedc764e52007-05-06 14:51:41 -0700835 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700836 if(disk != NULL){
837 del_gendisk(disk);
838 put_disk(disk);
839 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
841 if(fake_gendisk[n] != NULL){
842 del_gendisk(fake_gendisk[n]);
843 put_disk(fake_gendisk[n]);
844 fake_gendisk[n] = NULL;
845 }
846
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700848 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700849out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800850 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700851 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852}
853
Jeff Dikef28169d2007-02-10 01:43:53 -0800854/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800855 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800856 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800858 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 .name = "ubd",
860 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -0700861 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700862 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 .remove = ubd_remove,
864};
865
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800866static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867{
868 mconsole_register_dev(&ubd_mc);
869 return 0;
870}
871
872__initcall(ubd_mc_init);
873
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800874static int __init ubd0_init(void)
875{
876 struct ubd *ubd_dev = &ubd_devs[0];
877
Jeff Dikeb8831a12007-02-10 01:44:17 -0800878 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800879 if(ubd_dev->file == NULL)
880 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -0800881 mutex_unlock(&ubd_lock);
882
Jeff Dikedc764e52007-05-06 14:51:41 -0700883 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800884}
885
886__initcall(ubd0_init);
887
Jeff Dikeb8831a12007-02-10 01:44:17 -0800888/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +0000889static struct platform_driver ubd_driver = {
890 .driver = {
891 .name = DRIVER_NAME,
892 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893};
894
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800895static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896{
Jeff Dikef28169d2007-02-10 01:43:53 -0800897 char *error;
898 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 if (register_blkdev(MAJOR_NR, "ubd"))
901 return -1;
902
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 if (fake_major != MAJOR_NR) {
904 char name[sizeof("ubd_nnn\0")];
905
906 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 if (register_blkdev(fake_major, "ubd"))
908 return -1;
909 }
Russell King3ae5eae2005-11-09 22:32:44 +0000910 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -0700911 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800912 for (i = 0; i < MAX_DEV; i++){
913 err = ubd_add(i, &error);
914 if(err)
915 printk(KERN_ERR "Failed to initialize ubd device %d :"
916 "%s\n", i, error);
917 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700918 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 return 0;
920}
921
922late_initcall(ubd_init);
923
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800924static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400925 unsigned long stack;
926 int err;
927
928 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
929 if(global_openflags.s){
930 printk(KERN_INFO "ubd: Synchronous mode\n");
931 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
932 * enough. So use anyway the io thread. */
933 }
934 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800935 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400936 &thread_fd);
937 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800938 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400939 "ubd : Failed to start I/O thread (errno = %d) - "
940 "falling back to synchronous I/O\n", -io_pid);
941 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700942 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400943 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800944 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800945 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400946 if(err != 0)
947 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800948 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400949}
950
951device_initcall(ubd_driver_init);
952
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953static int ubd_open(struct inode *inode, struct file *filp)
954{
955 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800956 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 int err = 0;
958
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800959 if(ubd_dev->count == 0){
960 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 if(err){
962 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800963 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 goto out;
965 }
966 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800967 ubd_dev->count++;
968 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700969
970 /* This should no more be needed. And it didn't work anyway to exclude
971 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800972 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800973 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700975 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700977 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978}
979
980static int ubd_release(struct inode * inode, struct file * file)
981{
982 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800983 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800985 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800986 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -0700987 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988}
989
Jeff Dike91acb212005-10-10 23:10:32 -0400990static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
991 __u64 *cow_offset, unsigned long *bitmap,
992 __u64 bitmap_offset, unsigned long *bitmap_words,
993 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994{
Jeff Dike91acb212005-10-10 23:10:32 -0400995 __u64 sector = io_offset >> 9;
996 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
Jeff Dike91acb212005-10-10 23:10:32 -0400998 for(i = 0; i < length >> 9; i++){
999 if(cow_mask != NULL)
1000 ubd_set_bit(i, (unsigned char *) cow_mask);
1001 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1002 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
Jeff Dike91acb212005-10-10 23:10:32 -04001004 update_bitmap = 1;
1005 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1006 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
Jeff Dike91acb212005-10-10 23:10:32 -04001008 if(!update_bitmap)
1009 return;
1010
1011 *cow_offset = sector / (sizeof(unsigned long) * 8);
1012
1013 /* This takes care of the case where we're exactly at the end of the
1014 * device, and *cow_offset + 1 is off the end. So, just back it up
1015 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1016 * for the original diagnosis.
1017 */
1018 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1019 sizeof(unsigned long) - 1))
1020 (*cow_offset)--;
1021
1022 bitmap_words[0] = bitmap[*cow_offset];
1023 bitmap_words[1] = bitmap[*cow_offset + 1];
1024
1025 *cow_offset *= sizeof(unsigned long);
1026 *cow_offset += bitmap_offset;
1027}
1028
1029static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1030 __u64 bitmap_offset, __u64 bitmap_len)
1031{
1032 __u64 sector = req->offset >> 9;
1033 int i;
1034
1035 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1036 panic("Operation too long");
1037
1038 if(req->op == UBD_READ) {
1039 for(i = 0; i < req->length >> 9; i++){
1040 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001041 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001042 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001043 }
Jeff Dike91acb212005-10-10 23:10:32 -04001044 }
1045 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1046 &req->cow_offset, bitmap, bitmap_offset,
1047 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048}
1049
Jeff Dike62f96cb2007-02-10 01:44:16 -08001050/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001051static void prepare_request(struct request *req, struct io_thread_req *io_req,
1052 unsigned long long offset, int page_offset,
1053 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054{
1055 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001056 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001057
Jeff Dike62f96cb2007-02-10 01:44:16 -08001058 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001059 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1060 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001061 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001062 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 io_req->offset = offset;
1064 io_req->length = len;
1065 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001066 io_req->sector_mask = 0;
1067
1068 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001070 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001071 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 io_req->sectorsize = 1 << 9;
1073
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001074 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001075 cowify_req(io_req, ubd_dev->cow.bitmap,
1076 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078}
1079
Jeff Dike62f96cb2007-02-10 01:44:16 -08001080/* Called with dev->lock held */
Jens Axboe165125e2007-07-24 09:28:11 +02001081static void do_ubd_request(struct request_queue *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082{
Jeff Dike2adcec22007-05-06 14:51:37 -07001083 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 struct request *req;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001085 int n, last_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086
Jeff Dikea0044bd2007-05-06 14:51:36 -07001087 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001088 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001089 if(dev->end_sg == 0){
1090 struct request *req = elv_next_request(q);
1091 if(req == NULL)
1092 return;
1093
1094 dev->request = req;
1095 blkdev_dequeue_request(req);
1096 dev->start_sg = 0;
1097 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001098 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001099
1100 req = dev->request;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001101 last_sectors = 0;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001102 while(dev->start_sg < dev->end_sg){
1103 struct scatterlist *sg = &dev->sg[dev->start_sg];
1104
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001105 req->sector += last_sectors;
Jeff Dike2adcec22007-05-06 14:51:37 -07001106 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001107 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001108 if(io_req == NULL){
1109 if(list_empty(&dev->restart))
1110 list_add(&dev->restart, &restart);
1111 return;
1112 }
1113 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001114 (unsigned long long) req->sector << 9,
Jens Axboe45711f12007-10-22 21:19:53 +02001115 sg->offset, sg->length, sg_page(sg));
Jeff Dikea0044bd2007-05-06 14:51:36 -07001116
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001117 last_sectors = sg->length >> 9;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001118 n = os_write_file(thread_fd, &io_req,
1119 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001120 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001121 if(n != -EAGAIN)
1122 printk("write to io thread failed, "
1123 "errno = %d\n", -n);
1124 else if(list_empty(&dev->restart))
1125 list_add(&dev->restart, &restart);
Miklos Szeredi12429bf2007-11-28 16:21:52 -08001126 kfree(io_req);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001127 return;
1128 }
1129
Jeff Dikea0044bd2007-05-06 14:51:36 -07001130 dev->start_sg++;
1131 }
1132 dev->end_sg = 0;
1133 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001134 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135}
1136
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001137static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1138{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001139 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001140
1141 geo->heads = 128;
1142 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001143 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001144 return 0;
1145}
1146
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147static int ubd_ioctl(struct inode * inode, struct file * file,
1148 unsigned int cmd, unsigned long arg)
1149{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001150 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 struct hd_driveid ubd_id = {
1152 .cyls = 0,
1153 .heads = 128,
1154 .sectors = 32,
1155 };
1156
1157 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001160 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1162 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001163 return -EFAULT;
1164 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001165
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 case CDROMVOLREAD:
1167 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001168 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 volume.channel0 = 255;
1170 volume.channel1 = 255;
1171 volume.channel2 = 255;
1172 volume.channel3 = 255;
1173 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001174 return -EFAULT;
1175 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001177 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178}
1179
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001180static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181{
1182 struct uml_stat buf1, buf2;
1183 int err;
1184
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001185 if(from_cmdline == NULL)
1186 return 0;
1187 if(!strcmp(from_cmdline, from_cow))
1188 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189
1190 err = os_stat_file(from_cmdline, &buf1);
1191 if(err < 0){
1192 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001193 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 }
1195 err = os_stat_file(from_cow, &buf2);
1196 if(err < 0){
1197 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001198 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 }
1200 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001201 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
1203 printk("Backing file mismatch - \"%s\" requested,\n"
1204 "\"%s\" specified in COW header of \"%s\"\n",
1205 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001206 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207}
1208
1209static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1210{
1211 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001212 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 int err;
1214
1215 err = os_file_modtime(file, &modtime);
1216 if(err < 0){
1217 printk("Failed to get modification time of backing file "
1218 "\"%s\", err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001219 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 }
1221
1222 err = os_file_size(file, &actual);
1223 if(err < 0){
1224 printk("Failed to get size of backing file \"%s\", "
1225 "err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001226 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 }
1228
Jeff Dikedc764e52007-05-06 14:51:41 -07001229 if(actual != size){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1231 * the typecast.*/
1232 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1233 "file\n", (unsigned long long) size, actual);
Jeff Dikedc764e52007-05-06 14:51:41 -07001234 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 }
1236 if(modtime != mtime){
1237 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1238 "file\n", mtime, modtime);
Jeff Dikedc764e52007-05-06 14:51:41 -07001239 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001241 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242}
1243
1244int read_cow_bitmap(int fd, void *buf, int offset, int len)
1245{
1246 int err;
1247
1248 err = os_seek_file(fd, offset);
1249 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001250 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251
Jeff Dikedc764e52007-05-06 14:51:41 -07001252 err = os_read_file(fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001254 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255
Jeff Dikedc764e52007-05-06 14:51:41 -07001256 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257}
1258
Jeff Dike6c29256c2006-03-27 01:14:37 -08001259int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 char **backing_file_out, int *bitmap_offset_out,
1261 unsigned long *bitmap_len_out, int *data_offset_out,
1262 int *create_cow_out)
1263{
1264 time_t mtime;
1265 unsigned long long size;
1266 __u32 version, align;
1267 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001268 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269
1270 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001271 if (fd < 0) {
1272 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 *create_cow_out = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001274 if (!openflags->w ||
1275 ((fd != -EROFS) && (fd != -EACCES)))
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001276 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 openflags->w = 0;
1278 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001279 if (fd < 0)
1280 return fd;
Jeff Dikedc764e52007-05-06 14:51:41 -07001281 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282
Jeff Dike6c29256c2006-03-27 01:14:37 -08001283 if(shared)
1284 printk("Not locking \"%s\" on the host\n", file);
1285 else {
1286 err = os_lock_file(fd, openflags->w);
1287 if(err < 0){
1288 printk("Failed to lock '%s', err = %d\n", file, -err);
1289 goto out_close;
1290 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 }
1292
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001293 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001294 if(backing_file_out == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -07001295 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296
1297 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1298 &size, &sectorsize, &align, bitmap_offset_out);
1299 if(err && (*backing_file_out != NULL)){
1300 printk("Failed to read COW header from COW file \"%s\", "
1301 "errno = %d\n", file, -err);
1302 goto out_close;
1303 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001304 if(err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001305 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001307 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001309 /* Allow switching only if no mismatch. */
1310 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 printk("Switching backing file to '%s'\n", *backing_file_out);
1312 err = write_cow_header(file, fd, *backing_file_out,
1313 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001314 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001316 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001318 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 *backing_file_out = backing_file;
1320 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001321 if (err)
1322 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 }
1324
1325 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1326 bitmap_len_out, data_offset_out);
1327
Jeff Dikedc764e52007-05-06 14:51:41 -07001328 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 out_close:
1330 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001331 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332}
1333
1334int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1335 int sectorsize, int alignment, int *bitmap_offset_out,
1336 unsigned long *bitmap_len_out, int *data_offset_out)
1337{
1338 int err, fd;
1339
1340 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001341 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 if(fd < 0){
1343 err = fd;
1344 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1345 -err);
1346 goto out;
1347 }
1348
1349 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1350 bitmap_offset_out, bitmap_len_out,
1351 data_offset_out);
1352 if(!err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001353 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 os_close_file(fd);
1355 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001356 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357}
1358
Jeff Dike91acb212005-10-10 23:10:32 -04001359static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360{
Jeff Dike91acb212005-10-10 23:10:32 -04001361 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
Jeff Dike91acb212005-10-10 23:10:32 -04001363 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001364 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365
Jeff Dike91acb212005-10-10 23:10:32 -04001366 n = os_seek_file(req->fds[1], req->cow_offset);
1367 if(n < 0){
1368 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001369 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001370 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001372 n = os_write_file(req->fds[1], &req->bitmap_words,
1373 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001374 if(n != sizeof(req->bitmap_words)){
1375 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1376 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001377 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001378 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
Jeff Dikedc764e52007-05-06 14:51:41 -07001380 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381}
Jeff Dike91acb212005-10-10 23:10:32 -04001382
1383void do_io(struct io_thread_req *req)
1384{
1385 char *buf;
1386 unsigned long len;
1387 int n, nsectors, start, end, bit;
1388 int err;
1389 __u64 off;
1390
1391 nsectors = req->length / req->sectorsize;
1392 start = 0;
1393 do {
1394 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1395 end = start;
1396 while((end < nsectors) &&
1397 (ubd_test_bit(end, (unsigned char *)
1398 &req->sector_mask) == bit))
1399 end++;
1400
1401 off = req->offset + req->offsets[bit] +
1402 start * req->sectorsize;
1403 len = (end - start) * req->sectorsize;
1404 buf = &req->buffer[start * req->sectorsize];
1405
1406 err = os_seek_file(req->fds[bit], off);
1407 if(err < 0){
1408 printk("do_io - lseek failed : err = %d\n", -err);
1409 req->error = 1;
1410 return;
1411 }
1412 if(req->op == UBD_READ){
1413 n = 0;
1414 do {
1415 buf = &buf[n];
1416 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001417 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001418 if (n < 0) {
1419 printk("do_io - read failed, err = %d "
1420 "fd = %d\n", -n, req->fds[bit]);
1421 req->error = 1;
1422 return;
1423 }
1424 } while((n < len) && (n != 0));
1425 if (n < len) memset(&buf[n], 0, len - n);
1426 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001427 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001428 if(n != len){
1429 printk("do_io - write failed err = %d "
1430 "fd = %d\n", -n, req->fds[bit]);
1431 req->error = 1;
1432 return;
1433 }
1434 }
1435
1436 start = end;
1437 } while(start < nsectors);
1438
1439 req->error = update_bitmap(req);
1440}
1441
1442/* Changed in start_io_thread, which is serialized by being called only
1443 * from ubd_init, which is an initcall.
1444 */
1445int kernel_fd = -1;
1446
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001447/* Only changed by the io thread. XXX: currently unused. */
1448static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001449
1450int io_thread(void *arg)
1451{
Jeff Dike2adcec22007-05-06 14:51:37 -07001452 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001453 int n;
1454
1455 ignore_sigwinch_sig();
1456 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001457 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001458 sizeof(struct io_thread_req *));
1459 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001460 if(n < 0)
1461 printk("io_thread - read failed, fd = %d, "
1462 "err = %d\n", kernel_fd, -n);
1463 else {
1464 printk("io_thread - short read, fd = %d, "
1465 "length = %d\n", kernel_fd, n);
1466 }
1467 continue;
1468 }
1469 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001470 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001471 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001472 sizeof(struct io_thread_req *));
1473 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001474 printk("io_thread - write failed, fd = %d, err = %d\n",
1475 kernel_fd, -n);
1476 }
Jeff Dike91acb212005-10-10 23:10:32 -04001477
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001478 return 0;
1479}