X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fblock%2Fnbd.c;h=8bca4905d7f7357e31b8d66f53a79fc79379c874;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=b98b70b8fdb991cfed4087ae68109a877b93e094;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index b98b70b8f..8bca4905d 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -38,7 +38,7 @@ * 03-06-24 Cleanup PARANOIA usage & code. * 04-02-19 Remove PARANOIA, plus various cleanups (Paul Clements) * possible FIXME: make set_sock / set_blksize / set_size / do_it one syscall - * why not: would need verify_area and friends, would share yet another + * why not: would need access_ok and friends, would share yet another * structure with userland */ @@ -54,11 +54,15 @@ #include #include #include +#include +#include +#include #include #include #include +#include #include #include @@ -78,6 +82,7 @@ #define DBG_RX 0x0200 #define DBG_TX 0x0400 static unsigned int debugflags; +static unsigned int nbds_max = 16; #endif /* NDEBUG */ static struct nbd_device nbd_dev[MAX_NBD]; @@ -92,7 +97,7 @@ static struct nbd_device nbd_dev[MAX_NBD]; * a single lock. * Thanks go to Jens Axboe and Al Viro for their LKML emails explaining this! */ -static spinlock_t nbd_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(nbd_lock); #ifndef NDEBUG static const char *ioctl_cmd_to_ascii(int cmd) @@ -128,26 +133,14 @@ static void nbd_end_request(struct request *req) { int uptodate = (req->errors == 0) ? 1 : 0; request_queue_t *q = req->q; - struct nbd_device *lo = req->rq_disk->private_data; unsigned long flags; dprintk(DBG_BLKDEV, "%s: request %p: %s\n", req->rq_disk->disk_name, req, uptodate? "done": "failed"); - spin_lock(&lo->queue_lock); - while (req->ref_count > 1) { /* still in send */ - spin_unlock(&lo->queue_lock); - printk(KERN_DEBUG "%s: request %p still in use (%d), waiting\n", - lo->disk->disk_name, req, req->ref_count); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ); /* wait a second */ - spin_lock(&lo->queue_lock); - } - spin_unlock(&lo->queue_lock); - spin_lock_irqsave(q->queue_lock, flags); if (!end_that_request_first(req, uptodate, req->nr_sectors)) { - end_that_request_last(req); + end_that_request_last(req, uptodate); } spin_unlock_irqrestore(q->queue_lock, flags); } @@ -181,7 +174,6 @@ static int sock_xmit(struct socket *sock, int send, void *buf, int size, msg.msg_namelen = 0; msg.msg_control = NULL; msg.msg_controllen = 0; - msg.msg_namelen = 0; msg.msg_flags = msg_flags | MSG_NOSIGNAL; if (send) @@ -228,7 +220,7 @@ static inline int sock_send_bvec(struct socket *sock, struct bio_vec *bvec, return result; } -void nbd_send_req(struct nbd_device *lo, struct request *req) +static int nbd_send_req(struct nbd_device *lo, struct request *req) { int result, i, flags; struct nbd_request request; @@ -241,14 +233,6 @@ void nbd_send_req(struct nbd_device *lo, struct request *req) request.len = htonl(size); memcpy(request.handle, &req, sizeof(req)); - down(&lo->tx_lock); - - if (!sock || !lo->sock) { - printk(KERN_ERR "%s: Attempted send on closed socket\n", - lo->disk->disk_name); - goto error_out; - } - dprintk(DBG_TX, "%s: request %p: sending control (%s@%llu,%luB)\n", lo->disk->disk_name, req, nbdcmd_to_ascii(nbd_cmd(req)), @@ -287,12 +271,10 @@ void nbd_send_req(struct nbd_device *lo, struct request *req) } } } - up(&lo->tx_lock); - return; + return 0; error_out: - up(&lo->tx_lock); - req->errors++; + return 1; } static struct request *nbd_find_request(struct nbd_device *lo, char *handle) @@ -300,9 +282,14 @@ static struct request *nbd_find_request(struct nbd_device *lo, char *handle) struct request *req; struct list_head *tmp; struct request *xreq; + int err; memcpy(&xreq, handle, sizeof(xreq)); + err = wait_event_interruptible(lo->active_wq, lo->active_req != xreq); + if (unlikely(err)) + goto out; + spin_lock(&lo->queue_lock); list_for_each(tmp, &lo->queue_head) { req = list_entry(tmp, struct request, queuelist); @@ -313,7 +300,11 @@ static struct request *nbd_find_request(struct nbd_device *lo, char *handle) return req; } spin_unlock(&lo->queue_lock); - return NULL; + + err = -ENOENT; + +out: + return ERR_PTR(err); } static inline int sock_recv_bvec(struct socket *sock, struct bio_vec *bvec) @@ -327,7 +318,7 @@ static inline int sock_recv_bvec(struct socket *sock, struct bio_vec *bvec) } /* NULL returned = something went wrong, inform userspace */ -struct request *nbd_read_stat(struct nbd_device *lo) +static struct request *nbd_read_stat(struct nbd_device *lo) { int result; struct nbd_reply reply; @@ -342,7 +333,11 @@ struct request *nbd_read_stat(struct nbd_device *lo) goto harderror; } req = nbd_find_request(lo, reply.handle); - if (req == NULL) { + if (unlikely(IS_ERR(req))) { + result = PTR_ERR(req); + if (result != -ENOENT) + goto harderror; + printk(KERN_ERR "%s: Unexpected reply (%p)\n", lo->disk->disk_name, reply.handle); result = -EBADR; @@ -389,7 +384,7 @@ harderror: return NULL; } -void nbd_do_it(struct nbd_device *lo) +static void nbd_do_it(struct nbd_device *lo) { struct request *req; @@ -400,25 +395,30 @@ void nbd_do_it(struct nbd_device *lo) return; } -void nbd_clear_que(struct nbd_device *lo) +static void nbd_clear_que(struct nbd_device *lo) { struct request *req; BUG_ON(lo->magic != LO_MAGIC); - do { - req = NULL; - spin_lock(&lo->queue_lock); - if (!list_empty(&lo->queue_head)) { - req = list_entry(lo->queue_head.next, struct request, queuelist); - list_del_init(&req->queuelist); - } - spin_unlock(&lo->queue_lock); - if (req) { - req->errors++; - nbd_end_request(req); - } - } while (req); + /* + * Because we have set lo->sock to NULL under the tx_lock, all + * modifications to the list must have completed by now. For + * the same reason, the active_req must be NULL. + * + * As a consequence, we don't need to take the spin lock while + * purging the list here. + */ + BUG_ON(lo->sock); + BUG_ON(lo->active_req); + + while (!list_empty(&lo->queue_head)) { + req = list_entry(lo->queue_head.next, struct request, + queuelist); + list_del_init(&req->queuelist); + req->errors++; + nbd_end_request(req); + } } /* @@ -446,11 +446,6 @@ static void do_nbd_request(request_queue_t * q) BUG_ON(lo->magic != LO_MAGIC); - if (!lo->file) { - printk(KERN_ERR "%s: Request when not-ready\n", - lo->disk->disk_name); - goto error_out; - } nbd_cmd(req) = NBD_CMD_READ; if (rq_data_dir(req) == WRITE) { nbd_cmd(req) = NBD_CMD_WRITE; @@ -464,39 +459,34 @@ static void do_nbd_request(request_queue_t * q) req->errors = 0; spin_unlock_irq(q->queue_lock); - spin_lock(&lo->queue_lock); - - if (!lo->file) { - spin_unlock(&lo->queue_lock); - printk(KERN_ERR "%s: failed between accept and semaphore, file lost\n", - lo->disk->disk_name); + mutex_lock(&lo->tx_lock); + if (unlikely(!lo->sock)) { + mutex_unlock(&lo->tx_lock); + printk(KERN_ERR "%s: Attempted send on closed socket\n", + lo->disk->disk_name); req->errors++; nbd_end_request(req); spin_lock_irq(q->queue_lock); continue; } - list_add(&req->queuelist, &lo->queue_head); - req->ref_count++; /* make sure req does not get freed */ - spin_unlock(&lo->queue_lock); - - nbd_send_req(lo, req); + lo->active_req = req; - if (req->errors) { + if (nbd_send_req(lo, req) != 0) { printk(KERN_ERR "%s: Request send failed\n", lo->disk->disk_name); + req->errors++; + nbd_end_request(req); + } else { spin_lock(&lo->queue_lock); - list_del_init(&req->queuelist); - req->ref_count--; + list_add(&req->queuelist, &lo->queue_head); spin_unlock(&lo->queue_lock); - nbd_end_request(req); - spin_lock_irq(q->queue_lock); - continue; } - spin_lock(&lo->queue_lock); - req->ref_count--; - spin_unlock(&lo->queue_lock); + lo->active_req = NULL; + mutex_unlock(&lo->tx_lock); + wake_up_all(&lo->active_wq); + spin_lock_irq(q->queue_lock); continue; @@ -544,20 +534,13 @@ static int nbd_ioctl(struct inode *inode, struct file *file, case NBD_CLEAR_SOCK: error = 0; - down(&lo->tx_lock); + mutex_lock(&lo->tx_lock); lo->sock = NULL; - up(&lo->tx_lock); - spin_lock(&lo->queue_lock); + mutex_unlock(&lo->tx_lock); file = lo->file; lo->file = NULL; - spin_unlock(&lo->queue_lock); nbd_clear_que(lo); - spin_lock(&lo->queue_lock); - if (!list_empty(&lo->queue_head)) { - printk(KERN_ERR "nbd: disconnect: some requests are in progress -> please try again.\n"); - error = -EBUSY; - } - spin_unlock(&lo->queue_lock); + BUG_ON(!list_empty(&lo->queue_head)); if (file) fput(file); return error; @@ -568,7 +551,7 @@ static int nbd_ioctl(struct inode *inode, struct file *file, file = fget(arg); if (file) { inode = file->f_dentry->d_inode; - if (inode->i_sock) { + if (S_ISSOCK(inode->i_mode)) { lo->file = file; lo->sock = SOCKET_I(inode); error = 0; @@ -607,7 +590,7 @@ static int nbd_ioctl(struct inode *inode, struct file *file, * FIXME: This code is duplicated from sys_shutdown, but * there should be a more generic interface rather than * calling socket ops directly here */ - down(&lo->tx_lock); + mutex_lock(&lo->tx_lock); if (lo->sock) { printk(KERN_WARNING "%s: shutting down socket\n", lo->disk->disk_name); @@ -615,25 +598,20 @@ static int nbd_ioctl(struct inode *inode, struct file *file, SEND_SHUTDOWN|RCV_SHUTDOWN); lo->sock = NULL; } - up(&lo->tx_lock); - spin_lock(&lo->queue_lock); + mutex_unlock(&lo->tx_lock); file = lo->file; lo->file = NULL; - spin_unlock(&lo->queue_lock); nbd_clear_que(lo); printk(KERN_WARNING "%s: queue cleared\n", lo->disk->disk_name); if (file) fput(file); return lo->harderror; case NBD_CLEAR_QUE: - down(&lo->tx_lock); - if (lo->sock) { - up(&lo->tx_lock); - return 0; /* probably should be error, but that would - * break "nbd-client -d", so just return 0 */ - } - up(&lo->tx_lock); - nbd_clear_que(lo); + /* + * This is for compatibility only. The queue is always cleared + * by NBD_DO_IT or NBD_CLEAR_SOCK. + */ + BUG_ON(!lo->sock && !list_empty(&lo->queue_head)); return 0; case NBD_PRINT_DEBUG: printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n", @@ -661,12 +639,15 @@ static int __init nbd_init(void) int err = -ENOMEM; int i; - if (sizeof(struct nbd_request) != 28) { - printk(KERN_CRIT "nbd: sizeof nbd_request needs to be 28 in order to work!\n" ); - return -EIO; + BUILD_BUG_ON(sizeof(struct nbd_request) != 28); + + if (nbds_max > MAX_NBD) { + printk(KERN_CRIT "nbd: cannot allocate more than %u nbds; %u requested.\n", MAX_NBD, + nbds_max); + return -EINVAL; } - for (i = 0; i < MAX_NBD; i++) { + for (i = 0; i < nbds_max; i++) { struct gendisk *disk = alloc_disk(1); if (!disk) goto out; @@ -692,14 +673,15 @@ static int __init nbd_init(void) dprintk(DBG_INIT, "nbd: debugflags=0x%x\n", debugflags); devfs_mk_dir("nbd"); - for (i = 0; i < MAX_NBD; i++) { + for (i = 0; i < nbds_max; i++) { struct gendisk *disk = nbd_dev[i].disk; nbd_dev[i].file = NULL; nbd_dev[i].magic = LO_MAGIC; nbd_dev[i].flags = 0; spin_lock_init(&nbd_dev[i].queue_lock); INIT_LIST_HEAD(&nbd_dev[i].queue_head); - init_MUTEX(&nbd_dev[i].tx_lock); + mutex_init(&nbd_dev[i].tx_lock); + init_waitqueue_head(&nbd_dev[i].active_wq); nbd_dev[i].blksize = 1024; nbd_dev[i].bytesize = 0x7ffffc00ULL << 10; /* 2TB */ disk->major = NBD_MAJOR; @@ -725,8 +707,9 @@ out: static void __exit nbd_cleanup(void) { int i; - for (i = 0; i < MAX_NBD; i++) { + for (i = 0; i < nbds_max; i++) { struct gendisk *disk = nbd_dev[i].disk; + nbd_dev[i].magic = 0; if (disk) { del_gendisk(disk); blk_cleanup_queue(disk->queue); @@ -744,7 +727,9 @@ module_exit(nbd_cleanup); MODULE_DESCRIPTION("Network Block Device"); MODULE_LICENSE("GPL"); +module_param(nbds_max, int, 0444); +MODULE_PARM_DESC(nbds_max, "How many network block devices to initialize."); #ifndef NDEBUG -MODULE_PARM(debugflags, "i"); +module_param(debugflags, int, 0644); MODULE_PARM_DESC(debugflags, "flags for controlling debug output"); #endif