blob: 5e45e39a8a8db5a5c57e4f27162804773c465c9e [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"
Jeff Dikeedea1382008-02-04 22:30:46 -080052#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#include "os.h"
54#include "mem.h"
55#include "mem_kern.h"
56#include "cow.h"
57
Jeff Dike7b9014c2005-05-20 13:59:11 -070058enum ubd_req { UBD_READ, UBD_WRITE };
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
60struct io_thread_req {
Jeff Dike62f96cb2007-02-10 01:44:16 -080061 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -040062 enum ubd_req op;
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 int fds[2];
64 unsigned long offsets[2];
65 unsigned long long offset;
66 unsigned long length;
67 char *buffer;
68 int sectorsize;
Jeff Dike91acb212005-10-10 23:10:32 -040069 unsigned long sector_mask;
70 unsigned long long cow_offset;
71 unsigned long bitmap_words[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 int error;
73};
74
Jeff Dike91acb212005-10-10 23:10:32 -040075static inline int ubd_test_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070076{
77 __u64 n;
78 int bits, off;
79
Jeff Dike91acb212005-10-10 23:10:32 -040080 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 n = bit / bits;
82 off = bit % bits;
Jeff Dikedc764e52007-05-06 14:51:41 -070083 return (data[n] & (1 << off)) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084}
85
Jeff Dike91acb212005-10-10 23:10:32 -040086static inline void ubd_set_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 Dike91acb212005-10-10 23:10:32 -040094 data[n] |= (1 << off);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095}
96/*End stuff from ubd_user.h*/
97
98#define DRIVER_NAME "uml-blkdev"
99
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800100static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102static int ubd_open(struct inode * inode, struct file * filp);
103static int ubd_release(struct inode * inode, struct file * file);
104static int ubd_ioctl(struct inode * inode, struct file * file,
105 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800106static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800108#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110static struct block_device_operations ubd_blops = {
111 .owner = THIS_MODULE,
112 .open = ubd_open,
113 .release = ubd_release,
114 .ioctl = ubd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800115 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116};
117
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118/* Protected by ubd_lock */
119static int fake_major = MAJOR_NR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120static struct gendisk *ubd_gendisk[MAX_DEV];
121static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123#ifdef CONFIG_BLK_DEV_UBD_SYNC
124#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
125 .cl = 1 })
126#else
127#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
128 .cl = 1 })
129#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130static struct openflags global_openflags = OPEN_FLAGS;
131
132struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800133 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800135 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 int fd;
137 unsigned long *bitmap;
138 unsigned long bitmap_len;
139 int bitmap_offset;
Jeff Dikedc764e52007-05-06 14:51:41 -0700140 int data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141};
142
Jeff Dikea0044bd2007-05-06 14:51:36 -0700143#define MAX_SG 64
144
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145struct ubd {
Jeff Dikea0044bd2007-05-06 14:51:36 -0700146 struct list_head restart;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800147 /* name (and fd, below) of the file opened for writing, either the
148 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 char *file;
150 int count;
151 int fd;
152 __u64 size;
153 struct openflags boot_openflags;
154 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800155 unsigned shared:1;
156 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 struct cow cow;
158 struct platform_device pdev;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800159 struct request_queue *queue;
160 spinlock_t lock;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700161 struct scatterlist sg[MAX_SG];
162 struct request *request;
163 int start_sg, end_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164};
165
166#define DEFAULT_COW { \
167 .file = NULL, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700168 .fd = -1, \
169 .bitmap = NULL, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 .bitmap_offset = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700171 .data_offset = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172}
173
174#define DEFAULT_UBD { \
175 .file = NULL, \
176 .count = 0, \
177 .fd = -1, \
178 .size = -1, \
179 .boot_openflags = OPEN_FLAGS, \
180 .openflags = OPEN_FLAGS, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700181 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800182 .shared = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700183 .cow = DEFAULT_COW, \
Jeff Dike62f96cb2007-02-10 01:44:16 -0800184 .lock = SPIN_LOCK_UNLOCKED, \
Jeff Dikea0044bd2007-05-06 14:51:36 -0700185 .request = NULL, \
186 .start_sg = 0, \
187 .end_sg = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188}
189
Jeff Dikeb8831a12007-02-10 01:44:17 -0800190/* Protected by ubd_lock */
WANG Cong5dc62b12008-04-28 02:13:58 -0700191static struct ubd ubd_devs[MAX_DEV] = { [0 ... MAX_DEV - 1] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193/* Only changed by fake_ide_setup which is a setup */
194static int fake_ide = 0;
195static struct proc_dir_entry *proc_ide_root = NULL;
196static struct proc_dir_entry *proc_ide = NULL;
197
198static void make_proc_ide(void)
199{
200 proc_ide_root = proc_mkdir("ide", NULL);
201 proc_ide = proc_mkdir("ide0", proc_ide_root);
202}
203
204static int proc_ide_read_media(char *page, char **start, off_t off, int count,
205 int *eof, void *data)
206{
207 int len;
208
209 strcpy(page, "disk\n");
210 len = strlen("disk\n");
211 len -= off;
212 if (len < count){
213 *eof = 1;
214 if (len <= 0) return 0;
215 }
216 else len = count;
217 *start = page + off;
218 return len;
219}
220
WANG Congc0a92902008-02-04 22:30:41 -0800221static void make_ide_entries(const char *dev_name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222{
223 struct proc_dir_entry *dir, *ent;
224 char name[64];
225
226 if(proc_ide_root == NULL) make_proc_ide();
227
228 dir = proc_mkdir(dev_name, proc_ide);
229 if(!dir) return;
230
231 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
232 if(!ent) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 ent->data = NULL;
234 ent->read_proc = proc_ide_read_media;
235 ent->write_proc = NULL;
WANG Congc0a92902008-02-04 22:30:41 -0800236 snprintf(name, sizeof(name), "ide0/%s", dev_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 proc_symlink(dev_name, proc_ide_root, name);
238}
239
240static int fake_ide_setup(char *str)
241{
242 fake_ide = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700243 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244}
245
246__setup("fake_ide", fake_ide_setup);
247
248__uml_help(fake_ide_setup,
249"fake_ide\n"
250" Create ide0 entries that map onto ubd devices.\n\n"
251);
252
253static int parse_unit(char **ptr)
254{
255 char *str = *ptr, *end;
256 int n = -1;
257
258 if(isdigit(*str)) {
259 n = simple_strtoul(str, &end, 0);
260 if(end == str)
Jeff Dikedc764e52007-05-06 14:51:41 -0700261 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 *ptr = end;
263 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800264 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 n = *str - 'a';
266 str++;
267 *ptr = str;
268 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700269 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270}
271
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800272/* If *index_out == -1 at exit, the passed option was a general one;
273 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
274 * should not be freed on exit.
275 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800276static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800278 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 struct openflags flags = global_openflags;
280 char *backing_file;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800281 int n, err = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
283 if(index_out) *index_out = -1;
284 n = *str;
285 if(n == '='){
286 char *end;
287 int major;
288
289 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 if(!strcmp(str, "sync")){
291 global_openflags = of_sync(global_openflags);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800292 goto out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 }
294
Jeff Dikef28169d2007-02-10 01:43:53 -0800295 err = -EINVAL;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800296 major = simple_strtoul(str, &end, 0);
297 if((*end != '\0') || (end == str)){
298 *error_out = "Didn't parse major number";
299 goto out1;
300 }
301
Jeff Dikef28169d2007-02-10 01:43:53 -0800302 mutex_lock(&ubd_lock);
303 if(fake_major != MAJOR_NR){
304 *error_out = "Can't assign a fake major twice";
305 goto out1;
306 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800307
Jeff Dikef28169d2007-02-10 01:43:53 -0800308 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310 printk(KERN_INFO "Setting extra ubd major number to %d\n",
311 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800312 err = 0;
313 out1:
314 mutex_unlock(&ubd_lock);
315 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 }
317
318 n = parse_unit(&str);
319 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800320 *error_out = "Couldn't parse device number";
321 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 }
323 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800324 *error_out = "Device number out of range";
325 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
327
Jeff Dikef28169d2007-02-10 01:43:53 -0800328 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800329 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800331 ubd_dev = &ubd_devs[n];
332 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800333 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 goto out;
335 }
336
337 if (index_out)
338 *index_out = n;
339
Jeff Dikef28169d2007-02-10 01:43:53 -0800340 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800341 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 switch (*str) {
343 case 'r':
344 flags.w = 0;
345 break;
346 case 's':
347 flags.s = 1;
348 break;
349 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800350 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800352 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800353 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800354 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 case '=':
356 str++;
357 goto break_loop;
358 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800359 *error_out = "Expected '=' or flag letter "
360 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 goto out;
362 }
363 str++;
364 }
365
Jeff Dikef28169d2007-02-10 01:43:53 -0800366 if (*str == '=')
367 *error_out = "Too many flags specified";
368 else
369 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 goto out;
371
372break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 backing_file = strchr(str, ',');
374
Jeff Dikef28169d2007-02-10 01:43:53 -0800375 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
Jeff Dikef28169d2007-02-10 01:43:53 -0800378 if(backing_file != NULL){
379 if(ubd_dev->no_cow){
380 *error_out = "Can't specify both 'd' and a cow file";
381 goto out;
382 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 else {
384 *backing_file = '\0';
385 backing_file++;
386 }
387 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800388 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800389 ubd_dev->file = str;
390 ubd_dev->cow.file = backing_file;
391 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800393 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800394 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395}
396
397static int ubd_setup(char *str)
398{
Jeff Dikef28169d2007-02-10 01:43:53 -0800399 char *error;
400 int err;
401
402 err = ubd_setup_common(str, NULL, &error);
403 if(err)
404 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
405 "%s\n", str, error);
406 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407}
408
409__setup("ubd", ubd_setup);
410__uml_help(ubd_setup,
411"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
412" This is used to associate a device with a file in the underlying\n"
413" filesystem. When specifying two filenames, the first one is the\n"
414" COW name and the second is the backing file name. As separator you can\n"
415" use either a ':' or a ',': the first one allows writing things like;\n"
416" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
417" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800418" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419" a COW file or a backing file. To override this detection, add the 'd'\n"
420" flag:\n"
421" ubd0d=BackingFile\n"
422" Usually, there is a filesystem in the file, but \n"
423" that's not required. Swap devices containing swap files can be\n"
424" specified like this. Also, a file which doesn't contain a\n"
425" filesystem can have its contents read in the virtual \n"
426" machine by running 'dd' on the device. <n> must be in the range\n"
427" 0 to 7. Appending an 'r' to the number will cause that device\n"
428" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
Jeff Dike20ede452008-02-04 22:30:37 -0800429" an 's' will cause data to be written to disk on the host immediately.\n"
430" 'c' will cause the device to be treated as being shared between multiple\n"
431" UMLs and file locking will be turned off - this is appropriate for a\n"
432" cluster filesystem and inappropriate at almost all other times.\n\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433);
434
Jeff Dike8299ca52008-02-04 22:30:48 -0800435static int udb_setup(char *str)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436{
437 printk("udb%s specified on command line is almost certainly a ubd -> "
438 "udb TYPO\n", str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700439 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440}
441
442__setup("udb", udb_setup);
443__uml_help(udb_setup,
444"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700445" This option is here solely to catch ubd -> udb typos, which can be\n"
446" to impossible to catch visually unless you specifically look for\n"
447" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448" in the boot output.\n\n"
449);
450
Jens Axboe165125e2007-07-24 09:28:11 +0200451static void do_ubd_request(struct request_queue * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400452
453/* Only changed by ubd_init, which is an initcall. */
WANG Cong5dc62b12008-04-28 02:13:58 -0700454static int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500456static void ubd_end_request(struct request *req, int bytes, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457{
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500458 blk_end_request(req, error, bytes);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459}
460
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800461/* Callable only from interrupt context - otherwise you need to do
462 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700463static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700465 if(bytes < 0){
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500466 ubd_end_request(req, 0, -EIO);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700467 return;
468 }
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500469 ubd_end_request(req, bytes, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470}
471
Jeff Dikea0044bd2007-05-06 14:51:36 -0700472static LIST_HEAD(restart);
473
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800474/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800475/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400476static void ubd_handler(void)
477{
Jeff Dike2adcec22007-05-06 14:51:37 -0700478 struct io_thread_req *req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800479 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700480 struct ubd *ubd;
481 struct list_head *list, *next_ele;
482 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400483 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
Jeff Dikea0044bd2007-05-06 14:51:36 -0700485 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700486 n = os_read_file(thread_fd, &req,
487 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700488 if(n != sizeof(req)){
489 if(n == -EAGAIN)
490 break;
491 printk(KERN_ERR "spurious interrupt in ubd_handler, "
492 "err = %d\n", -n);
493 return;
494 }
495
Jeff Dike2adcec22007-05-06 14:51:37 -0700496 rq = req->req;
497 rq->nr_sectors -= req->length >> 9;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700498 if(rq->nr_sectors == 0)
499 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike2adcec22007-05-06 14:51:37 -0700500 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400501 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800502 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700503
504 list_for_each_safe(list, next_ele, &restart){
505 ubd = container_of(list, struct ubd, restart);
506 list_del_init(&ubd->restart);
507 spin_lock_irqsave(&ubd->lock, flags);
508 do_ubd_request(ubd->queue);
509 spin_unlock_irqrestore(&ubd->lock, flags);
510 }
Jeff Dike91acb212005-10-10 23:10:32 -0400511}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
Al Viro7bea96f2006-10-08 22:49:34 +0100513static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514{
Jeff Dike91acb212005-10-10 23:10:32 -0400515 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700516 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517}
518
Jeff Dike91acb212005-10-10 23:10:32 -0400519/* Only changed by ubd_init, which is an initcall. */
520static int io_pid = -1;
521
WANG Cong5dc62b12008-04-28 02:13:58 -0700522static void kill_io_thread(void)
Jeff Dike91acb212005-10-10 23:10:32 -0400523{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800524 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400525 os_kill_process(io_pid, 1);
526}
527
528__uml_exitcall(kill_io_thread);
529
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800530static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531{
532 char *file;
533
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800534 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700535 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536}
537
WANG Cong5dc62b12008-04-28 02:13:58 -0700538static int read_cow_bitmap(int fd, void *buf, int offset, int len)
539{
540 int err;
541
542 err = os_seek_file(fd, offset);
543 if (err < 0)
544 return err;
545
546 err = os_read_file(fd, buf, len);
547 if (err < 0)
548 return err;
549
550 return 0;
551}
552
553static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
554{
555 unsigned long modtime;
556 unsigned long long actual;
557 int err;
558
559 err = os_file_modtime(file, &modtime);
560 if (err < 0) {
561 printk(KERN_ERR "Failed to get modification time of backing "
562 "file \"%s\", err = %d\n", file, -err);
563 return err;
564 }
565
566 err = os_file_size(file, &actual);
567 if (err < 0) {
568 printk(KERN_ERR "Failed to get size of backing file \"%s\", "
569 "err = %d\n", file, -err);
570 return err;
571 }
572
573 if (actual != size) {
574 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
575 * the typecast.*/
576 printk(KERN_ERR "Size mismatch (%llu vs %llu) of COW header "
577 "vs backing file\n", (unsigned long long) size, actual);
578 return -EINVAL;
579 }
580 if (modtime != mtime) {
581 printk(KERN_ERR "mtime mismatch (%ld vs %ld) of COW header vs "
582 "backing file\n", mtime, modtime);
583 return -EINVAL;
584 }
585 return 0;
586}
587
588static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
589{
590 struct uml_stat buf1, buf2;
591 int err;
592
593 if (from_cmdline == NULL)
594 return 0;
595 if (!strcmp(from_cmdline, from_cow))
596 return 0;
597
598 err = os_stat_file(from_cmdline, &buf1);
599 if (err < 0) {
600 printk(KERN_ERR "Couldn't stat '%s', err = %d\n", from_cmdline,
601 -err);
602 return 0;
603 }
604 err = os_stat_file(from_cow, &buf2);
605 if (err < 0) {
606 printk(KERN_ERR "Couldn't stat '%s', err = %d\n", from_cow,
607 -err);
608 return 1;
609 }
610 if ((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
611 return 0;
612
613 printk(KERN_ERR "Backing file mismatch - \"%s\" requested, "
614 "\"%s\" specified in COW header of \"%s\"\n",
615 from_cmdline, from_cow, cow);
616 return 1;
617}
618
619static int open_ubd_file(char *file, struct openflags *openflags, int shared,
620 char **backing_file_out, int *bitmap_offset_out,
621 unsigned long *bitmap_len_out, int *data_offset_out,
622 int *create_cow_out)
623{
624 time_t mtime;
625 unsigned long long size;
626 __u32 version, align;
627 char *backing_file;
628 int fd, err, sectorsize, asked_switch, mode = 0644;
629
630 fd = os_open_file(file, *openflags, mode);
631 if (fd < 0) {
632 if ((fd == -ENOENT) && (create_cow_out != NULL))
633 *create_cow_out = 1;
634 if (!openflags->w ||
635 ((fd != -EROFS) && (fd != -EACCES)))
636 return fd;
637 openflags->w = 0;
638 fd = os_open_file(file, *openflags, mode);
639 if (fd < 0)
640 return fd;
641 }
642
643 if (shared)
644 printk(KERN_INFO "Not locking \"%s\" on the host\n", file);
645 else {
646 err = os_lock_file(fd, openflags->w);
647 if (err < 0) {
648 printk(KERN_ERR "Failed to lock '%s', err = %d\n",
649 file, -err);
650 goto out_close;
651 }
652 }
653
654 /* Successful return case! */
655 if (backing_file_out == NULL)
656 return fd;
657
658 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
659 &size, &sectorsize, &align, bitmap_offset_out);
660 if (err && (*backing_file_out != NULL)) {
661 printk(KERN_ERR "Failed to read COW header from COW file "
662 "\"%s\", errno = %d\n", file, -err);
663 goto out_close;
664 }
665 if (err)
666 return fd;
667
668 asked_switch = path_requires_switch(*backing_file_out, backing_file,
669 file);
670
671 /* Allow switching only if no mismatch. */
672 if (asked_switch && !backing_file_mismatch(*backing_file_out, size,
673 mtime)) {
674 printk(KERN_ERR "Switching backing file to '%s'\n",
675 *backing_file_out);
676 err = write_cow_header(file, fd, *backing_file_out,
677 sectorsize, align, &size);
678 if (err) {
679 printk(KERN_ERR "Switch failed, errno = %d\n", -err);
680 goto out_close;
681 }
682 } else {
683 *backing_file_out = backing_file;
684 err = backing_file_mismatch(*backing_file_out, size, mtime);
685 if (err)
686 goto out_close;
687 }
688
689 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
690 bitmap_len_out, data_offset_out);
691
692 return fd;
693 out_close:
694 os_close_file(fd);
695 return err;
696}
697
698static int create_cow_file(char *cow_file, char *backing_file,
699 struct openflags flags,
700 int sectorsize, int alignment, int *bitmap_offset_out,
701 unsigned long *bitmap_len_out, int *data_offset_out)
702{
703 int err, fd;
704
705 flags.c = 1;
706 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
707 if (fd < 0) {
708 err = fd;
709 printk(KERN_ERR "Open of COW file '%s' failed, errno = %d\n",
710 cow_file, -err);
711 goto out;
712 }
713
714 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
715 bitmap_offset_out, bitmap_len_out,
716 data_offset_out);
717 if (!err)
718 return fd;
719 os_close_file(fd);
720 out:
721 return err;
722}
723
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800724static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800726 os_close_file(ubd_dev->fd);
727 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 return;
729
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800730 os_close_file(ubd_dev->cow.fd);
731 vfree(ubd_dev->cow.bitmap);
732 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733}
734
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800735static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736{
737 struct openflags flags;
738 char **back_ptr;
739 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800740 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800742 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800744 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
745 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800746
747 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800748 back_ptr, &ubd_dev->cow.bitmap_offset,
749 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800750 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800752 if((fd == -ENOENT) && create_cow){
753 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800754 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
755 &ubd_dev->cow.bitmap_offset,
756 &ubd_dev->cow.bitmap_len,
757 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800758 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800760 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 }
762 }
763
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800764 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800765 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800766 -fd);
767 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800769 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800771 if(ubd_dev->cow.file != NULL){
Jeff Dikef4768ff2007-08-22 14:01:53 -0700772 blk_queue_max_sectors(ubd_dev->queue, 8 * sizeof(long));
773
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 err = -ENOMEM;
Jesper Juhlda2486b2007-10-16 01:27:19 -0700775 ubd_dev->cow.bitmap = vmalloc(ubd_dev->cow.bitmap_len);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800776 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
778 goto error;
779 }
780 flush_tlb_kernel_vm();
781
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800782 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
783 ubd_dev->cow.bitmap_offset,
784 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 if(err < 0)
786 goto error;
787
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800788 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800790 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800791 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800793 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700795 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800797 os_close_file(ubd_dev->fd);
Jeff Dikedc764e52007-05-06 14:51:41 -0700798 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799}
800
Jeff Dike2e3f5252007-05-06 14:51:29 -0700801static void ubd_device_release(struct device *dev)
802{
803 struct ubd *ubd_dev = dev->driver_data;
804
805 blk_cleanup_queue(ubd_dev->queue);
806 *ubd_dev = ((struct ubd) DEFAULT_UBD);
807}
808
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800809static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800810 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
812 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
814 disk = alloc_disk(1 << UBD_SHIFT);
815 if(disk == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -0700816 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
818 disk->major = major;
819 disk->first_minor = unit << UBD_SHIFT;
820 disk->fops = &ubd_blops;
821 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700822 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700824 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
827 /* sysfs register (not for ide fake devices) */
828 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800829 ubd_devs[unit].pdev.id = unit;
830 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700831 ubd_devs[unit].pdev.dev.release = ubd_device_release;
832 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800833 platform_device_register(&ubd_devs[unit].pdev);
834 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 }
836
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800837 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800838 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 add_disk(disk);
840
841 *disk_out = disk;
842 return 0;
843}
844
845#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
846
Jeff Dikef28169d2007-02-10 01:43:53 -0800847static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800849 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800850 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800852 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700853 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800855 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800856 if(err < 0){
857 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700858 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800859 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800861 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862
Jeff Dikea0044bd2007-05-06 14:51:36 -0700863 INIT_LIST_HEAD(&ubd_dev->restart);
WANG Cong4f40c052007-11-05 14:50:59 -0800864 sg_init_table(ubd_dev->sg, MAX_SG);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700865
Jeff Dike62f96cb2007-02-10 01:44:16 -0800866 err = -ENOMEM;
867 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
868 if (ubd_dev->queue == NULL) {
869 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700870 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800871 }
872 ubd_dev->queue->queuedata = ubd_dev;
873
Jeff Dikea0044bd2007-05-06 14:51:36 -0700874 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800875 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
876 if(err){
877 *error_out = "Failed to register device";
878 goto out_cleanup;
879 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800882 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800883 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
Jeff Dike83380cc2008-02-04 22:31:18 -0800885 /*
886 * Perhaps this should also be under the "if (fake_major)" above
887 * using the fake_disk->disk_name
888 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 if (fake_ide)
890 make_ide_entries(ubd_gendisk[n]->disk_name);
891
Jeff Dikeec7cf782005-09-03 15:57:29 -0700892 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700893out:
894 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800895
896out_cleanup:
897 blk_cleanup_queue(ubd_dev->queue);
898 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899}
900
Jeff Dikef28169d2007-02-10 01:43:53 -0800901static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800903 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
Jeff Dikef28169d2007-02-10 01:43:53 -0800905 /* This string is possibly broken up and stored, so it's only
906 * freed if ubd_setup_common fails, or if only general options
907 * were set.
908 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800909 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800910 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800911 *error_out = "Failed to allocate memory";
912 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800914
915 ret = ubd_setup_common(str, &n, error_out);
916 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800917 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800918
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800919 if (n == -1) {
920 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800921 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800922 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Jeff Dikedc764e52007-05-06 14:51:41 -0700924 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800925 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800926 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800927 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700928 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800930out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700931 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800932
933err_free:
934 kfree(str);
935 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936}
937
938static int ubd_get_config(char *name, char *str, int size, char **error_out)
939{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800940 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 int n, len = 0;
942
943 n = parse_unit(&name);
944 if((n >= MAX_DEV) || (n < 0)){
945 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700946 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 }
948
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800949 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800950 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800952 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 CONFIG_CHUNK(str, size, len, "", 1);
954 goto out;
955 }
956
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800957 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800959 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800961 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 }
963 else CONFIG_CHUNK(str, size, len, "", 1);
964
965 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800966 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700967 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968}
969
Jeff Dike29d56cf2005-06-25 14:55:25 -0700970static int ubd_id(char **str, int *start_out, int *end_out)
971{
Jeff Dikedc764e52007-05-06 14:51:41 -0700972 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700973
974 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700975 *start_out = 0;
976 *end_out = MAX_DEV - 1;
977 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700978}
979
Jeff Dikef28169d2007-02-10 01:43:53 -0800980static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700982 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800983 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700984 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800986 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800988 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700989
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800990 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700991 goto out;
992
993 /* you cannot remove a open disk */
994 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800995 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700996 goto out;
997
Jeff Dikedc764e52007-05-06 14:51:41 -0700998 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700999 if(disk != NULL){
1000 del_gendisk(disk);
1001 put_disk(disk);
1002 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
1004 if(fake_gendisk[n] != NULL){
1005 del_gendisk(fake_gendisk[n]);
1006 put_disk(fake_gendisk[n]);
1007 fake_gendisk[n] = NULL;
1008 }
1009
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -07001011 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -07001012out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -08001013 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -07001014 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015}
1016
Jeff Dikef28169d2007-02-10 01:43:53 -08001017/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -08001018 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -08001019 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -08001021 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 .name = "ubd",
1023 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -07001024 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -07001025 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 .remove = ubd_remove,
1027};
1028
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001029static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030{
1031 mconsole_register_dev(&ubd_mc);
1032 return 0;
1033}
1034
1035__initcall(ubd_mc_init);
1036
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001037static int __init ubd0_init(void)
1038{
1039 struct ubd *ubd_dev = &ubd_devs[0];
1040
Jeff Dikeb8831a12007-02-10 01:44:17 -08001041 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001042 if(ubd_dev->file == NULL)
1043 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -08001044 mutex_unlock(&ubd_lock);
1045
Jeff Dikedc764e52007-05-06 14:51:41 -07001046 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001047}
1048
1049__initcall(ubd0_init);
1050
Jeff Dikeb8831a12007-02-10 01:44:17 -08001051/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +00001052static struct platform_driver ubd_driver = {
1053 .driver = {
1054 .name = DRIVER_NAME,
1055 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056};
1057
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001058static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059{
Jeff Dikef28169d2007-02-10 01:43:53 -08001060 char *error;
1061 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 if (register_blkdev(MAJOR_NR, "ubd"))
1064 return -1;
1065
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 if (fake_major != MAJOR_NR) {
1067 char name[sizeof("ubd_nnn\0")];
1068
1069 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 if (register_blkdev(fake_major, "ubd"))
1071 return -1;
1072 }
Russell King3ae5eae2005-11-09 22:32:44 +00001073 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -07001074 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -08001075 for (i = 0; i < MAX_DEV; i++){
1076 err = ubd_add(i, &error);
1077 if(err)
1078 printk(KERN_ERR "Failed to initialize ubd device %d :"
1079 "%s\n", i, error);
1080 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001081 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 return 0;
1083}
1084
1085late_initcall(ubd_init);
1086
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001087static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -04001088 unsigned long stack;
1089 int err;
1090
1091 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
1092 if(global_openflags.s){
1093 printk(KERN_INFO "ubd: Synchronous mode\n");
1094 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
1095 * enough. So use anyway the io thread. */
1096 }
1097 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -08001098 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -04001099 &thread_fd);
1100 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -08001101 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -04001102 "ubd : Failed to start I/O thread (errno = %d) - "
1103 "falling back to synchronous I/O\n", -io_pid);
1104 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001105 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001106 }
Jeff Dike6c29256c2006-03-27 01:14:37 -08001107 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001108 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -04001109 if(err != 0)
1110 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -08001111 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001112}
1113
1114device_initcall(ubd_driver_init);
1115
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116static int ubd_open(struct inode *inode, struct file *filp)
1117{
1118 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001119 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 int err = 0;
1121
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001122 if(ubd_dev->count == 0){
1123 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 if(err){
1125 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001126 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 goto out;
1128 }
1129 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001130 ubd_dev->count++;
1131 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -07001132
1133 /* This should no more be needed. And it didn't work anyway to exclude
1134 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001135 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -08001136 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -07001138 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001140 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141}
1142
1143static int ubd_release(struct inode * inode, struct file * file)
1144{
1145 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001146 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001148 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -08001149 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -07001150 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151}
1152
Jeff Dike91acb212005-10-10 23:10:32 -04001153static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
1154 __u64 *cow_offset, unsigned long *bitmap,
1155 __u64 bitmap_offset, unsigned long *bitmap_words,
1156 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157{
Jeff Dike91acb212005-10-10 23:10:32 -04001158 __u64 sector = io_offset >> 9;
1159 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
Jeff Dike91acb212005-10-10 23:10:32 -04001161 for(i = 0; i < length >> 9; i++){
1162 if(cow_mask != NULL)
1163 ubd_set_bit(i, (unsigned char *) cow_mask);
1164 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1165 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166
Jeff Dike91acb212005-10-10 23:10:32 -04001167 update_bitmap = 1;
1168 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1169 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170
Jeff Dike91acb212005-10-10 23:10:32 -04001171 if(!update_bitmap)
1172 return;
1173
1174 *cow_offset = sector / (sizeof(unsigned long) * 8);
1175
1176 /* This takes care of the case where we're exactly at the end of the
1177 * device, and *cow_offset + 1 is off the end. So, just back it up
1178 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1179 * for the original diagnosis.
1180 */
1181 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1182 sizeof(unsigned long) - 1))
1183 (*cow_offset)--;
1184
1185 bitmap_words[0] = bitmap[*cow_offset];
1186 bitmap_words[1] = bitmap[*cow_offset + 1];
1187
1188 *cow_offset *= sizeof(unsigned long);
1189 *cow_offset += bitmap_offset;
1190}
1191
1192static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1193 __u64 bitmap_offset, __u64 bitmap_len)
1194{
1195 __u64 sector = req->offset >> 9;
1196 int i;
1197
1198 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1199 panic("Operation too long");
1200
1201 if(req->op == UBD_READ) {
1202 for(i = 0; i < req->length >> 9; i++){
1203 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001204 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001205 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001206 }
Jeff Dike91acb212005-10-10 23:10:32 -04001207 }
1208 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1209 &req->cow_offset, bitmap, bitmap_offset,
1210 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211}
1212
Jeff Dike62f96cb2007-02-10 01:44:16 -08001213/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001214static void prepare_request(struct request *req, struct io_thread_req *io_req,
1215 unsigned long long offset, int page_offset,
1216 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217{
1218 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001219 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001220
Jeff Dike62f96cb2007-02-10 01:44:16 -08001221 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001222 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1223 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001224 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001225 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 io_req->offset = offset;
1227 io_req->length = len;
1228 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001229 io_req->sector_mask = 0;
1230
1231 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001233 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001234 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 io_req->sectorsize = 1 << 9;
1236
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001237 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001238 cowify_req(io_req, ubd_dev->cow.bitmap,
1239 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001240
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241}
1242
Jeff Dike62f96cb2007-02-10 01:44:16 -08001243/* Called with dev->lock held */
Jens Axboe165125e2007-07-24 09:28:11 +02001244static void do_ubd_request(struct request_queue *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245{
Jeff Dike2adcec22007-05-06 14:51:37 -07001246 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 struct request *req;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001248 int n, last_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249
Jeff Dikea0044bd2007-05-06 14:51:36 -07001250 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001251 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001252 if(dev->end_sg == 0){
1253 struct request *req = elv_next_request(q);
1254 if(req == NULL)
1255 return;
1256
1257 dev->request = req;
1258 blkdev_dequeue_request(req);
1259 dev->start_sg = 0;
1260 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001261 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001262
1263 req = dev->request;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001264 last_sectors = 0;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001265 while(dev->start_sg < dev->end_sg){
1266 struct scatterlist *sg = &dev->sg[dev->start_sg];
1267
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001268 req->sector += last_sectors;
Jeff Dike2adcec22007-05-06 14:51:37 -07001269 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001270 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001271 if(io_req == NULL){
1272 if(list_empty(&dev->restart))
1273 list_add(&dev->restart, &restart);
1274 return;
1275 }
1276 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001277 (unsigned long long) req->sector << 9,
Jens Axboe45711f12007-10-22 21:19:53 +02001278 sg->offset, sg->length, sg_page(sg));
Jeff Dikea0044bd2007-05-06 14:51:36 -07001279
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001280 last_sectors = sg->length >> 9;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001281 n = os_write_file(thread_fd, &io_req,
1282 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001283 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001284 if(n != -EAGAIN)
1285 printk("write to io thread failed, "
1286 "errno = %d\n", -n);
1287 else if(list_empty(&dev->restart))
1288 list_add(&dev->restart, &restart);
Miklos Szeredi12429bf2007-11-28 16:21:52 -08001289 kfree(io_req);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001290 return;
1291 }
1292
Jeff Dikea0044bd2007-05-06 14:51:36 -07001293 dev->start_sg++;
1294 }
1295 dev->end_sg = 0;
1296 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001297 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298}
1299
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001300static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1301{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001302 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001303
1304 geo->heads = 128;
1305 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001306 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001307 return 0;
1308}
1309
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310static int ubd_ioctl(struct inode * inode, struct file * file,
1311 unsigned int cmd, unsigned long arg)
1312{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001313 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 struct hd_driveid ubd_id = {
1315 .cyls = 0,
1316 .heads = 128,
1317 .sectors = 32,
1318 };
1319
1320 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001323 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1325 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001326 return -EFAULT;
1327 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001328
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 case CDROMVOLREAD:
1330 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001331 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 volume.channel0 = 255;
1333 volume.channel1 = 255;
1334 volume.channel2 = 255;
1335 volume.channel3 = 255;
1336 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001337 return -EFAULT;
1338 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001340 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341}
1342
Jeff Dike91acb212005-10-10 23:10:32 -04001343static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344{
Jeff Dike91acb212005-10-10 23:10:32 -04001345 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346
Jeff Dike91acb212005-10-10 23:10:32 -04001347 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001348 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
Jeff Dike91acb212005-10-10 23:10:32 -04001350 n = os_seek_file(req->fds[1], req->cow_offset);
1351 if(n < 0){
1352 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001353 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001354 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001356 n = os_write_file(req->fds[1], &req->bitmap_words,
1357 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001358 if(n != sizeof(req->bitmap_words)){
1359 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1360 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001361 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001362 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
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
WANG Cong5dc62b12008-04-28 02:13:58 -07001367static void do_io(struct io_thread_req *req)
Jeff Dike91acb212005-10-10 23:10:32 -04001368{
1369 char *buf;
1370 unsigned long len;
1371 int n, nsectors, start, end, bit;
1372 int err;
1373 __u64 off;
1374
1375 nsectors = req->length / req->sectorsize;
1376 start = 0;
1377 do {
1378 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1379 end = start;
1380 while((end < nsectors) &&
1381 (ubd_test_bit(end, (unsigned char *)
1382 &req->sector_mask) == bit))
1383 end++;
1384
1385 off = req->offset + req->offsets[bit] +
1386 start * req->sectorsize;
1387 len = (end - start) * req->sectorsize;
1388 buf = &req->buffer[start * req->sectorsize];
1389
1390 err = os_seek_file(req->fds[bit], off);
1391 if(err < 0){
1392 printk("do_io - lseek failed : err = %d\n", -err);
1393 req->error = 1;
1394 return;
1395 }
1396 if(req->op == UBD_READ){
1397 n = 0;
1398 do {
1399 buf = &buf[n];
1400 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001401 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001402 if (n < 0) {
1403 printk("do_io - read failed, err = %d "
1404 "fd = %d\n", -n, req->fds[bit]);
1405 req->error = 1;
1406 return;
1407 }
1408 } while((n < len) && (n != 0));
1409 if (n < len) memset(&buf[n], 0, len - n);
1410 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001411 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001412 if(n != len){
1413 printk("do_io - write failed err = %d "
1414 "fd = %d\n", -n, req->fds[bit]);
1415 req->error = 1;
1416 return;
1417 }
1418 }
1419
1420 start = end;
1421 } while(start < nsectors);
1422
1423 req->error = update_bitmap(req);
1424}
1425
1426/* Changed in start_io_thread, which is serialized by being called only
1427 * from ubd_init, which is an initcall.
1428 */
1429int kernel_fd = -1;
1430
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001431/* Only changed by the io thread. XXX: currently unused. */
1432static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001433
1434int io_thread(void *arg)
1435{
Jeff Dike2adcec22007-05-06 14:51:37 -07001436 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001437 int n;
1438
1439 ignore_sigwinch_sig();
1440 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001441 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001442 sizeof(struct io_thread_req *));
1443 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001444 if(n < 0)
1445 printk("io_thread - read failed, fd = %d, "
1446 "err = %d\n", kernel_fd, -n);
1447 else {
1448 printk("io_thread - short read, fd = %d, "
1449 "length = %d\n", kernel_fd, n);
1450 }
1451 continue;
1452 }
1453 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001454 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001455 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001456 sizeof(struct io_thread_req *));
1457 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001458 printk("io_thread - write failed, fd = %d, err = %d\n",
1459 kernel_fd, -n);
1460 }
Jeff Dike91acb212005-10-10 23:10:32 -04001461
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001462 return 0;
1463}