blob: da992a3ad6b78254ee2cda6346dd10fd0e86ec0b [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#define UBD_SHIFT 4
21
Jeff Dikee16f5352007-06-08 13:46:54 -070022#include "linux/kernel.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include "linux/module.h"
24#include "linux/blkdev.h"
Bartlomiej Zolnierkiewicz73855e12009-04-01 21:42:21 +020025#include "linux/ata.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#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"
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -080030#include "linux/seq_file.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include "linux/ctype.h"
32#include "linux/capability.h"
33#include "linux/mm.h"
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090034#include "linux/slab.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include "linux/vmalloc.h"
36#include "linux/blkpg.h"
37#include "linux/genhd.h"
38#include "linux/spinlock.h"
Russell Kingd052d1b2005-10-29 19:07:23 +010039#include "linux/platform_device.h"
WANG Cong23464ff2007-10-24 13:07:11 +020040#include "linux/scatterlist.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include "asm/segment.h"
42#include "asm/uaccess.h"
43#include "asm/irq.h"
44#include "asm/types.h"
45#include "asm/tlbflush.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include "mem_user.h"
47#include "kern_util.h"
48#include "kern.h"
49#include "mconsole_kern.h"
50#include "init.h"
51#include "irq_user.h"
52#include "irq_kern.h"
53#include "ubd_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#include "os.h"
55#include "mem.h"
56#include "mem_kern.h"
57#include "cow.h"
58
Jeff Dike7b9014c2005-05-20 13:59:11 -070059enum ubd_req { UBD_READ, UBD_WRITE };
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61struct io_thread_req {
Jeff Dike62f96cb2007-02-10 01:44:16 -080062 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -040063 enum ubd_req op;
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 int fds[2];
65 unsigned long offsets[2];
66 unsigned long long offset;
67 unsigned long length;
68 char *buffer;
69 int sectorsize;
Jeff Dike91acb212005-10-10 23:10:32 -040070 unsigned long sector_mask;
71 unsigned long long cow_offset;
72 unsigned long bitmap_words[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 int error;
74};
75
Jeff Dike91acb212005-10-10 23:10:32 -040076static inline int ubd_test_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070077{
78 __u64 n;
79 int bits, off;
80
Jeff Dike91acb212005-10-10 23:10:32 -040081 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 n = bit / bits;
83 off = bit % bits;
Jeff Dikedc764e52007-05-06 14:51:41 -070084 return (data[n] & (1 << off)) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070085}
86
Jeff Dike91acb212005-10-10 23:10:32 -040087static inline void ubd_set_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 Dike91acb212005-10-10 23:10:32 -040095 data[n] |= (1 << off);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096}
97/*End stuff from ubd_user.h*/
98
99#define DRIVER_NAME "uml-blkdev"
100
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800101static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
Al Viroa625c992008-03-02 09:16:26 -0500103static int ubd_open(struct block_device *bdev, fmode_t mode);
104static int ubd_release(struct gendisk *disk, fmode_t mode);
105static int ubd_ioctl(struct block_device *bdev, fmode_t mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800107static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800109#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110
Alexey Dobriyan83d5cde2009-09-21 17:01:13 -0700111static const struct block_device_operations ubd_blops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 .owner = THIS_MODULE,
Al Viroa625c992008-03-02 09:16:26 -0500113 .open = ubd_open,
114 .release = ubd_release,
115 .ioctl = ubd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800116 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117};
118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119/* Protected by ubd_lock */
Christoph Hellwig792dd4f2009-03-31 15:23:39 -0700120static int fake_major = UBD_MAJOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121static struct gendisk *ubd_gendisk[MAX_DEV];
122static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800123
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124#ifdef CONFIG_BLK_DEV_UBD_SYNC
125#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
126 .cl = 1 })
127#else
128#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
129 .cl = 1 })
130#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131static struct openflags global_openflags = OPEN_FLAGS;
132
133struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800134 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800136 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 int fd;
138 unsigned long *bitmap;
139 unsigned long bitmap_len;
140 int bitmap_offset;
Jeff Dikedc764e52007-05-06 14:51:41 -0700141 int data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142};
143
Jeff Dikea0044bd2007-05-06 14:51:36 -0700144#define MAX_SG 64
145
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146struct ubd {
Jeff Dikea0044bd2007-05-06 14:51:36 -0700147 struct list_head restart;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800148 /* name (and fd, below) of the file opened for writing, either the
149 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 char *file;
151 int count;
152 int fd;
153 __u64 size;
154 struct openflags boot_openflags;
155 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800156 unsigned shared:1;
157 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 struct cow cow;
159 struct platform_device pdev;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800160 struct request_queue *queue;
161 spinlock_t lock;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700162 struct scatterlist sg[MAX_SG];
163 struct request *request;
164 int start_sg, end_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165};
166
167#define DEFAULT_COW { \
168 .file = NULL, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700169 .fd = -1, \
170 .bitmap = NULL, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 .bitmap_offset = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700172 .data_offset = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173}
174
175#define DEFAULT_UBD { \
176 .file = NULL, \
177 .count = 0, \
178 .fd = -1, \
179 .size = -1, \
180 .boot_openflags = OPEN_FLAGS, \
181 .openflags = OPEN_FLAGS, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700182 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800183 .shared = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700184 .cow = DEFAULT_COW, \
Jeff Dike62f96cb2007-02-10 01:44:16 -0800185 .lock = SPIN_LOCK_UNLOCKED, \
Jeff Dikea0044bd2007-05-06 14:51:36 -0700186 .request = NULL, \
187 .start_sg = 0, \
188 .end_sg = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189}
190
Jeff Dikeb8831a12007-02-10 01:44:17 -0800191/* Protected by ubd_lock */
WANG Cong5dc62b12008-04-28 02:13:58 -0700192static struct ubd ubd_devs[MAX_DEV] = { [0 ... MAX_DEV - 1] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194/* Only changed by fake_ide_setup which is a setup */
195static int fake_ide = 0;
196static struct proc_dir_entry *proc_ide_root = NULL;
197static struct proc_dir_entry *proc_ide = NULL;
198
199static void make_proc_ide(void)
200{
201 proc_ide_root = proc_mkdir("ide", NULL);
202 proc_ide = proc_mkdir("ide0", proc_ide_root);
203}
204
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800205static int fake_ide_media_proc_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206{
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800207 seq_puts(m, "disk\n");
208 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209}
210
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800211static int fake_ide_media_proc_open(struct inode *inode, struct file *file)
212{
213 return single_open(file, fake_ide_media_proc_show, NULL);
214}
215
216static const struct file_operations fake_ide_media_proc_fops = {
217 .owner = THIS_MODULE,
218 .open = fake_ide_media_proc_open,
219 .read = seq_read,
220 .llseek = seq_lseek,
221 .release = single_release,
222};
223
WANG Congc0a92902008-02-04 22:30:41 -0800224static void make_ide_entries(const char *dev_name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225{
226 struct proc_dir_entry *dir, *ent;
227 char name[64];
228
229 if(proc_ide_root == NULL) make_proc_ide();
230
231 dir = proc_mkdir(dev_name, proc_ide);
232 if(!dir) return;
233
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800234 ent = proc_create("media", S_IRUGO, dir, &fake_ide_media_proc_fops);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 if(!ent) return;
WANG Congc0a92902008-02-04 22:30:41 -0800236 snprintf(name, sizeof(name), "ide0/%s", dev_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 proc_symlink(dev_name, proc_ide_root, name);
238}
239
240static int fake_ide_setup(char *str)
241{
242 fake_ide = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700243 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244}
245
246__setup("fake_ide", fake_ide_setup);
247
248__uml_help(fake_ide_setup,
249"fake_ide\n"
250" Create ide0 entries that map onto ubd devices.\n\n"
251);
252
253static int parse_unit(char **ptr)
254{
255 char *str = *ptr, *end;
256 int n = -1;
257
258 if(isdigit(*str)) {
259 n = simple_strtoul(str, &end, 0);
260 if(end == str)
Jeff Dikedc764e52007-05-06 14:51:41 -0700261 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 *ptr = end;
263 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800264 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 n = *str - 'a';
266 str++;
267 *ptr = str;
268 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700269 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270}
271
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800272/* If *index_out == -1 at exit, the passed option was a general one;
273 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
274 * should not be freed on exit.
275 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800276static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800278 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 struct openflags flags = global_openflags;
280 char *backing_file;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800281 int n, err = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
283 if(index_out) *index_out = -1;
284 n = *str;
285 if(n == '='){
286 char *end;
287 int major;
288
289 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 if(!strcmp(str, "sync")){
291 global_openflags = of_sync(global_openflags);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800292 goto out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 }
294
Jeff Dikef28169d2007-02-10 01:43:53 -0800295 err = -EINVAL;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800296 major = simple_strtoul(str, &end, 0);
297 if((*end != '\0') || (end == str)){
298 *error_out = "Didn't parse major number";
299 goto out1;
300 }
301
Jeff Dikef28169d2007-02-10 01:43:53 -0800302 mutex_lock(&ubd_lock);
Christoph Hellwig792dd4f2009-03-31 15:23:39 -0700303 if (fake_major != UBD_MAJOR) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800304 *error_out = "Can't assign a fake major twice";
305 goto out1;
306 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800307
Jeff Dikef28169d2007-02-10 01:43:53 -0800308 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310 printk(KERN_INFO "Setting extra ubd major number to %d\n",
311 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800312 err = 0;
313 out1:
314 mutex_unlock(&ubd_lock);
315 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 }
317
318 n = parse_unit(&str);
319 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800320 *error_out = "Couldn't parse device number";
321 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 }
323 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800324 *error_out = "Device number out of range";
325 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
327
Jeff Dikef28169d2007-02-10 01:43:53 -0800328 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800329 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800331 ubd_dev = &ubd_devs[n];
332 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800333 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 goto out;
335 }
336
337 if (index_out)
338 *index_out = n;
339
Jeff Dikef28169d2007-02-10 01:43:53 -0800340 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800341 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 switch (*str) {
343 case 'r':
344 flags.w = 0;
345 break;
346 case 's':
347 flags.s = 1;
348 break;
349 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800350 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800352 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800353 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800354 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 case '=':
356 str++;
357 goto break_loop;
358 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800359 *error_out = "Expected '=' or flag letter "
360 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 goto out;
362 }
363 str++;
364 }
365
Jeff Dikef28169d2007-02-10 01:43:53 -0800366 if (*str == '=')
367 *error_out = "Too many flags specified";
368 else
369 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 goto out;
371
372break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 backing_file = strchr(str, ',');
374
Jeff Dikef28169d2007-02-10 01:43:53 -0800375 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
Jeff Dikef28169d2007-02-10 01:43:53 -0800378 if(backing_file != NULL){
379 if(ubd_dev->no_cow){
380 *error_out = "Can't specify both 'd' and a cow file";
381 goto out;
382 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 else {
384 *backing_file = '\0';
385 backing_file++;
386 }
387 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800388 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800389 ubd_dev->file = str;
390 ubd_dev->cow.file = backing_file;
391 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800393 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800394 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395}
396
397static int ubd_setup(char *str)
398{
Jeff Dikef28169d2007-02-10 01:43:53 -0800399 char *error;
400 int err;
401
402 err = ubd_setup_common(str, NULL, &error);
403 if(err)
404 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
405 "%s\n", str, error);
406 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407}
408
409__setup("ubd", ubd_setup);
410__uml_help(ubd_setup,
411"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
412" This is used to associate a device with a file in the underlying\n"
413" filesystem. When specifying two filenames, the first one is the\n"
414" COW name and the second is the backing file name. As separator you can\n"
415" use either a ':' or a ',': the first one allows writing things like;\n"
416" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
417" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800418" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419" a COW file or a backing file. To override this detection, add the 'd'\n"
420" flag:\n"
421" ubd0d=BackingFile\n"
422" Usually, there is a filesystem in the file, but \n"
423" that's not required. Swap devices containing swap files can be\n"
424" specified like this. Also, a file which doesn't contain a\n"
425" filesystem can have its contents read in the virtual \n"
426" machine by running 'dd' on the device. <n> must be in the range\n"
427" 0 to 7. Appending an 'r' to the number will cause that device\n"
428" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
Jeff Dike20ede452008-02-04 22:30:37 -0800429" an 's' will cause data to be written to disk on the host immediately.\n"
430" 'c' will cause the device to be treated as being shared between multiple\n"
431" UMLs and file locking will be turned off - this is appropriate for a\n"
432" cluster filesystem and inappropriate at almost all other times.\n\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433);
434
Jeff Dike8299ca52008-02-04 22:30:48 -0800435static int udb_setup(char *str)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436{
437 printk("udb%s specified on command line is almost certainly a ubd -> "
438 "udb TYPO\n", str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700439 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440}
441
442__setup("udb", udb_setup);
443__uml_help(udb_setup,
444"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700445" This option is here solely to catch ubd -> udb typos, which can be\n"
446" to impossible to catch visually unless you specifically look for\n"
447" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448" in the boot output.\n\n"
449);
450
Jens Axboe165125e2007-07-24 09:28:11 +0200451static void do_ubd_request(struct request_queue * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400452
453/* Only changed by ubd_init, which is an initcall. */
WANG Cong5dc62b12008-04-28 02:13:58 -0700454static int thread_fd = -1;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700455static LIST_HEAD(restart);
456
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800457/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800458/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400459static void ubd_handler(void)
460{
Jeff Dike2adcec22007-05-06 14:51:37 -0700461 struct io_thread_req *req;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700462 struct ubd *ubd;
463 struct list_head *list, *next_ele;
464 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400465 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
Jeff Dikea0044bd2007-05-06 14:51:36 -0700467 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700468 n = os_read_file(thread_fd, &req,
469 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700470 if(n != sizeof(req)){
471 if(n == -EAGAIN)
472 break;
473 printk(KERN_ERR "spurious interrupt in ubd_handler, "
474 "err = %d\n", -n);
475 return;
476 }
477
Tejun Heo4d6c84d2009-04-28 13:06:09 +0900478 blk_end_request(req->req, 0, req->length);
Jeff Dike2adcec22007-05-06 14:51:37 -0700479 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400480 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800481 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700482
483 list_for_each_safe(list, next_ele, &restart){
484 ubd = container_of(list, struct ubd, restart);
485 list_del_init(&ubd->restart);
486 spin_lock_irqsave(&ubd->lock, flags);
487 do_ubd_request(ubd->queue);
488 spin_unlock_irqrestore(&ubd->lock, flags);
489 }
Jeff Dike91acb212005-10-10 23:10:32 -0400490}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
Al Viro7bea96f2006-10-08 22:49:34 +0100492static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493{
Jeff Dike91acb212005-10-10 23:10:32 -0400494 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700495 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496}
497
Jeff Dike91acb212005-10-10 23:10:32 -0400498/* Only changed by ubd_init, which is an initcall. */
499static int io_pid = -1;
500
WANG Cong5dc62b12008-04-28 02:13:58 -0700501static void kill_io_thread(void)
Jeff Dike91acb212005-10-10 23:10:32 -0400502{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800503 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400504 os_kill_process(io_pid, 1);
505}
506
507__uml_exitcall(kill_io_thread);
508
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800509static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510{
511 char *file;
512
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800513 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700514 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515}
516
WANG Cong5dc62b12008-04-28 02:13:58 -0700517static int read_cow_bitmap(int fd, void *buf, int offset, int len)
518{
519 int err;
520
521 err = os_seek_file(fd, offset);
522 if (err < 0)
523 return err;
524
525 err = os_read_file(fd, buf, len);
526 if (err < 0)
527 return err;
528
529 return 0;
530}
531
532static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
533{
534 unsigned long modtime;
535 unsigned long long actual;
536 int err;
537
538 err = os_file_modtime(file, &modtime);
539 if (err < 0) {
540 printk(KERN_ERR "Failed to get modification time of backing "
541 "file \"%s\", err = %d\n", file, -err);
542 return err;
543 }
544
545 err = os_file_size(file, &actual);
546 if (err < 0) {
547 printk(KERN_ERR "Failed to get size of backing file \"%s\", "
548 "err = %d\n", file, -err);
549 return err;
550 }
551
552 if (actual != size) {
553 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
554 * the typecast.*/
555 printk(KERN_ERR "Size mismatch (%llu vs %llu) of COW header "
556 "vs backing file\n", (unsigned long long) size, actual);
557 return -EINVAL;
558 }
559 if (modtime != mtime) {
560 printk(KERN_ERR "mtime mismatch (%ld vs %ld) of COW header vs "
561 "backing file\n", mtime, modtime);
562 return -EINVAL;
563 }
564 return 0;
565}
566
567static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
568{
569 struct uml_stat buf1, buf2;
570 int err;
571
572 if (from_cmdline == NULL)
573 return 0;
574 if (!strcmp(from_cmdline, from_cow))
575 return 0;
576
577 err = os_stat_file(from_cmdline, &buf1);
578 if (err < 0) {
579 printk(KERN_ERR "Couldn't stat '%s', err = %d\n", from_cmdline,
580 -err);
581 return 0;
582 }
583 err = os_stat_file(from_cow, &buf2);
584 if (err < 0) {
585 printk(KERN_ERR "Couldn't stat '%s', err = %d\n", from_cow,
586 -err);
587 return 1;
588 }
589 if ((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
590 return 0;
591
592 printk(KERN_ERR "Backing file mismatch - \"%s\" requested, "
593 "\"%s\" specified in COW header of \"%s\"\n",
594 from_cmdline, from_cow, cow);
595 return 1;
596}
597
598static int open_ubd_file(char *file, struct openflags *openflags, int shared,
599 char **backing_file_out, int *bitmap_offset_out,
600 unsigned long *bitmap_len_out, int *data_offset_out,
601 int *create_cow_out)
602{
603 time_t mtime;
604 unsigned long long size;
605 __u32 version, align;
606 char *backing_file;
607 int fd, err, sectorsize, asked_switch, mode = 0644;
608
609 fd = os_open_file(file, *openflags, mode);
610 if (fd < 0) {
611 if ((fd == -ENOENT) && (create_cow_out != NULL))
612 *create_cow_out = 1;
613 if (!openflags->w ||
614 ((fd != -EROFS) && (fd != -EACCES)))
615 return fd;
616 openflags->w = 0;
617 fd = os_open_file(file, *openflags, mode);
618 if (fd < 0)
619 return fd;
620 }
621
622 if (shared)
623 printk(KERN_INFO "Not locking \"%s\" on the host\n", file);
624 else {
625 err = os_lock_file(fd, openflags->w);
626 if (err < 0) {
627 printk(KERN_ERR "Failed to lock '%s', err = %d\n",
628 file, -err);
629 goto out_close;
630 }
631 }
632
633 /* Successful return case! */
634 if (backing_file_out == NULL)
635 return fd;
636
637 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
638 &size, &sectorsize, &align, bitmap_offset_out);
639 if (err && (*backing_file_out != NULL)) {
640 printk(KERN_ERR "Failed to read COW header from COW file "
641 "\"%s\", errno = %d\n", file, -err);
642 goto out_close;
643 }
644 if (err)
645 return fd;
646
647 asked_switch = path_requires_switch(*backing_file_out, backing_file,
648 file);
649
650 /* Allow switching only if no mismatch. */
651 if (asked_switch && !backing_file_mismatch(*backing_file_out, size,
652 mtime)) {
653 printk(KERN_ERR "Switching backing file to '%s'\n",
654 *backing_file_out);
655 err = write_cow_header(file, fd, *backing_file_out,
656 sectorsize, align, &size);
657 if (err) {
658 printk(KERN_ERR "Switch failed, errno = %d\n", -err);
659 goto out_close;
660 }
661 } else {
662 *backing_file_out = backing_file;
663 err = backing_file_mismatch(*backing_file_out, size, mtime);
664 if (err)
665 goto out_close;
666 }
667
668 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
669 bitmap_len_out, data_offset_out);
670
671 return fd;
672 out_close:
673 os_close_file(fd);
674 return err;
675}
676
677static int create_cow_file(char *cow_file, char *backing_file,
678 struct openflags flags,
679 int sectorsize, int alignment, int *bitmap_offset_out,
680 unsigned long *bitmap_len_out, int *data_offset_out)
681{
682 int err, fd;
683
684 flags.c = 1;
685 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
686 if (fd < 0) {
687 err = fd;
688 printk(KERN_ERR "Open of COW file '%s' failed, errno = %d\n",
689 cow_file, -err);
690 goto out;
691 }
692
693 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
694 bitmap_offset_out, bitmap_len_out,
695 data_offset_out);
696 if (!err)
697 return fd;
698 os_close_file(fd);
699 out:
700 return err;
701}
702
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800703static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800705 os_close_file(ubd_dev->fd);
706 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 return;
708
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800709 os_close_file(ubd_dev->cow.fd);
710 vfree(ubd_dev->cow.bitmap);
711 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712}
713
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800714static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715{
716 struct openflags flags;
717 char **back_ptr;
718 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800719 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800721 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800723 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
724 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800725
726 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800727 back_ptr, &ubd_dev->cow.bitmap_offset,
728 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800729 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800731 if((fd == -ENOENT) && create_cow){
732 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800733 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
734 &ubd_dev->cow.bitmap_offset,
735 &ubd_dev->cow.bitmap_len,
736 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800737 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800739 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 }
741 }
742
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800743 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800744 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800745 -fd);
746 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800748 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800750 if(ubd_dev->cow.file != NULL){
Martin K. Petersen086fa5f2010-02-26 00:20:38 -0500751 blk_queue_max_hw_sectors(ubd_dev->queue, 8 * sizeof(long));
Jeff Dikef4768ff2007-08-22 14:01:53 -0700752
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 err = -ENOMEM;
Jesper Juhlda2486b2007-10-16 01:27:19 -0700754 ubd_dev->cow.bitmap = vmalloc(ubd_dev->cow.bitmap_len);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800755 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
757 goto error;
758 }
759 flush_tlb_kernel_vm();
760
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800761 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
762 ubd_dev->cow.bitmap_offset,
763 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 if(err < 0)
765 goto error;
766
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800767 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800769 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800770 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800772 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700774 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800776 os_close_file(ubd_dev->fd);
Jeff Dikedc764e52007-05-06 14:51:41 -0700777 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778}
779
Jeff Dike2e3f5252007-05-06 14:51:29 -0700780static void ubd_device_release(struct device *dev)
781{
Greg Kroah-Hartman8691b972009-05-04 12:40:54 -0700782 struct ubd *ubd_dev = dev_get_drvdata(dev);
Jeff Dike2e3f5252007-05-06 14:51:29 -0700783
784 blk_cleanup_queue(ubd_dev->queue);
785 *ubd_dev = ((struct ubd) DEFAULT_UBD);
786}
787
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800788static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800789 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790{
791 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
793 disk = alloc_disk(1 << UBD_SHIFT);
794 if(disk == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -0700795 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
797 disk->major = major;
798 disk->first_minor = unit << UBD_SHIFT;
799 disk->fops = &ubd_blops;
800 set_capacity(disk, size / 512);
Christoph Hellwig792dd4f2009-03-31 15:23:39 -0700801 if (major == UBD_MAJOR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700803 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
806 /* sysfs register (not for ide fake devices) */
Christoph Hellwig792dd4f2009-03-31 15:23:39 -0700807 if (major == UBD_MAJOR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800808 ubd_devs[unit].pdev.id = unit;
809 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700810 ubd_devs[unit].pdev.dev.release = ubd_device_release;
Greg Kroah-Hartman8691b972009-05-04 12:40:54 -0700811 dev_set_drvdata(&ubd_devs[unit].pdev.dev, &ubd_devs[unit]);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800812 platform_device_register(&ubd_devs[unit].pdev);
813 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 }
815
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800816 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800817 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 add_disk(disk);
819
820 *disk_out = disk;
821 return 0;
822}
823
824#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
825
Jeff Dikef28169d2007-02-10 01:43:53 -0800826static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800828 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800829 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800831 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700832 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800834 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800835 if(err < 0){
836 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700837 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800838 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800840 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
Jeff Dikea0044bd2007-05-06 14:51:36 -0700842 INIT_LIST_HEAD(&ubd_dev->restart);
WANG Cong4f40c052007-11-05 14:50:59 -0800843 sg_init_table(ubd_dev->sg, MAX_SG);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700844
Jeff Dike62f96cb2007-02-10 01:44:16 -0800845 err = -ENOMEM;
846 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
847 if (ubd_dev->queue == NULL) {
848 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700849 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800850 }
851 ubd_dev->queue->queuedata = ubd_dev;
852
Martin K. Petersen8a783622010-02-26 00:20:39 -0500853 blk_queue_max_segments(ubd_dev->queue, MAX_SG);
Christoph Hellwig792dd4f2009-03-31 15:23:39 -0700854 err = ubd_disk_register(UBD_MAJOR, ubd_dev->size, n, &ubd_gendisk[n]);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800855 if(err){
856 *error_out = "Failed to register device";
857 goto out_cleanup;
858 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800859
Christoph Hellwig792dd4f2009-03-31 15:23:39 -0700860 if (fake_major != UBD_MAJOR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800861 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800862 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
Jeff Dike83380cc2008-02-04 22:31:18 -0800864 /*
865 * Perhaps this should also be under the "if (fake_major)" above
866 * using the fake_disk->disk_name
867 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 if (fake_ide)
869 make_ide_entries(ubd_gendisk[n]->disk_name);
870
Jeff Dikeec7cf782005-09-03 15:57:29 -0700871 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700872out:
873 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800874
875out_cleanup:
876 blk_cleanup_queue(ubd_dev->queue);
877 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878}
879
Jeff Dikef28169d2007-02-10 01:43:53 -0800880static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800882 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
Jeff Dikef28169d2007-02-10 01:43:53 -0800884 /* This string is possibly broken up and stored, so it's only
885 * freed if ubd_setup_common fails, or if only general options
886 * were set.
887 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800888 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800889 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800890 *error_out = "Failed to allocate memory";
891 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800893
894 ret = ubd_setup_common(str, &n, error_out);
895 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800896 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800897
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800898 if (n == -1) {
899 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800900 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800901 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
Jeff Dikedc764e52007-05-06 14:51:41 -0700903 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800904 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800905 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800906 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700907 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800909out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700910 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800911
912err_free:
913 kfree(str);
914 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915}
916
917static int ubd_get_config(char *name, char *str, int size, char **error_out)
918{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800919 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 int n, len = 0;
921
922 n = parse_unit(&name);
923 if((n >= MAX_DEV) || (n < 0)){
924 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700925 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 }
927
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800928 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800929 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800931 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 CONFIG_CHUNK(str, size, len, "", 1);
933 goto out;
934 }
935
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800936 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800938 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800940 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 }
942 else CONFIG_CHUNK(str, size, len, "", 1);
943
944 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800945 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700946 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947}
948
Jeff Dike29d56cf2005-06-25 14:55:25 -0700949static int ubd_id(char **str, int *start_out, int *end_out)
950{
Jeff Dikedc764e52007-05-06 14:51:41 -0700951 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700952
953 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700954 *start_out = 0;
955 *end_out = MAX_DEV - 1;
956 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700957}
958
Jeff Dikef28169d2007-02-10 01:43:53 -0800959static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700961 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800962 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700963 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800965 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800967 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700968
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800969 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700970 goto out;
971
972 /* you cannot remove a open disk */
973 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800974 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700975 goto out;
976
Jeff Dikedc764e52007-05-06 14:51:41 -0700977 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700978 if(disk != NULL){
979 del_gendisk(disk);
980 put_disk(disk);
981 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
983 if(fake_gendisk[n] != NULL){
984 del_gendisk(fake_gendisk[n]);
985 put_disk(fake_gendisk[n]);
986 fake_gendisk[n] = NULL;
987 }
988
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700990 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700991out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800992 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700993 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994}
995
Jeff Dikef28169d2007-02-10 01:43:53 -0800996/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800997 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800998 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -08001000 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 .name = "ubd",
1002 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -07001003 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -07001004 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 .remove = ubd_remove,
1006};
1007
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001008static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009{
1010 mconsole_register_dev(&ubd_mc);
1011 return 0;
1012}
1013
1014__initcall(ubd_mc_init);
1015
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001016static int __init ubd0_init(void)
1017{
1018 struct ubd *ubd_dev = &ubd_devs[0];
1019
Jeff Dikeb8831a12007-02-10 01:44:17 -08001020 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001021 if(ubd_dev->file == NULL)
1022 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -08001023 mutex_unlock(&ubd_lock);
1024
Jeff Dikedc764e52007-05-06 14:51:41 -07001025 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001026}
1027
1028__initcall(ubd0_init);
1029
Jeff Dikeb8831a12007-02-10 01:44:17 -08001030/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +00001031static struct platform_driver ubd_driver = {
1032 .driver = {
1033 .name = DRIVER_NAME,
1034 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035};
1036
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001037static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038{
Jeff Dikef28169d2007-02-10 01:43:53 -08001039 char *error;
1040 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041
Christoph Hellwig792dd4f2009-03-31 15:23:39 -07001042 if (register_blkdev(UBD_MAJOR, "ubd"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 return -1;
1044
Christoph Hellwig792dd4f2009-03-31 15:23:39 -07001045 if (fake_major != UBD_MAJOR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 char name[sizeof("ubd_nnn\0")];
1047
1048 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 if (register_blkdev(fake_major, "ubd"))
1050 return -1;
1051 }
Russell King3ae5eae2005-11-09 22:32:44 +00001052 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -07001053 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -08001054 for (i = 0; i < MAX_DEV; i++){
1055 err = ubd_add(i, &error);
1056 if(err)
1057 printk(KERN_ERR "Failed to initialize ubd device %d :"
1058 "%s\n", i, error);
1059 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001060 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 return 0;
1062}
1063
1064late_initcall(ubd_init);
1065
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001066static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -04001067 unsigned long stack;
1068 int err;
1069
1070 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
1071 if(global_openflags.s){
1072 printk(KERN_INFO "ubd: Synchronous mode\n");
1073 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
1074 * enough. So use anyway the io thread. */
1075 }
1076 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -08001077 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -04001078 &thread_fd);
1079 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -08001080 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -04001081 "ubd : Failed to start I/O thread (errno = %d) - "
1082 "falling back to synchronous I/O\n", -io_pid);
1083 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001084 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001085 }
Jeff Dike6c29256c2006-03-27 01:14:37 -08001086 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001087 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -04001088 if(err != 0)
1089 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -08001090 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001091}
1092
1093device_initcall(ubd_driver_init);
1094
Al Viroa625c992008-03-02 09:16:26 -05001095static int ubd_open(struct block_device *bdev, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096{
Al Viroa625c992008-03-02 09:16:26 -05001097 struct gendisk *disk = bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001098 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 int err = 0;
1100
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001101 if(ubd_dev->count == 0){
1102 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 if(err){
1104 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001105 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 goto out;
1107 }
1108 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001109 ubd_dev->count++;
1110 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -07001111
1112 /* This should no more be needed. And it didn't work anyway to exclude
1113 * read-write remounting of filesystems.*/
Al Viroa625c992008-03-02 09:16:26 -05001114 /*if((mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -08001115 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -07001117 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001119 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120}
1121
Al Viroa625c992008-03-02 09:16:26 -05001122static int ubd_release(struct gendisk *disk, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001124 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001126 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -08001127 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -07001128 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129}
1130
Jeff Dike91acb212005-10-10 23:10:32 -04001131static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
1132 __u64 *cow_offset, unsigned long *bitmap,
1133 __u64 bitmap_offset, unsigned long *bitmap_words,
1134 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135{
Jeff Dike91acb212005-10-10 23:10:32 -04001136 __u64 sector = io_offset >> 9;
1137 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138
Jeff Dike91acb212005-10-10 23:10:32 -04001139 for(i = 0; i < length >> 9; i++){
1140 if(cow_mask != NULL)
1141 ubd_set_bit(i, (unsigned char *) cow_mask);
1142 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1143 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144
Jeff Dike91acb212005-10-10 23:10:32 -04001145 update_bitmap = 1;
1146 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1147 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148
Jeff Dike91acb212005-10-10 23:10:32 -04001149 if(!update_bitmap)
1150 return;
1151
1152 *cow_offset = sector / (sizeof(unsigned long) * 8);
1153
1154 /* This takes care of the case where we're exactly at the end of the
1155 * device, and *cow_offset + 1 is off the end. So, just back it up
1156 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1157 * for the original diagnosis.
1158 */
Jiri Olsa6d074242008-05-12 14:01:56 -07001159 if (*cow_offset == (DIV_ROUND_UP(bitmap_len,
1160 sizeof(unsigned long)) - 1))
Jeff Dike91acb212005-10-10 23:10:32 -04001161 (*cow_offset)--;
1162
1163 bitmap_words[0] = bitmap[*cow_offset];
1164 bitmap_words[1] = bitmap[*cow_offset + 1];
1165
1166 *cow_offset *= sizeof(unsigned long);
1167 *cow_offset += bitmap_offset;
1168}
1169
1170static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1171 __u64 bitmap_offset, __u64 bitmap_len)
1172{
1173 __u64 sector = req->offset >> 9;
1174 int i;
1175
1176 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1177 panic("Operation too long");
1178
1179 if(req->op == UBD_READ) {
1180 for(i = 0; i < req->length >> 9; i++){
1181 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001182 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001183 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001184 }
Jeff Dike91acb212005-10-10 23:10:32 -04001185 }
1186 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1187 &req->cow_offset, bitmap, bitmap_offset,
1188 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189}
1190
Jeff Dike62f96cb2007-02-10 01:44:16 -08001191/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001192static void prepare_request(struct request *req, struct io_thread_req *io_req,
1193 unsigned long long offset, int page_offset,
1194 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195{
1196 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001197 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001198
Jeff Dike62f96cb2007-02-10 01:44:16 -08001199 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001200 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1201 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001202 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001203 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 io_req->offset = offset;
1205 io_req->length = len;
1206 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001207 io_req->sector_mask = 0;
1208
1209 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001211 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001212 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 io_req->sectorsize = 1 << 9;
1214
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001215 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001216 cowify_req(io_req, ubd_dev->cow.bitmap,
1217 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001218
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219}
1220
Jeff Dike62f96cb2007-02-10 01:44:16 -08001221/* Called with dev->lock held */
Jens Axboe165125e2007-07-24 09:28:11 +02001222static void do_ubd_request(struct request_queue *q)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223{
Jeff Dike2adcec22007-05-06 14:51:37 -07001224 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 struct request *req;
Tejun Heof81f2f72009-04-28 13:06:10 +09001226 sector_t sector;
1227 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228
Jeff Dikea0044bd2007-05-06 14:51:36 -07001229 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001230 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001231 if(dev->end_sg == 0){
Tejun Heo9934c8c2009-05-08 11:54:16 +09001232 struct request *req = blk_fetch_request(q);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001233 if(req == NULL)
1234 return;
1235
1236 dev->request = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001237 dev->start_sg = 0;
1238 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001239 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001240
1241 req = dev->request;
Tejun Heo83096eb2009-05-07 22:24:39 +09001242 sector = blk_rq_pos(req);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001243 while(dev->start_sg < dev->end_sg){
1244 struct scatterlist *sg = &dev->sg[dev->start_sg];
1245
Jeff Dike2adcec22007-05-06 14:51:37 -07001246 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001247 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001248 if(io_req == NULL){
1249 if(list_empty(&dev->restart))
1250 list_add(&dev->restart, &restart);
1251 return;
1252 }
1253 prepare_request(req, io_req,
Tejun Heof81f2f72009-04-28 13:06:10 +09001254 (unsigned long long)sector << 9,
Jens Axboe45711f12007-10-22 21:19:53 +02001255 sg->offset, sg->length, sg_page(sg));
Jeff Dikea0044bd2007-05-06 14:51:36 -07001256
Tejun Heof81f2f72009-04-28 13:06:10 +09001257 sector += sg->length >> 9;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001258 n = os_write_file(thread_fd, &io_req,
1259 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001260 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001261 if(n != -EAGAIN)
1262 printk("write to io thread failed, "
1263 "errno = %d\n", -n);
1264 else if(list_empty(&dev->restart))
1265 list_add(&dev->restart, &restart);
Miklos Szeredi12429bf2007-11-28 16:21:52 -08001266 kfree(io_req);
Jeff Dikea0044bd2007-05-06 14:51:36 -07001267 return;
1268 }
1269
Jeff Dikea0044bd2007-05-06 14:51:36 -07001270 dev->start_sg++;
1271 }
1272 dev->end_sg = 0;
1273 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001274 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275}
1276
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001277static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1278{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001279 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001280
1281 geo->heads = 128;
1282 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001283 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001284 return 0;
1285}
1286
Al Viroa625c992008-03-02 09:16:26 -05001287static int ubd_ioctl(struct block_device *bdev, fmode_t mode,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 unsigned int cmd, unsigned long arg)
1289{
Al Viroa625c992008-03-02 09:16:26 -05001290 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Bartlomiej Zolnierkiewicz73855e12009-04-01 21:42:21 +02001291 u16 ubd_id[ATA_ID_WORDS];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292
1293 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 case HDIO_GET_IDENTITY:
Bartlomiej Zolnierkiewicz73855e12009-04-01 21:42:21 +02001296 memset(&ubd_id, 0, ATA_ID_WORDS * 2);
1297 ubd_id[ATA_ID_CYLS] = ubd_dev->size / (128 * 32 * 512);
1298 ubd_id[ATA_ID_HEADS] = 128;
1299 ubd_id[ATA_ID_SECTORS] = 32;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1301 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001302 return -EFAULT;
1303 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001304
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 case CDROMVOLREAD:
1306 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001307 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 volume.channel0 = 255;
1309 volume.channel1 = 255;
1310 volume.channel2 = 255;
1311 volume.channel3 = 255;
1312 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001313 return -EFAULT;
1314 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001316 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317}
1318
Jeff Dike91acb212005-10-10 23:10:32 -04001319static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320{
Jeff Dike91acb212005-10-10 23:10:32 -04001321 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
Jeff Dike91acb212005-10-10 23:10:32 -04001323 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001324 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325
Jeff Dike91acb212005-10-10 23:10:32 -04001326 n = os_seek_file(req->fds[1], req->cow_offset);
1327 if(n < 0){
1328 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001329 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001330 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001332 n = os_write_file(req->fds[1], &req->bitmap_words,
1333 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001334 if(n != sizeof(req->bitmap_words)){
1335 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1336 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001337 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001338 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
Jeff Dikedc764e52007-05-06 14:51:41 -07001340 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341}
Jeff Dike91acb212005-10-10 23:10:32 -04001342
WANG Cong5dc62b12008-04-28 02:13:58 -07001343static void do_io(struct io_thread_req *req)
Jeff Dike91acb212005-10-10 23:10:32 -04001344{
1345 char *buf;
1346 unsigned long len;
1347 int n, nsectors, start, end, bit;
1348 int err;
1349 __u64 off;
1350
1351 nsectors = req->length / req->sectorsize;
1352 start = 0;
1353 do {
1354 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1355 end = start;
1356 while((end < nsectors) &&
1357 (ubd_test_bit(end, (unsigned char *)
1358 &req->sector_mask) == bit))
1359 end++;
1360
1361 off = req->offset + req->offsets[bit] +
1362 start * req->sectorsize;
1363 len = (end - start) * req->sectorsize;
1364 buf = &req->buffer[start * req->sectorsize];
1365
1366 err = os_seek_file(req->fds[bit], off);
1367 if(err < 0){
1368 printk("do_io - lseek failed : err = %d\n", -err);
1369 req->error = 1;
1370 return;
1371 }
1372 if(req->op == UBD_READ){
1373 n = 0;
1374 do {
1375 buf = &buf[n];
1376 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001377 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001378 if (n < 0) {
1379 printk("do_io - read failed, err = %d "
1380 "fd = %d\n", -n, req->fds[bit]);
1381 req->error = 1;
1382 return;
1383 }
1384 } while((n < len) && (n != 0));
1385 if (n < len) memset(&buf[n], 0, len - n);
1386 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001387 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001388 if(n != len){
1389 printk("do_io - write failed err = %d "
1390 "fd = %d\n", -n, req->fds[bit]);
1391 req->error = 1;
1392 return;
1393 }
1394 }
1395
1396 start = end;
1397 } while(start < nsectors);
1398
1399 req->error = update_bitmap(req);
1400}
1401
1402/* Changed in start_io_thread, which is serialized by being called only
1403 * from ubd_init, which is an initcall.
1404 */
1405int kernel_fd = -1;
1406
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001407/* Only changed by the io thread. XXX: currently unused. */
1408static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001409
1410int io_thread(void *arg)
1411{
Jeff Dike2adcec22007-05-06 14:51:37 -07001412 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001413 int n;
1414
1415 ignore_sigwinch_sig();
1416 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001417 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001418 sizeof(struct io_thread_req *));
1419 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001420 if(n < 0)
1421 printk("io_thread - read failed, fd = %d, "
1422 "err = %d\n", kernel_fd, -n);
1423 else {
1424 printk("io_thread - short read, fd = %d, "
1425 "length = %d\n", kernel_fd, n);
1426 }
1427 continue;
1428 }
1429 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001430 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001431 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001432 sizeof(struct io_thread_req *));
1433 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001434 printk("io_thread - write failed, fd = %d, err = %d\n",
1435 kernel_fd, -n);
1436 }
Jeff Dike91acb212005-10-10 23:10:32 -04001437
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001438 return 0;
1439}