blob: b498f27401005fddc119d85383c267a525b0aa4d [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
463static int fakehd_set = 0;
464static int fakehd(char *str)
465{
466 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
467 fakehd_set = 1;
468 return 1;
469}
470
471__setup("fakehd", fakehd);
472__uml_help(fakehd,
473"fakehd\n"
474" Change the ubd device name to \"hd\".\n\n"
475);
476
Jens Axboe165125e2007-07-24 09:28:11 +0200477static void do_ubd_request(struct request_queue * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400478
479/* Only changed by ubd_init, which is an initcall. */
480int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500482static void ubd_end_request(struct request *req, int bytes, int error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483{
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500484 blk_end_request(req, error, bytes);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485}
486
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800487/* Callable only from interrupt context - otherwise you need to do
488 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700489static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700491 if(bytes < 0){
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500492 ubd_end_request(req, 0, -EIO);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700493 return;
494 }
Kiyoshi Ueda4898b532007-12-11 17:42:53 -0500495 ubd_end_request(req, bytes, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496}
497
Jeff Dikea0044bd2007-05-06 14:51:36 -0700498static LIST_HEAD(restart);
499
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800500/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800501/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400502static void ubd_handler(void)
503{
Jeff Dike2adcec22007-05-06 14:51:37 -0700504 struct io_thread_req *req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800505 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700506 struct ubd *ubd;
507 struct list_head *list, *next_ele;
508 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400509 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
Jeff Dikea0044bd2007-05-06 14:51:36 -0700511 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700512 n = os_read_file(thread_fd, &req,
513 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700514 if(n != sizeof(req)){
515 if(n == -EAGAIN)
516 break;
517 printk(KERN_ERR "spurious interrupt in ubd_handler, "
518 "err = %d\n", -n);
519 return;
520 }
521
Jeff Dike2adcec22007-05-06 14:51:37 -0700522 rq = req->req;
523 rq->nr_sectors -= req->length >> 9;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700524 if(rq->nr_sectors == 0)
525 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike2adcec22007-05-06 14:51:37 -0700526 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400527 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800528 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700529
530 list_for_each_safe(list, next_ele, &restart){
531 ubd = container_of(list, struct ubd, restart);
532 list_del_init(&ubd->restart);
533 spin_lock_irqsave(&ubd->lock, flags);
534 do_ubd_request(ubd->queue);
535 spin_unlock_irqrestore(&ubd->lock, flags);
536 }
Jeff Dike91acb212005-10-10 23:10:32 -0400537}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
Al Viro7bea96f2006-10-08 22:49:34 +0100539static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540{
Jeff Dike91acb212005-10-10 23:10:32 -0400541 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700542 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543}
544
Jeff Dike91acb212005-10-10 23:10:32 -0400545/* Only changed by ubd_init, which is an initcall. */
546static int io_pid = -1;
547
548void kill_io_thread(void)
549{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800550 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400551 os_kill_process(io_pid, 1);
552}
553
554__uml_exitcall(kill_io_thread);
555
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800556static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557{
558 char *file;
559
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800560 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700561 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562}
563
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800564static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800566 os_close_file(ubd_dev->fd);
567 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 return;
569
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800570 os_close_file(ubd_dev->cow.fd);
571 vfree(ubd_dev->cow.bitmap);
572 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573}
574
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800575static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576{
577 struct openflags flags;
578 char **back_ptr;
579 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800580 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800582 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800584 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
585 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800586
587 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800588 back_ptr, &ubd_dev->cow.bitmap_offset,
589 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800590 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800592 if((fd == -ENOENT) && create_cow){
593 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800594 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
595 &ubd_dev->cow.bitmap_offset,
596 &ubd_dev->cow.bitmap_len,
597 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800598 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800600 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 }
602 }
603
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800604 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800605 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800606 -fd);
607 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800609 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800611 if(ubd_dev->cow.file != NULL){
Jeff Dikef4768ff2007-08-22 14:01:53 -0700612 blk_queue_max_sectors(ubd_dev->queue, 8 * sizeof(long));
613
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 err = -ENOMEM;
Jesper Juhlda2486b2007-10-16 01:27:19 -0700615 ubd_dev->cow.bitmap = vmalloc(ubd_dev->cow.bitmap_len);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800616 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
618 goto error;
619 }
620 flush_tlb_kernel_vm();
621
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800622 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
623 ubd_dev->cow.bitmap_offset,
624 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 if(err < 0)
626 goto error;
627
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800628 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800630 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800631 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800633 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700635 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800637 os_close_file(ubd_dev->fd);
Jeff Dikedc764e52007-05-06 14:51:41 -0700638 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639}
640
Jeff Dike2e3f5252007-05-06 14:51:29 -0700641static void ubd_device_release(struct device *dev)
642{
643 struct ubd *ubd_dev = dev->driver_data;
644
645 blk_cleanup_queue(ubd_dev->queue);
646 *ubd_dev = ((struct ubd) DEFAULT_UBD);
647}
648
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800649static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800650 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651{
652 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
654 disk = alloc_disk(1 << UBD_SHIFT);
655 if(disk == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -0700656 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
658 disk->major = major;
659 disk->first_minor = unit << UBD_SHIFT;
660 disk->fops = &ubd_blops;
661 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700662 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700664 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
667 /* sysfs register (not for ide fake devices) */
668 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800669 ubd_devs[unit].pdev.id = unit;
670 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700671 ubd_devs[unit].pdev.dev.release = ubd_device_release;
672 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800673 platform_device_register(&ubd_devs[unit].pdev);
674 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 }
676
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800677 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800678 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 add_disk(disk);
680
681 *disk_out = disk;
682 return 0;
683}
684
685#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
686
Jeff Dikef28169d2007-02-10 01:43:53 -0800687static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800689 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800690 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800692 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700693 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800695 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800696 if(err < 0){
697 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700698 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800699 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800701 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702
Jeff Dikea0044bd2007-05-06 14:51:36 -0700703 INIT_LIST_HEAD(&ubd_dev->restart);
WANG Cong4f40c052007-11-05 14:50:59 -0800704 sg_init_table(ubd_dev->sg, MAX_SG);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700705
Jeff Dike62f96cb2007-02-10 01:44:16 -0800706 err = -ENOMEM;
707 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
708 if (ubd_dev->queue == NULL) {
709 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700710 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800711 }
712 ubd_dev->queue->queuedata = ubd_dev;
713
Jeff Dikea0044bd2007-05-06 14:51:36 -0700714 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800715 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
716 if(err){
717 *error_out = "Failed to register device";
718 goto out_cleanup;
719 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800720
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800722 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800723 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
725 /* perhaps this should also be under the "if (fake_major)" above */
726 /* using the fake_disk->disk_name and also the fakehd_set name */
727 if (fake_ide)
728 make_ide_entries(ubd_gendisk[n]->disk_name);
729
Jeff Dikeec7cf782005-09-03 15:57:29 -0700730 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700731out:
732 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800733
734out_cleanup:
735 blk_cleanup_queue(ubd_dev->queue);
736 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737}
738
Jeff Dikef28169d2007-02-10 01:43:53 -0800739static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800741 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
Jeff Dikef28169d2007-02-10 01:43:53 -0800743 /* This string is possibly broken up and stored, so it's only
744 * freed if ubd_setup_common fails, or if only general options
745 * were set.
746 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800747 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800748 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800749 *error_out = "Failed to allocate memory";
750 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800752
753 ret = ubd_setup_common(str, &n, error_out);
754 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800755 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800756
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800757 if (n == -1) {
758 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800759 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800760 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Jeff Dikedc764e52007-05-06 14:51:41 -0700762 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800763 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800764 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800765 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700766 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800768out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700769 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800770
771err_free:
772 kfree(str);
773 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774}
775
776static int ubd_get_config(char *name, char *str, int size, char **error_out)
777{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800778 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 int n, len = 0;
780
781 n = parse_unit(&name);
782 if((n >= MAX_DEV) || (n < 0)){
783 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700784 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 }
786
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800787 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800788 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800790 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 CONFIG_CHUNK(str, size, len, "", 1);
792 goto out;
793 }
794
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800795 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800797 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800799 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 }
801 else CONFIG_CHUNK(str, size, len, "", 1);
802
803 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800804 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700805 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806}
807
Jeff Dike29d56cf2005-06-25 14:55:25 -0700808static int ubd_id(char **str, int *start_out, int *end_out)
809{
Jeff Dikedc764e52007-05-06 14:51:41 -0700810 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700811
812 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700813 *start_out = 0;
814 *end_out = MAX_DEV - 1;
815 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700816}
817
Jeff Dikef28169d2007-02-10 01:43:53 -0800818static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700820 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800821 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700822 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800824 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800826 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700827
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800828 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700829 goto out;
830
831 /* you cannot remove a open disk */
832 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800833 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700834 goto out;
835
Jeff Dikedc764e52007-05-06 14:51:41 -0700836 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700837 if(disk != NULL){
838 del_gendisk(disk);
839 put_disk(disk);
840 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
842 if(fake_gendisk[n] != NULL){
843 del_gendisk(fake_gendisk[n]);
844 put_disk(fake_gendisk[n]);
845 fake_gendisk[n] = NULL;
846 }
847
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700849 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700850out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800851 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700852 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853}
854
Jeff Dikef28169d2007-02-10 01:43:53 -0800855/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800856 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800857 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800859 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 .name = "ubd",
861 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -0700862 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700863 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 .remove = ubd_remove,
865};
866
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800867static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868{
869 mconsole_register_dev(&ubd_mc);
870 return 0;
871}
872
873__initcall(ubd_mc_init);
874
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800875static int __init ubd0_init(void)
876{
877 struct ubd *ubd_dev = &ubd_devs[0];
878
Jeff Dikeb8831a12007-02-10 01:44:17 -0800879 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800880 if(ubd_dev->file == NULL)
881 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -0800882 mutex_unlock(&ubd_lock);
883
Jeff Dikedc764e52007-05-06 14:51:41 -0700884 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800885}
886
887__initcall(ubd0_init);
888
Jeff Dikeb8831a12007-02-10 01:44:17 -0800889/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +0000890static struct platform_driver ubd_driver = {
891 .driver = {
892 .name = DRIVER_NAME,
893 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894};
895
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800896static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897{
Jeff Dikef28169d2007-02-10 01:43:53 -0800898 char *error;
899 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 if (register_blkdev(MAJOR_NR, "ubd"))
902 return -1;
903
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 if (fake_major != MAJOR_NR) {
905 char name[sizeof("ubd_nnn\0")];
906
907 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 if (register_blkdev(fake_major, "ubd"))
909 return -1;
910 }
Russell King3ae5eae2005-11-09 22:32:44 +0000911 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -0700912 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800913 for (i = 0; i < MAX_DEV; i++){
914 err = ubd_add(i, &error);
915 if(err)
916 printk(KERN_ERR "Failed to initialize ubd device %d :"
917 "%s\n", i, error);
918 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700919 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 return 0;
921}
922
923late_initcall(ubd_init);
924
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800925static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400926 unsigned long stack;
927 int err;
928
929 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
930 if(global_openflags.s){
931 printk(KERN_INFO "ubd: Synchronous mode\n");
932 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
933 * enough. So use anyway the io thread. */
934 }
935 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800936 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400937 &thread_fd);
938 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800939 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400940 "ubd : Failed to start I/O thread (errno = %d) - "
941 "falling back to synchronous I/O\n", -io_pid);
942 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700943 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400944 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800945 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800946 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400947 if(err != 0)
948 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800949 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400950}
951
952device_initcall(ubd_driver_init);
953
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954static int ubd_open(struct inode *inode, struct file *filp)
955{
956 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800957 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 int err = 0;
959
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800960 if(ubd_dev->count == 0){
961 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 if(err){
963 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800964 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 goto out;
966 }
967 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800968 ubd_dev->count++;
969 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700970
971 /* This should no more be needed. And it didn't work anyway to exclude
972 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800973 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800974 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700976 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700978 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979}
980
981static int ubd_release(struct inode * inode, struct file * file)
982{
983 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800984 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800986 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800987 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -0700988 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989}
990
Jeff Dike91acb212005-10-10 23:10:32 -0400991static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
992 __u64 *cow_offset, unsigned long *bitmap,
993 __u64 bitmap_offset, unsigned long *bitmap_words,
994 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995{
Jeff Dike91acb212005-10-10 23:10:32 -0400996 __u64 sector = io_offset >> 9;
997 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998
Jeff Dike91acb212005-10-10 23:10:32 -0400999 for(i = 0; i < length >> 9; i++){
1000 if(cow_mask != NULL)
1001 ubd_set_bit(i, (unsigned char *) cow_mask);
1002 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1003 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
Jeff Dike91acb212005-10-10 23:10:32 -04001005 update_bitmap = 1;
1006 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1007 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
Jeff Dike91acb212005-10-10 23:10:32 -04001009 if(!update_bitmap)
1010 return;
1011
1012 *cow_offset = sector / (sizeof(unsigned long) * 8);
1013
1014 /* This takes care of the case where we're exactly at the end of the
1015 * device, and *cow_offset + 1 is off the end. So, just back it up
1016 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1017 * for the original diagnosis.
1018 */
1019 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1020 sizeof(unsigned long) - 1))
1021 (*cow_offset)--;
1022
1023 bitmap_words[0] = bitmap[*cow_offset];
1024 bitmap_words[1] = bitmap[*cow_offset + 1];
1025
1026 *cow_offset *= sizeof(unsigned long);
1027 *cow_offset += bitmap_offset;
1028}
1029
1030static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1031 __u64 bitmap_offset, __u64 bitmap_len)
1032{
1033 __u64 sector = req->offset >> 9;
1034 int i;
1035
1036 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1037 panic("Operation too long");
1038
1039 if(req->op == UBD_READ) {
1040 for(i = 0; i < req->length >> 9; i++){
1041 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001042 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001043 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001044 }
Jeff Dike91acb212005-10-10 23:10:32 -04001045 }
1046 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1047 &req->cow_offset, bitmap, bitmap_offset,
1048 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049}
1050
Jeff Dike62f96cb2007-02-10 01:44:16 -08001051/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001052static void prepare_request(struct request *req, struct io_thread_req *io_req,
1053 unsigned long long offset, int page_offset,
1054 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055{
1056 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001057 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001058
Jeff Dike62f96cb2007-02-10 01:44:16 -08001059 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001060 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1061 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001062 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001063 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 io_req->offset = offset;
1065 io_req->length = len;
1066 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001067 io_req->sector_mask = 0;
1068
1069 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001071 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001072 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 io_req->sectorsize = 1 << 9;
1074
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001075 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001076 cowify_req(io_req, ubd_dev->cow.bitmap,
1077 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001078
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079}
1080
Jeff Dike62f96cb2007-02-10 01:44:16 -08001081/* Called with dev->lock held */
Jens Axboe165125e2007-07-24 09:28:11 +02001082static void do_ubd_request(struct request_queue *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083{
Jeff Dike2adcec22007-05-06 14:51:37 -07001084 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 struct request *req;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001086 int n, last_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087
Jeff Dikea0044bd2007-05-06 14:51:36 -07001088 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001089 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001090 if(dev->end_sg == 0){
1091 struct request *req = elv_next_request(q);
1092 if(req == NULL)
1093 return;
1094
1095 dev->request = req;
1096 blkdev_dequeue_request(req);
1097 dev->start_sg = 0;
1098 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001099 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001100
1101 req = dev->request;
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001102 last_sectors = 0;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001103 while(dev->start_sg < dev->end_sg){
1104 struct scatterlist *sg = &dev->sg[dev->start_sg];
1105
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001106 req->sector += last_sectors;
Jeff Dike2adcec22007-05-06 14:51:37 -07001107 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001108 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001109 if(io_req == NULL){
1110 if(list_empty(&dev->restart))
1111 list_add(&dev->restart, &restart);
1112 return;
1113 }
1114 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001115 (unsigned long long) req->sector << 9,
Jens Axboe45711f12007-10-22 21:19:53 +02001116 sg->offset, sg->length, sg_page(sg));
Jeff Dikea0044bd2007-05-06 14:51:36 -07001117
Jeff Dike0a6d3a22007-07-15 23:38:47 -07001118 last_sectors = sg->length >> 9;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001119 n = os_write_file(thread_fd, &io_req,
1120 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001121 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001122 if(n != -EAGAIN)
1123 printk("write to io thread failed, "
1124 "errno = %d\n", -n);
1125 else if(list_empty(&dev->restart))
1126 list_add(&dev->restart, &restart);
Miklos Szeredi12429bf2007-11-28 16:21:52 -08001127 kfree(io_req);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001128 return;
1129 }
1130
Jeff Dikea0044bd2007-05-06 14:51:36 -07001131 dev->start_sg++;
1132 }
1133 dev->end_sg = 0;
1134 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001135 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136}
1137
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001138static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1139{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001140 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001141
1142 geo->heads = 128;
1143 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001144 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001145 return 0;
1146}
1147
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148static int ubd_ioctl(struct inode * inode, struct file * file,
1149 unsigned int cmd, unsigned long arg)
1150{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001151 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 struct hd_driveid ubd_id = {
1153 .cyls = 0,
1154 .heads = 128,
1155 .sectors = 32,
1156 };
1157
1158 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001161 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1163 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001164 return -EFAULT;
1165 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001166
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 case CDROMVOLREAD:
1168 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001169 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 volume.channel0 = 255;
1171 volume.channel1 = 255;
1172 volume.channel2 = 255;
1173 volume.channel3 = 255;
1174 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001175 return -EFAULT;
1176 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001178 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179}
1180
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001181static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182{
1183 struct uml_stat buf1, buf2;
1184 int err;
1185
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001186 if(from_cmdline == NULL)
1187 return 0;
1188 if(!strcmp(from_cmdline, from_cow))
1189 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190
1191 err = os_stat_file(from_cmdline, &buf1);
1192 if(err < 0){
1193 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001194 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 }
1196 err = os_stat_file(from_cow, &buf2);
1197 if(err < 0){
1198 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001199 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 }
1201 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001202 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203
1204 printk("Backing file mismatch - \"%s\" requested,\n"
1205 "\"%s\" specified in COW header of \"%s\"\n",
1206 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001207 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208}
1209
1210static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1211{
1212 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001213 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 int err;
1215
1216 err = os_file_modtime(file, &modtime);
1217 if(err < 0){
1218 printk("Failed to get modification time of backing file "
1219 "\"%s\", err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001220 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 }
1222
1223 err = os_file_size(file, &actual);
1224 if(err < 0){
1225 printk("Failed to get size of backing file \"%s\", "
1226 "err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001227 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 }
1229
Jeff Dikedc764e52007-05-06 14:51:41 -07001230 if(actual != size){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1232 * the typecast.*/
1233 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1234 "file\n", (unsigned long long) size, actual);
Jeff Dikedc764e52007-05-06 14:51:41 -07001235 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 }
1237 if(modtime != mtime){
1238 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1239 "file\n", mtime, modtime);
Jeff Dikedc764e52007-05-06 14:51:41 -07001240 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001242 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243}
1244
1245int read_cow_bitmap(int fd, void *buf, int offset, int len)
1246{
1247 int err;
1248
1249 err = os_seek_file(fd, offset);
1250 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001251 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252
Jeff Dikedc764e52007-05-06 14:51:41 -07001253 err = os_read_file(fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001255 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256
Jeff Dikedc764e52007-05-06 14:51:41 -07001257 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258}
1259
Jeff Dike6c29256c2006-03-27 01:14:37 -08001260int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 char **backing_file_out, int *bitmap_offset_out,
1262 unsigned long *bitmap_len_out, int *data_offset_out,
1263 int *create_cow_out)
1264{
1265 time_t mtime;
1266 unsigned long long size;
1267 __u32 version, align;
1268 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001269 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270
1271 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001272 if (fd < 0) {
1273 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 *create_cow_out = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001275 if (!openflags->w ||
1276 ((fd != -EROFS) && (fd != -EACCES)))
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001277 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 openflags->w = 0;
1279 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001280 if (fd < 0)
1281 return fd;
Jeff Dikedc764e52007-05-06 14:51:41 -07001282 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Jeff Dike6c29256c2006-03-27 01:14:37 -08001284 if(shared)
1285 printk("Not locking \"%s\" on the host\n", file);
1286 else {
1287 err = os_lock_file(fd, openflags->w);
1288 if(err < 0){
1289 printk("Failed to lock '%s', err = %d\n", file, -err);
1290 goto out_close;
1291 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 }
1293
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001294 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001295 if(backing_file_out == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -07001296 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297
1298 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1299 &size, &sectorsize, &align, bitmap_offset_out);
1300 if(err && (*backing_file_out != NULL)){
1301 printk("Failed to read COW header from COW file \"%s\", "
1302 "errno = %d\n", file, -err);
1303 goto out_close;
1304 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001305 if(err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001306 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001308 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001310 /* Allow switching only if no mismatch. */
1311 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 printk("Switching backing file to '%s'\n", *backing_file_out);
1313 err = write_cow_header(file, fd, *backing_file_out,
1314 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001315 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001317 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001319 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 *backing_file_out = backing_file;
1321 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001322 if (err)
1323 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 }
1325
1326 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1327 bitmap_len_out, data_offset_out);
1328
Jeff Dikedc764e52007-05-06 14:51:41 -07001329 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 out_close:
1331 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001332 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333}
1334
1335int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1336 int sectorsize, int alignment, int *bitmap_offset_out,
1337 unsigned long *bitmap_len_out, int *data_offset_out)
1338{
1339 int err, fd;
1340
1341 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001342 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 if(fd < 0){
1344 err = fd;
1345 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1346 -err);
1347 goto out;
1348 }
1349
1350 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1351 bitmap_offset_out, bitmap_len_out,
1352 data_offset_out);
1353 if(!err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001354 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 os_close_file(fd);
1356 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001357 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358}
1359
Jeff Dike91acb212005-10-10 23:10:32 -04001360static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361{
Jeff Dike91acb212005-10-10 23:10:32 -04001362 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
Jeff Dike91acb212005-10-10 23:10:32 -04001364 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001365 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
Jeff Dike91acb212005-10-10 23:10:32 -04001367 n = os_seek_file(req->fds[1], req->cow_offset);
1368 if(n < 0){
1369 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001370 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001371 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001373 n = os_write_file(req->fds[1], &req->bitmap_words,
1374 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001375 if(n != sizeof(req->bitmap_words)){
1376 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1377 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001378 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001379 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
Jeff Dikedc764e52007-05-06 14:51:41 -07001381 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382}
Jeff Dike91acb212005-10-10 23:10:32 -04001383
1384void do_io(struct io_thread_req *req)
1385{
1386 char *buf;
1387 unsigned long len;
1388 int n, nsectors, start, end, bit;
1389 int err;
1390 __u64 off;
1391
1392 nsectors = req->length / req->sectorsize;
1393 start = 0;
1394 do {
1395 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1396 end = start;
1397 while((end < nsectors) &&
1398 (ubd_test_bit(end, (unsigned char *)
1399 &req->sector_mask) == bit))
1400 end++;
1401
1402 off = req->offset + req->offsets[bit] +
1403 start * req->sectorsize;
1404 len = (end - start) * req->sectorsize;
1405 buf = &req->buffer[start * req->sectorsize];
1406
1407 err = os_seek_file(req->fds[bit], off);
1408 if(err < 0){
1409 printk("do_io - lseek failed : err = %d\n", -err);
1410 req->error = 1;
1411 return;
1412 }
1413 if(req->op == UBD_READ){
1414 n = 0;
1415 do {
1416 buf = &buf[n];
1417 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001418 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001419 if (n < 0) {
1420 printk("do_io - read failed, err = %d "
1421 "fd = %d\n", -n, req->fds[bit]);
1422 req->error = 1;
1423 return;
1424 }
1425 } while((n < len) && (n != 0));
1426 if (n < len) memset(&buf[n], 0, len - n);
1427 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001428 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001429 if(n != len){
1430 printk("do_io - write failed err = %d "
1431 "fd = %d\n", -n, req->fds[bit]);
1432 req->error = 1;
1433 return;
1434 }
1435 }
1436
1437 start = end;
1438 } while(start < nsectors);
1439
1440 req->error = update_bitmap(req);
1441}
1442
1443/* Changed in start_io_thread, which is serialized by being called only
1444 * from ubd_init, which is an initcall.
1445 */
1446int kernel_fd = -1;
1447
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001448/* Only changed by the io thread. XXX: currently unused. */
1449static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001450
1451int io_thread(void *arg)
1452{
Jeff Dike2adcec22007-05-06 14:51:37 -07001453 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001454 int n;
1455
1456 ignore_sigwinch_sig();
1457 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001458 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001459 sizeof(struct io_thread_req *));
1460 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001461 if(n < 0)
1462 printk("io_thread - read failed, fd = %d, "
1463 "err = %d\n", kernel_fd, -n);
1464 else {
1465 printk("io_thread - short read, fd = %d, "
1466 "length = %d\n", kernel_fd, n);
1467 }
1468 continue;
1469 }
1470 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001471 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001472 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001473 sizeof(struct io_thread_req *));
1474 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001475 printk("io_thread - write failed, fd = %d, err = %d\n",
1476 kernel_fd, -n);
1477 }
Jeff Dike91acb212005-10-10 23:10:32 -04001478
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001479 return 0;
1480}