All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mike Snitzer <snitzer@redhat.com>
To: dm-devel@redhat.com
Cc: Mikulas Patocka <mpatocka@redhat.com>
Subject: [PATCH 09/14] dm-multisnap-mikulas-freelist
Date: Mon,  1 Mar 2010 19:23:53 -0500	[thread overview]
Message-ID: <1267489438-6271-10-git-send-email-snitzer@redhat.com> (raw)
In-Reply-To: <1267489438-6271-1-git-send-email-snitzer@redhat.com>

From: Mikulas Patocka <mpatocka@redhat.com>

Freelist management.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
---
 drivers/md/dm-multisnap-freelist.c |  296 ++++++++++++++++++++++++++++++++++++
 1 files changed, 296 insertions(+), 0 deletions(-)
 create mode 100644 drivers/md/dm-multisnap-freelist.c

diff --git a/drivers/md/dm-multisnap-freelist.c b/drivers/md/dm-multisnap-freelist.c
new file mode 100644
index 0000000..6ec1476
--- /dev/null
+++ b/drivers/md/dm-multisnap-freelist.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2009 Red Hat Czech, s.r.o.
+ *
+ * Mikulas Patocka <mpatocka@redhat.com>
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm-multisnap-mikulas.h"
+
+/*
+ * Initialize in-memory freelist structure.
+ */
+void dm_multisnap_init_freelist(struct dm_multisnap_freelist *fl, unsigned chunk_size)
+{
+	cond_resched();
+	memset(fl, 0, chunk_size);
+	cond_resched();
+	fl->signature = FL_SIGNATURE;
+	write_48(fl, backlink, 0);
+	fl->n_entries = cpu_to_le32(0);
+}
+
+/*
+ * Add a given block to in-memory freelist.
+ * Returns:
+ *	-1 --- error
+ *	1 --- block was added
+ *	0 --- block could not be added because the freelist is full
+ */
+static int add_to_freelist(struct dm_exception_store *s, chunk_t block, unsigned flags)
+{
+	int i;
+	struct dm_multisnap_freelist *fl = s->freelist;
+	for (i = le32_to_cpu(fl->n_entries) - 1; i >= 0; i--) {
+		chunk_t x = read_48(&fl->entries[i], block);
+		unsigned r = le16_to_cpu(fl->entries[i].run_length) & FREELIST_RL_MASK;
+		unsigned f = le16_to_cpu(fl->entries[i].run_length) & FREELIST_DATA_FLAG;
+		if (block >= x && block < x + r) {
+			DM_MULTISNAP_SET_ERROR(s->dm, -EFSERROR,
+					       ("add_to_freelist: freeing already free block %llx (%llx - %x)",
+						(unsigned long long)block,
+						(unsigned long long)x,
+						r));
+			return -1;
+		}
+		if (likely(r < FREELIST_RL_MASK) && likely(f == flags)) {
+			if (block == x - 1) {
+				write_48(&fl->entries[i], block, x - 1);
+				goto inc_length;
+			}
+			if (block == x + r) {
+inc_length:
+				fl->entries[i].run_length = cpu_to_le16((r + 1) | f);
+				return 1;
+			}
+		}
+		cond_resched();
+	}
+	i = le32_to_cpu(fl->n_entries);
+	if (i < dm_multisnap_freelist_entries(s->chunk_size)) {
+		fl->n_entries = cpu_to_le32(i + 1);
+		write_48(&fl->entries[i], block, block);
+		fl->entries[i].run_length = cpu_to_le16(1 | flags);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Read a freelist block from the disk.
+ */
+static struct dm_multisnap_freelist *
+read_freelist(struct dm_exception_store *s, chunk_t block, struct dm_buffer **bp)
+{
+	struct dm_multisnap_freelist *fl;
+	fl = dm_bufio_read(s->bufio, block, bp);
+	if (IS_ERR(fl)) {
+		DM_MULTISNAP_SET_ERROR(s->dm, PTR_ERR(fl),
+				       ("read_freelist: can't read freelist block %llx",
+					(unsigned long long)block));
+		return NULL;
+	}
+	if (fl->signature != FL_SIGNATURE) {
+		dm_bufio_release(*bp);
+		DM_MULTISNAP_SET_ERROR(s->dm, -EFSERROR,
+				       ("read_freelist: bad signature freelist block %llx",
+					(unsigned long long)block));
+		return NULL;
+	}
+	if (le32_to_cpu(fl->n_entries) > dm_multisnap_freelist_entries(s->chunk_size)) {
+		dm_bufio_release(*bp);
+		DM_MULTISNAP_SET_ERROR(s->dm, -EFSERROR,
+				       ("read_freelist: bad number of entries in freelist block %llx",
+					(unsigned long long)block));
+		return NULL;
+	}
+	return fl;
+}
+
+/*
+ * Allocate a block and write the current in-memory freelist to it.
+ * Then, clear the in-memory freelist.
+ */
+static void alloc_write_freelist(struct dm_exception_store *s)
+{
+	chunk_t new_block;
+	struct dm_multisnap_freelist *fl;
+	struct dm_buffer *bp;
+
+	if (dm_multisnap_alloc_blocks(s, &new_block, 1, ALLOC_DRY))
+		return;
+
+	fl = dm_bufio_new(s->bufio, new_block, &bp);
+	if (IS_ERR(fl)) {
+		DM_MULTISNAP_SET_ERROR(s->dm, PTR_ERR(fl),
+				       ("alloc_write_freelist: can't make new freelist block %llx",
+					(unsigned long long)new_block));
+		return;
+	}
+
+	memcpy(fl, s->freelist, s->chunk_size);
+
+	dm_bufio_mark_buffer_dirty(bp);
+	dm_bufio_release(bp);
+
+	dm_multisnap_init_freelist(s->freelist, s->chunk_size);
+	write_48(s->freelist, backlink, new_block);
+}
+
+/*
+ * This function is called by other subsystems when they want to free a block.
+ * It adds the block to the current freelist, if the freelist is full, it
+ * flushes the freelist and makes a new one.
+ */
+void dm_multisnap_free_block(struct dm_exception_store *s, chunk_t block, unsigned flags)
+{
+	if (likely(add_to_freelist(s, block, flags)))
+		return;
+
+	alloc_write_freelist(s);
+	if (unlikely(dm_multisnap_has_error(s->dm)))
+		return;
+
+	if (likely(add_to_freelist(s, block, flags)))
+		return;
+
+	BUG();
+}
+
+/*
+ * Check if a given block is in a given freelist.
+ */
+static int check_against_freelist(struct dm_multisnap_freelist *fl, chunk_t block)
+{
+	int i;
+	for (i = le32_to_cpu(fl->n_entries) - 1; i >= 0; i--) {
+		chunk_t x = read_48(&fl->entries[i], block);
+		unsigned r = le16_to_cpu(fl->entries[i].run_length) & FREELIST_RL_MASK;
+		if (unlikely(block - x < r))
+			return 1;
+		cond_resched();
+	}
+	return 0;
+}
+
+/*
+ * Check if a given block is in any freelist in a freelist chain.
+ */
+static int check_against_freelist_chain(struct dm_exception_store *s,
+					chunk_t fl_block, chunk_t block)
+{
+	struct stop_cycles cy;
+	dm_multisnap_init_stop_cycles(&cy);
+
+	while (unlikely(fl_block != 0)) {
+		int c;
+		struct dm_buffer *bp;
+		struct dm_multisnap_freelist *fl;
+
+		if (dm_multisnap_stop_cycles(s, &cy, fl_block))
+			return -1;
+
+		if (unlikely(block == fl_block))
+			return 1;
+
+		fl = read_freelist(s, fl_block, &bp);
+		if (unlikely(!fl))
+			return -1;
+		c = check_against_freelist(fl, block);
+		fl_block = read_48(fl, backlink);
+		dm_bufio_release(bp);
+		if (unlikely(c))
+			return c;
+	}
+	return 0;
+}
+
+/*
+ * Check if a given block can be allocated. This checks against:
+ * - in-memory freelist
+ * - the current freelist chain
+ * - the freelist chain that was active on last commit
+ */
+int dm_multisnap_check_allocated_block(struct dm_exception_store *s, chunk_t block)
+{
+	int c;
+
+	c = check_against_freelist(s->freelist, block);
+	if (unlikely(c))
+		return c;
+
+	c = check_against_freelist_chain(s, read_48(s->freelist, backlink), block);
+	if (unlikely(c))
+		return c;
+
+	c = check_against_freelist_chain(s, s->freelist_ptr, block);
+	if (unlikely(c))
+		return c;
+
+	return 0;
+}
+
+/*
+ * This is called prior to commit, it writes the current freelist to the disk.
+ */
+void dm_multisnap_flush_freelist_before_commit(struct dm_exception_store *s)
+{
+	alloc_write_freelist(s);
+
+	if (dm_multisnap_has_error(s->dm))
+		return;
+
+	s->freelist_ptr = read_48(s->freelist, backlink);
+}
+
+/*
+ * Free the blocks in the freelist.
+ */
+static void free_blocks_in_freelist(struct dm_exception_store *s,
+				    struct dm_multisnap_freelist *fl)
+{
+	int i;
+	for (i = le32_to_cpu(fl->n_entries) - 1; i >= 0; i--) {
+		chunk_t x = read_48(&fl->entries[i], block);
+		unsigned r = le16_to_cpu(fl->entries[i].run_length) & FREELIST_RL_MASK;
+		unsigned f = le16_to_cpu(fl->entries[i].run_length) & FREELIST_DATA_FLAG;
+		dm_multisnap_free_blocks_immediate(s, x, r);
+		if (likely(f & FREELIST_DATA_FLAG)) {
+			dm_multisnap_status_lock(s->dm);
+			s->data_allocated -= r;
+			dm_multisnap_status_unlock(s->dm);
+		}
+		cond_resched();
+	}
+}
+
+/*
+ * This is called after a commit or after a mount. It walks the current freelist
+ * chain and frees the individual blocks.
+ *
+ * If the computer crashes while this operation is in progress, it is done again
+ * after a mount --- thus, it maintains data consistency.
+ */
+void dm_multisnap_load_freelist(struct dm_exception_store *s)
+{
+	chunk_t fl_block = s->freelist_ptr;
+
+	struct stop_cycles cy;
+	dm_multisnap_init_stop_cycles(&cy);
+
+	while (fl_block) {
+		struct dm_buffer *bp;
+		struct dm_multisnap_freelist *fl;
+
+		if (dm_multisnap_stop_cycles(s, &cy, fl_block))
+			break;
+
+		if (dm_multisnap_has_error(s->dm))
+			break;
+
+		fl = read_freelist(s, fl_block, &bp);
+		if (!fl)
+			break;
+		memcpy(s->freelist, fl, s->chunk_size);
+		dm_bufio_release(bp);
+
+		free_blocks_in_freelist(s, s->freelist);
+		fl_block = read_48(s->freelist, backlink);
+	}
+
+	/* Write the buffers eagerly to prevent further delays */
+	dm_bufio_write_dirty_buffers_async(s->bufio);
+
+	dm_multisnap_init_freelist(s->freelist, s->chunk_size);
+}
-- 
1.6.5.2

  parent reply	other threads:[~2010-03-02  0:23 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-03-02  0:23 [PATCH 00/14] mikulas' shared snapshot patches Mike Snitzer
2010-03-02  0:23 ` [PATCH 01/14] dm-multisnap-common Mike Snitzer
2010-03-02  0:23 ` [PATCH 02/14] dm-bufio Mike Snitzer
2010-03-02  0:23 ` [PATCH 03/14] dm-multisnap-mikulas-headers Mike Snitzer
2010-03-05 22:46   ` Mike Snitzer
2010-03-06  1:54     ` Mike Snitzer
2010-03-09  3:08     ` Mikulas Patocka
2010-03-09  3:30       ` Mike Snitzer
2010-03-02  0:23 ` [PATCH 04/14] dm-multisnap-mikulas-alloc Mike Snitzer
2010-03-02  0:23 ` [PATCH 05/14] dm-multisnap-mikulas-blocks Mike Snitzer
2010-03-02  0:23 ` [PATCH 06/14] dm-multisnap-mikulas-btree Mike Snitzer
2010-03-02  0:23 ` [PATCH 07/14] dm-multisnap-mikulas-commit Mike Snitzer
2010-03-02  0:23 ` [PATCH 08/14] dm-multisnap-mikulas-delete Mike Snitzer
2010-03-02  0:23 ` Mike Snitzer [this message]
2010-03-02  0:23 ` [PATCH 10/14] dm-multisnap-mikulas-io Mike Snitzer
2010-03-02  0:23 ` [PATCH 11/14] dm-multisnap-mikulas-snaps Mike Snitzer
2010-03-02  0:23 ` [PATCH 12/14] dm-multisnap-mikulas-common Mike Snitzer
2010-03-02  0:23 ` [PATCH 13/14] dm-multisnap-mikulas-config Mike Snitzer
2010-03-02  0:23 ` [PATCH 14/14] dm-multisnap-daniel Mike Snitzer
2010-03-02  0:57   ` FUJITA Tomonori
2010-03-02  0:32 ` [PATCH 00/14] mikulas' shared snapshot patches Mike Snitzer
2010-03-02 14:56 ` Mike Snitzer

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=1267489438-6271-10-git-send-email-snitzer@redhat.com \
    --to=snitzer@redhat.com \
    --cc=dm-devel@redhat.com \
    --cc=mpatocka@redhat.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.