patch-2.4.19 linux-2.4.19/drivers/block/nbd.c

Next file: linux-2.4.19/drivers/block/paride/bpck6.c
Previous file: linux-2.4.19/drivers/block/loop.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/block/nbd.c linux-2.4.19/drivers/block/nbd.c
@@ -24,6 +24,8 @@
  * 01-3-11 Make nbd work with new Linux block layer code. It now supports
  *   plugging like all the other block devices. Also added in MSG_MORE to
  *   reduce number of partial TCP segments sent. <steve@chygwyn.com>
+ * 01-12-6 Fix deadlock condition by making queue locks independant of
+ *   the transmit lock. <steve@chygwyn.com>
  *
  * 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 
@@ -146,11 +148,12 @@
 
 #define FAIL( s ) { printk( KERN_ERR "NBD: " s "(result %d)\n", result ); goto error_out; }
 
-void nbd_send_req(struct socket *sock, struct request *req)
+void nbd_send_req(struct nbd_device *lo, struct request *req)
 {
 	int result;
 	struct nbd_request request;
 	unsigned long size = req->nr_sectors << 9;
+	struct socket *sock = lo->sock;
 
 	DEBUG("NBD: sending control, ");
 	request.magic = htonl(NBD_REQUEST_MAGIC);
@@ -159,6 +162,8 @@
 	request.len = htonl(size);
 	memcpy(request.handle, &req, sizeof(req));
 
+	down(&lo->tx_lock);
+
 	result = nbd_xmit(1, sock, (char *) &request, sizeof(request), req->cmd == WRITE ? MSG_MORE : 0);
 	if (result <= 0)
 		FAIL("Sendmsg failed for control.");
@@ -173,30 +178,51 @@
 			bh = bh->b_reqnext;
 		} while(bh);
 	}
+	up(&lo->tx_lock);
 	return;
 
-      error_out:
+error_out:
+	up(&lo->tx_lock);
 	req->errors++;
 }
 
+static struct request *nbd_find_request(struct nbd_device *lo, char *handle)
+{
+	struct request *req;
+	struct list_head *tmp;
+	struct request *xreq;
+
+	memcpy(&xreq, handle, sizeof(xreq));
+
+	spin_lock(&lo->queue_lock);
+	list_for_each(tmp, &lo->queue_head) {
+		req = list_entry(tmp, struct request, queue);
+		if (req != xreq)
+			continue;
+		list_del(&req->queue);
+		spin_unlock(&lo->queue_lock);
+		return req;
+	}
+	spin_unlock(&lo->queue_lock);
+	return NULL;
+}
+
 #define HARDFAIL( s ) { printk( KERN_ERR "NBD: " s "(result %d)\n", result ); lo->harderror = result; return NULL; }
 struct request *nbd_read_stat(struct nbd_device *lo)
 		/* NULL returned = something went wrong, inform userspace       */ 
 {
 	int result;
 	struct nbd_reply reply;
-	struct request *xreq, *req;
+	struct request *req;
 
 	DEBUG("reading control, ");
 	reply.magic = 0;
 	result = nbd_xmit(0, lo->sock, (char *) &reply, sizeof(reply), MSG_WAITALL);
 	if (result <= 0)
 		HARDFAIL("Recv control failed.");
-	memcpy(&xreq, reply.handle, sizeof(xreq));
-	req = blkdev_entry_prev_request(&lo->queue_head);
-
-	if (xreq != req)
-		FAIL("Unexpected handle received.\n");
+	req = nbd_find_request(lo, reply.handle);
+	if (req == NULL)
+		HARDFAIL("Unexpected reply");
 
 	DEBUG("ok, ");
 	if (ntohl(reply.magic) != NBD_REPLY_MAGIC)
@@ -226,20 +252,14 @@
 {
 	struct request *req;
 
-	down (&lo->queue_lock);
 	while (1) {
-		up (&lo->queue_lock);
 		req = nbd_read_stat(lo);
-		down (&lo->queue_lock);
 
 		if (!req) {
 			printk(KERN_ALERT "req should never be null\n" );
 			goto out;
 		}
 #ifdef PARANOIA
-		if (req != blkdev_entry_prev_request(&lo->queue_head)) {
-			printk(KERN_ALERT "NBD: I have problem...\n");
-		}
 		if (lo != &nbd_dev[MINOR(req->rq_dev)]) {
 			printk(KERN_ALERT "NBD: request corrupted!\n");
 			continue;
@@ -249,15 +269,12 @@
 			goto out;
 		}
 #endif
-		list_del(&req->queue);
-		up (&lo->queue_lock);
 		
 		nbd_end_request(req);
 
-		down (&lo->queue_lock);
 	}
  out:
-	up (&lo->queue_lock);
+	return;
 }
 
 void nbd_clear_que(struct nbd_device *lo)
@@ -270,27 +287,20 @@
 		return;
 	}
 #endif
-
-	while (!list_empty(&lo->queue_head)) {
-		req = blkdev_entry_prev_request(&lo->queue_head);
-#ifdef PARANOIA
-		if (!req) {
-			printk( KERN_ALERT "NBD: panic, panic, panic\n" );
-			break;
+	do {
+		req = NULL;
+		spin_lock(&lo->queue_lock);
+		if (!list_empty(&lo->queue_head)) {
+			req = list_entry(lo->queue_head.next, struct request, queue);
+			list_del(&req->queue);
 		}
-		if (lo != &nbd_dev[MINOR(req->rq_dev)]) {
-			printk(KERN_ALERT "NBD: request corrupted when clearing!\n");
-			continue;
+		spin_unlock(&lo->queue_lock);
+		if (req) {
+			req->errors++;
+			nbd_end_request(req);
 		}
-#endif
-		req->errors++;
-		list_del(&req->queue);
-		up(&lo->queue_lock);
+	} while(req);
 
-		nbd_end_request(req);
-
-		down(&lo->queue_lock);
-	}
 }
 
 /*
@@ -334,10 +344,11 @@
 		blkdev_dequeue_request(req);
 		spin_unlock_irq(&io_request_lock);
 
-		down (&lo->queue_lock);
-		list_add(&req->queue, &lo->queue_head);
-		nbd_send_req(lo->sock, req);	/* Why does this block?         */
-		up (&lo->queue_lock);
+		spin_lock(&lo->queue_lock);
+		list_add_tail(&req->queue, &lo->queue_head);
+		spin_unlock(&lo->queue_lock);
+
+		nbd_send_req(lo, req);
 
 		spin_lock_irq(&io_request_lock);
 		continue;
@@ -372,21 +383,21 @@
 	lo = &nbd_dev[dev];
 	switch (cmd) {
 	case NBD_DISCONNECT:
-	        printk("NBD_DISCONNECT\n") ;
+	        printk("NBD_DISCONNECT\n");
                 sreq.cmd=2 ; /* shutdown command */
-                if (!lo->sock) return -EINVAL ;
-                nbd_send_req(lo->sock,&sreq) ;
+                if (!lo->sock) return -EINVAL;
+                nbd_send_req(lo, &sreq);
                 return 0 ;
  
 	case NBD_CLEAR_SOCK:
-		down(&lo->queue_lock);
 		nbd_clear_que(lo);
+		spin_lock(&lo->queue_lock);
 		if (!list_empty(&lo->queue_head)) {
-			up(&lo->queue_lock);
+			spin_unlock(&lo->queue_lock);
 			printk(KERN_ERR "nbd: Some requests are in progress -> can not turn off.\n");
 			return -EBUSY;
 		}
-		up(&lo->queue_lock);
+		spin_unlock(&lo->queue_lock);
 		file = lo->file;
 		if (!file)
 			return -EINVAL;
@@ -507,8 +518,9 @@
 		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].queue_lock);
+		init_MUTEX(&nbd_dev[i].tx_lock);
 		nbd_blksizes[i] = 1024;
 		nbd_blksize_bits[i] = 10;
 		nbd_bytesizes[i] = 0x7ffffc00; /* 2GB */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)