blob: b58fb8941d8d7eb4cb27447084332f686dca031a [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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101static int ubd_open(struct inode * inode, struct file * filp);
102static int ubd_release(struct inode * inode, struct file * file);
103static int ubd_ioctl(struct inode * inode, struct file * file,
104 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,
111 .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-Hartmance7b0f462005-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-Hartmance7b0f462005-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
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115static int ubd_open(struct inode *inode, struct file *filp)
1116{
1117 struct gendisk *disk = inode->i_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.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001134 /*if((filp->f_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
1142static int ubd_release(struct inode * inode, struct file * file)
1143{
1144 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001145 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001147 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -08001148 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -07001149 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150}
1151
Jeff Dike91acb212005-10-10 23:10:32 -04001152static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
1153 __u64 *cow_offset, unsigned long *bitmap,
1154 __u64 bitmap_offset, unsigned long *bitmap_words,
1155 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156{
Jeff Dike91acb212005-10-10 23:10:32 -04001157 __u64 sector = io_offset >> 9;
1158 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159
Jeff Dike91acb212005-10-10 23:10:32 -04001160 for(i = 0; i < length >> 9; i++){
1161 if(cow_mask != NULL)
1162 ubd_set_bit(i, (unsigned char *) cow_mask);
1163 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1164 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165
Jeff Dike91acb212005-10-10 23:10:32 -04001166 update_bitmap = 1;
1167 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1168 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169
Jeff Dike91acb212005-10-10 23:10:32 -04001170 if(!update_bitmap)
1171 return;
1172
1173 *cow_offset = sector / (sizeof(unsigned long) * 8);
1174
1175 /* This takes care of the case where we're exactly at the end of the
1176 * device, and *cow_offset + 1 is off the end. So, just back it up
1177 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1178 * for the original diagnosis.
1179 */
Jiri Olsa6d074242008-05-12 14:01:56 -07001180 if (*cow_offset == (DIV_ROUND_UP(bitmap_len,
1181 sizeof(unsigned long)) - 1))
Jeff Dike91acb212005-10-10 23:10:32 -04001182 (*cow_offset)--;
1183
1184 bitmap_words[0] = bitmap[*cow_offset];
1185 bitmap_words[1] = bitmap[*cow_offset + 1];
1186
1187 *cow_offset *= sizeof(unsigned long);
1188 *cow_offset += bitmap_offset;
1189}
1190
1191static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1192 __u64 bitmap_offset, __u64 bitmap_len)
1193{
1194 __u64 sector = req->offset >> 9;
1195 int i;
1196
1197 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1198 panic("Operation too long");
1199
1200 if(req->op == UBD_READ) {
1201 for(i = 0; i < req->length >> 9; i++){
1202 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001203 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001204 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001205 }
Jeff Dike91acb212005-10-10 23:10:32 -04001206 }
1207 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1208 &req->cow_offset, bitmap, bitmap_offset,
1209 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210}
1211
Jeff Dike62f96cb2007-02-10 01:44:16 -08001212/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001213static void prepare_request(struct request *req, struct io_thread_req *io_req,
1214 unsigned long long offset, int page_offset,
1215 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216{
1217 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001218 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001219
Jeff Dike62f96cb2007-02-10 01:44:16 -08001220 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001221 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1222 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001223 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001224 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 io_req->offset = offset;
1226 io_req->length = len;
1227 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001228 io_req->sector_mask = 0;
1229
1230 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001232 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001233 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 io_req->sectorsize = 1 << 9;
1235
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001236 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001237 cowify_req(io_req, ubd_dev->cow.bitmap,
1238 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001239
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240}
1241
Jeff Dike62f96cb2007-02-10 01:44:16 -08001242/* Called with dev->lock held */
Jens Axboe165125e2007-07-24 09:28:11 +02001243static void do_ubd_request(struct request_queue *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244{
Jeff Dike2adcec22007-05-06 14:51:37 -07001245 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 struct request *req;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001247 int n, last_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248
Jeff Dikea0044bd2007-05-06 14:51:36 -07001249 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001250 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001251 if(dev->end_sg == 0){
1252 struct request *req = elv_next_request(q);
1253 if(req == NULL)
1254 return;
1255
1256 dev->request = req;
1257 blkdev_dequeue_request(req);
1258 dev->start_sg = 0;
1259 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001260 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001261
1262 req = dev->request;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001263 last_sectors = 0;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001264 while(dev->start_sg < dev->end_sg){
1265 struct scatterlist *sg = &dev->sg[dev->start_sg];
1266
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001267 req->sector += last_sectors;
Jeff Dike2adcec22007-05-06 14:51:37 -07001268 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001269 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001270 if(io_req == NULL){
1271 if(list_empty(&dev->restart))
1272 list_add(&dev->restart, &restart);
1273 return;
1274 }
1275 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001276 (unsigned long long) req->sector << 9,
Jens Axboe45711f12007-10-22 21:19:53 +02001277 sg->offset, sg->length, sg_page(sg));
Jeff Dikea0044bd2007-05-06 14:51:36 -07001278
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001279 last_sectors = sg->length >> 9;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001280 n = os_write_file(thread_fd, &io_req,
1281 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001282 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001283 if(n != -EAGAIN)
1284 printk("write to io thread failed, "
1285 "errno = %d\n", -n);
1286 else if(list_empty(&dev->restart))
1287 list_add(&dev->restart, &restart);
Miklos Szeredi12429bf2007-11-28 16:21:52 -08001288 kfree(io_req);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001289 return;
1290 }
1291
Jeff Dikea0044bd2007-05-06 14:51:36 -07001292 dev->start_sg++;
1293 }
1294 dev->end_sg = 0;
1295 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001296 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297}
1298
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001299static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1300{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001301 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001302
1303 geo->heads = 128;
1304 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001305 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001306 return 0;
1307}
1308
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309static int ubd_ioctl(struct inode * inode, struct file * file,
1310 unsigned int cmd, unsigned long arg)
1311{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001312 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 struct hd_driveid ubd_id = {
1314 .cyls = 0,
1315 .heads = 128,
1316 .sectors = 32,
1317 };
1318
1319 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001322 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1324 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001325 return -EFAULT;
1326 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001327
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 case CDROMVOLREAD:
1329 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001330 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 volume.channel0 = 255;
1332 volume.channel1 = 255;
1333 volume.channel2 = 255;
1334 volume.channel3 = 255;
1335 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001336 return -EFAULT;
1337 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001339 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340}
1341
Jeff Dike91acb212005-10-10 23:10:32 -04001342static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343{
Jeff Dike91acb212005-10-10 23:10:32 -04001344 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345
Jeff Dike91acb212005-10-10 23:10:32 -04001346 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001347 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348
Jeff Dike91acb212005-10-10 23:10:32 -04001349 n = os_seek_file(req->fds[1], req->cow_offset);
1350 if(n < 0){
1351 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001352 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001353 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001355 n = os_write_file(req->fds[1], &req->bitmap_words,
1356 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001357 if(n != sizeof(req->bitmap_words)){
1358 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1359 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001360 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001361 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
Jeff Dikedc764e52007-05-06 14:51:41 -07001363 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364}
Jeff Dike91acb212005-10-10 23:10:32 -04001365
WANG Cong5dc62b12008-04-28 02:13:58 -07001366static void do_io(struct io_thread_req *req)
Jeff Dike91acb212005-10-10 23:10:32 -04001367{
1368 char *buf;
1369 unsigned long len;
1370 int n, nsectors, start, end, bit;
1371 int err;
1372 __u64 off;
1373
1374 nsectors = req->length / req->sectorsize;
1375 start = 0;
1376 do {
1377 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1378 end = start;
1379 while((end < nsectors) &&
1380 (ubd_test_bit(end, (unsigned char *)
1381 &req->sector_mask) == bit))
1382 end++;
1383
1384 off = req->offset + req->offsets[bit] +
1385 start * req->sectorsize;
1386 len = (end - start) * req->sectorsize;
1387 buf = &req->buffer[start * req->sectorsize];
1388
1389 err = os_seek_file(req->fds[bit], off);
1390 if(err < 0){
1391 printk("do_io - lseek failed : err = %d\n", -err);
1392 req->error = 1;
1393 return;
1394 }
1395 if(req->op == UBD_READ){
1396 n = 0;
1397 do {
1398 buf = &buf[n];
1399 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001400 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001401 if (n < 0) {
1402 printk("do_io - read failed, err = %d "
1403 "fd = %d\n", -n, req->fds[bit]);
1404 req->error = 1;
1405 return;
1406 }
1407 } while((n < len) && (n != 0));
1408 if (n < len) memset(&buf[n], 0, len - n);
1409 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001410 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001411 if(n != len){
1412 printk("do_io - write failed err = %d "
1413 "fd = %d\n", -n, req->fds[bit]);
1414 req->error = 1;
1415 return;
1416 }
1417 }
1418
1419 start = end;
1420 } while(start < nsectors);
1421
1422 req->error = update_bitmap(req);
1423}
1424
1425/* Changed in start_io_thread, which is serialized by being called only
1426 * from ubd_init, which is an initcall.
1427 */
1428int kernel_fd = -1;
1429
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001430/* Only changed by the io thread. XXX: currently unused. */
1431static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001432
1433int io_thread(void *arg)
1434{
Jeff Dike2adcec22007-05-06 14:51:37 -07001435 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001436 int n;
1437
1438 ignore_sigwinch_sig();
1439 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001440 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001441 sizeof(struct io_thread_req *));
1442 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001443 if(n < 0)
1444 printk("io_thread - read failed, fd = %d, "
1445 "err = %d\n", kernel_fd, -n);
1446 else {
1447 printk("io_thread - short read, fd = %d, "
1448 "length = %d\n", kernel_fd, n);
1449 }
1450 continue;
1451 }
1452 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001453 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001454 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001455 sizeof(struct io_thread_req *));
1456 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001457 printk("io_thread - write failed, fd = %d, err = %d\n",
1458 kernel_fd, -n);
1459 }
Jeff Dike91acb212005-10-10 23:10:32 -04001460
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001461 return 0;
1462}