blob: 70509ddaac035cd60537e286883614323410be6d [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include "linux/module.h"
24#include "linux/blkdev.h"
25#include "linux/hdreg.h"
26#include "linux/init.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include "linux/cdrom.h"
28#include "linux/proc_fs.h"
29#include "linux/ctype.h"
30#include "linux/capability.h"
31#include "linux/mm.h"
32#include "linux/vmalloc.h"
33#include "linux/blkpg.h"
34#include "linux/genhd.h"
35#include "linux/spinlock.h"
Russell Kingd052d1b2005-10-29 19:07:23 +010036#include "linux/platform_device.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include "asm/segment.h"
38#include "asm/uaccess.h"
39#include "asm/irq.h"
40#include "asm/types.h"
41#include "asm/tlbflush.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include "mem_user.h"
43#include "kern_util.h"
44#include "kern.h"
45#include "mconsole_kern.h"
46#include "init.h"
47#include "irq_user.h"
48#include "irq_kern.h"
49#include "ubd_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070050#include "os.h"
51#include "mem.h"
52#include "mem_kern.h"
53#include "cow.h"
54
Jeff Dike7b9014c2005-05-20 13:59:11 -070055enum ubd_req { UBD_READ, UBD_WRITE };
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
57struct io_thread_req {
Jeff Dike62f96cb2007-02-10 01:44:16 -080058 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -040059 enum ubd_req op;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 int fds[2];
61 unsigned long offsets[2];
62 unsigned long long offset;
63 unsigned long length;
64 char *buffer;
65 int sectorsize;
Jeff Dike91acb212005-10-10 23:10:32 -040066 unsigned long sector_mask;
67 unsigned long long cow_offset;
68 unsigned long bitmap_words[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 int error;
70};
71
Jeff Dike6c29256c2006-03-27 01:14:37 -080072extern int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 char **backing_file_out, int *bitmap_offset_out,
74 unsigned long *bitmap_len_out, int *data_offset_out,
75 int *create_cow_out);
76extern int create_cow_file(char *cow_file, char *backing_file,
77 struct openflags flags, int sectorsize,
78 int alignment, int *bitmap_offset_out,
79 unsigned long *bitmap_len_out,
80 int *data_offset_out);
81extern int read_cow_bitmap(int fd, void *buf, int offset, int len);
Jeff Dike91acb212005-10-10 23:10:32 -040082extern void do_io(struct io_thread_req *req);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
Jeff Dike91acb212005-10-10 23:10:32 -040084static inline int ubd_test_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085{
86 __u64 n;
87 int bits, off;
88
Jeff Dike91acb212005-10-10 23:10:32 -040089 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 n = bit / bits;
91 off = bit % bits;
Jeff Dikedc764e52007-05-06 14:51:41 -070092 return (data[n] & (1 << off)) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093}
94
Jeff Dike91acb212005-10-10 23:10:32 -040095static inline void ubd_set_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070096{
97 __u64 n;
98 int bits, off;
99
Jeff Dike91acb212005-10-10 23:10:32 -0400100 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 n = bit / bits;
102 off = bit % bits;
Jeff Dike91acb212005-10-10 23:10:32 -0400103 data[n] |= (1 << off);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104}
105/*End stuff from ubd_user.h*/
106
107#define DRIVER_NAME "uml-blkdev"
108
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800109static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111static int ubd_open(struct inode * inode, struct file * filp);
112static int ubd_release(struct inode * inode, struct file * file);
113static int ubd_ioctl(struct inode * inode, struct file * file,
114 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800115static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800117#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119static struct block_device_operations ubd_blops = {
120 .owner = THIS_MODULE,
121 .open = ubd_open,
122 .release = ubd_release,
123 .ioctl = ubd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800124 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125};
126
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127/* Protected by ubd_lock */
128static int fake_major = MAJOR_NR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129static struct gendisk *ubd_gendisk[MAX_DEV];
130static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800131
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132#ifdef CONFIG_BLK_DEV_UBD_SYNC
133#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
134 .cl = 1 })
135#else
136#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
137 .cl = 1 })
138#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139static struct openflags global_openflags = OPEN_FLAGS;
140
141struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800142 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800144 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 int fd;
146 unsigned long *bitmap;
147 unsigned long bitmap_len;
148 int bitmap_offset;
Jeff Dikedc764e52007-05-06 14:51:41 -0700149 int data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150};
151
Jeff Dikea0044bd2007-05-06 14:51:36 -0700152#define MAX_SG 64
153
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154struct ubd {
Jeff Dikea0044bd2007-05-06 14:51:36 -0700155 struct list_head restart;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800156 /* name (and fd, below) of the file opened for writing, either the
157 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 char *file;
159 int count;
160 int fd;
161 __u64 size;
162 struct openflags boot_openflags;
163 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800164 unsigned shared:1;
165 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 struct cow cow;
167 struct platform_device pdev;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800168 struct request_queue *queue;
169 spinlock_t lock;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700170 struct scatterlist sg[MAX_SG];
171 struct request *request;
172 int start_sg, end_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173};
174
175#define DEFAULT_COW { \
176 .file = NULL, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700177 .fd = -1, \
178 .bitmap = NULL, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 .bitmap_offset = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700180 .data_offset = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181}
182
183#define DEFAULT_UBD { \
184 .file = NULL, \
185 .count = 0, \
186 .fd = -1, \
187 .size = -1, \
188 .boot_openflags = OPEN_FLAGS, \
189 .openflags = OPEN_FLAGS, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700190 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800191 .shared = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700192 .cow = DEFAULT_COW, \
Jeff Dike62f96cb2007-02-10 01:44:16 -0800193 .lock = SPIN_LOCK_UNLOCKED, \
Jeff Dikea0044bd2007-05-06 14:51:36 -0700194 .request = NULL, \
195 .start_sg = 0, \
196 .end_sg = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197}
198
Jeff Dikeb8831a12007-02-10 01:44:17 -0800199/* Protected by ubd_lock */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800200struct ubd ubd_devs[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202/* Only changed by fake_ide_setup which is a setup */
203static int fake_ide = 0;
204static struct proc_dir_entry *proc_ide_root = NULL;
205static struct proc_dir_entry *proc_ide = NULL;
206
207static void make_proc_ide(void)
208{
209 proc_ide_root = proc_mkdir("ide", NULL);
210 proc_ide = proc_mkdir("ide0", proc_ide_root);
211}
212
213static int proc_ide_read_media(char *page, char **start, off_t off, int count,
214 int *eof, void *data)
215{
216 int len;
217
218 strcpy(page, "disk\n");
219 len = strlen("disk\n");
220 len -= off;
221 if (len < count){
222 *eof = 1;
223 if (len <= 0) return 0;
224 }
225 else len = count;
226 *start = page + off;
227 return len;
228}
229
230static void make_ide_entries(char *dev_name)
231{
232 struct proc_dir_entry *dir, *ent;
233 char name[64];
234
235 if(proc_ide_root == NULL) make_proc_ide();
236
237 dir = proc_mkdir(dev_name, proc_ide);
238 if(!dir) return;
239
240 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
241 if(!ent) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 ent->data = NULL;
243 ent->read_proc = proc_ide_read_media;
244 ent->write_proc = NULL;
245 sprintf(name,"ide0/%s", dev_name);
246 proc_symlink(dev_name, proc_ide_root, name);
247}
248
249static int fake_ide_setup(char *str)
250{
251 fake_ide = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700252 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253}
254
255__setup("fake_ide", fake_ide_setup);
256
257__uml_help(fake_ide_setup,
258"fake_ide\n"
259" Create ide0 entries that map onto ubd devices.\n\n"
260);
261
262static int parse_unit(char **ptr)
263{
264 char *str = *ptr, *end;
265 int n = -1;
266
267 if(isdigit(*str)) {
268 n = simple_strtoul(str, &end, 0);
269 if(end == str)
Jeff Dikedc764e52007-05-06 14:51:41 -0700270 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 *ptr = end;
272 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800273 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 n = *str - 'a';
275 str++;
276 *ptr = str;
277 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700278 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279}
280
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800281/* If *index_out == -1 at exit, the passed option was a general one;
282 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
283 * should not be freed on exit.
284 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800285static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800287 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 struct openflags flags = global_openflags;
289 char *backing_file;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800290 int n, err = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
292 if(index_out) *index_out = -1;
293 n = *str;
294 if(n == '='){
295 char *end;
296 int major;
297
298 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 if(!strcmp(str, "sync")){
300 global_openflags = of_sync(global_openflags);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800301 goto out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 }
303
Jeff Dikef28169d2007-02-10 01:43:53 -0800304 err = -EINVAL;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800305 major = simple_strtoul(str, &end, 0);
306 if((*end != '\0') || (end == str)){
307 *error_out = "Didn't parse major number";
308 goto out1;
309 }
310
Jeff Dikef28169d2007-02-10 01:43:53 -0800311 mutex_lock(&ubd_lock);
312 if(fake_major != MAJOR_NR){
313 *error_out = "Can't assign a fake major twice";
314 goto out1;
315 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800316
Jeff Dikef28169d2007-02-10 01:43:53 -0800317 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319 printk(KERN_INFO "Setting extra ubd major number to %d\n",
320 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800321 err = 0;
322 out1:
323 mutex_unlock(&ubd_lock);
324 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 }
326
327 n = parse_unit(&str);
328 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800329 *error_out = "Couldn't parse device number";
330 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 }
332 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800333 *error_out = "Device number out of range";
334 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 }
336
Jeff Dikef28169d2007-02-10 01:43:53 -0800337 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800338 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800340 ubd_dev = &ubd_devs[n];
341 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800342 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 goto out;
344 }
345
346 if (index_out)
347 *index_out = n;
348
Jeff Dikef28169d2007-02-10 01:43:53 -0800349 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800350 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 switch (*str) {
352 case 'r':
353 flags.w = 0;
354 break;
355 case 's':
356 flags.s = 1;
357 break;
358 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800359 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800361 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800362 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800363 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 case '=':
365 str++;
366 goto break_loop;
367 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800368 *error_out = "Expected '=' or flag letter "
369 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 goto out;
371 }
372 str++;
373 }
374
Jeff Dikef28169d2007-02-10 01:43:53 -0800375 if (*str == '=')
376 *error_out = "Too many flags specified";
377 else
378 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 goto out;
380
381break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 backing_file = strchr(str, ',');
383
Jeff Dikef28169d2007-02-10 01:43:53 -0800384 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
Jeff Dikef28169d2007-02-10 01:43:53 -0800387 if(backing_file != NULL){
388 if(ubd_dev->no_cow){
389 *error_out = "Can't specify both 'd' and a cow file";
390 goto out;
391 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 else {
393 *backing_file = '\0';
394 backing_file++;
395 }
396 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800397 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800398 ubd_dev->file = str;
399 ubd_dev->cow.file = backing_file;
400 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800402 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800403 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404}
405
406static int ubd_setup(char *str)
407{
Jeff Dikef28169d2007-02-10 01:43:53 -0800408 char *error;
409 int err;
410
411 err = ubd_setup_common(str, NULL, &error);
412 if(err)
413 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
414 "%s\n", str, error);
415 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416}
417
418__setup("ubd", ubd_setup);
419__uml_help(ubd_setup,
420"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
421" This is used to associate a device with a file in the underlying\n"
422" filesystem. When specifying two filenames, the first one is the\n"
423" COW name and the second is the backing file name. As separator you can\n"
424" use either a ':' or a ',': the first one allows writing things like;\n"
425" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
426" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800427" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428" a COW file or a backing file. To override this detection, add the 'd'\n"
429" flag:\n"
430" ubd0d=BackingFile\n"
431" Usually, there is a filesystem in the file, but \n"
432" that's not required. Swap devices containing swap files can be\n"
433" specified like this. Also, a file which doesn't contain a\n"
434" filesystem can have its contents read in the virtual \n"
435" machine by running 'dd' on the device. <n> must be in the range\n"
436" 0 to 7. Appending an 'r' to the number will cause that device\n"
437" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
438" an 's' will cause data to be written to disk on the host immediately.\n\n"
439);
440
441static int udb_setup(char *str)
442{
443 printk("udb%s specified on command line is almost certainly a ubd -> "
444 "udb TYPO\n", str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700445 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446}
447
448__setup("udb", udb_setup);
449__uml_help(udb_setup,
450"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700451" This option is here solely to catch ubd -> udb typos, which can be\n"
452" to impossible to catch visually unless you specifically look for\n"
453" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454" in the boot output.\n\n"
455);
456
457static int fakehd_set = 0;
458static int fakehd(char *str)
459{
460 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
461 fakehd_set = 1;
462 return 1;
463}
464
465__setup("fakehd", fakehd);
466__uml_help(fakehd,
467"fakehd\n"
468" Change the ubd device name to \"hd\".\n\n"
469);
470
471static void do_ubd_request(request_queue_t * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400472
473/* Only changed by ubd_init, which is an initcall. */
474int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475
Jeff Dikea0044bd2007-05-06 14:51:36 -0700476static void ubd_end_request(struct request *req, int bytes, int uptodate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700478 if (!end_that_request_first(req, uptodate, bytes >> 9)) {
479 struct ubd *dev = req->rq_disk->private_data;
480 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481
Jeff Dikea0044bd2007-05-06 14:51:36 -0700482 add_disk_randomness(req->rq_disk);
483 spin_lock_irqsave(&dev->lock, flags);
484 end_that_request_last(req, uptodate);
485 spin_unlock_irqrestore(&dev->lock, flags);
Jeff Dike91acb212005-10-10 23:10:32 -0400486 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487}
488
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800489/* Callable only from interrupt context - otherwise you need to do
490 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700491static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700493 if(bytes < 0){
494 ubd_end_request(req, 0, 0);
495 return;
496 }
497 ubd_end_request(req, bytes, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498}
499
Jeff Dikea0044bd2007-05-06 14:51:36 -0700500static LIST_HEAD(restart);
501
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800502/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800503/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400504static void ubd_handler(void)
505{
Jeff Dike2adcec22007-05-06 14:51:37 -0700506 struct io_thread_req *req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800507 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700508 struct ubd *ubd;
509 struct list_head *list, *next_ele;
510 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400511 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
Jeff Dikea0044bd2007-05-06 14:51:36 -0700513 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700514 n = os_read_file(thread_fd, &req,
515 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700516 if(n != sizeof(req)){
517 if(n == -EAGAIN)
518 break;
519 printk(KERN_ERR "spurious interrupt in ubd_handler, "
520 "err = %d\n", -n);
521 return;
522 }
523
Jeff Dike2adcec22007-05-06 14:51:37 -0700524 rq = req->req;
525 rq->nr_sectors -= req->length >> 9;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700526 if(rq->nr_sectors == 0)
527 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike2adcec22007-05-06 14:51:37 -0700528 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400529 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800530 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700531
532 list_for_each_safe(list, next_ele, &restart){
533 ubd = container_of(list, struct ubd, restart);
534 list_del_init(&ubd->restart);
535 spin_lock_irqsave(&ubd->lock, flags);
536 do_ubd_request(ubd->queue);
537 spin_unlock_irqrestore(&ubd->lock, flags);
538 }
Jeff Dike91acb212005-10-10 23:10:32 -0400539}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
Al Viro7bea96f2006-10-08 22:49:34 +0100541static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542{
Jeff Dike91acb212005-10-10 23:10:32 -0400543 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700544 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545}
546
Jeff Dike91acb212005-10-10 23:10:32 -0400547/* Only changed by ubd_init, which is an initcall. */
548static int io_pid = -1;
549
550void kill_io_thread(void)
551{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800552 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400553 os_kill_process(io_pid, 1);
554}
555
556__uml_exitcall(kill_io_thread);
557
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800558static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559{
560 char *file;
561
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800562 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700563 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564}
565
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800566static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800568 os_close_file(ubd_dev->fd);
569 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 return;
571
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800572 os_close_file(ubd_dev->cow.fd);
573 vfree(ubd_dev->cow.bitmap);
574 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575}
576
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800577static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578{
579 struct openflags flags;
580 char **back_ptr;
581 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800582 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800584 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800586 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
587 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800588
589 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800590 back_ptr, &ubd_dev->cow.bitmap_offset,
591 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800592 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800594 if((fd == -ENOENT) && create_cow){
595 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800596 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
597 &ubd_dev->cow.bitmap_offset,
598 &ubd_dev->cow.bitmap_len,
599 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800600 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800602 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 }
604 }
605
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800606 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800607 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800608 -fd);
609 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800611 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800613 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 err = -ENOMEM;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800615 ubd_dev->cow.bitmap = (void *) vmalloc(ubd_dev->cow.bitmap_len);
616 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);
704
Jeff Dike62f96cb2007-02-10 01:44:16 -0800705 err = -ENOMEM;
706 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
707 if (ubd_dev->queue == NULL) {
708 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700709 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800710 }
711 ubd_dev->queue->queuedata = ubd_dev;
712
Jeff Dikea0044bd2007-05-06 14:51:36 -0700713 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800714 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
715 if(err){
716 *error_out = "Failed to register device";
717 goto out_cleanup;
718 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800719
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800721 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800722 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 /* perhaps this should also be under the "if (fake_major)" above */
725 /* using the fake_disk->disk_name and also the fakehd_set name */
726 if (fake_ide)
727 make_ide_entries(ubd_gendisk[n]->disk_name);
728
Jeff Dikeec7cf782005-09-03 15:57:29 -0700729 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700730out:
731 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800732
733out_cleanup:
734 blk_cleanup_queue(ubd_dev->queue);
735 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736}
737
Jeff Dikef28169d2007-02-10 01:43:53 -0800738static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800740 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Jeff Dikef28169d2007-02-10 01:43:53 -0800742 /* This string is possibly broken up and stored, so it's only
743 * freed if ubd_setup_common fails, or if only general options
744 * were set.
745 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800746 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800747 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800748 *error_out = "Failed to allocate memory";
749 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800751
752 ret = ubd_setup_common(str, &n, error_out);
753 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800754 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800755
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800756 if (n == -1) {
757 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800758 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800759 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Jeff Dikedc764e52007-05-06 14:51:41 -0700761 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800762 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800763 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800764 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700765 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800767out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700768 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800769
770err_free:
771 kfree(str);
772 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773}
774
775static int ubd_get_config(char *name, char *str, int size, char **error_out)
776{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800777 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 int n, len = 0;
779
780 n = parse_unit(&name);
781 if((n >= MAX_DEV) || (n < 0)){
782 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700783 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 }
785
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800786 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800787 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800789 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 CONFIG_CHUNK(str, size, len, "", 1);
791 goto out;
792 }
793
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800794 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800796 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800798 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 }
800 else CONFIG_CHUNK(str, size, len, "", 1);
801
802 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800803 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700804 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805}
806
Jeff Dike29d56cf2005-06-25 14:55:25 -0700807static int ubd_id(char **str, int *start_out, int *end_out)
808{
Jeff Dikedc764e52007-05-06 14:51:41 -0700809 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700810
811 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700812 *start_out = 0;
813 *end_out = MAX_DEV - 1;
814 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700815}
816
Jeff Dikef28169d2007-02-10 01:43:53 -0800817static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700819 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800820 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700821 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800823 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800825 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700826
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800827 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700828 goto out;
829
830 /* you cannot remove a open disk */
831 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800832 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700833 goto out;
834
Jeff Dikedc764e52007-05-06 14:51:41 -0700835 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700836 if(disk != NULL){
837 del_gendisk(disk);
838 put_disk(disk);
839 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
841 if(fake_gendisk[n] != NULL){
842 del_gendisk(fake_gendisk[n]);
843 put_disk(fake_gendisk[n]);
844 fake_gendisk[n] = NULL;
845 }
846
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700848 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700849out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800850 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700851 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852}
853
Jeff Dikef28169d2007-02-10 01:43:53 -0800854/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800855 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800856 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800858 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 .name = "ubd",
860 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -0700861 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700862 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 .remove = ubd_remove,
864};
865
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800866static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867{
868 mconsole_register_dev(&ubd_mc);
869 return 0;
870}
871
872__initcall(ubd_mc_init);
873
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800874static int __init ubd0_init(void)
875{
876 struct ubd *ubd_dev = &ubd_devs[0];
877
Jeff Dikeb8831a12007-02-10 01:44:17 -0800878 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800879 if(ubd_dev->file == NULL)
880 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -0800881 mutex_unlock(&ubd_lock);
882
Jeff Dikedc764e52007-05-06 14:51:41 -0700883 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800884}
885
886__initcall(ubd0_init);
887
Jeff Dikeb8831a12007-02-10 01:44:17 -0800888/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +0000889static struct platform_driver ubd_driver = {
890 .driver = {
891 .name = DRIVER_NAME,
892 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893};
894
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800895static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896{
Jeff Dikef28169d2007-02-10 01:43:53 -0800897 char *error;
898 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 if (register_blkdev(MAJOR_NR, "ubd"))
901 return -1;
902
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 if (fake_major != MAJOR_NR) {
904 char name[sizeof("ubd_nnn\0")];
905
906 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 if (register_blkdev(fake_major, "ubd"))
908 return -1;
909 }
Russell King3ae5eae2005-11-09 22:32:44 +0000910 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -0700911 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800912 for (i = 0; i < MAX_DEV; i++){
913 err = ubd_add(i, &error);
914 if(err)
915 printk(KERN_ERR "Failed to initialize ubd device %d :"
916 "%s\n", i, error);
917 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700918 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 return 0;
920}
921
922late_initcall(ubd_init);
923
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800924static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400925 unsigned long stack;
926 int err;
927
928 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
929 if(global_openflags.s){
930 printk(KERN_INFO "ubd: Synchronous mode\n");
931 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
932 * enough. So use anyway the io thread. */
933 }
934 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800935 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400936 &thread_fd);
937 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800938 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400939 "ubd : Failed to start I/O thread (errno = %d) - "
940 "falling back to synchronous I/O\n", -io_pid);
941 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700942 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400943 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800944 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800945 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400946 if(err != 0)
947 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800948 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400949}
950
951device_initcall(ubd_driver_init);
952
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953static int ubd_open(struct inode *inode, struct file *filp)
954{
955 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800956 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 int err = 0;
958
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800959 if(ubd_dev->count == 0){
960 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 if(err){
962 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800963 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 goto out;
965 }
966 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800967 ubd_dev->count++;
968 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700969
970 /* This should no more be needed. And it didn't work anyway to exclude
971 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800972 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800973 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700975 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700977 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978}
979
980static int ubd_release(struct inode * inode, struct file * file)
981{
982 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800983 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800985 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800986 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -0700987 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988}
989
Jeff Dike91acb212005-10-10 23:10:32 -0400990static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
991 __u64 *cow_offset, unsigned long *bitmap,
992 __u64 bitmap_offset, unsigned long *bitmap_words,
993 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994{
Jeff Dike91acb212005-10-10 23:10:32 -0400995 __u64 sector = io_offset >> 9;
996 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
Jeff Dike91acb212005-10-10 23:10:32 -0400998 for(i = 0; i < length >> 9; i++){
999 if(cow_mask != NULL)
1000 ubd_set_bit(i, (unsigned char *) cow_mask);
1001 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1002 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
Jeff Dike91acb212005-10-10 23:10:32 -04001004 update_bitmap = 1;
1005 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1006 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
Jeff Dike91acb212005-10-10 23:10:32 -04001008 if(!update_bitmap)
1009 return;
1010
1011 *cow_offset = sector / (sizeof(unsigned long) * 8);
1012
1013 /* This takes care of the case where we're exactly at the end of the
1014 * device, and *cow_offset + 1 is off the end. So, just back it up
1015 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1016 * for the original diagnosis.
1017 */
1018 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1019 sizeof(unsigned long) - 1))
1020 (*cow_offset)--;
1021
1022 bitmap_words[0] = bitmap[*cow_offset];
1023 bitmap_words[1] = bitmap[*cow_offset + 1];
1024
1025 *cow_offset *= sizeof(unsigned long);
1026 *cow_offset += bitmap_offset;
1027}
1028
1029static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1030 __u64 bitmap_offset, __u64 bitmap_len)
1031{
1032 __u64 sector = req->offset >> 9;
1033 int i;
1034
1035 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1036 panic("Operation too long");
1037
1038 if(req->op == UBD_READ) {
1039 for(i = 0; i < req->length >> 9; i++){
1040 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001041 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001042 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001043 }
Jeff Dike91acb212005-10-10 23:10:32 -04001044 }
1045 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1046 &req->cow_offset, bitmap, bitmap_offset,
1047 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048}
1049
Jeff Dike62f96cb2007-02-10 01:44:16 -08001050/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001051static void prepare_request(struct request *req, struct io_thread_req *io_req,
1052 unsigned long long offset, int page_offset,
1053 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054{
1055 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001056 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001057
Jeff Dike62f96cb2007-02-10 01:44:16 -08001058 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001059 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1060 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001061 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001062 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 io_req->offset = offset;
1064 io_req->length = len;
1065 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001066 io_req->sector_mask = 0;
1067
1068 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001070 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001071 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 io_req->sectorsize = 1 << 9;
1073
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001074 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001075 cowify_req(io_req, ubd_dev->cow.bitmap,
1076 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078}
1079
Jeff Dike62f96cb2007-02-10 01:44:16 -08001080/* Called with dev->lock held */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081static void do_ubd_request(request_queue_t *q)
1082{
Jeff Dike2adcec22007-05-06 14:51:37 -07001083 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 struct request *req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001085 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086
Jeff Dikea0044bd2007-05-06 14:51:36 -07001087 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001088 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001089 if(dev->end_sg == 0){
1090 struct request *req = elv_next_request(q);
1091 if(req == NULL)
1092 return;
1093
1094 dev->request = req;
1095 blkdev_dequeue_request(req);
1096 dev->start_sg = 0;
1097 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001098 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001099
1100 req = dev->request;
1101 while(dev->start_sg < dev->end_sg){
1102 struct scatterlist *sg = &dev->sg[dev->start_sg];
1103
Jeff Dike2adcec22007-05-06 14:51:37 -07001104 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001105 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001106 if(io_req == NULL){
1107 if(list_empty(&dev->restart))
1108 list_add(&dev->restart, &restart);
1109 return;
1110 }
1111 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001112 (unsigned long long) req->sector << 9,
1113 sg->offset, sg->length, sg->page);
1114
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001115 n = os_write_file(thread_fd, &io_req,
1116 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001117 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001118 if(n != -EAGAIN)
1119 printk("write to io thread failed, "
1120 "errno = %d\n", -n);
1121 else if(list_empty(&dev->restart))
1122 list_add(&dev->restart, &restart);
1123 return;
1124 }
1125
1126 req->sector += sg->length >> 9;
1127 dev->start_sg++;
1128 }
1129 dev->end_sg = 0;
1130 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001131 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132}
1133
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001134static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1135{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001136 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001137
1138 geo->heads = 128;
1139 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001140 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001141 return 0;
1142}
1143
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144static int ubd_ioctl(struct inode * inode, struct file * file,
1145 unsigned int cmd, unsigned long arg)
1146{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001147 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 struct hd_driveid ubd_id = {
1149 .cyls = 0,
1150 .heads = 128,
1151 .sectors = 32,
1152 };
1153
1154 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001157 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1159 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001160 return -EFAULT;
1161 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001162
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 case CDROMVOLREAD:
1164 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001165 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 volume.channel0 = 255;
1167 volume.channel1 = 255;
1168 volume.channel2 = 255;
1169 volume.channel3 = 255;
1170 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001171 return -EFAULT;
1172 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001174 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175}
1176
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001177static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178{
1179 struct uml_stat buf1, buf2;
1180 int err;
1181
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001182 if(from_cmdline == NULL)
1183 return 0;
1184 if(!strcmp(from_cmdline, from_cow))
1185 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186
1187 err = os_stat_file(from_cmdline, &buf1);
1188 if(err < 0){
1189 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001190 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 }
1192 err = os_stat_file(from_cow, &buf2);
1193 if(err < 0){
1194 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001195 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 }
1197 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001198 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
1200 printk("Backing file mismatch - \"%s\" requested,\n"
1201 "\"%s\" specified in COW header of \"%s\"\n",
1202 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001203 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204}
1205
1206static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1207{
1208 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001209 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 int err;
1211
1212 err = os_file_modtime(file, &modtime);
1213 if(err < 0){
1214 printk("Failed to get modification time of backing file "
1215 "\"%s\", err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001216 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 }
1218
1219 err = os_file_size(file, &actual);
1220 if(err < 0){
1221 printk("Failed to get size of backing file \"%s\", "
1222 "err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001223 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 }
1225
Jeff Dikedc764e52007-05-06 14:51:41 -07001226 if(actual != size){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1228 * the typecast.*/
1229 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1230 "file\n", (unsigned long long) size, actual);
Jeff Dikedc764e52007-05-06 14:51:41 -07001231 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 }
1233 if(modtime != mtime){
1234 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1235 "file\n", mtime, modtime);
Jeff Dikedc764e52007-05-06 14:51:41 -07001236 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001238 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239}
1240
1241int read_cow_bitmap(int fd, void *buf, int offset, int len)
1242{
1243 int err;
1244
1245 err = os_seek_file(fd, offset);
1246 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001247 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248
Jeff Dikedc764e52007-05-06 14:51:41 -07001249 err = os_read_file(fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 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 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254}
1255
Jeff Dike6c29256c2006-03-27 01:14:37 -08001256int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 char **backing_file_out, int *bitmap_offset_out,
1258 unsigned long *bitmap_len_out, int *data_offset_out,
1259 int *create_cow_out)
1260{
1261 time_t mtime;
1262 unsigned long long size;
1263 __u32 version, align;
1264 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001265 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266
1267 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001268 if (fd < 0) {
1269 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 *create_cow_out = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001271 if (!openflags->w ||
1272 ((fd != -EROFS) && (fd != -EACCES)))
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001273 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 openflags->w = 0;
1275 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001276 if (fd < 0)
1277 return fd;
Jeff Dikedc764e52007-05-06 14:51:41 -07001278 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
Jeff Dike6c29256c2006-03-27 01:14:37 -08001280 if(shared)
1281 printk("Not locking \"%s\" on the host\n", file);
1282 else {
1283 err = os_lock_file(fd, openflags->w);
1284 if(err < 0){
1285 printk("Failed to lock '%s', err = %d\n", file, -err);
1286 goto out_close;
1287 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 }
1289
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001290 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001291 if(backing_file_out == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -07001292 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293
1294 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1295 &size, &sectorsize, &align, bitmap_offset_out);
1296 if(err && (*backing_file_out != NULL)){
1297 printk("Failed to read COW header from COW file \"%s\", "
1298 "errno = %d\n", file, -err);
1299 goto out_close;
1300 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001301 if(err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001302 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001304 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001306 /* Allow switching only if no mismatch. */
1307 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 printk("Switching backing file to '%s'\n", *backing_file_out);
1309 err = write_cow_header(file, fd, *backing_file_out,
1310 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001311 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001313 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001315 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 *backing_file_out = backing_file;
1317 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001318 if (err)
1319 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 }
1321
1322 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1323 bitmap_len_out, data_offset_out);
1324
Jeff Dikedc764e52007-05-06 14:51:41 -07001325 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 out_close:
1327 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001328 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329}
1330
1331int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1332 int sectorsize, int alignment, int *bitmap_offset_out,
1333 unsigned long *bitmap_len_out, int *data_offset_out)
1334{
1335 int err, fd;
1336
1337 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001338 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 if(fd < 0){
1340 err = fd;
1341 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1342 -err);
1343 goto out;
1344 }
1345
1346 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1347 bitmap_offset_out, bitmap_len_out,
1348 data_offset_out);
1349 if(!err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001350 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 os_close_file(fd);
1352 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001353 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354}
1355
Jeff Dike91acb212005-10-10 23:10:32 -04001356static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357{
Jeff Dike91acb212005-10-10 23:10:32 -04001358 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359
Jeff Dike91acb212005-10-10 23:10:32 -04001360 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001361 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
Jeff Dike91acb212005-10-10 23:10:32 -04001363 n = os_seek_file(req->fds[1], req->cow_offset);
1364 if(n < 0){
1365 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001366 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001367 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001369 n = os_write_file(req->fds[1], &req->bitmap_words,
1370 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001371 if(n != sizeof(req->bitmap_words)){
1372 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1373 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001374 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001375 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
Jeff Dikedc764e52007-05-06 14:51:41 -07001377 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378}
Jeff Dike91acb212005-10-10 23:10:32 -04001379
1380void do_io(struct io_thread_req *req)
1381{
1382 char *buf;
1383 unsigned long len;
1384 int n, nsectors, start, end, bit;
1385 int err;
1386 __u64 off;
1387
1388 nsectors = req->length / req->sectorsize;
1389 start = 0;
1390 do {
1391 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1392 end = start;
1393 while((end < nsectors) &&
1394 (ubd_test_bit(end, (unsigned char *)
1395 &req->sector_mask) == bit))
1396 end++;
1397
1398 off = req->offset + req->offsets[bit] +
1399 start * req->sectorsize;
1400 len = (end - start) * req->sectorsize;
1401 buf = &req->buffer[start * req->sectorsize];
1402
1403 err = os_seek_file(req->fds[bit], off);
1404 if(err < 0){
1405 printk("do_io - lseek failed : err = %d\n", -err);
1406 req->error = 1;
1407 return;
1408 }
1409 if(req->op == UBD_READ){
1410 n = 0;
1411 do {
1412 buf = &buf[n];
1413 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001414 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001415 if (n < 0) {
1416 printk("do_io - read failed, err = %d "
1417 "fd = %d\n", -n, req->fds[bit]);
1418 req->error = 1;
1419 return;
1420 }
1421 } while((n < len) && (n != 0));
1422 if (n < len) memset(&buf[n], 0, len - n);
1423 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001424 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001425 if(n != len){
1426 printk("do_io - write failed err = %d "
1427 "fd = %d\n", -n, req->fds[bit]);
1428 req->error = 1;
1429 return;
1430 }
1431 }
1432
1433 start = end;
1434 } while(start < nsectors);
1435
1436 req->error = update_bitmap(req);
1437}
1438
1439/* Changed in start_io_thread, which is serialized by being called only
1440 * from ubd_init, which is an initcall.
1441 */
1442int kernel_fd = -1;
1443
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001444/* Only changed by the io thread. XXX: currently unused. */
1445static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001446
1447int io_thread(void *arg)
1448{
Jeff Dike2adcec22007-05-06 14:51:37 -07001449 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001450 int n;
1451
1452 ignore_sigwinch_sig();
1453 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001454 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001455 sizeof(struct io_thread_req *));
1456 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001457 if(n < 0)
1458 printk("io_thread - read failed, fd = %d, "
1459 "err = %d\n", kernel_fd, -n);
1460 else {
1461 printk("io_thread - short read, fd = %d, "
1462 "length = %d\n", kernel_fd, n);
1463 }
1464 continue;
1465 }
1466 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001467 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001468 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001469 sizeof(struct io_thread_req *));
1470 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001471 printk("io_thread - write failed, fd = %d, err = %d\n",
1472 kernel_fd, -n);
1473 }
Jeff Dike91acb212005-10-10 23:10:32 -04001474
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001475 return 0;
1476}