|
@@ -578,7 +578,7 @@ static void write_endio(struct bio *bio, int error)
|
|
|
struct dm_buffer *b = container_of(bio, struct dm_buffer, bio);
|
|
|
|
|
|
b->write_error = error;
|
|
|
- if (error) {
|
|
|
+ if (unlikely(error)) {
|
|
|
struct dm_bufio_client *c = b->c;
|
|
|
(void)cmpxchg(&c->async_write_error, 0, error);
|
|
|
}
|
|
@@ -697,13 +697,20 @@ static void __wait_for_free_buffer(struct dm_bufio_client *c)
|
|
|
dm_bufio_lock(c);
|
|
|
}
|
|
|
|
|
|
+enum new_flag {
|
|
|
+ NF_FRESH = 0,
|
|
|
+ NF_READ = 1,
|
|
|
+ NF_GET = 2,
|
|
|
+ NF_PREFETCH = 3
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* Allocate a new buffer. If the allocation is not possible, wait until
|
|
|
* some other thread frees a buffer.
|
|
|
*
|
|
|
* May drop the lock and regain it.
|
|
|
*/
|
|
|
-static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client *c)
|
|
|
+static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client *c, enum new_flag nf)
|
|
|
{
|
|
|
struct dm_buffer *b;
|
|
|
|
|
@@ -726,6 +733,9 @@ static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client
|
|
|
return b;
|
|
|
}
|
|
|
|
|
|
+ if (nf == NF_PREFETCH)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
if (!list_empty(&c->reserved_buffers)) {
|
|
|
b = list_entry(c->reserved_buffers.next,
|
|
|
struct dm_buffer, lru_list);
|
|
@@ -743,9 +753,12 @@ static struct dm_buffer *__alloc_buffer_wait_no_callback(struct dm_bufio_client
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static struct dm_buffer *__alloc_buffer_wait(struct dm_bufio_client *c)
|
|
|
+static struct dm_buffer *__alloc_buffer_wait(struct dm_bufio_client *c, enum new_flag nf)
|
|
|
{
|
|
|
- struct dm_buffer *b = __alloc_buffer_wait_no_callback(c);
|
|
|
+ struct dm_buffer *b = __alloc_buffer_wait_no_callback(c, nf);
|
|
|
+
|
|
|
+ if (!b)
|
|
|
+ return NULL;
|
|
|
|
|
|
if (c->alloc_callback)
|
|
|
c->alloc_callback(b);
|
|
@@ -865,32 +878,23 @@ static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block)
|
|
|
* Getting a buffer
|
|
|
*--------------------------------------------------------------*/
|
|
|
|
|
|
-enum new_flag {
|
|
|
- NF_FRESH = 0,
|
|
|
- NF_READ = 1,
|
|
|
- NF_GET = 2
|
|
|
-};
|
|
|
-
|
|
|
static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
|
|
|
- enum new_flag nf, struct dm_buffer **bp,
|
|
|
- int *need_submit)
|
|
|
+ enum new_flag nf, int *need_submit)
|
|
|
{
|
|
|
struct dm_buffer *b, *new_b = NULL;
|
|
|
|
|
|
*need_submit = 0;
|
|
|
|
|
|
b = __find(c, block);
|
|
|
- if (b) {
|
|
|
- b->hold_count++;
|
|
|
- __relink_lru(b, test_bit(B_DIRTY, &b->state) ||
|
|
|
- test_bit(B_WRITING, &b->state));
|
|
|
- return b;
|
|
|
- }
|
|
|
+ if (b)
|
|
|
+ goto found_buffer;
|
|
|
|
|
|
if (nf == NF_GET)
|
|
|
return NULL;
|
|
|
|
|
|
- new_b = __alloc_buffer_wait(c);
|
|
|
+ new_b = __alloc_buffer_wait(c, nf);
|
|
|
+ if (!new_b)
|
|
|
+ return NULL;
|
|
|
|
|
|
/*
|
|
|
* We've had a period where the mutex was unlocked, so need to
|
|
@@ -899,10 +903,7 @@ static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
|
|
|
b = __find(c, block);
|
|
|
if (b) {
|
|
|
__free_buffer_wake(new_b);
|
|
|
- b->hold_count++;
|
|
|
- __relink_lru(b, test_bit(B_DIRTY, &b->state) ||
|
|
|
- test_bit(B_WRITING, &b->state));
|
|
|
- return b;
|
|
|
+ goto found_buffer;
|
|
|
}
|
|
|
|
|
|
__check_watermark(c);
|
|
@@ -922,6 +923,24 @@ static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
|
|
|
*need_submit = 1;
|
|
|
|
|
|
return b;
|
|
|
+
|
|
|
+found_buffer:
|
|
|
+ if (nf == NF_PREFETCH)
|
|
|
+ return NULL;
|
|
|
+ /*
|
|
|
+ * Note: it is essential that we don't wait for the buffer to be
|
|
|
+ * read if dm_bufio_get function is used. Both dm_bufio_get and
|
|
|
+ * dm_bufio_prefetch can be used in the driver request routine.
|
|
|
+ * If the user called both dm_bufio_prefetch and dm_bufio_get on
|
|
|
+ * the same buffer, it would deadlock if we waited.
|
|
|
+ */
|
|
|
+ if (nf == NF_GET && unlikely(test_bit(B_READING, &b->state)))
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ b->hold_count++;
|
|
|
+ __relink_lru(b, test_bit(B_DIRTY, &b->state) ||
|
|
|
+ test_bit(B_WRITING, &b->state));
|
|
|
+ return b;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -956,10 +975,10 @@ static void *new_read(struct dm_bufio_client *c, sector_t block,
|
|
|
struct dm_buffer *b;
|
|
|
|
|
|
dm_bufio_lock(c);
|
|
|
- b = __bufio_new(c, block, nf, bp, &need_submit);
|
|
|
+ b = __bufio_new(c, block, nf, &need_submit);
|
|
|
dm_bufio_unlock(c);
|
|
|
|
|
|
- if (!b || IS_ERR(b))
|
|
|
+ if (!b)
|
|
|
return b;
|
|
|
|
|
|
if (need_submit)
|
|
@@ -1005,13 +1024,47 @@ void *dm_bufio_new(struct dm_bufio_client *c, sector_t block,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(dm_bufio_new);
|
|
|
|
|
|
+void dm_bufio_prefetch(struct dm_bufio_client *c,
|
|
|
+ sector_t block, unsigned n_blocks)
|
|
|
+{
|
|
|
+ struct blk_plug plug;
|
|
|
+
|
|
|
+ blk_start_plug(&plug);
|
|
|
+ dm_bufio_lock(c);
|
|
|
+
|
|
|
+ for (; n_blocks--; block++) {
|
|
|
+ int need_submit;
|
|
|
+ struct dm_buffer *b;
|
|
|
+ b = __bufio_new(c, block, NF_PREFETCH, &need_submit);
|
|
|
+ if (unlikely(b != NULL)) {
|
|
|
+ dm_bufio_unlock(c);
|
|
|
+
|
|
|
+ if (need_submit)
|
|
|
+ submit_io(b, READ, b->block, read_endio);
|
|
|
+ dm_bufio_release(b);
|
|
|
+
|
|
|
+ dm_bufio_cond_resched();
|
|
|
+
|
|
|
+ if (!n_blocks)
|
|
|
+ goto flush_plug;
|
|
|
+ dm_bufio_lock(c);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ dm_bufio_unlock(c);
|
|
|
+
|
|
|
+flush_plug:
|
|
|
+ blk_finish_plug(&plug);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(dm_bufio_prefetch);
|
|
|
+
|
|
|
void dm_bufio_release(struct dm_buffer *b)
|
|
|
{
|
|
|
struct dm_bufio_client *c = b->c;
|
|
|
|
|
|
dm_bufio_lock(c);
|
|
|
|
|
|
- BUG_ON(test_bit(B_READING, &b->state));
|
|
|
BUG_ON(!b->hold_count);
|
|
|
|
|
|
b->hold_count--;
|
|
@@ -1024,6 +1077,7 @@ void dm_bufio_release(struct dm_buffer *b)
|
|
|
* invalid buffer.
|
|
|
*/
|
|
|
if ((b->read_error || b->write_error) &&
|
|
|
+ !test_bit(B_READING, &b->state) &&
|
|
|
!test_bit(B_WRITING, &b->state) &&
|
|
|
!test_bit(B_DIRTY, &b->state)) {
|
|
|
__unlink_buffer(b);
|
|
@@ -1041,6 +1095,8 @@ void dm_bufio_mark_buffer_dirty(struct dm_buffer *b)
|
|
|
|
|
|
dm_bufio_lock(c);
|
|
|
|
|
|
+ BUG_ON(test_bit(B_READING, &b->state));
|
|
|
+
|
|
|
if (!test_and_set_bit(B_DIRTY, &b->state))
|
|
|
__relink_lru(b, LIST_DIRTY);
|
|
|
|