blob: 2e09f162c42f01b6ac39f3689eae208f5d4b75b1 [file] [log] [blame]
Jeff Dike6c29256c2006-03-27 01:14:37 -08001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL
4 */
5
6/* 2001-09-28...2002-04-17
7 * Partition stuff by James_McMechan@hotmail.com
8 * old style ubd by setting UBD_SHIFT to 0
9 * 2002-09-27...2002-10-18 massive tinkering for 2.5
10 * partitions have changed in 2.5
11 * 2003-01-29 more tinkering for 2.5.59-1
12 * This should now address the sysfs problems and has
13 * the symlink for devfs to allow for booting with
14 * the common /dev/ubd/discX/... names rather than
15 * only /dev/ubdN/discN this version also has lots of
16 * clean ups preparing for ubd-many.
17 * James McMechan
18 */
19
20#define MAJOR_NR UBD_MAJOR
21#define UBD_SHIFT 4
22
Jeff Dikee16f5352007-06-08 13:46:54 -070023#include "linux/kernel.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include "linux/module.h"
25#include "linux/blkdev.h"
26#include "linux/hdreg.h"
27#include "linux/init.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include "linux/cdrom.h"
29#include "linux/proc_fs.h"
30#include "linux/ctype.h"
31#include "linux/capability.h"
32#include "linux/mm.h"
33#include "linux/vmalloc.h"
34#include "linux/blkpg.h"
35#include "linux/genhd.h"
36#include "linux/spinlock.h"
Russell Kingd052d1b2005-10-29 19:07:23 +010037#include "linux/platform_device.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include "asm/segment.h"
39#include "asm/uaccess.h"
40#include "asm/irq.h"
41#include "asm/types.h"
42#include "asm/tlbflush.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include "mem_user.h"
44#include "kern_util.h"
45#include "kern.h"
46#include "mconsole_kern.h"
47#include "init.h"
48#include "irq_user.h"
49#include "irq_kern.h"
50#include "ubd_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#include "os.h"
52#include "mem.h"
53#include "mem_kern.h"
54#include "cow.h"
55
Jeff Dike7b9014c2005-05-20 13:59:11 -070056enum ubd_req { UBD_READ, UBD_WRITE };
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
58struct io_thread_req {
Jeff Dike62f96cb2007-02-10 01:44:16 -080059 struct request *req;
Jeff Dike91acb212005-10-10 23:10:32 -040060 enum ubd_req op;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 int fds[2];
62 unsigned long offsets[2];
63 unsigned long long offset;
64 unsigned long length;
65 char *buffer;
66 int sectorsize;
Jeff Dike91acb212005-10-10 23:10:32 -040067 unsigned long sector_mask;
68 unsigned long long cow_offset;
69 unsigned long bitmap_words[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 int error;
71};
72
Jeff Dike6c29256c2006-03-27 01:14:37 -080073extern int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 char **backing_file_out, int *bitmap_offset_out,
75 unsigned long *bitmap_len_out, int *data_offset_out,
76 int *create_cow_out);
77extern int create_cow_file(char *cow_file, char *backing_file,
78 struct openflags flags, int sectorsize,
79 int alignment, int *bitmap_offset_out,
80 unsigned long *bitmap_len_out,
81 int *data_offset_out);
82extern int read_cow_bitmap(int fd, void *buf, int offset, int len);
Jeff Dike91acb212005-10-10 23:10:32 -040083extern void do_io(struct io_thread_req *req);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
Jeff Dike91acb212005-10-10 23:10:32 -040085static inline int ubd_test_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070086{
87 __u64 n;
88 int bits, off;
89
Jeff Dike91acb212005-10-10 23:10:32 -040090 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 n = bit / bits;
92 off = bit % bits;
Jeff Dikedc764e52007-05-06 14:51:41 -070093 return (data[n] & (1 << off)) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094}
95
Jeff Dike91acb212005-10-10 23:10:32 -040096static inline void ubd_set_bit(__u64 bit, unsigned char *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070097{
98 __u64 n;
99 int bits, off;
100
Jeff Dike91acb212005-10-10 23:10:32 -0400101 bits = sizeof(data[0]) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 n = bit / bits;
103 off = bit % bits;
Jeff Dike91acb212005-10-10 23:10:32 -0400104 data[n] |= (1 << off);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105}
106/*End stuff from ubd_user.h*/
107
108#define DRIVER_NAME "uml-blkdev"
109
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800110static DEFINE_MUTEX(ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112static int ubd_open(struct inode * inode, struct file * filp);
113static int ubd_release(struct inode * inode, struct file * file);
114static int ubd_ioctl(struct inode * inode, struct file * file,
115 unsigned int cmd, unsigned long arg);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800116static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800118#define MAX_DEV (16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120static struct block_device_operations ubd_blops = {
121 .owner = THIS_MODULE,
122 .open = ubd_open,
123 .release = ubd_release,
124 .ioctl = ubd_ioctl,
Christoph Hellwiga885c8c2006-01-08 01:02:50 -0800125 .getgeo = ubd_getgeo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126};
127
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128/* Protected by ubd_lock */
129static int fake_major = MAJOR_NR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130static struct gendisk *ubd_gendisk[MAX_DEV];
131static struct gendisk *fake_gendisk[MAX_DEV];
Jeff Dike6c29256c2006-03-27 01:14:37 -0800132
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133#ifdef CONFIG_BLK_DEV_UBD_SYNC
134#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \
135 .cl = 1 })
136#else
137#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \
138 .cl = 1 })
139#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140static struct openflags global_openflags = OPEN_FLAGS;
141
142struct cow {
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800143 /* backing file name */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 char *file;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800145 /* backing file fd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 int fd;
147 unsigned long *bitmap;
148 unsigned long bitmap_len;
149 int bitmap_offset;
Jeff Dikedc764e52007-05-06 14:51:41 -0700150 int data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151};
152
Jeff Dikea0044bd2007-05-06 14:51:36 -0700153#define MAX_SG 64
154
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155struct ubd {
Jeff Dikea0044bd2007-05-06 14:51:36 -0700156 struct list_head restart;
Paolo 'Blaisorblade' Giarrusso2a9d32f2006-10-30 22:07:04 -0800157 /* name (and fd, below) of the file opened for writing, either the
158 * backing or the cow file. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 char *file;
160 int count;
161 int fd;
162 __u64 size;
163 struct openflags boot_openflags;
164 struct openflags openflags;
Paolo 'Blaisorblade' Giarrusso84e945e2006-10-30 22:07:10 -0800165 unsigned shared:1;
166 unsigned no_cow:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 struct cow cow;
168 struct platform_device pdev;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800169 struct request_queue *queue;
170 spinlock_t lock;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700171 struct scatterlist sg[MAX_SG];
172 struct request *request;
173 int start_sg, end_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174};
175
176#define DEFAULT_COW { \
177 .file = NULL, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700178 .fd = -1, \
179 .bitmap = NULL, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 .bitmap_offset = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700181 .data_offset = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182}
183
184#define DEFAULT_UBD { \
185 .file = NULL, \
186 .count = 0, \
187 .fd = -1, \
188 .size = -1, \
189 .boot_openflags = OPEN_FLAGS, \
190 .openflags = OPEN_FLAGS, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700191 .no_cow = 0, \
Jeff Dike6c29256c2006-03-27 01:14:37 -0800192 .shared = 0, \
Jeff Dikedc764e52007-05-06 14:51:41 -0700193 .cow = DEFAULT_COW, \
Jeff Dike62f96cb2007-02-10 01:44:16 -0800194 .lock = SPIN_LOCK_UNLOCKED, \
Jeff Dikea0044bd2007-05-06 14:51:36 -0700195 .request = NULL, \
196 .start_sg = 0, \
197 .end_sg = 0, \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198}
199
Jeff Dikeb8831a12007-02-10 01:44:17 -0800200/* Protected by ubd_lock */
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800201struct ubd ubd_devs[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203/* Only changed by fake_ide_setup which is a setup */
204static int fake_ide = 0;
205static struct proc_dir_entry *proc_ide_root = NULL;
206static struct proc_dir_entry *proc_ide = NULL;
207
208static void make_proc_ide(void)
209{
210 proc_ide_root = proc_mkdir("ide", NULL);
211 proc_ide = proc_mkdir("ide0", proc_ide_root);
212}
213
214static int proc_ide_read_media(char *page, char **start, off_t off, int count,
215 int *eof, void *data)
216{
217 int len;
218
219 strcpy(page, "disk\n");
220 len = strlen("disk\n");
221 len -= off;
222 if (len < count){
223 *eof = 1;
224 if (len <= 0) return 0;
225 }
226 else len = count;
227 *start = page + off;
228 return len;
229}
230
231static void make_ide_entries(char *dev_name)
232{
233 struct proc_dir_entry *dir, *ent;
234 char name[64];
235
236 if(proc_ide_root == NULL) make_proc_ide();
237
238 dir = proc_mkdir(dev_name, proc_ide);
239 if(!dir) return;
240
241 ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir);
242 if(!ent) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 ent->data = NULL;
244 ent->read_proc = proc_ide_read_media;
245 ent->write_proc = NULL;
246 sprintf(name,"ide0/%s", dev_name);
247 proc_symlink(dev_name, proc_ide_root, name);
248}
249
250static int fake_ide_setup(char *str)
251{
252 fake_ide = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700253 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254}
255
256__setup("fake_ide", fake_ide_setup);
257
258__uml_help(fake_ide_setup,
259"fake_ide\n"
260" Create ide0 entries that map onto ubd devices.\n\n"
261);
262
263static int parse_unit(char **ptr)
264{
265 char *str = *ptr, *end;
266 int n = -1;
267
268 if(isdigit(*str)) {
269 n = simple_strtoul(str, &end, 0);
270 if(end == str)
Jeff Dikedc764e52007-05-06 14:51:41 -0700271 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 *ptr = end;
273 }
Paolo 'Blaisorblade' Giarrusso97d88ac2006-10-30 22:07:03 -0800274 else if (('a' <= *str) && (*str <= 'z')) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 n = *str - 'a';
276 str++;
277 *ptr = str;
278 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700279 return n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280}
281
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800282/* If *index_out == -1 at exit, the passed option was a general one;
283 * otherwise, the str pointer is used (and owned) inside ubd_devs array, so it
284 * should not be freed on exit.
285 */
Jeff Dikef28169d2007-02-10 01:43:53 -0800286static int ubd_setup_common(char *str, int *index_out, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800288 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 struct openflags flags = global_openflags;
290 char *backing_file;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800291 int n, err = 0, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
293 if(index_out) *index_out = -1;
294 n = *str;
295 if(n == '='){
296 char *end;
297 int major;
298
299 str++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 if(!strcmp(str, "sync")){
301 global_openflags = of_sync(global_openflags);
Jeff Dikeb8831a12007-02-10 01:44:17 -0800302 goto out1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 }
304
Jeff Dikef28169d2007-02-10 01:43:53 -0800305 err = -EINVAL;
Jeff Dikeb8831a12007-02-10 01:44:17 -0800306 major = simple_strtoul(str, &end, 0);
307 if((*end != '\0') || (end == str)){
308 *error_out = "Didn't parse major number";
309 goto out1;
310 }
311
Jeff Dikef28169d2007-02-10 01:43:53 -0800312 mutex_lock(&ubd_lock);
313 if(fake_major != MAJOR_NR){
314 *error_out = "Can't assign a fake major twice";
315 goto out1;
316 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800317
Jeff Dikef28169d2007-02-10 01:43:53 -0800318 fake_major = major;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
320 printk(KERN_INFO "Setting extra ubd major number to %d\n",
321 major);
Jeff Dikef28169d2007-02-10 01:43:53 -0800322 err = 0;
323 out1:
324 mutex_unlock(&ubd_lock);
325 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
327
328 n = parse_unit(&str);
329 if(n < 0){
Jeff Dikef28169d2007-02-10 01:43:53 -0800330 *error_out = "Couldn't parse device number";
331 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 }
333 if(n >= MAX_DEV){
Jeff Dikef28169d2007-02-10 01:43:53 -0800334 *error_out = "Device number out of range";
335 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 }
337
Jeff Dikef28169d2007-02-10 01:43:53 -0800338 err = -EBUSY;
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800339 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800341 ubd_dev = &ubd_devs[n];
342 if(ubd_dev->file != NULL){
Jeff Dikef28169d2007-02-10 01:43:53 -0800343 *error_out = "Device is already configured";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 goto out;
345 }
346
347 if (index_out)
348 *index_out = n;
349
Jeff Dikef28169d2007-02-10 01:43:53 -0800350 err = -EINVAL;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800351 for (i = 0; i < sizeof("rscd="); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 switch (*str) {
353 case 'r':
354 flags.w = 0;
355 break;
356 case 's':
357 flags.s = 1;
358 break;
359 case 'd':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800360 ubd_dev->no_cow = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 break;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800362 case 'c':
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800363 ubd_dev->shared = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -0800364 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 case '=':
366 str++;
367 goto break_loop;
368 default:
Jeff Dikef28169d2007-02-10 01:43:53 -0800369 *error_out = "Expected '=' or flag letter "
370 "(r, s, c, or d)";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 goto out;
372 }
373 str++;
374 }
375
Jeff Dikef28169d2007-02-10 01:43:53 -0800376 if (*str == '=')
377 *error_out = "Too many flags specified";
378 else
379 *error_out = "Missing '='";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 goto out;
381
382break_loop:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 backing_file = strchr(str, ',');
384
Jeff Dikef28169d2007-02-10 01:43:53 -0800385 if (backing_file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 backing_file = strchr(str, ':');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387
Jeff Dikef28169d2007-02-10 01:43:53 -0800388 if(backing_file != NULL){
389 if(ubd_dev->no_cow){
390 *error_out = "Can't specify both 'd' and a cow file";
391 goto out;
392 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 else {
394 *backing_file = '\0';
395 backing_file++;
396 }
397 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800398 err = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800399 ubd_dev->file = str;
400 ubd_dev->cow.file = backing_file;
401 ubd_dev->boot_openflags = flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800403 mutex_unlock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800404 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405}
406
407static int ubd_setup(char *str)
408{
Jeff Dikef28169d2007-02-10 01:43:53 -0800409 char *error;
410 int err;
411
412 err = ubd_setup_common(str, NULL, &error);
413 if(err)
414 printk(KERN_ERR "Failed to initialize device with \"%s\" : "
415 "%s\n", str, error);
416 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417}
418
419__setup("ubd", ubd_setup);
420__uml_help(ubd_setup,
421"ubd<n><flags>=<filename>[(:|,)<filename2>]\n"
422" This is used to associate a device with a file in the underlying\n"
423" filesystem. When specifying two filenames, the first one is the\n"
424" COW name and the second is the backing file name. As separator you can\n"
425" use either a ':' or a ',': the first one allows writing things like;\n"
426" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n"
427" while with a ',' the shell would not expand the 2nd '~'.\n"
Jeff Dikef28169d2007-02-10 01:43:53 -0800428" When using only one filename, UML will detect whether to treat it like\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429" a COW file or a backing file. To override this detection, add the 'd'\n"
430" flag:\n"
431" ubd0d=BackingFile\n"
432" Usually, there is a filesystem in the file, but \n"
433" that's not required. Swap devices containing swap files can be\n"
434" specified like this. Also, a file which doesn't contain a\n"
435" filesystem can have its contents read in the virtual \n"
436" machine by running 'dd' on the device. <n> must be in the range\n"
437" 0 to 7. Appending an 'r' to the number will cause that device\n"
438" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n"
439" an 's' will cause data to be written to disk on the host immediately.\n\n"
440);
441
442static int udb_setup(char *str)
443{
444 printk("udb%s specified on command line is almost certainly a ubd -> "
445 "udb TYPO\n", str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700446 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447}
448
449__setup("udb", udb_setup);
450__uml_help(udb_setup,
451"udb\n"
Jeff Dike0894e272005-05-28 15:51:55 -0700452" This option is here solely to catch ubd -> udb typos, which can be\n"
453" to impossible to catch visually unless you specifically look for\n"
454" them. The only result of any option starting with 'udb' is an error\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455" in the boot output.\n\n"
456);
457
458static int fakehd_set = 0;
459static int fakehd(char *str)
460{
461 printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n");
462 fakehd_set = 1;
463 return 1;
464}
465
466__setup("fakehd", fakehd);
467__uml_help(fakehd,
468"fakehd\n"
469" Change the ubd device name to \"hd\".\n\n"
470);
471
472static void do_ubd_request(request_queue_t * q);
Jeff Dike91acb212005-10-10 23:10:32 -0400473
474/* Only changed by ubd_init, which is an initcall. */
475int thread_fd = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
Jeff Dikea0044bd2007-05-06 14:51:36 -0700477static void ubd_end_request(struct request *req, int bytes, int uptodate)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700479 if (!end_that_request_first(req, uptodate, bytes >> 9)) {
480 struct ubd *dev = req->rq_disk->private_data;
481 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
Jeff Dikea0044bd2007-05-06 14:51:36 -0700483 add_disk_randomness(req->rq_disk);
484 spin_lock_irqsave(&dev->lock, flags);
485 end_that_request_last(req, uptodate);
486 spin_unlock_irqrestore(&dev->lock, flags);
Jeff Dike91acb212005-10-10 23:10:32 -0400487 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488}
489
Paolo 'Blaisorblade' Giarrusso33f775e2006-10-30 22:07:08 -0800490/* Callable only from interrupt context - otherwise you need to do
491 * spin_lock_irq()/spin_lock_irqsave() */
Jeff Dikea0044bd2007-05-06 14:51:36 -0700492static inline void ubd_finish(struct request *req, int bytes)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493{
Jeff Dikea0044bd2007-05-06 14:51:36 -0700494 if(bytes < 0){
495 ubd_end_request(req, 0, 0);
496 return;
497 }
498 ubd_end_request(req, bytes, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499}
500
Jeff Dikea0044bd2007-05-06 14:51:36 -0700501static LIST_HEAD(restart);
502
Paolo 'Blaisorblade' Giarrusso2fe30a32006-10-30 22:07:09 -0800503/* XXX - move this inside ubd_intr. */
Jeff Dike62f96cb2007-02-10 01:44:16 -0800504/* Called without dev->lock held, and only in interrupt context. */
Jeff Dike91acb212005-10-10 23:10:32 -0400505static void ubd_handler(void)
506{
Jeff Dike2adcec22007-05-06 14:51:37 -0700507 struct io_thread_req *req;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800508 struct request *rq;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700509 struct ubd *ubd;
510 struct list_head *list, *next_ele;
511 unsigned long flags;
Jeff Dike91acb212005-10-10 23:10:32 -0400512 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
Jeff Dikea0044bd2007-05-06 14:51:36 -0700514 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -0700515 n = os_read_file(thread_fd, &req,
516 sizeof(struct io_thread_req *));
Jeff Dikea0044bd2007-05-06 14:51:36 -0700517 if(n != sizeof(req)){
518 if(n == -EAGAIN)
519 break;
520 printk(KERN_ERR "spurious interrupt in ubd_handler, "
521 "err = %d\n", -n);
522 return;
523 }
524
Jeff Dike2adcec22007-05-06 14:51:37 -0700525 rq = req->req;
526 rq->nr_sectors -= req->length >> 9;
Jeff Dikea0044bd2007-05-06 14:51:36 -0700527 if(rq->nr_sectors == 0)
528 ubd_finish(rq, rq->hard_nr_sectors << 9);
Jeff Dike2adcec22007-05-06 14:51:37 -0700529 kfree(req);
Jeff Dike91acb212005-10-10 23:10:32 -0400530 }
Jeff Dike62f96cb2007-02-10 01:44:16 -0800531 reactivate_fd(thread_fd, UBD_IRQ);
Jeff Dikea0044bd2007-05-06 14:51:36 -0700532
533 list_for_each_safe(list, next_ele, &restart){
534 ubd = container_of(list, struct ubd, restart);
535 list_del_init(&ubd->restart);
536 spin_lock_irqsave(&ubd->lock, flags);
537 do_ubd_request(ubd->queue);
538 spin_unlock_irqrestore(&ubd->lock, flags);
539 }
Jeff Dike91acb212005-10-10 23:10:32 -0400540}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
Al Viro7bea96f2006-10-08 22:49:34 +0100542static irqreturn_t ubd_intr(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543{
Jeff Dike91acb212005-10-10 23:10:32 -0400544 ubd_handler();
Jeff Dikedc764e52007-05-06 14:51:41 -0700545 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546}
547
Jeff Dike91acb212005-10-10 23:10:32 -0400548/* Only changed by ubd_init, which is an initcall. */
549static int io_pid = -1;
550
551void kill_io_thread(void)
552{
Jeff Dike6c29256c2006-03-27 01:14:37 -0800553 if(io_pid != -1)
Jeff Dike91acb212005-10-10 23:10:32 -0400554 os_kill_process(io_pid, 1);
555}
556
557__uml_exitcall(kill_io_thread);
558
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800559static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
561 char *file;
562
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800563 file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file;
Jeff Dikedc764e52007-05-06 14:51:41 -0700564 return os_file_size(file, size_out);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800567static void ubd_close_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800569 os_close_file(ubd_dev->fd);
570 if(ubd_dev->cow.file == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 return;
572
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800573 os_close_file(ubd_dev->cow.fd);
574 vfree(ubd_dev->cow.bitmap);
575 ubd_dev->cow.bitmap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576}
577
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800578static int ubd_open_dev(struct ubd *ubd_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579{
580 struct openflags flags;
581 char **back_ptr;
582 int err, create_cow, *create_ptr;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800583 int fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800585 ubd_dev->openflags = ubd_dev->boot_openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 create_cow = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800587 create_ptr = (ubd_dev->cow.file != NULL) ? &create_cow : NULL;
588 back_ptr = ubd_dev->no_cow ? NULL : &ubd_dev->cow.file;
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800589
590 fd = open_ubd_file(ubd_dev->file, &ubd_dev->openflags, ubd_dev->shared,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800591 back_ptr, &ubd_dev->cow.bitmap_offset,
592 &ubd_dev->cow.bitmap_len, &ubd_dev->cow.data_offset,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800593 create_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800595 if((fd == -ENOENT) && create_cow){
596 fd = create_cow_file(ubd_dev->file, ubd_dev->cow.file,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800597 ubd_dev->openflags, 1 << 9, PAGE_SIZE,
598 &ubd_dev->cow.bitmap_offset,
599 &ubd_dev->cow.bitmap_len,
600 &ubd_dev->cow.data_offset);
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800601 if(fd >= 0){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 printk(KERN_INFO "Creating \"%s\" as COW file for "
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800603 "\"%s\"\n", ubd_dev->file, ubd_dev->cow.file);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 }
605 }
606
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800607 if(fd < 0){
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800608 printk("Failed to open '%s', errno = %d\n", ubd_dev->file,
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800609 -fd);
610 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 }
Paolo 'Blaisorblade' Giarrusso0bf16bf2006-10-30 22:07:11 -0800612 ubd_dev->fd = fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800614 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 err = -ENOMEM;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800616 ubd_dev->cow.bitmap = (void *) vmalloc(ubd_dev->cow.bitmap_len);
617 if(ubd_dev->cow.bitmap == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 printk(KERN_ERR "Failed to vmalloc COW bitmap\n");
619 goto error;
620 }
621 flush_tlb_kernel_vm();
622
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800623 err = read_cow_bitmap(ubd_dev->fd, ubd_dev->cow.bitmap,
624 ubd_dev->cow.bitmap_offset,
625 ubd_dev->cow.bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 if(err < 0)
627 goto error;
628
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800629 flags = ubd_dev->openflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 flags.w = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800631 err = open_ubd_file(ubd_dev->cow.file, &flags, ubd_dev->shared, NULL,
Jeff Dike6c29256c2006-03-27 01:14:37 -0800632 NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 if(err < 0) goto error;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800634 ubd_dev->cow.fd = err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700636 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 error:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800638 os_close_file(ubd_dev->fd);
Jeff Dikedc764e52007-05-06 14:51:41 -0700639 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640}
641
Jeff Dike2e3f5252007-05-06 14:51:29 -0700642static void ubd_device_release(struct device *dev)
643{
644 struct ubd *ubd_dev = dev->driver_data;
645
646 blk_cleanup_queue(ubd_dev->queue);
647 *ubd_dev = ((struct ubd) DEFAULT_UBD);
648}
649
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800650static int ubd_disk_register(int major, u64 size, int unit,
Jeff Dikeb8831a12007-02-10 01:44:17 -0800651 struct gendisk **disk_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652{
653 struct gendisk *disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
655 disk = alloc_disk(1 << UBD_SHIFT);
656 if(disk == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -0700657 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
659 disk->major = major;
660 disk->first_minor = unit << UBD_SHIFT;
661 disk->fops = &ubd_blops;
662 set_capacity(disk, size / 512);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700663 if(major == MAJOR_NR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 sprintf(disk->disk_name, "ubd%c", 'a' + unit);
Greg Kroah-Hartmance7b0f42005-06-20 21:15:16 -0700665 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 sprintf(disk->disk_name, "ubd_fake%d", unit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667
668 /* sysfs register (not for ide fake devices) */
669 if (major == MAJOR_NR) {
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800670 ubd_devs[unit].pdev.id = unit;
671 ubd_devs[unit].pdev.name = DRIVER_NAME;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700672 ubd_devs[unit].pdev.dev.release = ubd_device_release;
673 ubd_devs[unit].pdev.dev.driver_data = &ubd_devs[unit];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800674 platform_device_register(&ubd_devs[unit].pdev);
675 disk->driverfs_dev = &ubd_devs[unit].pdev.dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 }
677
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800678 disk->private_data = &ubd_devs[unit];
Jeff Dike62f96cb2007-02-10 01:44:16 -0800679 disk->queue = ubd_devs[unit].queue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 add_disk(disk);
681
682 *disk_out = disk;
683 return 0;
684}
685
686#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9))
687
Jeff Dikef28169d2007-02-10 01:43:53 -0800688static int ubd_add(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800690 struct ubd *ubd_dev = &ubd_devs[n];
Jeff Dikef28169d2007-02-10 01:43:53 -0800691 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800693 if(ubd_dev->file == NULL)
Jeff Dikeec7cf782005-09-03 15:57:29 -0700694 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800696 err = ubd_file_size(ubd_dev, &ubd_dev->size);
Jeff Dikef28169d2007-02-10 01:43:53 -0800697 if(err < 0){
698 *error_out = "Couldn't determine size of device's file";
Jeff Dike80c13742006-09-29 01:58:51 -0700699 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800700 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800702 ubd_dev->size = ROUND_BLOCK(ubd_dev->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
Jeff Dikea0044bd2007-05-06 14:51:36 -0700704 INIT_LIST_HEAD(&ubd_dev->restart);
705
Jeff Dike62f96cb2007-02-10 01:44:16 -0800706 err = -ENOMEM;
707 ubd_dev->queue = blk_init_queue(do_ubd_request, &ubd_dev->lock);
708 if (ubd_dev->queue == NULL) {
709 *error_out = "Failed to initialize device queue";
Jeff Dike80c13742006-09-29 01:58:51 -0700710 goto out;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800711 }
712 ubd_dev->queue->queuedata = ubd_dev;
713
Jeff Dikea0044bd2007-05-06 14:51:36 -0700714 blk_queue_max_hw_segments(ubd_dev->queue, MAX_SG);
Jeff Dike62f96cb2007-02-10 01:44:16 -0800715 err = ubd_disk_register(MAJOR_NR, ubd_dev->size, n, &ubd_gendisk[n]);
716 if(err){
717 *error_out = "Failed to register device";
718 goto out_cleanup;
719 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800720
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 if(fake_major != MAJOR_NR)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800722 ubd_disk_register(fake_major, ubd_dev->size, n,
Jeff Dike62f96cb2007-02-10 01:44:16 -0800723 &fake_gendisk[n]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
725 /* perhaps this should also be under the "if (fake_major)" above */
726 /* using the fake_disk->disk_name and also the fakehd_set name */
727 if (fake_ide)
728 make_ide_entries(ubd_gendisk[n]->disk_name);
729
Jeff Dikeec7cf782005-09-03 15:57:29 -0700730 err = 0;
Jeff Dikeec7cf782005-09-03 15:57:29 -0700731out:
732 return err;
Jeff Dike62f96cb2007-02-10 01:44:16 -0800733
734out_cleanup:
735 blk_cleanup_queue(ubd_dev->queue);
736 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737}
738
Jeff Dikef28169d2007-02-10 01:43:53 -0800739static int ubd_config(char *str, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740{
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800741 int n, ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
Jeff Dikef28169d2007-02-10 01:43:53 -0800743 /* This string is possibly broken up and stored, so it's only
744 * freed if ubd_setup_common fails, or if only general options
745 * were set.
746 */
Jeff Dike970d6e32006-01-06 00:18:48 -0800747 str = kstrdup(str, GFP_KERNEL);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800748 if (str == NULL) {
Jeff Dikef28169d2007-02-10 01:43:53 -0800749 *error_out = "Failed to allocate memory";
750 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800752
753 ret = ubd_setup_common(str, &n, error_out);
754 if (ret)
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800755 goto err_free;
Jeff Dikef28169d2007-02-10 01:43:53 -0800756
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800757 if (n == -1) {
758 ret = 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800759 goto err_free;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800760 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Jeff Dikedc764e52007-05-06 14:51:41 -0700762 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800763 ret = ubd_add(n, error_out);
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800764 if (ret)
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800765 ubd_devs[n].file = NULL;
Jeff Dikedc764e52007-05-06 14:51:41 -0700766 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800768out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700769 return ret;
Paolo 'Blaisorblade' Giarrussoe7f65522006-10-30 22:07:09 -0800770
771err_free:
772 kfree(str);
773 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774}
775
776static int ubd_get_config(char *name, char *str, int size, char **error_out)
777{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800778 struct ubd *ubd_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 int n, len = 0;
780
781 n = parse_unit(&name);
782 if((n >= MAX_DEV) || (n < 0)){
783 *error_out = "ubd_get_config : device number out of range";
Jeff Dikedc764e52007-05-06 14:51:41 -0700784 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 }
786
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800787 ubd_dev = &ubd_devs[n];
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800788 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800790 if(ubd_dev->file == NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 CONFIG_CHUNK(str, size, len, "", 1);
792 goto out;
793 }
794
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800795 CONFIG_CHUNK(str, size, len, ubd_dev->file, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800797 if(ubd_dev->cow.file != NULL){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 CONFIG_CHUNK(str, size, len, ",", 0);
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800799 CONFIG_CHUNK(str, size, len, ubd_dev->cow.file, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 }
801 else CONFIG_CHUNK(str, size, len, "", 1);
802
803 out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800804 mutex_unlock(&ubd_lock);
Jeff Dikedc764e52007-05-06 14:51:41 -0700805 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806}
807
Jeff Dike29d56cf2005-06-25 14:55:25 -0700808static int ubd_id(char **str, int *start_out, int *end_out)
809{
Jeff Dikedc764e52007-05-06 14:51:41 -0700810 int n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700811
812 n = parse_unit(str);
Jeff Dikedc764e52007-05-06 14:51:41 -0700813 *start_out = 0;
814 *end_out = MAX_DEV - 1;
815 return n;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700816}
817
Jeff Dikef28169d2007-02-10 01:43:53 -0800818static int ubd_remove(int n, char **error_out)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819{
Jeff Dike2e3f5252007-05-06 14:51:29 -0700820 struct gendisk *disk = ubd_gendisk[n];
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800821 struct ubd *ubd_dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700822 int err = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800824 mutex_lock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800826 ubd_dev = &ubd_devs[n];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700827
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800828 if(ubd_dev->file == NULL)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700829 goto out;
830
831 /* you cannot remove a open disk */
832 err = -EBUSY;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800833 if(ubd_dev->count > 0)
Jeff Dike29d56cf2005-06-25 14:55:25 -0700834 goto out;
835
Jeff Dikedc764e52007-05-06 14:51:41 -0700836 ubd_gendisk[n] = NULL;
Jeff Dikeb47d2de2007-05-06 14:51:01 -0700837 if(disk != NULL){
838 del_gendisk(disk);
839 put_disk(disk);
840 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
842 if(fake_gendisk[n] != NULL){
843 del_gendisk(fake_gendisk[n]);
844 put_disk(fake_gendisk[n]);
845 fake_gendisk[n] = NULL;
846 }
847
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 err = 0;
Jeff Dike2e3f5252007-05-06 14:51:29 -0700849 platform_device_unregister(&ubd_dev->pdev);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700850out:
Paolo 'Blaisorblade' Giarrussod7fb2c32006-10-30 22:07:07 -0800851 mutex_unlock(&ubd_lock);
Jeff Dike29d56cf2005-06-25 14:55:25 -0700852 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853}
854
Jeff Dikef28169d2007-02-10 01:43:53 -0800855/* All these are called by mconsole in process context and without
Jeff Dikeb8831a12007-02-10 01:44:17 -0800856 * ubd-specific locks. The structure itself is const except for .list.
Jeff Dikef28169d2007-02-10 01:43:53 -0800857 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858static struct mc_device ubd_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800859 .list = LIST_HEAD_INIT(ubd_mc.list),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 .name = "ubd",
861 .config = ubd_config,
Jeff Dikedc764e52007-05-06 14:51:41 -0700862 .get_config = ubd_get_config,
Jeff Dike29d56cf2005-06-25 14:55:25 -0700863 .id = ubd_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 .remove = ubd_remove,
865};
866
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800867static int __init ubd_mc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868{
869 mconsole_register_dev(&ubd_mc);
870 return 0;
871}
872
873__initcall(ubd_mc_init);
874
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800875static int __init ubd0_init(void)
876{
877 struct ubd *ubd_dev = &ubd_devs[0];
878
Jeff Dikeb8831a12007-02-10 01:44:17 -0800879 mutex_lock(&ubd_lock);
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800880 if(ubd_dev->file == NULL)
881 ubd_dev->file = "root_fs";
Jeff Dikeb8831a12007-02-10 01:44:17 -0800882 mutex_unlock(&ubd_lock);
883
Jeff Dikedc764e52007-05-06 14:51:41 -0700884 return 0;
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800885}
886
887__initcall(ubd0_init);
888
Jeff Dikeb8831a12007-02-10 01:44:17 -0800889/* Used in ubd_init, which is an initcall */
Russell King3ae5eae2005-11-09 22:32:44 +0000890static struct platform_driver ubd_driver = {
891 .driver = {
892 .name = DRIVER_NAME,
893 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894};
895
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800896static int __init ubd_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897{
Jeff Dikef28169d2007-02-10 01:43:53 -0800898 char *error;
899 int i, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 if (register_blkdev(MAJOR_NR, "ubd"))
902 return -1;
903
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 if (fake_major != MAJOR_NR) {
905 char name[sizeof("ubd_nnn\0")];
906
907 snprintf(name, sizeof(name), "ubd_%d", fake_major);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 if (register_blkdev(fake_major, "ubd"))
909 return -1;
910 }
Russell King3ae5eae2005-11-09 22:32:44 +0000911 platform_driver_register(&ubd_driver);
Jeff Dikedc764e52007-05-06 14:51:41 -0700912 mutex_lock(&ubd_lock);
Jeff Dikef28169d2007-02-10 01:43:53 -0800913 for (i = 0; i < MAX_DEV; i++){
914 err = ubd_add(i, &error);
915 if(err)
916 printk(KERN_ERR "Failed to initialize ubd device %d :"
917 "%s\n", i, error);
918 }
Jeff Dikedc764e52007-05-06 14:51:41 -0700919 mutex_unlock(&ubd_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 return 0;
921}
922
923late_initcall(ubd_init);
924
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -0800925static int __init ubd_driver_init(void){
Jeff Dike91acb212005-10-10 23:10:32 -0400926 unsigned long stack;
927 int err;
928
929 /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/
930 if(global_openflags.s){
931 printk(KERN_INFO "ubd: Synchronous mode\n");
932 /* Letting ubd=sync be like using ubd#s= instead of ubd#= is
933 * enough. So use anyway the io thread. */
934 }
935 stack = alloc_stack(0, 0);
Jeff Dike6c29256c2006-03-27 01:14:37 -0800936 io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
Jeff Dike91acb212005-10-10 23:10:32 -0400937 &thread_fd);
938 if(io_pid < 0){
Jeff Dike6c29256c2006-03-27 01:14:37 -0800939 printk(KERN_ERR
Jeff Dike91acb212005-10-10 23:10:32 -0400940 "ubd : Failed to start I/O thread (errno = %d) - "
941 "falling back to synchronous I/O\n", -io_pid);
942 io_pid = -1;
Jeff Dikedc764e52007-05-06 14:51:41 -0700943 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400944 }
Jeff Dike6c29256c2006-03-27 01:14:37 -0800945 err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr,
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800946 IRQF_DISABLED, "ubd", ubd_devs);
Jeff Dike91acb212005-10-10 23:10:32 -0400947 if(err != 0)
948 printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err);
Jeff Dikef4c57a72006-03-31 02:30:10 -0800949 return 0;
Jeff Dike91acb212005-10-10 23:10:32 -0400950}
951
952device_initcall(ubd_driver_init);
953
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954static int ubd_open(struct inode *inode, struct file *filp)
955{
956 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800957 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 int err = 0;
959
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800960 if(ubd_dev->count == 0){
961 err = ubd_open_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 if(err){
963 printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n",
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800964 disk->disk_name, ubd_dev->file, -err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 goto out;
966 }
967 }
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800968 ubd_dev->count++;
969 set_disk_ro(disk, !ubd_dev->openflags.w);
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700970
971 /* This should no more be needed. And it didn't work anyway to exclude
972 * read-write remounting of filesystems.*/
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800973 /*if((filp->f_mode & FMODE_WRITE) && !ubd_dev->openflags.w){
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800974 if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 err = -EROFS;
Paolo 'Blaisorblade' Giarrusso2c49be92005-05-01 08:58:57 -0700976 }*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 out:
Jeff Dikedc764e52007-05-06 14:51:41 -0700978 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979}
980
981static int ubd_release(struct inode * inode, struct file * file)
982{
983 struct gendisk *disk = inode->i_bdev->bd_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800984 struct ubd *ubd_dev = disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -0800986 if(--ubd_dev->count == 0)
Paolo 'Blaisorblade' Giarrusso5f75a4f2006-10-30 22:07:06 -0800987 ubd_close_dev(ubd_dev);
Jeff Dikedc764e52007-05-06 14:51:41 -0700988 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989}
990
Jeff Dike91acb212005-10-10 23:10:32 -0400991static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask,
992 __u64 *cow_offset, unsigned long *bitmap,
993 __u64 bitmap_offset, unsigned long *bitmap_words,
994 __u64 bitmap_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995{
Jeff Dike91acb212005-10-10 23:10:32 -0400996 __u64 sector = io_offset >> 9;
997 int i, update_bitmap = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998
Jeff Dike91acb212005-10-10 23:10:32 -0400999 for(i = 0; i < length >> 9; i++){
1000 if(cow_mask != NULL)
1001 ubd_set_bit(i, (unsigned char *) cow_mask);
1002 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
1003 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
Jeff Dike91acb212005-10-10 23:10:32 -04001005 update_bitmap = 1;
1006 ubd_set_bit(sector + i, (unsigned char *) bitmap);
1007 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
Jeff Dike91acb212005-10-10 23:10:32 -04001009 if(!update_bitmap)
1010 return;
1011
1012 *cow_offset = sector / (sizeof(unsigned long) * 8);
1013
1014 /* This takes care of the case where we're exactly at the end of the
1015 * device, and *cow_offset + 1 is off the end. So, just back it up
1016 * by one word. Thanks to Lynn Kerby for the fix and James McMechan
1017 * for the original diagnosis.
1018 */
1019 if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) /
1020 sizeof(unsigned long) - 1))
1021 (*cow_offset)--;
1022
1023 bitmap_words[0] = bitmap[*cow_offset];
1024 bitmap_words[1] = bitmap[*cow_offset + 1];
1025
1026 *cow_offset *= sizeof(unsigned long);
1027 *cow_offset += bitmap_offset;
1028}
1029
1030static void cowify_req(struct io_thread_req *req, unsigned long *bitmap,
1031 __u64 bitmap_offset, __u64 bitmap_len)
1032{
1033 __u64 sector = req->offset >> 9;
1034 int i;
1035
1036 if(req->length > (sizeof(req->sector_mask) * 8) << 9)
1037 panic("Operation too long");
1038
1039 if(req->op == UBD_READ) {
1040 for(i = 0; i < req->length >> 9; i++){
1041 if(ubd_test_bit(sector + i, (unsigned char *) bitmap))
Jeff Dike6c29256c2006-03-27 01:14:37 -08001042 ubd_set_bit(i, (unsigned char *)
Jeff Dike91acb212005-10-10 23:10:32 -04001043 &req->sector_mask);
Jeff Dikedc764e52007-05-06 14:51:41 -07001044 }
Jeff Dike91acb212005-10-10 23:10:32 -04001045 }
1046 else cowify_bitmap(req->offset, req->length, &req->sector_mask,
1047 &req->cow_offset, bitmap, bitmap_offset,
1048 req->bitmap_words, bitmap_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049}
1050
Jeff Dike62f96cb2007-02-10 01:44:16 -08001051/* Called with dev->lock held */
Jeff Dikea0044bd2007-05-06 14:51:36 -07001052static void prepare_request(struct request *req, struct io_thread_req *io_req,
1053 unsigned long long offset, int page_offset,
1054 int len, struct page *page)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055{
1056 struct gendisk *disk = req->rq_disk;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001057 struct ubd *ubd_dev = disk->private_data;
Jeff Dike91acb212005-10-10 23:10:32 -04001058
Jeff Dike62f96cb2007-02-10 01:44:16 -08001059 io_req->req = req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001060 io_req->fds[0] = (ubd_dev->cow.file != NULL) ? ubd_dev->cow.fd :
1061 ubd_dev->fd;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001062 io_req->fds[1] = ubd_dev->fd;
Jeff Dike91acb212005-10-10 23:10:32 -04001063 io_req->cow_offset = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 io_req->offset = offset;
1065 io_req->length = len;
1066 io_req->error = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001067 io_req->sector_mask = 0;
1068
1069 io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 io_req->offsets[0] = 0;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001071 io_req->offsets[1] = ubd_dev->cow.data_offset;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001072 io_req->buffer = page_address(page) + page_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 io_req->sectorsize = 1 << 9;
1074
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001075 if(ubd_dev->cow.file != NULL)
Jeff Dikea0044bd2007-05-06 14:51:36 -07001076 cowify_req(io_req, ubd_dev->cow.bitmap,
1077 ubd_dev->cow.bitmap_offset, ubd_dev->cow.bitmap_len);
Jeff Dike91acb212005-10-10 23:10:32 -04001078
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079}
1080
Jeff Dike62f96cb2007-02-10 01:44:16 -08001081/* Called with dev->lock held */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082static void do_ubd_request(request_queue_t *q)
1083{
Jeff Dike2adcec22007-05-06 14:51:37 -07001084 struct io_thread_req *io_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 struct request *req;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001086 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087
Jeff Dikea0044bd2007-05-06 14:51:36 -07001088 while(1){
Jeff Dike2a9529a2007-03-29 01:20:27 -07001089 struct ubd *dev = q->queuedata;
Jeff Dikea0044bd2007-05-06 14:51:36 -07001090 if(dev->end_sg == 0){
1091 struct request *req = elv_next_request(q);
1092 if(req == NULL)
1093 return;
1094
1095 dev->request = req;
1096 blkdev_dequeue_request(req);
1097 dev->start_sg = 0;
1098 dev->end_sg = blk_rq_map_sg(q, req, dev->sg);
Jeff Dike91acb212005-10-10 23:10:32 -04001099 }
Jeff Dikea0044bd2007-05-06 14:51:36 -07001100
1101 req = dev->request;
1102 while(dev->start_sg < dev->end_sg){
1103 struct scatterlist *sg = &dev->sg[dev->start_sg];
1104
Jeff Dike2adcec22007-05-06 14:51:37 -07001105 io_req = kmalloc(sizeof(struct io_thread_req),
Peter Zijlstra990c5582007-05-06 14:51:38 -07001106 GFP_ATOMIC);
Jeff Dike2adcec22007-05-06 14:51:37 -07001107 if(io_req == NULL){
1108 if(list_empty(&dev->restart))
1109 list_add(&dev->restart, &restart);
1110 return;
1111 }
1112 prepare_request(req, io_req,
Jeff Dikea0044bd2007-05-06 14:51:36 -07001113 (unsigned long long) req->sector << 9,
1114 sg->offset, sg->length, sg->page);
1115
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001116 n = os_write_file(thread_fd, &io_req,
1117 sizeof(struct io_thread_req *));
Jeff Dike2adcec22007-05-06 14:51:37 -07001118 if(n != sizeof(struct io_thread_req *)){
Jeff Dikea0044bd2007-05-06 14:51:36 -07001119 if(n != -EAGAIN)
1120 printk("write to io thread failed, "
1121 "errno = %d\n", -n);
1122 else if(list_empty(&dev->restart))
1123 list_add(&dev->restart, &restart);
1124 return;
1125 }
1126
1127 req->sector += sg->length >> 9;
1128 dev->start_sg++;
1129 }
1130 dev->end_sg = 0;
1131 dev->request = NULL;
Jeff Dike91acb212005-10-10 23:10:32 -04001132 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133}
1134
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001135static int ubd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
1136{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001137 struct ubd *ubd_dev = bdev->bd_disk->private_data;
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001138
1139 geo->heads = 128;
1140 geo->sectors = 32;
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001141 geo->cylinders = ubd_dev->size / (128 * 32 * 512);
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08001142 return 0;
1143}
1144
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145static int ubd_ioctl(struct inode * inode, struct file * file,
1146 unsigned int cmd, unsigned long arg)
1147{
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001148 struct ubd *ubd_dev = inode->i_bdev->bd_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 struct hd_driveid ubd_id = {
1150 .cyls = 0,
1151 .heads = 128,
1152 .sectors = 32,
1153 };
1154
1155 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 struct cdrom_volctrl volume;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 case HDIO_GET_IDENTITY:
Paolo 'Blaisorblade' Giarrusso7d314e32006-10-30 22:07:05 -08001158 ubd_id.cyls = ubd_dev->size / (128 * 32 * 512);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 if(copy_to_user((char __user *) arg, (char *) &ubd_id,
1160 sizeof(ubd_id)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001161 return -EFAULT;
1162 return 0;
Jeff Dikeb8831a12007-02-10 01:44:17 -08001163
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 case CDROMVOLREAD:
1165 if(copy_from_user(&volume, (char __user *) arg, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001166 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 volume.channel0 = 255;
1168 volume.channel1 = 255;
1169 volume.channel2 = 255;
1170 volume.channel3 = 255;
1171 if(copy_to_user((char __user *) arg, &volume, sizeof(volume)))
Jeff Dikedc764e52007-05-06 14:51:41 -07001172 return -EFAULT;
1173 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001175 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176}
1177
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001178static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179{
1180 struct uml_stat buf1, buf2;
1181 int err;
1182
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001183 if(from_cmdline == NULL)
1184 return 0;
1185 if(!strcmp(from_cmdline, from_cow))
1186 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187
1188 err = os_stat_file(from_cmdline, &buf1);
1189 if(err < 0){
1190 printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001191 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 }
1193 err = os_stat_file(from_cow, &buf2);
1194 if(err < 0){
1195 printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001196 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 }
1198 if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001199 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200
1201 printk("Backing file mismatch - \"%s\" requested,\n"
1202 "\"%s\" specified in COW header of \"%s\"\n",
1203 from_cmdline, from_cow, cow);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001204 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205}
1206
1207static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
1208{
1209 unsigned long modtime;
Paolo 'Blaisorblade' Giarrussofe1db502006-02-24 13:03:58 -08001210 unsigned long long actual;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 int err;
1212
1213 err = os_file_modtime(file, &modtime);
1214 if(err < 0){
1215 printk("Failed to get modification time of backing file "
1216 "\"%s\", err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001217 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 }
1219
1220 err = os_file_size(file, &actual);
1221 if(err < 0){
1222 printk("Failed to get size of backing file \"%s\", "
1223 "err = %d\n", file, -err);
Jeff Dikedc764e52007-05-06 14:51:41 -07001224 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 }
1226
Jeff Dikedc764e52007-05-06 14:51:41 -07001227 if(actual != size){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 /*__u64 can be a long on AMD64 and with %lu GCC complains; so
1229 * the typecast.*/
1230 printk("Size mismatch (%llu vs %llu) of COW header vs backing "
1231 "file\n", (unsigned long long) size, actual);
Jeff Dikedc764e52007-05-06 14:51:41 -07001232 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 }
1234 if(modtime != mtime){
1235 printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
1236 "file\n", mtime, modtime);
Jeff Dikedc764e52007-05-06 14:51:41 -07001237 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 }
Jeff Dikedc764e52007-05-06 14:51:41 -07001239 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240}
1241
1242int read_cow_bitmap(int fd, void *buf, int offset, int len)
1243{
1244 int err;
1245
1246 err = os_seek_file(fd, offset);
1247 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001248 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249
Jeff Dikedc764e52007-05-06 14:51:41 -07001250 err = os_read_file(fd, buf, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 if(err < 0)
Jeff Dikedc764e52007-05-06 14:51:41 -07001252 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
Jeff Dikedc764e52007-05-06 14:51:41 -07001254 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255}
1256
Jeff Dike6c29256c2006-03-27 01:14:37 -08001257int open_ubd_file(char *file, struct openflags *openflags, int shared,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 char **backing_file_out, int *bitmap_offset_out,
1259 unsigned long *bitmap_len_out, int *data_offset_out,
1260 int *create_cow_out)
1261{
1262 time_t mtime;
1263 unsigned long long size;
1264 __u32 version, align;
1265 char *backing_file;
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001266 int fd, err, sectorsize, asked_switch, mode = 0644;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267
1268 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001269 if (fd < 0) {
1270 if ((fd == -ENOENT) && (create_cow_out != NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 *create_cow_out = 1;
Jeff Dikedc764e52007-05-06 14:51:41 -07001272 if (!openflags->w ||
1273 ((fd != -EROFS) && (fd != -EACCES)))
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001274 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 openflags->w = 0;
1276 fd = os_open_file(file, *openflags, mode);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001277 if (fd < 0)
1278 return fd;
Jeff Dikedc764e52007-05-06 14:51:41 -07001279 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280
Jeff Dike6c29256c2006-03-27 01:14:37 -08001281 if(shared)
1282 printk("Not locking \"%s\" on the host\n", file);
1283 else {
1284 err = os_lock_file(fd, openflags->w);
1285 if(err < 0){
1286 printk("Failed to lock '%s', err = %d\n", file, -err);
1287 goto out_close;
1288 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 }
1290
Andreas Mohrd6e05ed2006-06-26 18:35:02 +02001291 /* Successful return case! */
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001292 if(backing_file_out == NULL)
Jeff Dikedc764e52007-05-06 14:51:41 -07001293 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
1295 err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
1296 &size, &sectorsize, &align, bitmap_offset_out);
1297 if(err && (*backing_file_out != NULL)){
1298 printk("Failed to read COW header from COW file \"%s\", "
1299 "errno = %d\n", file, -err);
1300 goto out_close;
1301 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001302 if(err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001303 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001305 asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001307 /* Allow switching only if no mismatch. */
1308 if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 printk("Switching backing file to '%s'\n", *backing_file_out);
1310 err = write_cow_header(file, fd, *backing_file_out,
1311 sectorsize, align, &size);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001312 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 printk("Switch failed, errno = %d\n", -err);
Paolo 'Blaisorblade' Giarrusso4833aff2006-01-18 17:43:00 -08001314 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 }
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001316 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 *backing_file_out = backing_file;
1318 err = backing_file_mismatch(*backing_file_out, size, mtime);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001319 if (err)
1320 goto out_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 }
1322
1323 cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
1324 bitmap_len_out, data_offset_out);
1325
Jeff Dikedc764e52007-05-06 14:51:41 -07001326 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 out_close:
1328 os_close_file(fd);
Paolo 'Blaisorblade' Giarrussoa374a482006-01-18 17:43:01 -08001329 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330}
1331
1332int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
1333 int sectorsize, int alignment, int *bitmap_offset_out,
1334 unsigned long *bitmap_len_out, int *data_offset_out)
1335{
1336 int err, fd;
1337
1338 flags.c = 1;
Jeff Dike6c29256c2006-03-27 01:14:37 -08001339 fd = open_ubd_file(cow_file, &flags, 0, NULL, NULL, NULL, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 if(fd < 0){
1341 err = fd;
1342 printk("Open of COW file '%s' failed, errno = %d\n", cow_file,
1343 -err);
1344 goto out;
1345 }
1346
1347 err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
1348 bitmap_offset_out, bitmap_len_out,
1349 data_offset_out);
1350 if(!err)
Jeff Dikedc764e52007-05-06 14:51:41 -07001351 return fd;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 os_close_file(fd);
1353 out:
Jeff Dikedc764e52007-05-06 14:51:41 -07001354 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355}
1356
Jeff Dike91acb212005-10-10 23:10:32 -04001357static int update_bitmap(struct io_thread_req *req)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358{
Jeff Dike91acb212005-10-10 23:10:32 -04001359 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
Jeff Dike91acb212005-10-10 23:10:32 -04001361 if(req->cow_offset == -1)
Jeff Dikedc764e52007-05-06 14:51:41 -07001362 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
Jeff Dike91acb212005-10-10 23:10:32 -04001364 n = os_seek_file(req->fds[1], req->cow_offset);
1365 if(n < 0){
1366 printk("do_io - bitmap lseek failed : err = %d\n", -n);
Jeff Dikedc764e52007-05-06 14:51:41 -07001367 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001368 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001370 n = os_write_file(req->fds[1], &req->bitmap_words,
1371 sizeof(req->bitmap_words));
Jeff Dike91acb212005-10-10 23:10:32 -04001372 if(n != sizeof(req->bitmap_words)){
1373 printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
1374 req->fds[1]);
Jeff Dikedc764e52007-05-06 14:51:41 -07001375 return 1;
Jeff Dike91acb212005-10-10 23:10:32 -04001376 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Jeff Dikedc764e52007-05-06 14:51:41 -07001378 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379}
Jeff Dike91acb212005-10-10 23:10:32 -04001380
1381void do_io(struct io_thread_req *req)
1382{
1383 char *buf;
1384 unsigned long len;
1385 int n, nsectors, start, end, bit;
1386 int err;
1387 __u64 off;
1388
1389 nsectors = req->length / req->sectorsize;
1390 start = 0;
1391 do {
1392 bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask);
1393 end = start;
1394 while((end < nsectors) &&
1395 (ubd_test_bit(end, (unsigned char *)
1396 &req->sector_mask) == bit))
1397 end++;
1398
1399 off = req->offset + req->offsets[bit] +
1400 start * req->sectorsize;
1401 len = (end - start) * req->sectorsize;
1402 buf = &req->buffer[start * req->sectorsize];
1403
1404 err = os_seek_file(req->fds[bit], off);
1405 if(err < 0){
1406 printk("do_io - lseek failed : err = %d\n", -err);
1407 req->error = 1;
1408 return;
1409 }
1410 if(req->op == UBD_READ){
1411 n = 0;
1412 do {
1413 buf = &buf[n];
1414 len -= n;
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001415 n = os_read_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001416 if (n < 0) {
1417 printk("do_io - read failed, err = %d "
1418 "fd = %d\n", -n, req->fds[bit]);
1419 req->error = 1;
1420 return;
1421 }
1422 } while((n < len) && (n != 0));
1423 if (n < len) memset(&buf[n], 0, len - n);
1424 } else {
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001425 n = os_write_file(req->fds[bit], buf, len);
Jeff Dike91acb212005-10-10 23:10:32 -04001426 if(n != len){
1427 printk("do_io - write failed err = %d "
1428 "fd = %d\n", -n, req->fds[bit]);
1429 req->error = 1;
1430 return;
1431 }
1432 }
1433
1434 start = end;
1435 } while(start < nsectors);
1436
1437 req->error = update_bitmap(req);
1438}
1439
1440/* Changed in start_io_thread, which is serialized by being called only
1441 * from ubd_init, which is an initcall.
1442 */
1443int kernel_fd = -1;
1444
Paolo 'Blaisorblade' Giarrussod8d7c282006-10-30 22:07:12 -08001445/* Only changed by the io thread. XXX: currently unused. */
1446static int io_count = 0;
Jeff Dike91acb212005-10-10 23:10:32 -04001447
1448int io_thread(void *arg)
1449{
Jeff Dike2adcec22007-05-06 14:51:37 -07001450 struct io_thread_req *req;
Jeff Dike91acb212005-10-10 23:10:32 -04001451 int n;
1452
1453 ignore_sigwinch_sig();
1454 while(1){
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001455 n = os_read_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001456 sizeof(struct io_thread_req *));
1457 if(n != sizeof(struct io_thread_req *)){
Jeff Dike91acb212005-10-10 23:10:32 -04001458 if(n < 0)
1459 printk("io_thread - read failed, fd = %d, "
1460 "err = %d\n", kernel_fd, -n);
1461 else {
1462 printk("io_thread - short read, fd = %d, "
1463 "length = %d\n", kernel_fd, n);
1464 }
1465 continue;
1466 }
1467 io_count++;
Jeff Dike2adcec22007-05-06 14:51:37 -07001468 do_io(req);
Jeff Dikea6ea4cc2007-05-06 14:51:43 -07001469 n = os_write_file(kernel_fd, &req,
Jeff Dike2adcec22007-05-06 14:51:37 -07001470 sizeof(struct io_thread_req *));
1471 if(n != sizeof(struct io_thread_req *))
Jeff Dike91acb212005-10-10 23:10:32 -04001472 printk("io_thread - write failed, fd = %d, err = %d\n",
1473 kernel_fd, -n);
1474 }
Jeff Dike91acb212005-10-10 23:10:32 -04001475
Jeff Dike1b57e9c2006-01-06 00:18:49 -08001476 return 0;
1477}