This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / block / mambo_bd.c
1 /*
2  *  Bogus Block Driver for PowerPC Full System Simulator
3  *
4  *  (C) Copyright IBM Corporation 2003-2005
5  *
6  *  Bogus Disk Driver
7  *
8  *  Author: Eric Van Hensbegren <ericvh@gmail.com>
9  *
10  *    inspired by drivers/block/nbd.c
11  *    written by Pavel Machek and Steven Whitehouse
12  *
13  *  Some code is from the IBM Full System Simulator Group in ARL
14  *  Author: PAtrick Bohrer <IBM Austin Research Lab>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2, or (at your option)
19  * any later version.
20  * 
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  * 
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to:
28  * Free Software Foundation
29  * 51 Franklin Street, Fifth Floor
30  * Boston, MA  02111-1301  USA
31  *
32  */
33
34 #include <linux/major.h>
35 #include <linux/kernel.h>
36 #include <linux/module.h>
37 #include <linux/init.h>
38 #include <linux/sched.h>
39 #include <linux/fs.h>
40 #include <linux/stat.h>
41 #include <linux/errno.h>
42 #include <linux/file.h>
43 #include <linux/ioctl.h>
44 #include <linux/blkdev.h>
45 #include <net/sock.h>
46
47 #include <asm/systemsim.h>
48
49 #include <linux/devfs_fs_kernel.h>
50
51 #include <asm/uaccess.h>
52 #include <asm/types.h>
53
54 #define MAJOR_NR 112
55 #define MAX_MBD 128
56
57 #define MBD_SET_BLKSIZE _IO( 0xab, 1 )
58 #define MBD_SET_SIZE    _IO( 0xab, 2 )
59 #define MBD_SET_SIZE_BLOCKS     _IO( 0xab, 7 )
60 #define MBD_DISCONNECT  _IO( 0xab, 8 )
61
62 struct mbd_device {
63         int initialized;
64         int refcnt;
65         int flags;
66         struct gendisk *disk;
67 };
68
69 static struct mbd_device mbd_dev[MAX_MBD];
70
71 #define BD_INFO_SYNC   0
72 #define BD_INFO_STATUS 1
73 #define BD_INFO_BLKSZ  2
74 #define BD_INFO_DEVSZ  3
75 #define BD_INFO_CHANGE 4
76
77 #define BOGUS_DISK_READ  116
78 #define BOGUS_DISK_WRITE 117
79 #define BOGUS_DISK_INFO  118
80
81 static inline int
82 MamboBogusDiskRead(int devno, void *buf, ulong sect, ulong nrsect)
83 {
84         return callthru3(BOGUS_DISK_READ, (unsigned long)buf,
85                          (unsigned long)sect,
86                          (unsigned long)((nrsect << 16) | devno));
87 }
88
89 static inline int
90 MamboBogusDiskWrite(int devno, void *buf, ulong sect, ulong nrsect)
91 {
92         return callthru3(BOGUS_DISK_WRITE, (unsigned long)buf,
93                          (unsigned long)sect,
94                          (unsigned long)((nrsect << 16) | devno));
95 }
96
97 static inline int MamboBogusDiskInfo(int op, int devno)
98 {
99         return callthru2(BOGUS_DISK_INFO, (unsigned long)op,
100                          (unsigned long)devno);
101 }
102
103 static int mbd_init_disk(int devno)
104 {
105         struct gendisk *disk = mbd_dev[devno].disk;
106         unsigned int sz;
107
108         if (!__onsim())
109                 return -1;
110
111         /* check disk configured */
112         if (!MamboBogusDiskInfo(BD_INFO_STATUS, devno)) {
113                 printk(KERN_ERR
114                        "Attempting to open bogus disk before initializaiton\n");
115                 return 0;
116         }
117
118         mbd_dev[devno].initialized++;
119
120         sz = MamboBogusDiskInfo(BD_INFO_DEVSZ, devno);
121
122         if (sz == -1)
123                 return 0;
124
125         printk("Initializing disk %d with devsz %u\n", devno, sz);
126
127         set_capacity(disk, sz << 1);
128
129         return 1;
130 }
131
132 static void do_mbd_request(request_queue_t * q)
133 {
134         int result = 0;
135         struct request *req;
136
137         while ((req = elv_next_request(q)) != NULL) {
138                 int minor = req->rq_disk->first_minor;
139
140                 switch (rq_data_dir(req)) {
141                 case READ:
142                         result = MamboBogusDiskRead(minor,
143                                                     req->buffer, req->sector,
144                                                     req->current_nr_sectors);
145                         break;
146                 case WRITE:
147                         result = MamboBogusDiskWrite(minor,
148                                                      req->buffer, req->sector,
149                                                      req->current_nr_sectors);
150                 };
151
152                 if (result)
153                         end_request(req, 0);    /* failure */
154                 else
155                         end_request(req, 1);    /* success */
156         }
157 }
158
159 static int mbd_release(struct inode *inode, struct file *file)
160 {
161         struct mbd_device *lo;
162         int dev;
163
164         if (!inode)
165                 return -ENODEV;
166         dev = inode->i_bdev->bd_disk->first_minor;
167         if (dev >= MAX_MBD)
168                 return -ENODEV;
169         if (MamboBogusDiskInfo(BD_INFO_SYNC, dev) < 0) {
170                 printk(KERN_ALERT "mbd_release: unable to sync\n");
171         }
172         lo = &mbd_dev[dev];
173         if (lo->refcnt <= 0)
174                 printk(KERN_ALERT "mbd_release: refcount(%d) <= 0\n",
175                        lo->refcnt);
176         lo->refcnt--;
177         return 0;
178 }
179
180 static int mbd_revalidate(struct gendisk *disk)
181 {
182         int devno = disk->first_minor;
183
184         mbd_init_disk(devno);
185
186         return 0;
187 }
188
189 static int mbd_open(struct inode *inode, struct file *file)
190 {
191         int dev;
192
193         if (!inode)
194                 return -EINVAL;
195         dev = inode->i_bdev->bd_disk->first_minor;
196         if (dev >= MAX_MBD)
197                 return -ENODEV;
198
199         check_disk_change(inode->i_bdev);
200
201         if (!mbd_dev[dev].initialized)
202                 if (!mbd_init_disk(dev))
203                         return -ENODEV;
204
205         mbd_dev[dev].refcnt++;
206         return 0;
207 }
208
209 static struct block_device_operations mbd_fops = {
210       owner:THIS_MODULE,
211       open:mbd_open,
212       release:mbd_release,
213         /* media_changed:      mbd_check_change, */
214       revalidate_disk:mbd_revalidate,
215 };
216
217 static spinlock_t mbd_lock = SPIN_LOCK_UNLOCKED;
218
219 static int __init mbd_init(void)
220 {
221         int err = -ENOMEM;
222         int i;
223
224         for (i = 0; i < MAX_MBD; i++) {
225                 struct gendisk *disk = alloc_disk(1);
226                 if (!disk)
227                         goto out;
228                 mbd_dev[i].disk = disk;
229                 /*
230                  * The new linux 2.5 block layer implementation requires
231                  * every gendisk to have its very own request_queue struct.
232                  * These structs are big so we dynamically allocate them.
233                  */
234                 disk->queue = blk_init_queue(do_mbd_request, &mbd_lock);
235                 if (!disk->queue) {
236                         put_disk(disk);
237                         goto out;
238                 }
239         }
240
241         if (register_blkdev(MAJOR_NR, "mbd")) {
242                 err = -EIO;
243                 goto out;
244         }
245 #ifdef MODULE
246         printk("mambo bogus disk: registered device at major %d\n", MAJOR_NR);
247 #else
248         printk("mambo bogus disk: compiled in with kernel\n");
249 #endif
250
251         devfs_mk_dir("mambobd");
252         for (i = 0; i < MAX_MBD; i++) { /* load defaults */
253                 struct gendisk *disk = mbd_dev[i].disk;
254                 mbd_dev[i].initialized = 0;
255                 mbd_dev[i].refcnt = 0;
256                 mbd_dev[i].flags = 0;
257                 disk->major = MAJOR_NR;
258                 disk->first_minor = i;
259                 disk->fops = &mbd_fops;
260                 disk->private_data = &mbd_dev[i];
261                 sprintf(disk->disk_name, "mambobd%d", i);
262                 sprintf(disk->devfs_name, "mambobd%d", i);
263                 set_capacity(disk, 0x7ffffc00ULL << 1); /* 2 TB */
264                 add_disk(disk);
265         }
266
267         return 0;
268       out:
269         while (i--) {
270                 if (mbd_dev[i].disk->queue)
271                         blk_cleanup_queue(mbd_dev[i].disk->queue);
272                 put_disk(mbd_dev[i].disk);
273         }
274         return -EIO;
275 }
276
277 static void __exit mbd_cleanup(void)
278 {
279         devfs_remove("mambobd");
280
281         if (unregister_blkdev(MAJOR_NR, "mbd") != 0)
282                 printk("mbd: cleanup_module failed\n");
283         else
284                 printk("mbd: module cleaned up.\n");
285 }
286
287 module_init(mbd_init);
288 module_exit(mbd_cleanup);
289
290 MODULE_DESCRIPTION("Mambo Block Device");
291 MODULE_LICENSE("GPL");