ia64/linux-2.6.18-xen.hg

view drivers/block/nbd.c @ 897:329ea0ccb344

balloon: try harder to balloon up under memory pressure.

Currently if the balloon driver is unable to increase the guest's
reservation it assumes the failure was due to reaching its full
allocation, gives up on the ballooning operation and records the limit
it reached as the "hard limit". The driver will not try again until
the target is set again (even to the same value).

However it is possible that ballooning has in fact failed due to
memory pressure in the host and therefore it is desirable to keep
attempting to reach the target in case memory becomes available. The
most likely scenario is that some guests are ballooning down while
others are ballooning up and therefore there is temporary memory
pressure while things stabilise. You would not expect a well behaved
toolstack to ask a domain to balloon to more than its allocation nor
would you expect it to deliberately over-commit memory by setting
balloon targets which exceed the total host memory.

This patch drops the concept of a hard limit and causes the balloon
driver to retry increasing the reservation on a timer in the same
manner as when decreasing the reservation.

Also if we partially succeed in increasing the reservation
(i.e. receive less pages than we asked for) then we may as well keep
those pages rather than returning them to Xen.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 05 14:01:20 2009 +0100 (2009-06-05)
parents 831230e53067
children
line source
1 /*
2 * Network block device - make block devices work over TCP
3 *
4 * Note that you can not swap over this thing, yet. Seems to work but
5 * deadlocks sometimes - you can not swap over TCP in general.
6 *
7 * Copyright 1997-2000 Pavel Machek <pavel@ucw.cz>
8 * Parts copyright 2001 Steven Whitehouse <steve@chygwyn.com>
9 *
10 * This file is released under GPLv2 or later.
11 *
12 * (part of code stolen from loop.c)
13 */
15 #include <linux/major.h>
17 #include <linux/blkdev.h>
18 #include <linux/module.h>
19 #include <linux/init.h>
20 #include <linux/sched.h>
21 #include <linux/fs.h>
22 #include <linux/bio.h>
23 #include <linux/stat.h>
24 #include <linux/errno.h>
25 #include <linux/file.h>
26 #include <linux/ioctl.h>
27 #include <linux/compiler.h>
28 #include <linux/err.h>
29 #include <linux/kernel.h>
30 #include <net/sock.h>
32 #include <asm/uaccess.h>
33 #include <asm/system.h>
34 #include <asm/types.h>
36 #include <linux/nbd.h>
38 #define LO_MAGIC 0x68797548
40 #ifdef NDEBUG
41 #define dprintk(flags, fmt...)
42 #else /* NDEBUG */
43 #define dprintk(flags, fmt...) do { \
44 if (debugflags & (flags)) printk(KERN_DEBUG fmt); \
45 } while (0)
46 #define DBG_IOCTL 0x0004
47 #define DBG_INIT 0x0010
48 #define DBG_EXIT 0x0020
49 #define DBG_BLKDEV 0x0100
50 #define DBG_RX 0x0200
51 #define DBG_TX 0x0400
52 static unsigned int debugflags;
53 #endif /* NDEBUG */
55 static unsigned int nbds_max = 16;
56 static struct nbd_device nbd_dev[MAX_NBD];
58 /*
59 * Use just one lock (or at most 1 per NIC). Two arguments for this:
60 * 1. Each NIC is essentially a synchronization point for all servers
61 * accessed through that NIC so there's no need to have more locks
62 * than NICs anyway.
63 * 2. More locks lead to more "Dirty cache line bouncing" which will slow
64 * down each lock to the point where they're actually slower than just
65 * a single lock.
66 * Thanks go to Jens Axboe and Al Viro for their LKML emails explaining this!
67 */
68 static DEFINE_SPINLOCK(nbd_lock);
70 #ifndef NDEBUG
71 static const char *ioctl_cmd_to_ascii(int cmd)
72 {
73 switch (cmd) {
74 case NBD_SET_SOCK: return "set-sock";
75 case NBD_SET_BLKSIZE: return "set-blksize";
76 case NBD_SET_SIZE: return "set-size";
77 case NBD_DO_IT: return "do-it";
78 case NBD_CLEAR_SOCK: return "clear-sock";
79 case NBD_CLEAR_QUE: return "clear-que";
80 case NBD_PRINT_DEBUG: return "print-debug";
81 case NBD_SET_SIZE_BLOCKS: return "set-size-blocks";
82 case NBD_DISCONNECT: return "disconnect";
83 case BLKROSET: return "set-read-only";
84 case BLKFLSBUF: return "flush-buffer-cache";
85 }
86 return "unknown";
87 }
89 static const char *nbdcmd_to_ascii(int cmd)
90 {
91 switch (cmd) {
92 case NBD_CMD_READ: return "read";
93 case NBD_CMD_WRITE: return "write";
94 case NBD_CMD_DISC: return "disconnect";
95 }
96 return "invalid";
97 }
98 #endif /* NDEBUG */
100 static void nbd_end_request(struct request *req)
101 {
102 int uptodate = (req->errors == 0) ? 1 : 0;
103 request_queue_t *q = req->q;
104 unsigned long flags;
106 dprintk(DBG_BLKDEV, "%s: request %p: %s\n", req->rq_disk->disk_name,
107 req, uptodate? "done": "failed");
109 spin_lock_irqsave(q->queue_lock, flags);
110 if (!end_that_request_first(req, uptodate, req->nr_sectors)) {
111 end_that_request_last(req, uptodate);
112 }
113 spin_unlock_irqrestore(q->queue_lock, flags);
114 }
116 /*
117 * Send or receive packet.
118 */
119 static int sock_xmit(struct socket *sock, int send, void *buf, int size,
120 int msg_flags)
121 {
122 int result;
123 struct msghdr msg;
124 struct kvec iov;
125 unsigned long flags;
126 sigset_t oldset;
128 /* Allow interception of SIGKILL only
129 * Don't allow other signals to interrupt the transmission */
130 spin_lock_irqsave(&current->sighand->siglock, flags);
131 oldset = current->blocked;
132 sigfillset(&current->blocked);
133 sigdelsetmask(&current->blocked, sigmask(SIGKILL));
134 recalc_sigpending();
135 spin_unlock_irqrestore(&current->sighand->siglock, flags);
137 do {
138 sock->sk->sk_allocation = GFP_NOIO;
139 iov.iov_base = buf;
140 iov.iov_len = size;
141 msg.msg_name = NULL;
142 msg.msg_namelen = 0;
143 msg.msg_control = NULL;
144 msg.msg_controllen = 0;
145 msg.msg_flags = msg_flags | MSG_NOSIGNAL;
147 if (send)
148 result = kernel_sendmsg(sock, &msg, &iov, 1, size);
149 else
150 result = kernel_recvmsg(sock, &msg, &iov, 1, size, 0);
152 if (signal_pending(current)) {
153 siginfo_t info;
154 spin_lock_irqsave(&current->sighand->siglock, flags);
155 printk(KERN_WARNING "nbd (pid %d: %s) got signal %d\n",
156 current->pid, current->comm,
157 dequeue_signal(current, &current->blocked, &info));
158 spin_unlock_irqrestore(&current->sighand->siglock, flags);
159 result = -EINTR;
160 break;
161 }
163 if (result <= 0) {
164 if (result == 0)
165 result = -EPIPE; /* short read */
166 break;
167 }
168 size -= result;
169 buf += result;
170 } while (size > 0);
172 spin_lock_irqsave(&current->sighand->siglock, flags);
173 current->blocked = oldset;
174 recalc_sigpending();
175 spin_unlock_irqrestore(&current->sighand->siglock, flags);
177 return result;
178 }
180 static inline int sock_send_bvec(struct socket *sock, struct bio_vec *bvec,
181 int flags)
182 {
183 int result;
184 void *kaddr = kmap(bvec->bv_page);
185 result = sock_xmit(sock, 1, kaddr + bvec->bv_offset, bvec->bv_len,
186 flags);
187 kunmap(bvec->bv_page);
188 return result;
189 }
191 static int nbd_send_req(struct nbd_device *lo, struct request *req)
192 {
193 int result, i, flags;
194 struct nbd_request request;
195 unsigned long size = req->nr_sectors << 9;
196 struct socket *sock = lo->sock;
198 request.magic = htonl(NBD_REQUEST_MAGIC);
199 request.type = htonl(nbd_cmd(req));
200 request.from = cpu_to_be64((u64) req->sector << 9);
201 request.len = htonl(size);
202 memcpy(request.handle, &req, sizeof(req));
204 dprintk(DBG_TX, "%s: request %p: sending control (%s@%llu,%luB)\n",
205 lo->disk->disk_name, req,
206 nbdcmd_to_ascii(nbd_cmd(req)),
207 (unsigned long long)req->sector << 9,
208 req->nr_sectors << 9);
209 result = sock_xmit(sock, 1, &request, sizeof(request),
210 (nbd_cmd(req) == NBD_CMD_WRITE)? MSG_MORE: 0);
211 if (result <= 0) {
212 printk(KERN_ERR "%s: Send control failed (result %d)\n",
213 lo->disk->disk_name, result);
214 goto error_out;
215 }
217 if (nbd_cmd(req) == NBD_CMD_WRITE) {
218 struct bio *bio;
219 /*
220 * we are really probing at internals to determine
221 * whether to set MSG_MORE or not...
222 */
223 rq_for_each_bio(bio, req) {
224 struct bio_vec *bvec;
225 bio_for_each_segment(bvec, bio, i) {
226 flags = 0;
227 if ((i < (bio->bi_vcnt - 1)) || bio->bi_next)
228 flags = MSG_MORE;
229 dprintk(DBG_TX, "%s: request %p: sending %d bytes data\n",
230 lo->disk->disk_name, req,
231 bvec->bv_len);
232 result = sock_send_bvec(sock, bvec, flags);
233 if (result <= 0) {
234 printk(KERN_ERR "%s: Send data failed (result %d)\n",
235 lo->disk->disk_name,
236 result);
237 goto error_out;
238 }
239 }
240 }
241 }
242 return 0;
244 error_out:
245 return 1;
246 }
248 static struct request *nbd_find_request(struct nbd_device *lo, char *handle)
249 {
250 struct request *req;
251 struct list_head *tmp;
252 struct request *xreq;
253 int err;
255 memcpy(&xreq, handle, sizeof(xreq));
257 err = wait_event_interruptible(lo->active_wq, lo->active_req != xreq);
258 if (unlikely(err))
259 goto out;
261 spin_lock(&lo->queue_lock);
262 list_for_each(tmp, &lo->queue_head) {
263 req = list_entry(tmp, struct request, queuelist);
264 if (req != xreq)
265 continue;
266 list_del_init(&req->queuelist);
267 spin_unlock(&lo->queue_lock);
268 return req;
269 }
270 spin_unlock(&lo->queue_lock);
272 err = -ENOENT;
274 out:
275 return ERR_PTR(err);
276 }
278 static inline int sock_recv_bvec(struct socket *sock, struct bio_vec *bvec)
279 {
280 int result;
281 void *kaddr = kmap(bvec->bv_page);
282 result = sock_xmit(sock, 0, kaddr + bvec->bv_offset, bvec->bv_len,
283 MSG_WAITALL);
284 kunmap(bvec->bv_page);
285 return result;
286 }
288 /* NULL returned = something went wrong, inform userspace */
289 static struct request *nbd_read_stat(struct nbd_device *lo)
290 {
291 int result;
292 struct nbd_reply reply;
293 struct request *req;
294 struct socket *sock = lo->sock;
296 reply.magic = 0;
297 result = sock_xmit(sock, 0, &reply, sizeof(reply), MSG_WAITALL);
298 if (result <= 0) {
299 printk(KERN_ERR "%s: Receive control failed (result %d)\n",
300 lo->disk->disk_name, result);
301 goto harderror;
302 }
304 if (ntohl(reply.magic) != NBD_REPLY_MAGIC) {
305 printk(KERN_ERR "%s: Wrong magic (0x%lx)\n",
306 lo->disk->disk_name,
307 (unsigned long)ntohl(reply.magic));
308 result = -EPROTO;
309 goto harderror;
310 }
312 req = nbd_find_request(lo, reply.handle);
313 if (unlikely(IS_ERR(req))) {
314 result = PTR_ERR(req);
315 if (result != -ENOENT)
316 goto harderror;
318 printk(KERN_ERR "%s: Unexpected reply (%p)\n",
319 lo->disk->disk_name, reply.handle);
320 result = -EBADR;
321 goto harderror;
322 }
324 if (ntohl(reply.error)) {
325 printk(KERN_ERR "%s: Other side returned error (%d)\n",
326 lo->disk->disk_name, ntohl(reply.error));
327 req->errors++;
328 return req;
329 }
331 dprintk(DBG_RX, "%s: request %p: got reply\n",
332 lo->disk->disk_name, req);
333 if (nbd_cmd(req) == NBD_CMD_READ) {
334 int i;
335 struct bio *bio;
336 rq_for_each_bio(bio, req) {
337 struct bio_vec *bvec;
338 bio_for_each_segment(bvec, bio, i) {
339 result = sock_recv_bvec(sock, bvec);
340 if (result <= 0) {
341 printk(KERN_ERR "%s: Receive data failed (result %d)\n",
342 lo->disk->disk_name,
343 result);
344 req->errors++;
345 return req;
346 }
347 dprintk(DBG_RX, "%s: request %p: got %d bytes data\n",
348 lo->disk->disk_name, req, bvec->bv_len);
349 }
350 }
351 }
352 return req;
353 harderror:
354 lo->harderror = result;
355 return NULL;
356 }
358 static void nbd_do_it(struct nbd_device *lo)
359 {
360 struct request *req;
362 BUG_ON(lo->magic != LO_MAGIC);
364 while ((req = nbd_read_stat(lo)) != NULL)
365 nbd_end_request(req);
366 return;
367 }
369 static void nbd_clear_que(struct nbd_device *lo)
370 {
371 struct request *req;
373 BUG_ON(lo->magic != LO_MAGIC);
375 /*
376 * Because we have set lo->sock to NULL under the tx_lock, all
377 * modifications to the list must have completed by now. For
378 * the same reason, the active_req must be NULL.
379 *
380 * As a consequence, we don't need to take the spin lock while
381 * purging the list here.
382 */
383 BUG_ON(lo->sock);
384 BUG_ON(lo->active_req);
386 while (!list_empty(&lo->queue_head)) {
387 req = list_entry(lo->queue_head.next, struct request,
388 queuelist);
389 list_del_init(&req->queuelist);
390 req->errors++;
391 nbd_end_request(req);
392 }
393 }
395 /*
396 * We always wait for result of write, for now. It would be nice to make it optional
397 * in future
398 * if ((req->cmd == WRITE) && (lo->flags & NBD_WRITE_NOCHK))
399 * { printk( "Warning: Ignoring result!\n"); nbd_end_request( req ); }
400 */
402 static void do_nbd_request(request_queue_t * q)
403 {
404 struct request *req;
406 while ((req = elv_next_request(q)) != NULL) {
407 struct nbd_device *lo;
409 blkdev_dequeue_request(req);
410 dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%lx)\n",
411 req->rq_disk->disk_name, req, req->flags);
413 if (!(req->flags & REQ_CMD))
414 goto error_out;
416 lo = req->rq_disk->private_data;
418 BUG_ON(lo->magic != LO_MAGIC);
420 nbd_cmd(req) = NBD_CMD_READ;
421 if (rq_data_dir(req) == WRITE) {
422 nbd_cmd(req) = NBD_CMD_WRITE;
423 if (lo->flags & NBD_READ_ONLY) {
424 printk(KERN_ERR "%s: Write on read-only\n",
425 lo->disk->disk_name);
426 goto error_out;
427 }
428 }
430 req->errors = 0;
431 spin_unlock_irq(q->queue_lock);
433 mutex_lock(&lo->tx_lock);
434 if (unlikely(!lo->sock)) {
435 mutex_unlock(&lo->tx_lock);
436 printk(KERN_ERR "%s: Attempted send on closed socket\n",
437 lo->disk->disk_name);
438 req->errors++;
439 nbd_end_request(req);
440 spin_lock_irq(q->queue_lock);
441 continue;
442 }
444 lo->active_req = req;
446 if (nbd_send_req(lo, req) != 0) {
447 printk(KERN_ERR "%s: Request send failed\n",
448 lo->disk->disk_name);
449 req->errors++;
450 nbd_end_request(req);
451 } else {
452 spin_lock(&lo->queue_lock);
453 list_add(&req->queuelist, &lo->queue_head);
454 spin_unlock(&lo->queue_lock);
455 }
457 lo->active_req = NULL;
458 mutex_unlock(&lo->tx_lock);
459 wake_up_all(&lo->active_wq);
461 spin_lock_irq(q->queue_lock);
462 continue;
464 error_out:
465 req->errors++;
466 spin_unlock(q->queue_lock);
467 nbd_end_request(req);
468 spin_lock(q->queue_lock);
469 }
470 return;
471 }
473 static int nbd_ioctl(struct inode *inode, struct file *file,
474 unsigned int cmd, unsigned long arg)
475 {
476 struct nbd_device *lo = inode->i_bdev->bd_disk->private_data;
477 int error;
478 struct request sreq ;
480 if (!capable(CAP_SYS_ADMIN))
481 return -EPERM;
483 BUG_ON(lo->magic != LO_MAGIC);
485 /* Anyone capable of this syscall can do *real bad* things */
486 dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n",
487 lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg);
489 switch (cmd) {
490 case NBD_DISCONNECT:
491 printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name);
492 sreq.flags = REQ_SPECIAL;
493 nbd_cmd(&sreq) = NBD_CMD_DISC;
494 /*
495 * Set these to sane values in case server implementation
496 * fails to check the request type first and also to keep
497 * debugging output cleaner.
498 */
499 sreq.sector = 0;
500 sreq.nr_sectors = 0;
501 if (!lo->sock)
502 return -EINVAL;
503 nbd_send_req(lo, &sreq);
504 return 0;
506 case NBD_CLEAR_SOCK:
507 error = 0;
508 mutex_lock(&lo->tx_lock);
509 lo->sock = NULL;
510 mutex_unlock(&lo->tx_lock);
511 file = lo->file;
512 lo->file = NULL;
513 nbd_clear_que(lo);
514 BUG_ON(!list_empty(&lo->queue_head));
515 if (file)
516 fput(file);
517 return error;
518 case NBD_SET_SOCK:
519 if (lo->file)
520 return -EBUSY;
521 error = -EINVAL;
522 file = fget(arg);
523 if (file) {
524 inode = file->f_dentry->d_inode;
525 if (S_ISSOCK(inode->i_mode)) {
526 lo->file = file;
527 lo->sock = SOCKET_I(inode);
528 error = 0;
529 } else {
530 fput(file);
531 }
532 }
533 return error;
534 case NBD_SET_BLKSIZE:
535 lo->blksize = arg;
536 lo->bytesize &= ~(lo->blksize-1);
537 inode->i_bdev->bd_inode->i_size = lo->bytesize;
538 set_blocksize(inode->i_bdev, lo->blksize);
539 set_capacity(lo->disk, lo->bytesize >> 9);
540 return 0;
541 case NBD_SET_SIZE:
542 lo->bytesize = arg & ~(lo->blksize-1);
543 inode->i_bdev->bd_inode->i_size = lo->bytesize;
544 set_blocksize(inode->i_bdev, lo->blksize);
545 set_capacity(lo->disk, lo->bytesize >> 9);
546 return 0;
547 case NBD_SET_SIZE_BLOCKS:
548 lo->bytesize = ((u64) arg) * lo->blksize;
549 inode->i_bdev->bd_inode->i_size = lo->bytesize;
550 set_blocksize(inode->i_bdev, lo->blksize);
551 set_capacity(lo->disk, lo->bytesize >> 9);
552 return 0;
553 case NBD_DO_IT:
554 if (!lo->file)
555 return -EINVAL;
556 nbd_do_it(lo);
557 /* on return tidy up in case we have a signal */
558 /* Forcibly shutdown the socket causing all listeners
559 * to error
560 *
561 * FIXME: This code is duplicated from sys_shutdown, but
562 * there should be a more generic interface rather than
563 * calling socket ops directly here */
564 mutex_lock(&lo->tx_lock);
565 if (lo->sock) {
566 printk(KERN_WARNING "%s: shutting down socket\n",
567 lo->disk->disk_name);
568 lo->sock->ops->shutdown(lo->sock,
569 SEND_SHUTDOWN|RCV_SHUTDOWN);
570 lo->sock = NULL;
571 }
572 mutex_unlock(&lo->tx_lock);
573 file = lo->file;
574 lo->file = NULL;
575 nbd_clear_que(lo);
576 printk(KERN_WARNING "%s: queue cleared\n", lo->disk->disk_name);
577 if (file)
578 fput(file);
579 return lo->harderror;
580 case NBD_CLEAR_QUE:
581 /*
582 * This is for compatibility only. The queue is always cleared
583 * by NBD_DO_IT or NBD_CLEAR_SOCK.
584 */
585 BUG_ON(!lo->sock && !list_empty(&lo->queue_head));
586 return 0;
587 case NBD_PRINT_DEBUG:
588 printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n",
589 inode->i_bdev->bd_disk->disk_name,
590 lo->queue_head.next, lo->queue_head.prev,
591 &lo->queue_head);
592 return 0;
593 }
594 return -EINVAL;
595 }
597 static struct block_device_operations nbd_fops =
598 {
599 .owner = THIS_MODULE,
600 .ioctl = nbd_ioctl,
601 };
603 /*
604 * And here should be modules and kernel interface
605 * (Just smiley confuses emacs :-)
606 */
608 static int __init nbd_init(void)
609 {
610 int err = -ENOMEM;
611 int i;
613 BUILD_BUG_ON(sizeof(struct nbd_request) != 28);
615 if (nbds_max > MAX_NBD) {
616 printk(KERN_CRIT "nbd: cannot allocate more than %u nbds; %u requested.\n", MAX_NBD,
617 nbds_max);
618 return -EINVAL;
619 }
621 for (i = 0; i < nbds_max; i++) {
622 struct gendisk *disk = alloc_disk(1);
623 if (!disk)
624 goto out;
625 nbd_dev[i].disk = disk;
626 /*
627 * The new linux 2.5 block layer implementation requires
628 * every gendisk to have its very own request_queue struct.
629 * These structs are big so we dynamically allocate them.
630 */
631 disk->queue = blk_init_queue(do_nbd_request, &nbd_lock);
632 if (!disk->queue) {
633 put_disk(disk);
634 goto out;
635 }
636 }
638 if (register_blkdev(NBD_MAJOR, "nbd")) {
639 err = -EIO;
640 goto out;
641 }
643 printk(KERN_INFO "nbd: registered device at major %d\n", NBD_MAJOR);
644 dprintk(DBG_INIT, "nbd: debugflags=0x%x\n", debugflags);
646 for (i = 0; i < nbds_max; i++) {
647 struct gendisk *disk = nbd_dev[i].disk;
648 nbd_dev[i].file = NULL;
649 nbd_dev[i].magic = LO_MAGIC;
650 nbd_dev[i].flags = 0;
651 spin_lock_init(&nbd_dev[i].queue_lock);
652 INIT_LIST_HEAD(&nbd_dev[i].queue_head);
653 mutex_init(&nbd_dev[i].tx_lock);
654 init_waitqueue_head(&nbd_dev[i].active_wq);
655 nbd_dev[i].blksize = 1024;
656 nbd_dev[i].bytesize = 0x7ffffc00ULL << 10; /* 2TB */
657 disk->major = NBD_MAJOR;
658 disk->first_minor = i;
659 disk->fops = &nbd_fops;
660 disk->private_data = &nbd_dev[i];
661 disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
662 sprintf(disk->disk_name, "nbd%d", i);
663 set_capacity(disk, 0x7ffffc00ULL << 1); /* 2 TB */
664 add_disk(disk);
665 }
667 return 0;
668 out:
669 while (i--) {
670 blk_cleanup_queue(nbd_dev[i].disk->queue);
671 put_disk(nbd_dev[i].disk);
672 }
673 return err;
674 }
676 static void __exit nbd_cleanup(void)
677 {
678 int i;
679 for (i = 0; i < nbds_max; i++) {
680 struct gendisk *disk = nbd_dev[i].disk;
681 nbd_dev[i].magic = 0;
682 if (disk) {
683 del_gendisk(disk);
684 blk_cleanup_queue(disk->queue);
685 put_disk(disk);
686 }
687 }
688 unregister_blkdev(NBD_MAJOR, "nbd");
689 printk(KERN_INFO "nbd: unregistered device at major %d\n", NBD_MAJOR);
690 }
692 module_init(nbd_init);
693 module_exit(nbd_cleanup);
695 MODULE_DESCRIPTION("Network Block Device");
696 MODULE_LICENSE("GPL");
698 module_param(nbds_max, int, 0444);
699 MODULE_PARM_DESC(nbds_max, "How many network block devices to initialize.");
700 #ifndef NDEBUG
701 module_param(debugflags, int, 0644);
702 MODULE_PARM_DESC(debugflags, "flags for controlling debug output");
703 #endif