|
|
@@ -654,6 +654,11 @@ static int ioctl_dev(struct block_device *b, fmode_t mode,
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
{
|
|
|
struct bcache_device *d = b->bd_disk->private_data;
|
|
|
+ struct cached_dev *dc = container_of(d, struct cached_dev, disk);
|
|
|
+
|
|
|
+ if (dc->io_disable)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
return d->ioctl(d, mode, cmd, arg);
|
|
|
}
|
|
|
|
|
|
@@ -864,6 +869,44 @@ static void calc_cached_dev_sectors(struct cache_set *c)
|
|
|
c->cached_dev_sectors = sectors;
|
|
|
}
|
|
|
|
|
|
+#define BACKING_DEV_OFFLINE_TIMEOUT 5
|
|
|
+static int cached_dev_status_update(void *arg)
|
|
|
+{
|
|
|
+ struct cached_dev *dc = arg;
|
|
|
+ struct request_queue *q;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If this delayed worker is stopping outside, directly quit here.
|
|
|
+ * dc->io_disable might be set via sysfs interface, so check it
|
|
|
+ * here too.
|
|
|
+ */
|
|
|
+ while (!kthread_should_stop() && !dc->io_disable) {
|
|
|
+ q = bdev_get_queue(dc->bdev);
|
|
|
+ if (blk_queue_dying(q))
|
|
|
+ dc->offline_seconds++;
|
|
|
+ else
|
|
|
+ dc->offline_seconds = 0;
|
|
|
+
|
|
|
+ if (dc->offline_seconds >= BACKING_DEV_OFFLINE_TIMEOUT) {
|
|
|
+ pr_err("%s: device offline for %d seconds",
|
|
|
+ dc->backing_dev_name,
|
|
|
+ BACKING_DEV_OFFLINE_TIMEOUT);
|
|
|
+ pr_err("%s: disable I/O request due to backing "
|
|
|
+ "device offline", dc->disk.name);
|
|
|
+ dc->io_disable = true;
|
|
|
+ /* let others know earlier that io_disable is true */
|
|
|
+ smp_mb();
|
|
|
+ bcache_device_stop(&dc->disk);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ schedule_timeout_interruptible(HZ);
|
|
|
+ }
|
|
|
+
|
|
|
+ wait_for_kthread_stop();
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
void bch_cached_dev_run(struct cached_dev *dc)
|
|
|
{
|
|
|
struct bcache_device *d = &dc->disk;
|
|
|
@@ -906,6 +949,14 @@ void bch_cached_dev_run(struct cached_dev *dc)
|
|
|
if (sysfs_create_link(&d->kobj, &disk_to_dev(d->disk)->kobj, "dev") ||
|
|
|
sysfs_create_link(&disk_to_dev(d->disk)->kobj, &d->kobj, "bcache"))
|
|
|
pr_debug("error creating sysfs link");
|
|
|
+
|
|
|
+ dc->status_update_thread = kthread_run(cached_dev_status_update,
|
|
|
+ dc, "bcache_status_update");
|
|
|
+ if (IS_ERR(dc->status_update_thread)) {
|
|
|
+ pr_warn("failed to create bcache_status_update kthread, "
|
|
|
+ "continue to run without monitoring backing "
|
|
|
+ "device status");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
@@ -1139,6 +1190,8 @@ static void cached_dev_free(struct closure *cl)
|
|
|
kthread_stop(dc->writeback_thread);
|
|
|
if (dc->writeback_write_wq)
|
|
|
destroy_workqueue(dc->writeback_write_wq);
|
|
|
+ if (!IS_ERR_OR_NULL(dc->status_update_thread))
|
|
|
+ kthread_stop(dc->status_update_thread);
|
|
|
|
|
|
if (atomic_read(&dc->running))
|
|
|
bd_unlink_disk_holder(dc->bdev, dc->disk.disk);
|