diff options
author | Daniel Stodden <daniel.stodden@citrix.com> | 2010-04-30 18:01:19 -0400 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2010-08-07 12:38:43 -0400 |
commit | b70f5fa043b318659c936d8c3c696250e6528944 (patch) | |
tree | ca627bca1a32250206d0af888b2c9e98a7a3c4ef /drivers | |
parent | a66b5aebb7dc9e695dcb4b528906fd398b63f3d9 (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.c | 61 |
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 | */ |
80 | struct blkfront_info | 80 | struct 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 | ||
955 | static void | ||
956 | blkfront_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 | } |