From: Elias Oltmanns <eo@nebensachen.de>
To: linux-ide@vger.kernel.org
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>,
linux-kernel@vger.kernel.org, Jens Axboe <jens.axboe@oracle.com>
Subject: Re: [PATCH 4/4] disk-protect: Add a generic block layer queue freezing facility
Date: Sun, 16 Mar 2008 17:16:19 +0100 [thread overview]
Message-ID: <87ve3mfxv0.fsf@denkblock.local> (raw)
In-Reply-To: 20080315124952.GC4669@ucw.cz
[-- Attachment #1: Type: text/plain, Size: 1439 bytes --]
Pavel Machek <pavel@ucw.cz> wrote:
> On Fri 2008-03-07 19:26:41, Elias Oltmanns wrote:
>> This patch provides a generic way to freeze the request queue of a block
>> device temporarily. This functionality is exposed to user space via sysfs.
>>
>> Signed-off-by: Elias Oltmanns <eo@nebensachen.de>
>
> I guess this should have patch going to documentation. Otherwise it
> looks ok.
Yes, I'll add documentation eventually. There is more to be added before
this patch can go in because the protect attribute will appear for all
block devices employing request queues. In particular, we will have to
make sure that drivers will handle REQ_TYPE_LINUX_BLOCK requests
gracefully even if they don't implement support for a particular
command.
>
>> +/*
>> + * When reading the 'protect' attribute, we return seconds remaining
>> + * before the unfreeze timeout expires.
>> + */
>> +static ssize_t queue_protect_show(struct request_queue *q, char *page)
>> +{
>> + unsigned int seconds = 0;
>> +
>> + if (blk_queue_stopped(q) && timer_pending(&q->unfreeze_timer))
>> + /*
>> + * Adding 1 in order to guarantee nonzero value until timer
>> + * has actually expired.
>> + */
>> + seconds = jiffies_to_msecs(q->unfreeze_timer.expires
>> + - jiffies) / 1000 + 1;
>
> Is it okay to read expires without locking?
No, I have been a bit careless with regard to locking in this patch. See
the revised version below.
Regards,
Elias
---
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: tmp.patch --]
[-- Type: text/x-patch, Size: 6660 bytes --]
This patch provides a generic way to freeze the request queue of a block
device temporarily. This functionality is exposed to user space via sysfs.
Signed-off-by: Elias Oltmanns <eo@nebensachen.de>
---
block/ll_rw_blk.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/blkdev.h | 7 ++
2 files changed, 154 insertions(+), 0 deletions(-)
diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c
index 8b91994..f295855 100644
--- a/block/ll_rw_blk.c
+++ b/block/ll_rw_blk.c
@@ -39,6 +39,8 @@
static void blk_unplug_work(struct work_struct *work);
static void blk_unplug_timeout(unsigned long data);
+static void blk_unfreeze_work(struct work_struct *work);
+static void blk_unfreeze_timeout(unsigned long data);
static void drive_stat_acct(struct request *rq, int new_io);
static void init_request_from_bio(struct request *req, struct bio *bio);
static int __make_request(struct request_queue *q, struct bio *bio);
@@ -231,6 +233,13 @@ void blk_queue_make_request(struct request_queue * q, make_request_fn * mfn)
q->unplug_timer.function = blk_unplug_timeout;
q->unplug_timer.data = (unsigned long)q;
+ q->max_unfreeze = 30;
+
+ INIT_WORK(&q->unfreeze_work, blk_unfreeze_work);
+
+ q->unfreeze_timer.function = blk_unfreeze_timeout;
+ q->unfreeze_timer.data = (unsigned long)q;
+
/*
* by default assume old behaviour and bounce for any highmem page
*/
@@ -1861,6 +1870,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
}
init_timer(&q->unplug_timer);
+ init_timer(&q->unfreeze_timer);
kobject_set_name(&q->kobj, "%s", "queue");
q->kobj.ktype = &queue_ktype;
@@ -1975,6 +1985,97 @@ int blk_get_queue(struct request_queue *q)
EXPORT_SYMBOL(blk_get_queue);
+static void issue_queue_freeze_cmd(struct request_queue *q, int thaw)
+{
+ struct request *rq;
+ unsigned long flags;
+
+ rq = blk_get_request(q, 0, GFP_NOIO);
+ if (!rq)
+ return;
+ rq->cmd_type = REQ_TYPE_LINUX_BLOCK;
+ rq->cmd_len = 1;
+ rq->cmd_flags |= REQ_NOMERGE | REQ_SOFTBARRIER;
+ if (thaw)
+ rq->cmd[0] = REQ_LB_OP_THAW;
+ else
+ rq->cmd[0] = REQ_LB_OP_FREEZE;
+ rq->rq_disk = NULL;
+ rq->sense = NULL;
+ rq->sense_len = 0;
+ rq->retries = 5;
+ rq->timeout = HZ;
+ spin_lock_irqsave(q->queue_lock, flags);
+ __elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 0);
+ if (thaw)
+ blk_start_queue(q);
+ else {
+ blk_stop_queue(q);
+ q->request_fn(q);
+ }
+ spin_unlock_irqrestore(q->queue_lock, flags);
+}
+
+static void blk_unfreeze_work(struct work_struct *work)
+{
+ struct request_queue *q = container_of(work, struct request_queue,
+ unfreeze_work);
+ int pending;
+
+ spin_lock_irq(q->queue_lock);
+ pending = timer_pending(&q->unfreeze_timer);
+ spin_unlock_irq(q->queue_lock);
+ if (!pending)
+ issue_queue_freeze_cmd(q, 1);
+}
+
+static void blk_unfreeze_timeout(unsigned long data)
+{
+ struct request_queue *q = (struct request_queue *) data;
+
+ kblockd_schedule_work(&q->unfreeze_work);
+}
+
+/**
+ * blk_freeze_queue - (un)freeze queue and manage the queue unfreeze timer
+ * @q: queue to be (un)frozen
+ * @second: number of seconds to freeze the queue for
+ *
+ * Description: This function (re)sets the unfreeze timer of a request
+ * queue. When a timer is started / stopped (not just modified),
+ * then low level drivers are notified about this event.
+ */
+static int blk_freeze_queue(struct request_queue *q, unsigned int seconds)
+{
+ int thaw, rc;
+
+ spin_lock_irq(q->queue_lock);
+ if (seconds > 0) {
+ if (seconds > q->max_unfreeze)
+ seconds = q->max_unfreeze;
+ /* set / reset the thaw timer */
+ rc = !mod_timer(&q->unfreeze_timer,
+ msecs_to_jiffies(seconds * 1000) + jiffies);
+ thaw = 0;
+ if (rc && blk_queue_stopped(q)) {
+ /* Someone else has stopped the queue already.
+ * Best leave it alone.
+ */
+ del_timer(&q->unfreeze_timer);
+ rc = 0;
+ }
+ } else {
+ rc = del_timer(&q->unfreeze_timer);
+ thaw = 1;
+ }
+ spin_unlock_irq(q->queue_lock);
+
+ if (rc)
+ issue_queue_freeze_cmd(q, thaw);
+
+ return rc;
+}
+
static inline void blk_free_request(struct request_queue *q, struct request *rq)
{
if (rq->cmd_flags & REQ_ELVPRIV)
@@ -4080,6 +4181,45 @@ static ssize_t queue_max_hw_sectors_show(struct request_queue *q, char *page)
return queue_var_show(max_hw_sectors_kb, (page));
}
+/*
+ * When reading the 'protect' attribute, we return seconds remaining
+ * before the unfreeze timeout expires.
+ */
+static ssize_t queue_protect_show(struct request_queue *q, char *page)
+{
+ unsigned int seconds = 0;
+
+ spin_lock_irq(q->queue_lock);
+ if (blk_queue_stopped(q) && timer_pending(&q->unfreeze_timer))
+ /*
+ * Adding 1 in order to guarantee nonzero value until timer
+ * has actually expired.
+ */
+ seconds = jiffies_to_msecs(q->unfreeze_timer.expires
+ - jiffies) / 1000 + 1;
+ spin_unlock_irq(q->queue_lock);
+
+ return queue_var_show(seconds, (page));
+}
+
+/*
+ * When writing to the 'protect' attribute, input is the number of
+ * seconds to freeze the queue for. We call a helper function to park
+ * the heads and freeze/block the queue, then we make a block layer
+ * call to setup the thaw timeout. If input is 0, then the queue will
+ * be thawed by that helper function immediately.
+ */
+static ssize_t queue_protect_store(struct request_queue *q,
+ const char *page, size_t count)
+{
+ unsigned long seconds;
+
+ queue_var_store(&seconds, page, count);
+ blk_freeze_queue(q, seconds);
+
+ return count;
+}
+
static struct queue_sysfs_entry queue_requests_entry = {
.attr = {.name = "nr_requests", .mode = S_IRUGO | S_IWUSR },
@@ -4110,12 +4250,19 @@ static struct queue_sysfs_entry queue_iosched_entry = {
.store = elv_iosched_store,
};
+static struct queue_sysfs_entry queue_protect_entry = {
+ .attr = { .name = "protect", .mode = S_IRUGO | S_IWUSR },
+ .show = queue_protect_show,
+ .store = queue_protect_store,
+};
+
static struct attribute *default_attrs[] = {
&queue_requests_entry.attr,
&queue_ra_entry.attr,
&queue_max_hw_sectors_entry.attr,
&queue_max_sectors_entry.attr,
&queue_iosched_entry.attr,
+ &queue_protect_entry.attr,
NULL,
};
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 1854a69..082e2d3 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -385,6 +385,13 @@ struct request_queue
unsigned long unplug_delay; /* After this many jiffies */
struct work_struct unplug_work;
+ /*
+ * Auto-unfreeze state
+ */
+ struct timer_list unfreeze_timer;
+ int max_unfreeze; /* At most this many seconds */
+ struct work_struct unfreeze_work;
+
struct backing_dev_info backing_dev_info;
/*
next prev parent reply other threads:[~2008-03-16 16:16 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-02-25 23:56 [RFC] Disk shock protection (revisited) Elias Oltmanns
2008-02-26 0:02 ` Jeff Garzik
2008-02-26 0:30 ` Elias Oltmanns
2008-02-26 1:33 ` Henrique de Moraes Holschuh
2008-02-26 12:39 ` Alan Cox
2008-02-28 8:24 ` Elias Oltmanns
2008-02-28 11:13 ` Alan Cox
2008-02-24 18:03 ` Pavel Machek
2008-02-28 17:00 ` Greg Freemyer
2008-03-07 18:03 ` Elias Oltmanns
2008-03-07 18:25 ` [PATCH 1/4] disk-protect: Add disk shock protection helpers to libata Elias Oltmanns
2008-03-15 12:39 ` Pavel Machek
2008-03-20 14:13 ` Alan Cox
2008-03-07 18:25 ` [PATCH 2/4] disk-protect: SCSI support for REQ_TYPE_LINUX_BLOCK requests Elias Oltmanns
2008-03-07 18:26 ` [PATCH 3/4] disk-protect: Add a REQ_TYPE_LINUX_BLOCK request handler to libata Elias Oltmanns
2008-03-15 12:42 ` Pavel Machek
2008-03-07 18:26 ` [PATCH 4/4] disk-protect: Add a generic block layer queue freezing facility Elias Oltmanns
2008-03-15 12:49 ` Pavel Machek
2008-03-16 16:16 ` Elias Oltmanns [this message]
2008-03-17 23:00 ` Elias Oltmanns
2008-03-07 22:43 ` [RFC] Disk shock protection (revisited) Alan Cox
2008-03-13 14:51 ` Elias Oltmanns
2008-03-15 14:30 ` Alan Cox
2008-02-26 20:47 ` Willy Tarreau
2008-02-28 10:10 ` Elias Oltmanns
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87ve3mfxv0.fsf@denkblock.local \
--to=eo@nebensachen.de \
--cc=alan@lxorguk.ukuu.org.uk \
--cc=jens.axboe@oracle.com \
--cc=linux-ide@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).