blob: be3a2797dac4ee054c3c4f5e44734d2d76ce85f7 [file] [log] [blame]
Jeff Dike6c29256c2006-03-27 01:14:37 -08001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL
4 */
5
6/* 2001-09-28...2002-04-17
7 * Partition stuff by James_McMechan@hotmail.com
8 * old style ubd by setting UBD_SHIFT to 0
9 * 2002-09-27...2002-10-18 massive tinkering for 2.5
10 * partitions have changed in 2.5
11 * 2003-01-29 more tinkering for 2.5.59-1
12 * This should now address the sysfs problems and has
13 * the symlink for devfs to allow for booting with
14 * the common /dev/ubd/discX/... names rather than
15 * only /dev/ubdN/discN this version also has lots of
16 * clean ups preparing for ubd-many.
17 * James McMechan
18 */
19
20#define MAJOR_NR UBD_MAJOR
21#define UBD_SHIFT 4
22
Jeff Dikee16f5352007-06-08 13:46:54 -070023#include "linux/kernel.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include "linux/module.h"
25#include "linux/blkdev.h"
26#include "linux/hdreg.h"
27#include "linux/init.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include "linux/cdrom.h"
29#include "linux/proc_fs.h"
30#include "linux/ctype.h"
31#include "linux/capability.h"
32#include "linux/mm.h"
33#include "linux/vmalloc.h"
34#include "linux/blkpg.h"
35#include "linux/genhd.h"
36#include "linux/spinlock.h"
Russell Kingd052d1b2005-10-29 19:07:23 +010037#include "linux/platform_device.h"
WANG Cong23464ff2007-10-24 13:07:11 +020038#include "linux/scatterlist.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include "asm/segment.h"
40#include "asm/uaccess.h"
41#include "asm/irq.h"
42#include "asm/types.h"
43#include "asm/tlbflush.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include "mem_user.h"
45#include "kern_util.h"
46#include "kern.h"
47#include "mconsole_kern.h"
48#include "init.h"
49#include "irq_user.h"
50#include "irq_kern.h"
51#include "ubd_user.h"
Jeff Dikeedea1382008-02-04 22:30:46 -080052#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#include "os.h"
54#include "mem.h"
55#include "mem_kern.h"
56#include "cow.h"
57
Jeff Dike7b9014c2005-05-20 13:59:11 -070058enum ubd_req { UBD_READ, UBD_WRITE };
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
60struct io_thread_req {
Jeff Dike62f96cb2007-02-10 01:44:16 -080061 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -040062 enum ubd_req op;
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 int fds[2];
64 unsigned long offsets[2];
65 unsigned long long offset;
66 unsigned long length;
67 char *buffer;
68 int sectorsize;
Jeff Dike91acb212005-10-10 23:10:32 -040069 unsigned long sector_mask;
70 unsigned long long cow_offset;
71 unsigned long bitmap_words[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 int error;
73};
74
Jeff Dike6c29256c2006-03-27 01:14:37 -080075extern int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 char **backing_file_out, int *bitmap_offset_out,
77 unsigned long *bitmap_len_out, int *data_offset_out,
78 int *create_cow_out);
79extern int create_cow_file(char *cow_file, char *backing_file,
80 struct openflags flags, int sectorsize,
81 int alignment, int *bitmap_offset_out,
82 unsigned long *bitmap_len_out,
83 int *data_offset_out);
84extern int read_cow_bitmap(int fd, void *buf, int offset, int len);
Jeff Dike91acb212005-10-10 23:10:32 -040085extern void do_io(struct io_thread_req *req);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
Jeff Dike91acb212005-10-10 23:10:32 -040087static inline int ubd_test_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088{
89 __u64 n;
90 int bits, off;
91
Jeff Dike91acb212005-10-10 23:10:32 -040092 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 n = bit / bits;
94 off = bit % bits;
Jeff Dikedc764e52007-05-06 14:51:41 -070095 return (data[n] & (1 << off)) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096}
97
Jeff Dike91acb212005-10-10 23:10:32 -040098static inline void ubd_set_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070099{
100 __u64 n;
101 int bits, off;
102
Jeff Dike91acb212005-10-10 23:10:32 -0400103 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 n = bit / bits;
105 off = bit % bits;
Jeff Dike91acb212005-10-10 23:10:32 -0400106 data[n] |= (1 << off);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107}
108/*End stuff from ubd_user.h*/
109
110#define DRIVER_NAME "uml-blkdev"
111
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800112static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114static int ubd_open(struct inode * inode, struct file * filp);
115static int ubd_release(struct inode * inode, struct file * file);
116static int ubd_ioctl(struct inode * inode, struct file * file,
117 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800118static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800120#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122static struct block_device_operations ubd_blops = {
123 .owner = THIS_MODULE,
124 .open = ubd_open,
125 .release = ubd_release,
126 .ioctl = ubd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800127 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128};
129
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130/* Protected by ubd_lock */
131static int fake_major = MAJOR_NR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132static struct gendisk *ubd_gendisk[MAX_DEV];
133static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800134
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135#ifdef CONFIG_BLK_DEV_UBD_SYNC
136#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
137 .cl = 1 })
138#else
139#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
140 .cl = 1 })
141#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142static struct openflags global_openflags = OPEN_FLAGS;
143
144struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800145 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800147 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 int fd;
149 unsigned long *bitmap;
150 unsigned long bitmap_len;
151 int bitmap_offset;
Jeff Dikedc764e52007-05-06 14:51:41 -0700152 int data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153};
154
Jeff Dikea0044bd2007-05-06 14:51:36 -0700155#define MAX_SG 64
156
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157struct ubd {
Jeff Dikea0044bd2007-05-06 14:51:36 -0700158 struct list_head restart;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800159 /* name (and fd, below) of the file opened for writing, either the
160 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 char *file;
162 int count;
163 int fd;
164 __u64 size;
165 struct openflags boot_openflags;
166 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800167 unsigned shared:1;
168 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 struct cow cow;
170 struct platform_device pdev;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800171 struct request_queue *queue;
172 spinlock_t lock;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700173 struct scatterlist sg[MAX_SG];
174 struct request *request;
175 int start_sg, end_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176};
177
178#define DEFAULT_COW { \
179 .file = NULL, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700180 .fd = -1, \
181 .bitmap = NULL, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 .bitmap_offset = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700183 .data_offset = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184}
185
186#define DEFAULT_UBD { \
187 .file = NULL, \
188 .count = 0, \
189 .fd = -1, \
190 .size = -1, \
191 .boot_openflags = OPEN_FLAGS, \
192 .openflags = OPEN_FLAGS, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700193 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800194 .shared = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700195 .cow = DEFAULT_COW, \
Jeff Dike62f96cb2007-02-10 01:44:16 -0800196 .lock = SPIN_LOCK_UNLOCKED, \
Jeff Dikea0044bd2007-05-06 14:51:36 -0700197 .request = NULL, \
198 .start_sg = 0, \
199 .end_sg = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200}
201
Jeff Dikeb8831a12007-02-10 01:44:17 -0800202/* Protected by ubd_lock */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800203struct ubd ubd_devs[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205/* Only changed by fake_ide_setup which is a setup */
206static int fake_ide = 0;
207static struct proc_dir_entry *proc_ide_root = NULL;
208static struct proc_dir_entry *proc_ide = NULL;
209
210static void make_proc_ide(void)
211{
212 proc_ide_root = proc_mkdir("ide", NULL);
213 proc_ide = proc_mkdir("ide0", proc_ide_root);
214}
215
216static int proc_ide_read_media(char *page, char **start, off_t off, int count,
217 int *eof, void *data)
218{
219 int len;
220
221 strcpy(page, "disk\n");
222 len = strlen("disk\n");
223 len -= off;
224 if (len < count){
225 *eof = 1;
226 if (len <= 0) return 0;
227 }
228 else len = count;
229 *start = page + off;
230 return len;
231}
232
WANG Congc0a92902008-02-04 22:30:41 -0800233static void make_ide_entries(const char *dev_name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234{
235 struct proc_dir_entry *dir, *ent;
236 char name[64];
237
238 if(proc_ide_root == NULL) make_proc_ide();
239
240 dir = proc_mkdir(dev_name, proc_ide);
241 if(!dir) return;
242
243 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
244 if(!ent) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 ent->data = NULL;
246 ent->read_proc = proc_ide_read_media;
247 ent->write_proc = NULL;
WANG Congc0a92902008-02-04 22:30:41 -0800248 snprintf(name, sizeof(name), "ide0/%s", dev_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 proc_symlink(dev_name, proc_ide_root, name);
250}
251
252static int fake_ide_setup(char *str)
253{
254 fake_ide = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700255 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256}
257
258__setup("fake_ide", fake_ide_setup);
259
260__uml_help(fake_ide_setup,
261"fake_ide\n"
262" Create ide0 entries that map onto ubd devices.\n\n"
263);
264
265static int parse_unit(char **ptr)
266{
267 char *str = *ptr, *end;
268 int n = -1;
269
270 if(isdigit(*str)) {
271 n = simple_strtoul(str, &end, 0);
272 if(end == str)
Jeff Dikedc764e52007-05-06 14:51:41 -0700273 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 *ptr = end;
275 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800276 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 n = *str - 'a';
278 str++;
279 *ptr = str;
280 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700281 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282}
283
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800284/* If *index_out == -1 at exit, the passed option was a general one;
285 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
286 * should not be freed on exit.
287 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800288static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800290 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 struct openflags flags = global_openflags;
292 char *backing_file;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800293 int n, err = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
295 if(index_out) *index_out = -1;
296 n = *str;
297 if(n == '='){
298 char *end;
299 int major;
300
301 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 if(!strcmp(str, "sync")){
303 global_openflags = of_sync(global_openflags);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800304 goto out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 }
306
Jeff Dikef28169d2007-02-10 01:43:53 -0800307 err = -EINVAL;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800308 major = simple_strtoul(str, &end, 0);
309 if((*end != '\0') || (end == str)){
310 *error_out = "Didn't parse major number";
311 goto out1;
312 }
313
Jeff Dikef28169d2007-02-10 01:43:53 -0800314 mutex_lock(&ubd_lock);
315 if(fake_major != MAJOR_NR){
316 *error_out = "Can't assign a fake major twice";
317 goto out1;
318 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800319
Jeff Dikef28169d2007-02-10 01:43:53 -0800320 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
322 printk(KERN_INFO "Setting extra ubd major number to %d\n",
323 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800324 err = 0;
325 out1:
326 mutex_unlock(&ubd_lock);
327 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 }
329
330 n = parse_unit(&str);
331 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800332 *error_out = "Couldn't parse device number";
333 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 }
335 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800336 *error_out = "Device number out of range";
337 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 }
339
Jeff Dikef28169d2007-02-10 01:43:53 -0800340 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800341 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800343 ubd_dev = &ubd_devs[n];
344 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800345 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 goto out;
347 }
348
349 if (index_out)
350 *index_out = n;
351
Jeff Dikef28169d2007-02-10 01:43:53 -0800352 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800353 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 switch (*str) {
355 case 'r':
356 flags.w = 0;
357 break;
358 case 's':
359 flags.s = 1;
360 break;
361 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800362 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800364 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800365 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800366 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 case '=':
368 str++;
369 goto break_loop;
370 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800371 *error_out = "Expected '=' or flag letter "
372 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 goto out;
374 }
375 str++;
376 }
377
Jeff Dikef28169d2007-02-10 01:43:53 -0800378 if (*str == '=')
379 *error_out = "Too many flags specified";
380 else
381 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 goto out;
383
384break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 backing_file = strchr(str, ',');
386
Jeff Dikef28169d2007-02-10 01:43:53 -0800387 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389
Jeff Dikef28169d2007-02-10 01:43:53 -0800390 if(backing_file != NULL){
391 if(ubd_dev->no_cow){
392 *error_out = "Can't specify both 'd' and a cow file";
393 goto out;
394 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 else {
396 *backing_file = '\0';
397 backing_file++;
398 }
399 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800400 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800401 ubd_dev->file = str;
402 ubd_dev->cow.file = backing_file;
403 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800405 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800406 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407}
408
409static int ubd_setup(char *str)
410{
Jeff Dikef28169d2007-02-10 01:43:53 -0800411 char *error;
412 int err;
413
414 err = ubd_setup_common(str, NULL, &error);
415 if(err)
416 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
417 "%s\n", str, error);
418 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419}
420
421__setup("ubd", ubd_setup);
422__uml_help(ubd_setup,
423"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
424" This is used to associate a device with a file in the underlying\n"
425" filesystem. When specifying two filenames, the first one is the\n"
426" COW name and the second is the backing file name. As separator you can\n"
427" use either a ':' or a ',': the first one allows writing things like;\n"
428" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
429" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800430" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431" a COW file or a backing file. To override this detection, add the 'd'\n"
432" flag:\n"
433" ubd0d=BackingFile\n"
434" Usually, there is a filesystem in the file, but \n"
435" that's not required. Swap devices containing swap files can be\n"
436" specified like this. Also, a file which doesn't contain a\n"
437" filesystem can have its contents read in the virtual \n"
438" machine by running 'dd' on the device. <n> must be in the range\n"
439" 0 to 7. Appending an 'r' to the number will cause that device\n"
440" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
Jeff Dike20ede452008-02-04 22:30:37 -0800441" an 's' will cause data to be written to disk on the host immediately.\n"
442" 'c' will cause the device to be treated as being shared between multiple\n"
443" UMLs and file locking will be turned off - this is appropriate for a\n"
444" cluster filesystem and inappropriate at almost all other times.\n\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445);
446
Jeff Dike8299ca52008-02-04 22:30:48 -0800447static int udb_setup(char *str)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448{
449 printk("udb%s specified on command line is almost certainly a ubd -> "
450 "udb TYPO\n", str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700451 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452}
453
454__setup("udb", udb_setup);
455__uml_help(udb_setup,
456"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700457" This option is here solely to catch ubd -> udb typos, which can be\n"
458" to impossible to catch visually unless you specifically look for\n"
459" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460" in the boot output.\n\n"
461);
462
Jens Axboe165125e2007-07-24 09:28:11 +0200463static void do_ubd_request(struct request_queue * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400464
465/* Only changed by ubd_init, which is an initcall. */
466int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500468static void ubd_end_request(struct request *req, int bytes, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469{
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500470 blk_end_request(req, error, bytes);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800473/* Callable only from interrupt context - otherwise you need to do
474 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700475static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700477 if(bytes < 0){
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500478 ubd_end_request(req, 0, -EIO);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700479 return;
480 }
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500481 ubd_end_request(req, bytes, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482}
483
Jeff Dikea0044bd2007-05-06 14:51:36 -0700484static LIST_HEAD(restart);
485
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800486/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800487/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400488static void ubd_handler(void)
489{
Jeff Dike2adcec22007-05-06 14:51:37 -0700490 struct io_thread_req *req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800491 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700492 struct ubd *ubd;
493 struct list_head *list, *next_ele;
494 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400495 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
Jeff Dikea0044bd2007-05-06 14:51:36 -0700497 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700498 n = os_read_file(thread_fd, &req,
499 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700500 if(n != sizeof(req)){
501 if(n == -EAGAIN)
502 break;
503 printk(KERN_ERR "spurious interrupt in ubd_handler, "
504 "err = %d\n", -n);
505 return;
506 }
507
Jeff Dike2adcec22007-05-06 14:51:37 -0700508 rq = req->req;
509 rq->nr_sectors -= req->length >> 9;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700510 if(rq->nr_sectors == 0)
511 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike2adcec22007-05-06 14:51:37 -0700512 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400513 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800514 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700515
516 list_for_each_safe(list, next_ele, &restart){
517 ubd = container_of(list, struct ubd, restart);
518 list_del_init(&ubd->restart);
519 spin_lock_irqsave(&ubd->lock, flags);
520 do_ubd_request(ubd->queue);
521 spin_unlock_irqrestore(&ubd->lock, flags);
522 }
Jeff Dike91acb212005-10-10 23:10:32 -0400523}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524
Al Viro7bea96f2006-10-08 22:49:34 +0100525static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526{
Jeff Dike91acb212005-10-10 23:10:32 -0400527 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700528 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529}
530
Jeff Dike91acb212005-10-10 23:10:32 -0400531/* Only changed by ubd_init, which is an initcall. */
532static int io_pid = -1;
533
534void kill_io_thread(void)
535{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800536 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400537 os_kill_process(io_pid, 1);
538}
539
540__uml_exitcall(kill_io_thread);
541
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800542static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
544 char *file;
545
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800546 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700547 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548}
549
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800550static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800552 os_close_file(ubd_dev->fd);
553 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 return;
555
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800556 os_close_file(ubd_dev->cow.fd);
557 vfree(ubd_dev->cow.bitmap);
558 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559}
560
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800561static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562{
563 struct openflags flags;
564 char **back_ptr;
565 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800566 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800568 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800570 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
571 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800572
573 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800574 back_ptr, &ubd_dev->cow.bitmap_offset,
575 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800576 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800578 if((fd == -ENOENT) && create_cow){
579 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800580 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
581 &ubd_dev->cow.bitmap_offset,
582 &ubd_dev->cow.bitmap_len,
583 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800584 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800586 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 }
588 }
589
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800590 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800591 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800592 -fd);
593 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800595 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800597 if(ubd_dev->cow.file != NULL){
Jeff Dikef4768ff2007-08-22 14:01:53 -0700598 blk_queue_max_sectors(ubd_dev->queue, 8 * sizeof(long));
599
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 err = -ENOMEM;
Jesper Juhlda2486b2007-10-16 01:27:19 -0700601 ubd_dev->cow.bitmap = vmalloc(ubd_dev->cow.bitmap_len);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800602 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
604 goto error;
605 }
606 flush_tlb_kernel_vm();
607
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800608 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
609 ubd_dev->cow.bitmap_offset,
610 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 if(err < 0)
612 goto error;
613
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800614 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800616 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800617 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800619 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700621 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800623 os_close_file(ubd_dev->fd);
Jeff Dikedc764e52007-05-06 14:51:41 -0700624 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625}
626
Jeff Dike2e3f5252007-05-06 14:51:29 -0700627static void ubd_device_release(struct device *dev)
628{
629 struct ubd *ubd_dev = dev->driver_data;
630
631 blk_cleanup_queue(ubd_dev->queue);
632 *ubd_dev = ((struct ubd) DEFAULT_UBD);
633}
634
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800635static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800636 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637{
638 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
640 disk = alloc_disk(1 << UBD_SHIFT);
641 if(disk == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -0700642 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643
644 disk->major = major;
645 disk->first_minor = unit << UBD_SHIFT;
646 disk->fops = &ubd_blops;
647 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700648 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700650 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
653 /* sysfs register (not for ide fake devices) */
654 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800655 ubd_devs[unit].pdev.id = unit;
656 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700657 ubd_devs[unit].pdev.dev.release = ubd_device_release;
658 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800659 platform_device_register(&ubd_devs[unit].pdev);
660 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 }
662
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800663 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800664 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 add_disk(disk);
666
667 *disk_out = disk;
668 return 0;
669}
670
671#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
672
Jeff Dikef28169d2007-02-10 01:43:53 -0800673static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800675 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800676 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800678 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700679 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800681 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800682 if(err < 0){
683 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700684 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800685 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800687 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688
Jeff Dikea0044bd2007-05-06 14:51:36 -0700689 INIT_LIST_HEAD(&ubd_dev->restart);
WANG Cong4f40c052007-11-05 14:50:59 -0800690 sg_init_table(ubd_dev->sg, MAX_SG);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700691
Jeff Dike62f96cb2007-02-10 01:44:16 -0800692 err = -ENOMEM;
693 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
694 if (ubd_dev->queue == NULL) {
695 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700696 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800697 }
698 ubd_dev->queue->queuedata = ubd_dev;
699
Jeff Dikea0044bd2007-05-06 14:51:36 -0700700 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800701 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
702 if(err){
703 *error_out = "Failed to register device";
704 goto out_cleanup;
705 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800706
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800708 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800709 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
Jeff Dike83380cc2008-02-04 22:31:18 -0800711 /*
712 * Perhaps this should also be under the "if (fake_major)" above
713 * using the fake_disk->disk_name
714 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 if (fake_ide)
716 make_ide_entries(ubd_gendisk[n]->disk_name);
717
Jeff Dikeec7cf782005-09-03 15:57:29 -0700718 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700719out:
720 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800721
722out_cleanup:
723 blk_cleanup_queue(ubd_dev->queue);
724 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725}
726
Jeff Dikef28169d2007-02-10 01:43:53 -0800727static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800729 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
Jeff Dikef28169d2007-02-10 01:43:53 -0800731 /* This string is possibly broken up and stored, so it's only
732 * freed if ubd_setup_common fails, or if only general options
733 * were set.
734 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800735 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800736 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800737 *error_out = "Failed to allocate memory";
738 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800740
741 ret = ubd_setup_common(str, &n, error_out);
742 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800743 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800744
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800745 if (n == -1) {
746 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800747 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800748 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
Jeff Dikedc764e52007-05-06 14:51:41 -0700750 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800751 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800752 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800753 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700754 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800756out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700757 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800758
759err_free:
760 kfree(str);
761 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762}
763
764static int ubd_get_config(char *name, char *str, int size, char **error_out)
765{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800766 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 int n, len = 0;
768
769 n = parse_unit(&name);
770 if((n >= MAX_DEV) || (n < 0)){
771 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700772 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 }
774
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800775 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800776 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800778 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 CONFIG_CHUNK(str, size, len, "", 1);
780 goto out;
781 }
782
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800783 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800785 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800787 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 }
789 else CONFIG_CHUNK(str, size, len, "", 1);
790
791 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800792 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700793 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794}
795
Jeff Dike29d56cf2005-06-25 14:55:25 -0700796static int ubd_id(char **str, int *start_out, int *end_out)
797{
Jeff Dikedc764e52007-05-06 14:51:41 -0700798 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700799
800 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700801 *start_out = 0;
802 *end_out = MAX_DEV - 1;
803 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700804}
805
Jeff Dikef28169d2007-02-10 01:43:53 -0800806static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700808 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800809 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700810 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800812 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800814 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700815
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800816 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700817 goto out;
818
819 /* you cannot remove a open disk */
820 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800821 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700822 goto out;
823
Jeff Dikedc764e52007-05-06 14:51:41 -0700824 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700825 if(disk != NULL){
826 del_gendisk(disk);
827 put_disk(disk);
828 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
830 if(fake_gendisk[n] != NULL){
831 del_gendisk(fake_gendisk[n]);
832 put_disk(fake_gendisk[n]);
833 fake_gendisk[n] = NULL;
834 }
835
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700837 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700838out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800839 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700840 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841}
842
Jeff Dikef28169d2007-02-10 01:43:53 -0800843/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800844 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800845 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800847 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 .name = "ubd",
849 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -0700850 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700851 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 .remove = ubd_remove,
853};
854
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800855static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856{
857 mconsole_register_dev(&ubd_mc);
858 return 0;
859}
860
861__initcall(ubd_mc_init);
862
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800863static int __init ubd0_init(void)
864{
865 struct ubd *ubd_dev = &ubd_devs[0];
866
Jeff Dikeb8831a12007-02-10 01:44:17 -0800867 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800868 if(ubd_dev->file == NULL)
869 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -0800870 mutex_unlock(&ubd_lock);
871
Jeff Dikedc764e52007-05-06 14:51:41 -0700872 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800873}
874
875__initcall(ubd0_init);
876
Jeff Dikeb8831a12007-02-10 01:44:17 -0800877/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +0000878static struct platform_driver ubd_driver = {
879 .driver = {
880 .name = DRIVER_NAME,
881 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882};
883
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800884static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885{
Jeff Dikef28169d2007-02-10 01:43:53 -0800886 char *error;
887 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 if (register_blkdev(MAJOR_NR, "ubd"))
890 return -1;
891
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 if (fake_major != MAJOR_NR) {
893 char name[sizeof("ubd_nnn\0")];
894
895 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 if (register_blkdev(fake_major, "ubd"))
897 return -1;
898 }
Russell King3ae5eae2005-11-09 22:32:44 +0000899 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -0700900 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800901 for (i = 0; i < MAX_DEV; i++){
902 err = ubd_add(i, &error);
903 if(err)
904 printk(KERN_ERR "Failed to initialize ubd device %d :"
905 "%s\n", i, error);
906 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700907 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 return 0;
909}
910
911late_initcall(ubd_init);
912
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800913static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400914 unsigned long stack;
915 int err;
916
917 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
918 if(global_openflags.s){
919 printk(KERN_INFO "ubd: Synchronous mode\n");
920 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
921 * enough. So use anyway the io thread. */
922 }
923 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800924 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400925 &thread_fd);
926 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800927 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400928 "ubd : Failed to start I/O thread (errno = %d) - "
929 "falling back to synchronous I/O\n", -io_pid);
930 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700931 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400932 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800933 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800934 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400935 if(err != 0)
936 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800937 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400938}
939
940device_initcall(ubd_driver_init);
941
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942static int ubd_open(struct inode *inode, struct file *filp)
943{
944 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800945 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 int err = 0;
947
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800948 if(ubd_dev->count == 0){
949 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 if(err){
951 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800952 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 goto out;
954 }
955 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800956 ubd_dev->count++;
957 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700958
959 /* This should no more be needed. And it didn't work anyway to exclude
960 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800961 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800962 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700964 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700966 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967}
968
969static int ubd_release(struct inode * inode, struct file * file)
970{
971 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800972 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800974 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800975 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -0700976 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977}
978
Jeff Dike91acb212005-10-10 23:10:32 -0400979static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
980 __u64 *cow_offset, unsigned long *bitmap,
981 __u64 bitmap_offset, unsigned long *bitmap_words,
982 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983{
Jeff Dike91acb212005-10-10 23:10:32 -0400984 __u64 sector = io_offset >> 9;
985 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
Jeff Dike91acb212005-10-10 23:10:32 -0400987 for(i = 0; i < length >> 9; i++){
988 if(cow_mask != NULL)
989 ubd_set_bit(i, (unsigned char *) cow_mask);
990 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
991 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992
Jeff Dike91acb212005-10-10 23:10:32 -0400993 update_bitmap = 1;
994 ubd_set_bit(sector + i, (unsigned char *) bitmap);
995 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996
Jeff Dike91acb212005-10-10 23:10:32 -0400997 if(!update_bitmap)
998 return;
999
1000 *cow_offset = sector / (sizeof(unsigned long) * 8);
1001
1002 /* This takes care of the case where we're exactly at the end of the
1003 * device, and *cow_offset + 1 is off the end. So, just back it up
1004 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1005 * for the original diagnosis.
1006 */
1007 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1008 sizeof(unsigned long) - 1))
1009 (*cow_offset)--;
1010
1011 bitmap_words[0] = bitmap[*cow_offset];
1012 bitmap_words[1] = bitmap[*cow_offset + 1];
1013
1014 *cow_offset *= sizeof(unsigned long);
1015 *cow_offset += bitmap_offset;
1016}
1017
1018static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1019 __u64 bitmap_offset, __u64 bitmap_len)
1020{
1021 __u64 sector = req->offset >> 9;
1022 int i;
1023
1024 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1025 panic("Operation too long");
1026
1027 if(req->op == UBD_READ) {
1028 for(i = 0; i < req->length >> 9; i++){
1029 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001030 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001031 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001032 }
Jeff Dike91acb212005-10-10 23:10:32 -04001033 }
1034 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1035 &req->cow_offset, bitmap, bitmap_offset,
1036 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037}
1038
Jeff Dike62f96cb2007-02-10 01:44:16 -08001039/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001040static void prepare_request(struct request *req, struct io_thread_req *io_req,
1041 unsigned long long offset, int page_offset,
1042 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043{
1044 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001045 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001046
Jeff Dike62f96cb2007-02-10 01:44:16 -08001047 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001048 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1049 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001050 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001051 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 io_req->offset = offset;
1053 io_req->length = len;
1054 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001055 io_req->sector_mask = 0;
1056
1057 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001059 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001060 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 io_req->sectorsize = 1 << 9;
1062
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001063 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001064 cowify_req(io_req, ubd_dev->cow.bitmap,
1065 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001066
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067}
1068
Jeff Dike62f96cb2007-02-10 01:44:16 -08001069/* Called with dev->lock held */
Jens Axboe165125e2007-07-24 09:28:11 +02001070static void do_ubd_request(struct request_queue *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071{
Jeff Dike2adcec22007-05-06 14:51:37 -07001072 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 struct request *req;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001074 int n, last_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075
Jeff Dikea0044bd2007-05-06 14:51:36 -07001076 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001077 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001078 if(dev->end_sg == 0){
1079 struct request *req = elv_next_request(q);
1080 if(req == NULL)
1081 return;
1082
1083 dev->request = req;
1084 blkdev_dequeue_request(req);
1085 dev->start_sg = 0;
1086 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001087 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001088
1089 req = dev->request;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001090 last_sectors = 0;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001091 while(dev->start_sg < dev->end_sg){
1092 struct scatterlist *sg = &dev->sg[dev->start_sg];
1093
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001094 req->sector += last_sectors;
Jeff Dike2adcec22007-05-06 14:51:37 -07001095 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001096 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001097 if(io_req == NULL){
1098 if(list_empty(&dev->restart))
1099 list_add(&dev->restart, &restart);
1100 return;
1101 }
1102 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001103 (unsigned long long) req->sector << 9,
Jens Axboe45711f12007-10-22 21:19:53 +02001104 sg->offset, sg->length, sg_page(sg));
Jeff Dikea0044bd2007-05-06 14:51:36 -07001105
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001106 last_sectors = sg->length >> 9;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001107 n = os_write_file(thread_fd, &io_req,
1108 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001109 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001110 if(n != -EAGAIN)
1111 printk("write to io thread failed, "
1112 "errno = %d\n", -n);
1113 else if(list_empty(&dev->restart))
1114 list_add(&dev->restart, &restart);
Miklos Szeredi12429bf2007-11-28 16:21:52 -08001115 kfree(io_req);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001116 return;
1117 }
1118
Jeff Dikea0044bd2007-05-06 14:51:36 -07001119 dev->start_sg++;
1120 }
1121 dev->end_sg = 0;
1122 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001123 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124}
1125
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001126static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1127{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001128 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001129
1130 geo->heads = 128;
1131 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001132 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001133 return 0;
1134}
1135
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136static int ubd_ioctl(struct inode * inode, struct file * file,
1137 unsigned int cmd, unsigned long arg)
1138{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001139 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 struct hd_driveid ubd_id = {
1141 .cyls = 0,
1142 .heads = 128,
1143 .sectors = 32,
1144 };
1145
1146 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001149 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1151 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001152 return -EFAULT;
1153 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001154
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 case CDROMVOLREAD:
1156 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001157 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 volume.channel0 = 255;
1159 volume.channel1 = 255;
1160 volume.channel2 = 255;
1161 volume.channel3 = 255;
1162 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001163 return -EFAULT;
1164 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001166 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167}
1168
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001169static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170{
1171 struct uml_stat buf1, buf2;
1172 int err;
1173
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001174 if(from_cmdline == NULL)
1175 return 0;
1176 if(!strcmp(from_cmdline, from_cow))
1177 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178
1179 err = os_stat_file(from_cmdline, &buf1);
1180 if(err < 0){
1181 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001182 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 }
1184 err = os_stat_file(from_cow, &buf2);
1185 if(err < 0){
1186 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001187 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 }
1189 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001190 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191
1192 printk("Backing file mismatch - \"%s\" requested,\n"
1193 "\"%s\" specified in COW header of \"%s\"\n",
1194 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001195 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196}
1197
1198static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1199{
1200 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001201 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 int err;
1203
1204 err = os_file_modtime(file, &modtime);
1205 if(err < 0){
1206 printk("Failed to get modification time of backing file "
1207 "\"%s\", err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001208 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 }
1210
1211 err = os_file_size(file, &actual);
1212 if(err < 0){
1213 printk("Failed to get size of backing file \"%s\", "
1214 "err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001215 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 }
1217
Jeff Dikedc764e52007-05-06 14:51:41 -07001218 if(actual != size){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1220 * the typecast.*/
1221 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1222 "file\n", (unsigned long long) size, actual);
Jeff Dikedc764e52007-05-06 14:51:41 -07001223 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 }
1225 if(modtime != mtime){
1226 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1227 "file\n", mtime, modtime);
Jeff Dikedc764e52007-05-06 14:51:41 -07001228 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001230 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231}
1232
1233int read_cow_bitmap(int fd, void *buf, int offset, int len)
1234{
1235 int err;
1236
1237 err = os_seek_file(fd, offset);
1238 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001239 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240
Jeff Dikedc764e52007-05-06 14:51:41 -07001241 err = os_read_file(fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001243 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244
Jeff Dikedc764e52007-05-06 14:51:41 -07001245 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246}
1247
Jeff Dike6c29256c2006-03-27 01:14:37 -08001248int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 char **backing_file_out, int *bitmap_offset_out,
1250 unsigned long *bitmap_len_out, int *data_offset_out,
1251 int *create_cow_out)
1252{
1253 time_t mtime;
1254 unsigned long long size;
1255 __u32 version, align;
1256 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001257 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258
1259 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001260 if (fd < 0) {
1261 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 *create_cow_out = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001263 if (!openflags->w ||
1264 ((fd != -EROFS) && (fd != -EACCES)))
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001265 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 openflags->w = 0;
1267 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001268 if (fd < 0)
1269 return fd;
Jeff Dikedc764e52007-05-06 14:51:41 -07001270 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271
Jeff Dike6c29256c2006-03-27 01:14:37 -08001272 if(shared)
1273 printk("Not locking \"%s\" on the host\n", file);
1274 else {
1275 err = os_lock_file(fd, openflags->w);
1276 if(err < 0){
1277 printk("Failed to lock '%s', err = %d\n", file, -err);
1278 goto out_close;
1279 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 }
1281
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001282 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001283 if(backing_file_out == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -07001284 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
1286 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1287 &size, &sectorsize, &align, bitmap_offset_out);
1288 if(err && (*backing_file_out != NULL)){
1289 printk("Failed to read COW header from COW file \"%s\", "
1290 "errno = %d\n", file, -err);
1291 goto out_close;
1292 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001293 if(err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001294 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001296 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001298 /* Allow switching only if no mismatch. */
1299 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 printk("Switching backing file to '%s'\n", *backing_file_out);
1301 err = write_cow_header(file, fd, *backing_file_out,
1302 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001303 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001305 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001307 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 *backing_file_out = backing_file;
1309 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001310 if (err)
1311 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 }
1313
1314 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1315 bitmap_len_out, data_offset_out);
1316
Jeff Dikedc764e52007-05-06 14:51:41 -07001317 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 out_close:
1319 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001320 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321}
1322
1323int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1324 int sectorsize, int alignment, int *bitmap_offset_out,
1325 unsigned long *bitmap_len_out, int *data_offset_out)
1326{
1327 int err, fd;
1328
1329 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001330 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 if(fd < 0){
1332 err = fd;
1333 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1334 -err);
1335 goto out;
1336 }
1337
1338 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1339 bitmap_offset_out, bitmap_len_out,
1340 data_offset_out);
1341 if(!err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001342 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 os_close_file(fd);
1344 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001345 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346}
1347
Jeff Dike91acb212005-10-10 23:10:32 -04001348static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349{
Jeff Dike91acb212005-10-10 23:10:32 -04001350 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351
Jeff Dike91acb212005-10-10 23:10:32 -04001352 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001353 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354
Jeff Dike91acb212005-10-10 23:10:32 -04001355 n = os_seek_file(req->fds[1], req->cow_offset);
1356 if(n < 0){
1357 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001358 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001359 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001361 n = os_write_file(req->fds[1], &req->bitmap_words,
1362 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001363 if(n != sizeof(req->bitmap_words)){
1364 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1365 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001366 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001367 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
Jeff Dikedc764e52007-05-06 14:51:41 -07001369 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370}
Jeff Dike91acb212005-10-10 23:10:32 -04001371
1372void do_io(struct io_thread_req *req)
1373{
1374 char *buf;
1375 unsigned long len;
1376 int n, nsectors, start, end, bit;
1377 int err;
1378 __u64 off;
1379
1380 nsectors = req->length / req->sectorsize;
1381 start = 0;
1382 do {
1383 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1384 end = start;
1385 while((end < nsectors) &&
1386 (ubd_test_bit(end, (unsigned char *)
1387 &req->sector_mask) == bit))
1388 end++;
1389
1390 off = req->offset + req->offsets[bit] +
1391 start * req->sectorsize;
1392 len = (end - start) * req->sectorsize;
1393 buf = &req->buffer[start * req->sectorsize];
1394
1395 err = os_seek_file(req->fds[bit], off);
1396 if(err < 0){
1397 printk("do_io - lseek failed : err = %d\n", -err);
1398 req->error = 1;
1399 return;
1400 }
1401 if(req->op == UBD_READ){
1402 n = 0;
1403 do {
1404 buf = &buf[n];
1405 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001406 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001407 if (n < 0) {
1408 printk("do_io - read failed, err = %d "
1409 "fd = %d\n", -n, req->fds[bit]);
1410 req->error = 1;
1411 return;
1412 }
1413 } while((n < len) && (n != 0));
1414 if (n < len) memset(&buf[n], 0, len - n);
1415 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001416 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001417 if(n != len){
1418 printk("do_io - write failed err = %d "
1419 "fd = %d\n", -n, req->fds[bit]);
1420 req->error = 1;
1421 return;
1422 }
1423 }
1424
1425 start = end;
1426 } while(start < nsectors);
1427
1428 req->error = update_bitmap(req);
1429}
1430
1431/* Changed in start_io_thread, which is serialized by being called only
1432 * from ubd_init, which is an initcall.
1433 */
1434int kernel_fd = -1;
1435
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001436/* Only changed by the io thread. XXX: currently unused. */
1437static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001438
1439int io_thread(void *arg)
1440{
Jeff Dike2adcec22007-05-06 14:51:37 -07001441 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001442 int n;
1443
1444 ignore_sigwinch_sig();
1445 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001446 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001447 sizeof(struct io_thread_req *));
1448 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001449 if(n < 0)
1450 printk("io_thread - read failed, fd = %d, "
1451 "err = %d\n", kernel_fd, -n);
1452 else {
1453 printk("io_thread - short read, fd = %d, "
1454 "length = %d\n", kernel_fd, n);
1455 }
1456 continue;
1457 }
1458 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001459 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001460 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001461 sizeof(struct io_thread_req *));
1462 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001463 printk("io_thread - write failed, fd = %d, err = %d\n",
1464 kernel_fd, -n);
1465 }
Jeff Dike91acb212005-10-10 23:10:32 -04001466
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001467 return 0;
1468}