blob: 0a868118cf06b2bb2aece4afbbd89d18228d43dc [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 Dike91acb212005-10-10 23:10:32 -040074static inline int ubd_test_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070075{
76 __u64 n;
77 int bits, off;
78
Jeff Dike91acb212005-10-10 23:10:32 -040079 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 n = bit / bits;
81 off = bit % bits;
Jeff Dikedc764e52007-05-06 14:51:41 -070082 return (data[n] & (1 << off)) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083}
84
Jeff Dike91acb212005-10-10 23:10:32 -040085static inline void ubd_set_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070086{
87 __u64 n;
88 int bits, off;
89
Jeff Dike91acb212005-10-10 23:10:32 -040090 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 n = bit / bits;
92 off = bit % bits;
Jeff Dike91acb212005-10-10 23:10:32 -040093 data[n] |= (1 << off);
Linus Torvalds1da177e2005-04-16 15:20:36 -070094}
95/*End stuff from ubd_user.h*/
96
97#define DRIVER_NAME "uml-blkdev"
98
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -080099static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
Al Viroa625c992008-03-02 09:16:26 -0500101static int ubd_open(struct block_device *bdev, fmode_t mode);
102static int ubd_release(struct gendisk *disk, fmode_t mode);
103static int ubd_ioctl(struct block_device *bdev, fmode_t mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800105static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800107#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109static struct block_device_operations ubd_blops = {
110 .owner = THIS_MODULE,
Al Viroa625c992008-03-02 09:16:26 -0500111 .open = ubd_open,
112 .release = ubd_release,
113 .ioctl = ubd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800114 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115};
116
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117/* Protected by ubd_lock */
118static int fake_major = MAJOR_NR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119static struct gendisk *ubd_gendisk[MAX_DEV];
120static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800121
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122#ifdef CONFIG_BLK_DEV_UBD_SYNC
123#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
124 .cl = 1 })
125#else
126#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
127 .cl = 1 })
128#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129static struct openflags global_openflags = OPEN_FLAGS;
130
131struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800132 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800134 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 int fd;
136 unsigned long *bitmap;
137 unsigned long bitmap_len;
138 int bitmap_offset;
Jeff Dikedc764e52007-05-06 14:51:41 -0700139 int data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140};
141
Jeff Dikea0044bd2007-05-06 14:51:36 -0700142#define MAX_SG 64
143
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144struct ubd {
Jeff Dikea0044bd2007-05-06 14:51:36 -0700145 struct list_head restart;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800146 /* name (and fd, below) of the file opened for writing, either the
147 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 char *file;
149 int count;
150 int fd;
151 __u64 size;
152 struct openflags boot_openflags;
153 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800154 unsigned shared:1;
155 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 struct cow cow;
157 struct platform_device pdev;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800158 struct request_queue *queue;
159 spinlock_t lock;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700160 struct scatterlist sg[MAX_SG];
161 struct request *request;
162 int start_sg, end_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163};
164
165#define DEFAULT_COW { \
166 .file = NULL, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700167 .fd = -1, \
168 .bitmap = NULL, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 .bitmap_offset = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700170 .data_offset = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171}
172
173#define DEFAULT_UBD { \
174 .file = NULL, \
175 .count = 0, \
176 .fd = -1, \
177 .size = -1, \
178 .boot_openflags = OPEN_FLAGS, \
179 .openflags = OPEN_FLAGS, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700180 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800181 .shared = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700182 .cow = DEFAULT_COW, \
Jeff Dike62f96cb2007-02-10 01:44:16 -0800183 .lock = SPIN_LOCK_UNLOCKED, \
Jeff Dikea0044bd2007-05-06 14:51:36 -0700184 .request = NULL, \
185 .start_sg = 0, \
186 .end_sg = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187}
188
Jeff Dikeb8831a12007-02-10 01:44:17 -0800189/* Protected by ubd_lock */
WANG Cong5dc62b12008-04-28 02:13:58 -0700190static struct ubd ubd_devs[MAX_DEV] = { [0 ... MAX_DEV - 1] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192/* Only changed by fake_ide_setup which is a setup */
193static int fake_ide = 0;
194static struct proc_dir_entry *proc_ide_root = NULL;
195static struct proc_dir_entry *proc_ide = NULL;
196
197static void make_proc_ide(void)
198{
199 proc_ide_root = proc_mkdir("ide", NULL);
200 proc_ide = proc_mkdir("ide0", proc_ide_root);
201}
202
203static int proc_ide_read_media(char *page, char **start, off_t off, int count,
204 int *eof, void *data)
205{
206 int len;
207
208 strcpy(page, "disk\n");
209 len = strlen("disk\n");
210 len -= off;
211 if (len < count){
212 *eof = 1;
213 if (len <= 0) return 0;
214 }
215 else len = count;
216 *start = page + off;
217 return len;
218}
219
WANG Congc0a92902008-02-04 22:30:41 -0800220static void make_ide_entries(const char *dev_name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221{
222 struct proc_dir_entry *dir, *ent;
223 char name[64];
224
225 if(proc_ide_root == NULL) make_proc_ide();
226
227 dir = proc_mkdir(dev_name, proc_ide);
228 if(!dir) return;
229
230 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
231 if(!ent) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 ent->data = NULL;
233 ent->read_proc = proc_ide_read_media;
234 ent->write_proc = NULL;
WANG Congc0a92902008-02-04 22:30:41 -0800235 snprintf(name, sizeof(name), "ide0/%s", dev_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 proc_symlink(dev_name, proc_ide_root, name);
237}
238
239static int fake_ide_setup(char *str)
240{
241 fake_ide = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700242 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243}
244
245__setup("fake_ide", fake_ide_setup);
246
247__uml_help(fake_ide_setup,
248"fake_ide\n"
249" Create ide0 entries that map onto ubd devices.\n\n"
250);
251
252static int parse_unit(char **ptr)
253{
254 char *str = *ptr, *end;
255 int n = -1;
256
257 if(isdigit(*str)) {
258 n = simple_strtoul(str, &end, 0);
259 if(end == str)
Jeff Dikedc764e52007-05-06 14:51:41 -0700260 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 *ptr = end;
262 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800263 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 n = *str - 'a';
265 str++;
266 *ptr = str;
267 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700268 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269}
270
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800271/* If *index_out == -1 at exit, the passed option was a general one;
272 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
273 * should not be freed on exit.
274 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800275static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800277 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 struct openflags flags = global_openflags;
279 char *backing_file;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800280 int n, err = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
282 if(index_out) *index_out = -1;
283 n = *str;
284 if(n == '='){
285 char *end;
286 int major;
287
288 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 if(!strcmp(str, "sync")){
290 global_openflags = of_sync(global_openflags);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800291 goto out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 }
293
Jeff Dikef28169d2007-02-10 01:43:53 -0800294 err = -EINVAL;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800295 major = simple_strtoul(str, &end, 0);
296 if((*end != '\0') || (end == str)){
297 *error_out = "Didn't parse major number";
298 goto out1;
299 }
300
Jeff Dikef28169d2007-02-10 01:43:53 -0800301 mutex_lock(&ubd_lock);
302 if(fake_major != MAJOR_NR){
303 *error_out = "Can't assign a fake major twice";
304 goto out1;
305 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800306
Jeff Dikef28169d2007-02-10 01:43:53 -0800307 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309 printk(KERN_INFO "Setting extra ubd major number to %d\n",
310 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800311 err = 0;
312 out1:
313 mutex_unlock(&ubd_lock);
314 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 }
316
317 n = parse_unit(&str);
318 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800319 *error_out = "Couldn't parse device number";
320 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 }
322 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800323 *error_out = "Device number out of range";
324 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 }
326
Jeff Dikef28169d2007-02-10 01:43:53 -0800327 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800328 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800330 ubd_dev = &ubd_devs[n];
331 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800332 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 goto out;
334 }
335
336 if (index_out)
337 *index_out = n;
338
Jeff Dikef28169d2007-02-10 01:43:53 -0800339 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800340 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 switch (*str) {
342 case 'r':
343 flags.w = 0;
344 break;
345 case 's':
346 flags.s = 1;
347 break;
348 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800349 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800351 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800352 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800353 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 case '=':
355 str++;
356 goto break_loop;
357 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800358 *error_out = "Expected '=' or flag letter "
359 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 goto out;
361 }
362 str++;
363 }
364
Jeff Dikef28169d2007-02-10 01:43:53 -0800365 if (*str == '=')
366 *error_out = "Too many flags specified";
367 else
368 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 goto out;
370
371break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 backing_file = strchr(str, ',');
373
Jeff Dikef28169d2007-02-10 01:43:53 -0800374 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
Jeff Dikef28169d2007-02-10 01:43:53 -0800377 if(backing_file != NULL){
378 if(ubd_dev->no_cow){
379 *error_out = "Can't specify both 'd' and a cow file";
380 goto out;
381 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 else {
383 *backing_file = '\0';
384 backing_file++;
385 }
386 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800387 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800388 ubd_dev->file = str;
389 ubd_dev->cow.file = backing_file;
390 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800392 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800393 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394}
395
396static int ubd_setup(char *str)
397{
Jeff Dikef28169d2007-02-10 01:43:53 -0800398 char *error;
399 int err;
400
401 err = ubd_setup_common(str, NULL, &error);
402 if(err)
403 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
404 "%s\n", str, error);
405 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406}
407
408__setup("ubd", ubd_setup);
409__uml_help(ubd_setup,
410"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
411" This is used to associate a device with a file in the underlying\n"
412" filesystem. When specifying two filenames, the first one is the\n"
413" COW name and the second is the backing file name. As separator you can\n"
414" use either a ':' or a ',': the first one allows writing things like;\n"
415" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
416" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800417" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418" a COW file or a backing file. To override this detection, add the 'd'\n"
419" flag:\n"
420" ubd0d=BackingFile\n"
421" Usually, there is a filesystem in the file, but \n"
422" that's not required. Swap devices containing swap files can be\n"
423" specified like this. Also, a file which doesn't contain a\n"
424" filesystem can have its contents read in the virtual \n"
425" machine by running 'dd' on the device. <n> must be in the range\n"
426" 0 to 7. Appending an 'r' to the number will cause that device\n"
427" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
Jeff Dike20ede452008-02-04 22:30:37 -0800428" an 's' will cause data to be written to disk on the host immediately.\n"
429" 'c' will cause the device to be treated as being shared between multiple\n"
430" UMLs and file locking will be turned off - this is appropriate for a\n"
431" cluster filesystem and inappropriate at almost all other times.\n\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432);
433
Jeff Dike8299ca52008-02-04 22:30:48 -0800434static int udb_setup(char *str)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435{
436 printk("udb%s specified on command line is almost certainly a ubd -> "
437 "udb TYPO\n", str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700438 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439}
440
441__setup("udb", udb_setup);
442__uml_help(udb_setup,
443"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700444" This option is here solely to catch ubd -> udb typos, which can be\n"
445" to impossible to catch visually unless you specifically look for\n"
446" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447" in the boot output.\n\n"
448);
449
Jens Axboe165125e2007-07-24 09:28:11 +0200450static void do_ubd_request(struct request_queue * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400451
452/* Only changed by ubd_init, which is an initcall. */
WANG Cong5dc62b12008-04-28 02:13:58 -0700453static int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500455static void ubd_end_request(struct request *req, int bytes, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500457 blk_end_request(req, error, bytes);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458}
459
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800460/* Callable only from interrupt context - otherwise you need to do
461 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700462static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700464 if(bytes < 0){
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500465 ubd_end_request(req, 0, -EIO);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700466 return;
467 }
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500468 ubd_end_request(req, bytes, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469}
470
Jeff Dikea0044bd2007-05-06 14:51:36 -0700471static LIST_HEAD(restart);
472
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800473/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800474/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400475static void ubd_handler(void)
476{
Jeff Dike2adcec22007-05-06 14:51:37 -0700477 struct io_thread_req *req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800478 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700479 struct ubd *ubd;
480 struct list_head *list, *next_ele;
481 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400482 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483
Jeff Dikea0044bd2007-05-06 14:51:36 -0700484 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700485 n = os_read_file(thread_fd, &req,
486 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700487 if(n != sizeof(req)){
488 if(n == -EAGAIN)
489 break;
490 printk(KERN_ERR "spurious interrupt in ubd_handler, "
491 "err = %d\n", -n);
492 return;
493 }
494
Jeff Dike2adcec22007-05-06 14:51:37 -0700495 rq = req->req;
496 rq->nr_sectors -= req->length >> 9;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700497 if(rq->nr_sectors == 0)
498 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike2adcec22007-05-06 14:51:37 -0700499 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400500 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800501 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700502
503 list_for_each_safe(list, next_ele, &restart){
504 ubd = container_of(list, struct ubd, restart);
505 list_del_init(&ubd->restart);
506 spin_lock_irqsave(&ubd->lock, flags);
507 do_ubd_request(ubd->queue);
508 spin_unlock_irqrestore(&ubd->lock, flags);
509 }
Jeff Dike91acb212005-10-10 23:10:32 -0400510}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
Al Viro7bea96f2006-10-08 22:49:34 +0100512static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513{
Jeff Dike91acb212005-10-10 23:10:32 -0400514 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700515 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516}
517
Jeff Dike91acb212005-10-10 23:10:32 -0400518/* Only changed by ubd_init, which is an initcall. */
519static int io_pid = -1;
520
WANG Cong5dc62b12008-04-28 02:13:58 -0700521static void kill_io_thread(void)
Jeff Dike91acb212005-10-10 23:10:32 -0400522{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800523 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400524 os_kill_process(io_pid, 1);
525}
526
527__uml_exitcall(kill_io_thread);
528
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800529static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530{
531 char *file;
532
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800533 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700534 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535}
536
WANG Cong5dc62b12008-04-28 02:13:58 -0700537static int read_cow_bitmap(int fd, void *buf, int offset, int len)
538{
539 int err;
540
541 err = os_seek_file(fd, offset);
542 if (err < 0)
543 return err;
544
545 err = os_read_file(fd, buf, len);
546 if (err < 0)
547 return err;
548
549 return 0;
550}
551
552static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
553{
554 unsigned long modtime;
555 unsigned long long actual;
556 int err;
557
558 err = os_file_modtime(file, &modtime);
559 if (err < 0) {
560 printk(KERN_ERR "Failed to get modification time of backing "
561 "file \"%s\", err = %d\n", file, -err);
562 return err;
563 }
564
565 err = os_file_size(file, &actual);
566 if (err < 0) {
567 printk(KERN_ERR "Failed to get size of backing file \"%s\", "
568 "err = %d\n", file, -err);
569 return err;
570 }
571
572 if (actual != size) {
573 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
574 * the typecast.*/
575 printk(KERN_ERR "Size mismatch (%llu vs %llu) of COW header "
576 "vs backing file\n", (unsigned long long) size, actual);
577 return -EINVAL;
578 }
579 if (modtime != mtime) {
580 printk(KERN_ERR "mtime mismatch (%ld vs %ld) of COW header vs "
581 "backing file\n", mtime, modtime);
582 return -EINVAL;
583 }
584 return 0;
585}
586
587static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
588{
589 struct uml_stat buf1, buf2;
590 int err;
591
592 if (from_cmdline == NULL)
593 return 0;
594 if (!strcmp(from_cmdline, from_cow))
595 return 0;
596
597 err = os_stat_file(from_cmdline, &buf1);
598 if (err < 0) {
599 printk(KERN_ERR "Couldn't stat '%s', err = %d\n", from_cmdline,
600 -err);
601 return 0;
602 }
603 err = os_stat_file(from_cow, &buf2);
604 if (err < 0) {
605 printk(KERN_ERR "Couldn't stat '%s', err = %d\n", from_cow,
606 -err);
607 return 1;
608 }
609 if ((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
610 return 0;
611
612 printk(KERN_ERR "Backing file mismatch - \"%s\" requested, "
613 "\"%s\" specified in COW header of \"%s\"\n",
614 from_cmdline, from_cow, cow);
615 return 1;
616}
617
618static int open_ubd_file(char *file, struct openflags *openflags, int shared,
619 char **backing_file_out, int *bitmap_offset_out,
620 unsigned long *bitmap_len_out, int *data_offset_out,
621 int *create_cow_out)
622{
623 time_t mtime;
624 unsigned long long size;
625 __u32 version, align;
626 char *backing_file;
627 int fd, err, sectorsize, asked_switch, mode = 0644;
628
629 fd = os_open_file(file, *openflags, mode);
630 if (fd < 0) {
631 if ((fd == -ENOENT) && (create_cow_out != NULL))
632 *create_cow_out = 1;
633 if (!openflags->w ||
634 ((fd != -EROFS) && (fd != -EACCES)))
635 return fd;
636 openflags->w = 0;
637 fd = os_open_file(file, *openflags, mode);
638 if (fd < 0)
639 return fd;
640 }
641
642 if (shared)
643 printk(KERN_INFO "Not locking \"%s\" on the host\n", file);
644 else {
645 err = os_lock_file(fd, openflags->w);
646 if (err < 0) {
647 printk(KERN_ERR "Failed to lock '%s', err = %d\n",
648 file, -err);
649 goto out_close;
650 }
651 }
652
653 /* Successful return case! */
654 if (backing_file_out == NULL)
655 return fd;
656
657 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
658 &size, &sectorsize, &align, bitmap_offset_out);
659 if (err && (*backing_file_out != NULL)) {
660 printk(KERN_ERR "Failed to read COW header from COW file "
661 "\"%s\", errno = %d\n", file, -err);
662 goto out_close;
663 }
664 if (err)
665 return fd;
666
667 asked_switch = path_requires_switch(*backing_file_out, backing_file,
668 file);
669
670 /* Allow switching only if no mismatch. */
671 if (asked_switch && !backing_file_mismatch(*backing_file_out, size,
672 mtime)) {
673 printk(KERN_ERR "Switching backing file to '%s'\n",
674 *backing_file_out);
675 err = write_cow_header(file, fd, *backing_file_out,
676 sectorsize, align, &size);
677 if (err) {
678 printk(KERN_ERR "Switch failed, errno = %d\n", -err);
679 goto out_close;
680 }
681 } else {
682 *backing_file_out = backing_file;
683 err = backing_file_mismatch(*backing_file_out, size, mtime);
684 if (err)
685 goto out_close;
686 }
687
688 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
689 bitmap_len_out, data_offset_out);
690
691 return fd;
692 out_close:
693 os_close_file(fd);
694 return err;
695}
696
697static int create_cow_file(char *cow_file, char *backing_file,
698 struct openflags flags,
699 int sectorsize, int alignment, int *bitmap_offset_out,
700 unsigned long *bitmap_len_out, int *data_offset_out)
701{
702 int err, fd;
703
704 flags.c = 1;
705 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
706 if (fd < 0) {
707 err = fd;
708 printk(KERN_ERR "Open of COW file '%s' failed, errno = %d\n",
709 cow_file, -err);
710 goto out;
711 }
712
713 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
714 bitmap_offset_out, bitmap_len_out,
715 data_offset_out);
716 if (!err)
717 return fd;
718 os_close_file(fd);
719 out:
720 return err;
721}
722
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800723static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800725 os_close_file(ubd_dev->fd);
726 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 return;
728
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800729 os_close_file(ubd_dev->cow.fd);
730 vfree(ubd_dev->cow.bitmap);
731 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732}
733
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800734static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735{
736 struct openflags flags;
737 char **back_ptr;
738 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800739 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800741 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800743 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
744 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800745
746 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800747 back_ptr, &ubd_dev->cow.bitmap_offset,
748 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800749 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800751 if((fd == -ENOENT) && create_cow){
752 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800753 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
754 &ubd_dev->cow.bitmap_offset,
755 &ubd_dev->cow.bitmap_len,
756 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800757 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800759 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 }
761 }
762
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800763 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800764 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800765 -fd);
766 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800768 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800770 if(ubd_dev->cow.file != NULL){
Jeff Dikef4768ff2007-08-22 14:01:53 -0700771 blk_queue_max_sectors(ubd_dev->queue, 8 * sizeof(long));
772
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 err = -ENOMEM;
Jesper Juhlda2486b2007-10-16 01:27:19 -0700774 ubd_dev->cow.bitmap = vmalloc(ubd_dev->cow.bitmap_len);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800775 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
777 goto error;
778 }
779 flush_tlb_kernel_vm();
780
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800781 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
782 ubd_dev->cow.bitmap_offset,
783 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 if(err < 0)
785 goto error;
786
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800787 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800789 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800790 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800792 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700794 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800796 os_close_file(ubd_dev->fd);
Jeff Dikedc764e52007-05-06 14:51:41 -0700797 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798}
799
Jeff Dike2e3f5252007-05-06 14:51:29 -0700800static void ubd_device_release(struct device *dev)
801{
802 struct ubd *ubd_dev = dev->driver_data;
803
804 blk_cleanup_queue(ubd_dev->queue);
805 *ubd_dev = ((struct ubd) DEFAULT_UBD);
806}
807
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800808static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800809 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810{
811 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
813 disk = alloc_disk(1 << UBD_SHIFT);
814 if(disk == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -0700815 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
817 disk->major = major;
818 disk->first_minor = unit << UBD_SHIFT;
819 disk->fops = &ubd_blops;
820 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700821 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700823 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
826 /* sysfs register (not for ide fake devices) */
827 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800828 ubd_devs[unit].pdev.id = unit;
829 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700830 ubd_devs[unit].pdev.dev.release = ubd_device_release;
831 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800832 platform_device_register(&ubd_devs[unit].pdev);
833 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 }
835
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800836 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800837 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 add_disk(disk);
839
840 *disk_out = disk;
841 return 0;
842}
843
844#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
845
Jeff Dikef28169d2007-02-10 01:43:53 -0800846static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800848 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800849 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800851 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700852 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800854 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800855 if(err < 0){
856 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700857 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800858 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800860 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
Jeff Dikea0044bd2007-05-06 14:51:36 -0700862 INIT_LIST_HEAD(&ubd_dev->restart);
WANG Cong4f40c052007-11-05 14:50:59 -0800863 sg_init_table(ubd_dev->sg, MAX_SG);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700864
Jeff Dike62f96cb2007-02-10 01:44:16 -0800865 err = -ENOMEM;
866 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
867 if (ubd_dev->queue == NULL) {
868 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700869 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800870 }
871 ubd_dev->queue->queuedata = ubd_dev;
872
Jeff Dikea0044bd2007-05-06 14:51:36 -0700873 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800874 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
875 if(err){
876 *error_out = "Failed to register device";
877 goto out_cleanup;
878 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800879
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800881 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800882 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
Jeff Dike83380cc2008-02-04 22:31:18 -0800884 /*
885 * Perhaps this should also be under the "if (fake_major)" above
886 * using the fake_disk->disk_name
887 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 if (fake_ide)
889 make_ide_entries(ubd_gendisk[n]->disk_name);
890
Jeff Dikeec7cf782005-09-03 15:57:29 -0700891 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700892out:
893 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800894
895out_cleanup:
896 blk_cleanup_queue(ubd_dev->queue);
897 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898}
899
Jeff Dikef28169d2007-02-10 01:43:53 -0800900static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800902 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
Jeff Dikef28169d2007-02-10 01:43:53 -0800904 /* This string is possibly broken up and stored, so it's only
905 * freed if ubd_setup_common fails, or if only general options
906 * were set.
907 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800908 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800909 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800910 *error_out = "Failed to allocate memory";
911 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800913
914 ret = ubd_setup_common(str, &n, error_out);
915 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800916 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800917
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800918 if (n == -1) {
919 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800920 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800921 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
Jeff Dikedc764e52007-05-06 14:51:41 -0700923 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800924 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800925 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800926 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700927 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800929out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700930 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800931
932err_free:
933 kfree(str);
934 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935}
936
937static int ubd_get_config(char *name, char *str, int size, char **error_out)
938{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800939 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 int n, len = 0;
941
942 n = parse_unit(&name);
943 if((n >= MAX_DEV) || (n < 0)){
944 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700945 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 }
947
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800948 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800949 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800951 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 CONFIG_CHUNK(str, size, len, "", 1);
953 goto out;
954 }
955
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800956 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800958 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800960 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 }
962 else CONFIG_CHUNK(str, size, len, "", 1);
963
964 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800965 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700966 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967}
968
Jeff Dike29d56cf2005-06-25 14:55:25 -0700969static int ubd_id(char **str, int *start_out, int *end_out)
970{
Jeff Dikedc764e52007-05-06 14:51:41 -0700971 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700972
973 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700974 *start_out = 0;
975 *end_out = MAX_DEV - 1;
976 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700977}
978
Jeff Dikef28169d2007-02-10 01:43:53 -0800979static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700981 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800982 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700983 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800985 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800987 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700988
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800989 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700990 goto out;
991
992 /* you cannot remove a open disk */
993 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800994 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700995 goto out;
996
Jeff Dikedc764e52007-05-06 14:51:41 -0700997 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700998 if(disk != NULL){
999 del_gendisk(disk);
1000 put_disk(disk);
1001 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
1003 if(fake_gendisk[n] != NULL){
1004 del_gendisk(fake_gendisk[n]);
1005 put_disk(fake_gendisk[n]);
1006 fake_gendisk[n] = NULL;
1007 }
1008
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -07001010 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -07001011out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -08001012 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -07001013 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014}
1015
Jeff Dikef28169d2007-02-10 01:43:53 -08001016/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -08001017 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -08001018 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -08001020 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 .name = "ubd",
1022 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -07001023 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -07001024 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 .remove = ubd_remove,
1026};
1027
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001028static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029{
1030 mconsole_register_dev(&ubd_mc);
1031 return 0;
1032}
1033
1034__initcall(ubd_mc_init);
1035
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001036static int __init ubd0_init(void)
1037{
1038 struct ubd *ubd_dev = &ubd_devs[0];
1039
Jeff Dikeb8831a12007-02-10 01:44:17 -08001040 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001041 if(ubd_dev->file == NULL)
1042 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -08001043 mutex_unlock(&ubd_lock);
1044
Jeff Dikedc764e52007-05-06 14:51:41 -07001045 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001046}
1047
1048__initcall(ubd0_init);
1049
Jeff Dikeb8831a12007-02-10 01:44:17 -08001050/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +00001051static struct platform_driver ubd_driver = {
1052 .driver = {
1053 .name = DRIVER_NAME,
1054 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055};
1056
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001057static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058{
Jeff Dikef28169d2007-02-10 01:43:53 -08001059 char *error;
1060 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 if (register_blkdev(MAJOR_NR, "ubd"))
1063 return -1;
1064
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 if (fake_major != MAJOR_NR) {
1066 char name[sizeof("ubd_nnn\0")];
1067
1068 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 if (register_blkdev(fake_major, "ubd"))
1070 return -1;
1071 }
Russell King3ae5eae2005-11-09 22:32:44 +00001072 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -07001073 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -08001074 for (i = 0; i < MAX_DEV; i++){
1075 err = ubd_add(i, &error);
1076 if(err)
1077 printk(KERN_ERR "Failed to initialize ubd device %d :"
1078 "%s\n", i, error);
1079 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001080 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 return 0;
1082}
1083
1084late_initcall(ubd_init);
1085
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001086static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -04001087 unsigned long stack;
1088 int err;
1089
1090 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
1091 if(global_openflags.s){
1092 printk(KERN_INFO "ubd: Synchronous mode\n");
1093 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
1094 * enough. So use anyway the io thread. */
1095 }
1096 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -08001097 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -04001098 &thread_fd);
1099 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -08001100 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -04001101 "ubd : Failed to start I/O thread (errno = %d) - "
1102 "falling back to synchronous I/O\n", -io_pid);
1103 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001104 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001105 }
Jeff Dike6c29256c2006-03-27 01:14:37 -08001106 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001107 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -04001108 if(err != 0)
1109 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -08001110 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001111}
1112
1113device_initcall(ubd_driver_init);
1114
Al Viroa625c992008-03-02 09:16:26 -05001115static int ubd_open(struct block_device *bdev, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116{
Al Viroa625c992008-03-02 09:16:26 -05001117 struct gendisk *disk = bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001118 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 int err = 0;
1120
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001121 if(ubd_dev->count == 0){
1122 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 if(err){
1124 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001125 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 goto out;
1127 }
1128 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001129 ubd_dev->count++;
1130 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -07001131
1132 /* This should no more be needed. And it didn't work anyway to exclude
1133 * read-write remounting of filesystems.*/
Al Viroa625c992008-03-02 09:16:26 -05001134 /*if((mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -08001135 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -07001137 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001139 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140}
1141
Al Viroa625c992008-03-02 09:16:26 -05001142static int ubd_release(struct gendisk *disk, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001144 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001146 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -08001147 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -07001148 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149}
1150
Jeff Dike91acb212005-10-10 23:10:32 -04001151static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
1152 __u64 *cow_offset, unsigned long *bitmap,
1153 __u64 bitmap_offset, unsigned long *bitmap_words,
1154 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155{
Jeff Dike91acb212005-10-10 23:10:32 -04001156 __u64 sector = io_offset >> 9;
1157 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158
Jeff Dike91acb212005-10-10 23:10:32 -04001159 for(i = 0; i < length >> 9; i++){
1160 if(cow_mask != NULL)
1161 ubd_set_bit(i, (unsigned char *) cow_mask);
1162 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1163 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164
Jeff Dike91acb212005-10-10 23:10:32 -04001165 update_bitmap = 1;
1166 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1167 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168
Jeff Dike91acb212005-10-10 23:10:32 -04001169 if(!update_bitmap)
1170 return;
1171
1172 *cow_offset = sector / (sizeof(unsigned long) * 8);
1173
1174 /* This takes care of the case where we're exactly at the end of the
1175 * device, and *cow_offset + 1 is off the end. So, just back it up
1176 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1177 * for the original diagnosis.
1178 */
Jiri Olsa6d074242008-05-12 14:01:56 -07001179 if (*cow_offset == (DIV_ROUND_UP(bitmap_len,
1180 sizeof(unsigned long)) - 1))
Jeff Dike91acb212005-10-10 23:10:32 -04001181 (*cow_offset)--;
1182
1183 bitmap_words[0] = bitmap[*cow_offset];
1184 bitmap_words[1] = bitmap[*cow_offset + 1];
1185
1186 *cow_offset *= sizeof(unsigned long);
1187 *cow_offset += bitmap_offset;
1188}
1189
1190static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1191 __u64 bitmap_offset, __u64 bitmap_len)
1192{
1193 __u64 sector = req->offset >> 9;
1194 int i;
1195
1196 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1197 panic("Operation too long");
1198
1199 if(req->op == UBD_READ) {
1200 for(i = 0; i < req->length >> 9; i++){
1201 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001202 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001203 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001204 }
Jeff Dike91acb212005-10-10 23:10:32 -04001205 }
1206 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1207 &req->cow_offset, bitmap, bitmap_offset,
1208 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209}
1210
Jeff Dike62f96cb2007-02-10 01:44:16 -08001211/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001212static void prepare_request(struct request *req, struct io_thread_req *io_req,
1213 unsigned long long offset, int page_offset,
1214 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215{
1216 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001217 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001218
Jeff Dike62f96cb2007-02-10 01:44:16 -08001219 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001220 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1221 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001222 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001223 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 io_req->offset = offset;
1225 io_req->length = len;
1226 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001227 io_req->sector_mask = 0;
1228
1229 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001231 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001232 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 io_req->sectorsize = 1 << 9;
1234
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001235 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001236 cowify_req(io_req, ubd_dev->cow.bitmap,
1237 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001238
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239}
1240
Jeff Dike62f96cb2007-02-10 01:44:16 -08001241/* Called with dev->lock held */
Jens Axboe165125e2007-07-24 09:28:11 +02001242static void do_ubd_request(struct request_queue *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243{
Jeff Dike2adcec22007-05-06 14:51:37 -07001244 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 struct request *req;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001246 int n, last_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247
Jeff Dikea0044bd2007-05-06 14:51:36 -07001248 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001249 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001250 if(dev->end_sg == 0){
1251 struct request *req = elv_next_request(q);
1252 if(req == NULL)
1253 return;
1254
1255 dev->request = req;
1256 blkdev_dequeue_request(req);
1257 dev->start_sg = 0;
1258 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001259 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001260
1261 req = dev->request;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001262 last_sectors = 0;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001263 while(dev->start_sg < dev->end_sg){
1264 struct scatterlist *sg = &dev->sg[dev->start_sg];
1265
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001266 req->sector += last_sectors;
Jeff Dike2adcec22007-05-06 14:51:37 -07001267 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001268 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001269 if(io_req == NULL){
1270 if(list_empty(&dev->restart))
1271 list_add(&dev->restart, &restart);
1272 return;
1273 }
1274 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001275 (unsigned long long) req->sector << 9,
Jens Axboe45711f12007-10-22 21:19:53 +02001276 sg->offset, sg->length, sg_page(sg));
Jeff Dikea0044bd2007-05-06 14:51:36 -07001277
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001278 last_sectors = sg->length >> 9;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001279 n = os_write_file(thread_fd, &io_req,
1280 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001281 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001282 if(n != -EAGAIN)
1283 printk("write to io thread failed, "
1284 "errno = %d\n", -n);
1285 else if(list_empty(&dev->restart))
1286 list_add(&dev->restart, &restart);
Miklos Szeredi12429bf2007-11-28 16:21:52 -08001287 kfree(io_req);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001288 return;
1289 }
1290
Jeff Dikea0044bd2007-05-06 14:51:36 -07001291 dev->start_sg++;
1292 }
1293 dev->end_sg = 0;
1294 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001295 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296}
1297
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001298static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1299{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001300 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001301
1302 geo->heads = 128;
1303 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001304 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001305 return 0;
1306}
1307
Al Viroa625c992008-03-02 09:16:26 -05001308static int ubd_ioctl(struct block_device *bdev, fmode_t mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 unsigned int cmd, unsigned long arg)
1310{
Al Viroa625c992008-03-02 09:16:26 -05001311 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 struct hd_driveid ubd_id = {
1313 .cyls = 0,
1314 .heads = 128,
1315 .sectors = 32,
1316 };
1317
1318 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001321 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1323 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001324 return -EFAULT;
1325 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001326
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 case CDROMVOLREAD:
1328 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001329 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 volume.channel0 = 255;
1331 volume.channel1 = 255;
1332 volume.channel2 = 255;
1333 volume.channel3 = 255;
1334 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001335 return -EFAULT;
1336 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001338 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339}
1340
Jeff Dike91acb212005-10-10 23:10:32 -04001341static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342{
Jeff Dike91acb212005-10-10 23:10:32 -04001343 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
Jeff Dike91acb212005-10-10 23:10:32 -04001345 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001346 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347
Jeff Dike91acb212005-10-10 23:10:32 -04001348 n = os_seek_file(req->fds[1], req->cow_offset);
1349 if(n < 0){
1350 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001351 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001352 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001354 n = os_write_file(req->fds[1], &req->bitmap_words,
1355 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001356 if(n != sizeof(req->bitmap_words)){
1357 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1358 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001359 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001360 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
Jeff Dikedc764e52007-05-06 14:51:41 -07001362 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363}
Jeff Dike91acb212005-10-10 23:10:32 -04001364
WANG Cong5dc62b12008-04-28 02:13:58 -07001365static void do_io(struct io_thread_req *req)
Jeff Dike91acb212005-10-10 23:10:32 -04001366{
1367 char *buf;
1368 unsigned long len;
1369 int n, nsectors, start, end, bit;
1370 int err;
1371 __u64 off;
1372
1373 nsectors = req->length / req->sectorsize;
1374 start = 0;
1375 do {
1376 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1377 end = start;
1378 while((end < nsectors) &&
1379 (ubd_test_bit(end, (unsigned char *)
1380 &req->sector_mask) == bit))
1381 end++;
1382
1383 off = req->offset + req->offsets[bit] +
1384 start * req->sectorsize;
1385 len = (end - start) * req->sectorsize;
1386 buf = &req->buffer[start * req->sectorsize];
1387
1388 err = os_seek_file(req->fds[bit], off);
1389 if(err < 0){
1390 printk("do_io - lseek failed : err = %d\n", -err);
1391 req->error = 1;
1392 return;
1393 }
1394 if(req->op == UBD_READ){
1395 n = 0;
1396 do {
1397 buf = &buf[n];
1398 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001399 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001400 if (n < 0) {
1401 printk("do_io - read failed, err = %d "
1402 "fd = %d\n", -n, req->fds[bit]);
1403 req->error = 1;
1404 return;
1405 }
1406 } while((n < len) && (n != 0));
1407 if (n < len) memset(&buf[n], 0, len - n);
1408 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001409 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001410 if(n != len){
1411 printk("do_io - write failed err = %d "
1412 "fd = %d\n", -n, req->fds[bit]);
1413 req->error = 1;
1414 return;
1415 }
1416 }
1417
1418 start = end;
1419 } while(start < nsectors);
1420
1421 req->error = update_bitmap(req);
1422}
1423
1424/* Changed in start_io_thread, which is serialized by being called only
1425 * from ubd_init, which is an initcall.
1426 */
1427int kernel_fd = -1;
1428
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001429/* Only changed by the io thread. XXX: currently unused. */
1430static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001431
1432int io_thread(void *arg)
1433{
Jeff Dike2adcec22007-05-06 14:51:37 -07001434 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001435 int n;
1436
1437 ignore_sigwinch_sig();
1438 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001439 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001440 sizeof(struct io_thread_req *));
1441 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001442 if(n < 0)
1443 printk("io_thread - read failed, fd = %d, "
1444 "err = %d\n", kernel_fd, -n);
1445 else {
1446 printk("io_thread - short read, fd = %d, "
1447 "length = %d\n", kernel_fd, n);
1448 }
1449 continue;
1450 }
1451 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001452 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001453 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001454 sizeof(struct io_thread_req *));
1455 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001456 printk("io_thread - write failed, fd = %d, err = %d\n",
1457 kernel_fd, -n);
1458 }
Jeff Dike91acb212005-10-10 23:10:32 -04001459
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001460 return 0;
1461}