aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDaniel Stodden <daniel.stodden@citrix.com>2010-04-30 18:01:19 -0400
committerJens Axboe <jaxboe@fusionio.com>2010-08-07 12:38:43 -0400
commitb70f5fa043b318659c936d8c3c696250e6528944 (patch)
treeca627bca1a32250206d0af888b2c9e98a7a3c4ef /drivers
parenta66b5aebb7dc9e695dcb4b528906fd398b63f3d9 (diff)
blkfront: Lock blkfront_info when closing
The bdev .open/.release fops race against backend switches to Closing, handled by the XenBus thread. The original code attempted to serialize block device holders and xenbus only via bd_mutex. This is insufficient, the info->bd pointer may already be stale (or null) while xenbus tries to bump up the refcount. Protect blkfront_info with a dedicated mutex. Signed-off-by: Daniel Stodden <daniel.stodden@citrix.com> Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/xen-blkfront.c61
1 files changed, 40 insertions, 21 deletions
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index d4cb7fd82b4c..1e406f0331e7 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -79,6 +79,7 @@ static const struct block_device_operations xlvbd_block_fops;
79 */ 79 */
80struct blkfront_info 80struct blkfront_info
81{ 81{
82 struct mutex mutex;
82 struct xenbus_device *xbdev; 83 struct xenbus_device *xbdev;
83 struct gendisk *gd; 84 struct gendisk *gd;
84 int vdevice; 85 int vdevice;
@@ -804,7 +805,6 @@ again:
804 return err; 805 return err;
805} 806}
806 807
807
808/** 808/**
809 * Entry point to this code when a new device is created. Allocate the basic 809 * Entry point to this code when a new device is created. Allocate the basic
810 * structures and the ring buffer for communication with the backend, and 810 * structures and the ring buffer for communication with the backend, and
@@ -836,6 +836,7 @@ static int blkfront_probe(struct xenbus_device *dev,
836 return -ENOMEM; 836 return -ENOMEM;
837 } 837 }
838 838
839 mutex_init(&info->mutex);
839 info->xbdev = dev; 840 info->xbdev = dev;
840 info->vdevice = vdevice; 841 info->vdevice = vdevice;
841 info->connected = BLKIF_STATE_DISCONNECTED; 842 info->connected = BLKIF_STATE_DISCONNECTED;
@@ -951,6 +952,43 @@ static int blkfront_resume(struct xenbus_device *dev)
951 return err; 952 return err;
952} 953}
953 954
955static void
956blkfront_closing(struct blkfront_info *info)
957{
958 struct xenbus_device *xbdev = info->xbdev;
959 struct block_device *bdev = NULL;
960
961 mutex_lock(&info->mutex);
962
963 if (xbdev->state == XenbusStateClosing) {
964 mutex_unlock(&info->mutex);
965 return;
966 }
967
968 if (info->gd)
969 bdev = bdget_disk(info->gd, 0);
970
971 mutex_unlock(&info->mutex);
972
973 if (!bdev) {
974 xenbus_frontend_closed(xbdev);
975 return;
976 }
977
978 mutex_lock(&bdev->bd_mutex);
979
980 if (info->users) {
981 xenbus_dev_error(xbdev, -EBUSY,
982 "Device in use; refusing to close");
983 xenbus_switch_state(xbdev, XenbusStateClosing);
984 } else {
985 xlvbd_release_gendisk(info);
986 xenbus_frontend_closed(xbdev);
987 }
988
989 mutex_unlock(&bdev->bd_mutex);
990 bdput(bdev);
991}
954 992
955/* 993/*
956 * Invoked when the backend is finally 'ready' (and has told produced 994 * Invoked when the backend is finally 'ready' (and has told produced
@@ -1034,7 +1072,6 @@ static void blkback_changed(struct xenbus_device *dev,
1034 enum xenbus_state backend_state) 1072 enum xenbus_state backend_state)
1035{ 1073{
1036 struct blkfront_info *info = dev_get_drvdata(&dev->dev); 1074 struct blkfront_info *info = dev_get_drvdata(&dev->dev);
1037 struct block_device *bd;
1038 1075
1039 dev_dbg(&dev->dev, "blkfront:blkback_changed to state %d.\n", backend_state); 1076 dev_dbg(&dev->dev, "blkfront:blkback_changed to state %d.\n", backend_state);
1040 1077
@@ -1051,25 +1088,7 @@ static void blkback_changed(struct xenbus_device *dev,
1051 break; 1088 break;
1052 1089
1053 case XenbusStateClosing: 1090 case XenbusStateClosing:
1054 if (info->gd == NULL) { 1091 blkfront_closing(info);
1055 xenbus_frontend_closed(dev);
1056 break;
1057 }
1058 bd = bdget_disk(info->gd, 0);
1059 if (bd == NULL)
1060 xenbus_dev_fatal(dev, -ENODEV, "bdget failed");
1061
1062 mutex_lock(&bd->bd_mutex);
1063 if (info->users > 0)
1064 xenbus_dev_error(dev, -EBUSY,
1065 "Device in use; refusing to close");
1066 else {
1067 xlvbd_release_gendisk(info);
1068 xenbus_frontend_closed(info->xbdev);
1069 }
1070
1071 mutex_unlock(&bd->bd_mutex);
1072 bdput(bd);
1073 break; 1092 break;
1074 } 1093 }
1075} 1094}