Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9fc0178c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ryusuke/nilfs2:
  nilfs2: fix possible mismatch of sufile counters on recovery
  nilfs2: segment usage file cleanups
  nilfs2: fix wrong accounting and duplicate brelse in nilfs_sufile_set_error
  nilfs2: simplify handling of active state of segments fix
  nilfs2: remove module version
  nilfs2: fix lockdep recursive locking warning on meta data files
  nilfs2: fix lockdep recursive locking warning on bmap
  nilfs2: return f_fsid for statfs2
parents 2b6b6d38 c85399c2
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -688,6 +688,8 @@ static const struct nilfs_bmap_ptr_operations nilfs_bmap_ptr_ops_gc = {
	.bpop_translate		=	NULL,
};

static struct lock_class_key nilfs_bmap_dat_lock_key;

/**
 * nilfs_bmap_read - read a bmap from an inode
 * @bmap: bmap
@@ -715,6 +717,7 @@ int nilfs_bmap_read(struct nilfs_bmap *bmap, struct nilfs_inode *raw_inode)
		bmap->b_pops = &nilfs_bmap_ptr_ops_p;
		bmap->b_last_allocated_key = 0;	/* XXX: use macro */
		bmap->b_last_allocated_ptr = NILFS_BMAP_NEW_PTR_INIT;
		lockdep_set_class(&bmap->b_sem, &nilfs_bmap_dat_lock_key);
		break;
	case NILFS_CPFILE_INO:
	case NILFS_SUFILE_INO:
@@ -772,6 +775,7 @@ void nilfs_bmap_init_gcdat(struct nilfs_bmap *gcbmap, struct nilfs_bmap *bmap)
{
	memcpy(gcbmap, bmap, sizeof(union nilfs_bmap_union));
	init_rwsem(&gcbmap->b_sem);
	lockdep_set_class(&bmap->b_sem, &nilfs_bmap_dat_lock_key);
	gcbmap->b_inode = &NILFS_BMAP_I(gcbmap)->vfs_inode;
}

@@ -779,5 +783,6 @@ void nilfs_bmap_commit_gcdat(struct nilfs_bmap *gcbmap, struct nilfs_bmap *bmap)
{
	memcpy(bmap, gcbmap, sizeof(union nilfs_bmap_union));
	init_rwsem(&bmap->b_sem);
	lockdep_set_class(&bmap->b_sem, &nilfs_bmap_dat_lock_key);
	bmap->b_inode = &NILFS_BMAP_I(bmap)->vfs_inode;
}
+0 −5
Original line number Diff line number Diff line
@@ -34,11 +34,6 @@
#include "bmap.h"
#include "bmap_union.h"

/*
 * NILFS filesystem version
 */
#define NILFS_VERSION		"2.0.5"

/*
 * nilfs inode data in memory
 */
+4 −16
Original line number Diff line number Diff line
@@ -413,7 +413,6 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs,
	struct nilfs_segment_entry *ent, *n;
	struct inode *sufile = nilfs->ns_sufile;
	__u64 segnum[4];
	time_t mtime;
	int err;
	int i;

@@ -442,24 +441,13 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs,
	 * Collecting segments written after the latest super root.
	 * These are marked dirty to avoid being reallocated in the next write.
	 */
	mtime = get_seconds();
	list_for_each_entry_safe(ent, n, head, list) {
		if (ent->segnum == segnum[0]) {
			list_del(&ent->list);
			nilfs_free_segment_entry(ent);
			continue;
		}
		err = nilfs_open_segment_entry(ent, sufile);
		if (ent->segnum != segnum[0]) {
			err = nilfs_sufile_scrap(sufile, ent->segnum);
			if (unlikely(err))
				goto failed;
		if (!nilfs_segment_usage_dirty(ent->raw_su)) {
			/* make the segment garbage */
			ent->raw_su->su_nblocks = cpu_to_le32(0);
			ent->raw_su->su_lastmod = cpu_to_le32(mtime);
			nilfs_segment_usage_set_dirty(ent->raw_su);
		}
		list_del(&ent->list);
		nilfs_close_segment_entry(ent, sufile);
		nilfs_free_segment_entry(ent);
	}

+104 −186
Original line number Diff line number Diff line
@@ -93,6 +93,52 @@ nilfs_sufile_get_segment_usage_block(struct inode *sufile, __u64 segnum,
				   create, NULL, bhp);
}

static void nilfs_sufile_mod_counter(struct buffer_head *header_bh,
				     u64 ncleanadd, u64 ndirtyadd)
{
	struct nilfs_sufile_header *header;
	void *kaddr;

	kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
	header = kaddr + bh_offset(header_bh);
	le64_add_cpu(&header->sh_ncleansegs, ncleanadd);
	le64_add_cpu(&header->sh_ndirtysegs, ndirtyadd);
	kunmap_atomic(kaddr, KM_USER0);

	nilfs_mdt_mark_buffer_dirty(header_bh);
}

int nilfs_sufile_update(struct inode *sufile, __u64 segnum, int create,
			void (*dofunc)(struct inode *, __u64,
				       struct buffer_head *,
				       struct buffer_head *))
{
	struct buffer_head *header_bh, *bh;
	int ret;

	if (unlikely(segnum >= nilfs_sufile_get_nsegments(sufile))) {
		printk(KERN_WARNING "%s: invalid segment number: %llu\n",
		       __func__, (unsigned long long)segnum);
		return -EINVAL;
	}
	down_write(&NILFS_MDT(sufile)->mi_sem);

	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
	if (ret < 0)
		goto out_sem;

	ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, create, &bh);
	if (!ret) {
		dofunc(sufile, segnum, header_bh, bh);
		brelse(bh);
	}
	brelse(header_bh);

 out_sem:
	up_write(&NILFS_MDT(sufile)->mi_sem);
	return ret;
}

/**
 * nilfs_sufile_alloc - allocate a segment
 * @sufile: inode of segment usage file
@@ -113,7 +159,6 @@ nilfs_sufile_get_segment_usage_block(struct inode *sufile, __u64 segnum,
int nilfs_sufile_alloc(struct inode *sufile, __u64 *segnump)
{
	struct buffer_head *header_bh, *su_bh;
	struct the_nilfs *nilfs;
	struct nilfs_sufile_header *header;
	struct nilfs_segment_usage *su;
	size_t susz = NILFS_MDT(sufile)->mi_entry_size;
@@ -124,8 +169,6 @@ int nilfs_sufile_alloc(struct inode *sufile, __u64 *segnump)

	down_write(&NILFS_MDT(sufile)->mi_sem);

	nilfs = NILFS_MDT(sufile)->mi_nilfs;

	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
	if (ret < 0)
		goto out_sem;
@@ -192,165 +235,84 @@ int nilfs_sufile_alloc(struct inode *sufile, __u64 *segnump)
	return ret;
}

/**
 * nilfs_sufile_cancel_free -
 * @sufile: inode of segment usage file
 * @segnum: segment number
 *
 * Description:
 *
 * Return Value: On success, 0 is returned. On error, one of the following
 * negative error codes is returned.
 *
 * %-EIO - I/O error.
 *
 * %-ENOMEM - Insufficient amount of memory available.
 */
int nilfs_sufile_cancel_free(struct inode *sufile, __u64 segnum)
void nilfs_sufile_do_cancel_free(struct inode *sufile, __u64 segnum,
				 struct buffer_head *header_bh,
				 struct buffer_head *su_bh)
{
	struct buffer_head *header_bh, *su_bh;
	struct the_nilfs *nilfs;
	struct nilfs_sufile_header *header;
	struct nilfs_segment_usage *su;
	void *kaddr;
	int ret;

	down_write(&NILFS_MDT(sufile)->mi_sem);

	nilfs = NILFS_MDT(sufile)->mi_nilfs;

	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
	if (ret < 0)
		goto out_sem;

	ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &su_bh);
	if (ret < 0)
		goto out_header;

	kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
	su = nilfs_sufile_block_get_segment_usage(
		sufile, segnum, su_bh, kaddr);
	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr);
	if (unlikely(!nilfs_segment_usage_clean(su))) {
		printk(KERN_WARNING "%s: segment %llu must be clean\n",
		       __func__, (unsigned long long)segnum);
		kunmap_atomic(kaddr, KM_USER0);
		goto out_su_bh;
		return;
	}
	nilfs_segment_usage_set_dirty(su);
	kunmap_atomic(kaddr, KM_USER0);

	kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
	header = nilfs_sufile_block_get_header(sufile, header_bh, kaddr);
	le64_add_cpu(&header->sh_ncleansegs, -1);
	le64_add_cpu(&header->sh_ndirtysegs, 1);
	kunmap_atomic(kaddr, KM_USER0);

	nilfs_mdt_mark_buffer_dirty(header_bh);
	nilfs_sufile_mod_counter(header_bh, -1, 1);
	nilfs_mdt_mark_buffer_dirty(su_bh);
	nilfs_mdt_mark_dirty(sufile);

 out_su_bh:
	brelse(su_bh);
 out_header:
	brelse(header_bh);
 out_sem:
	up_write(&NILFS_MDT(sufile)->mi_sem);
	return ret;
}

/**
 * nilfs_sufile_freev - free segments
 * @sufile: inode of segment usage file
 * @segnum: array of segment numbers
 * @nsegs: number of segments
 *
 * Description: nilfs_sufile_freev() frees segments specified by @segnum and
 * @nsegs, which must have been returned by a previous call to
 * nilfs_sufile_alloc().
 *
 * Return Value: On success, 0 is returned. On error, one of the following
 * negative error codes is returned.
 *
 * %-EIO - I/O error.
 *
 * %-ENOMEM - Insufficient amount of memory available.
 */
#define NILFS_SUFILE_FREEV_PREALLOC	16
int nilfs_sufile_freev(struct inode *sufile, __u64 *segnum, size_t nsegs)
void nilfs_sufile_do_scrap(struct inode *sufile, __u64 segnum,
			   struct buffer_head *header_bh,
			   struct buffer_head *su_bh)
{
	struct buffer_head *header_bh, **su_bh,
		*su_bh_prealloc[NILFS_SUFILE_FREEV_PREALLOC];
	struct the_nilfs *nilfs;
	struct nilfs_sufile_header *header;
	struct nilfs_segment_usage *su;
	void *kaddr;
	int ret, i;
	int clean, dirty;

	down_write(&NILFS_MDT(sufile)->mi_sem);
	kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr);
	if (su->su_flags == cpu_to_le32(1UL << NILFS_SEGMENT_USAGE_DIRTY) &&
	    su->su_nblocks == cpu_to_le32(0)) {
		kunmap_atomic(kaddr, KM_USER0);
		return;
	}
	clean = nilfs_segment_usage_clean(su);
	dirty = nilfs_segment_usage_dirty(su);

	nilfs = NILFS_MDT(sufile)->mi_nilfs;
	/* make the segment garbage */
	su->su_lastmod = cpu_to_le64(0);
	su->su_nblocks = cpu_to_le32(0);
	su->su_flags = cpu_to_le32(1UL << NILFS_SEGMENT_USAGE_DIRTY);
	kunmap_atomic(kaddr, KM_USER0);

	/* prepare resources */
	if (nsegs <= NILFS_SUFILE_FREEV_PREALLOC)
		su_bh = su_bh_prealloc;
	else {
		su_bh = kmalloc(sizeof(*su_bh) * nsegs, GFP_NOFS);
		if (su_bh == NULL) {
			ret = -ENOMEM;
			goto out_sem;
		}
	nilfs_sufile_mod_counter(header_bh, clean ? (u64)-1 : 0, dirty ? 0 : 1);
	nilfs_mdt_mark_buffer_dirty(su_bh);
	nilfs_mdt_mark_dirty(sufile);
}

	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
	if (ret < 0)
		goto out_su_bh;
	for (i = 0; i < nsegs; i++) {
		ret = nilfs_sufile_get_segment_usage_block(sufile, segnum[i],
							   0, &su_bh[i]);
		if (ret < 0)
			goto out_bh;
	}
void nilfs_sufile_do_free(struct inode *sufile, __u64 segnum,
			  struct buffer_head *header_bh,
			  struct buffer_head *su_bh)
{
	struct nilfs_segment_usage *su;
	void *kaddr;
	int sudirty;

	/* free segments */
	for (i = 0; i < nsegs; i++) {
		kaddr = kmap_atomic(su_bh[i]->b_page, KM_USER0);
		su = nilfs_sufile_block_get_segment_usage(
			sufile, segnum[i], su_bh[i], kaddr);
		WARN_ON(nilfs_segment_usage_error(su));
		nilfs_segment_usage_set_clean(su);
	kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr);
	if (nilfs_segment_usage_clean(su)) {
		printk(KERN_WARNING "%s: segment %llu is already clean\n",
		       __func__, (unsigned long long)segnum);
		kunmap_atomic(kaddr, KM_USER0);
		nilfs_mdt_mark_buffer_dirty(su_bh[i]);
		return;
	}
	kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
	header = nilfs_sufile_block_get_header(sufile, header_bh, kaddr);
	le64_add_cpu(&header->sh_ncleansegs, nsegs);
	le64_add_cpu(&header->sh_ndirtysegs, -(u64)nsegs);
	kunmap_atomic(kaddr, KM_USER0);
	nilfs_mdt_mark_buffer_dirty(header_bh);
	nilfs_mdt_mark_dirty(sufile);

 out_bh:
	for (i--; i >= 0; i--)
		brelse(su_bh[i]);
	brelse(header_bh);

 out_su_bh:
	if (su_bh != su_bh_prealloc)
		kfree(su_bh);
	WARN_ON(nilfs_segment_usage_error(su));
	WARN_ON(!nilfs_segment_usage_dirty(su));

 out_sem:
	up_write(&NILFS_MDT(sufile)->mi_sem);
	return ret;
}
	sudirty = nilfs_segment_usage_dirty(su);
	nilfs_segment_usage_set_clean(su);
	kunmap_atomic(kaddr, KM_USER0);
	nilfs_mdt_mark_buffer_dirty(su_bh);

/**
 * nilfs_sufile_free -
 * @sufile:
 * @segnum:
 */
int nilfs_sufile_free(struct inode *sufile, __u64 segnum)
{
	return nilfs_sufile_freev(sufile, &segnum, 1);
	nilfs_sufile_mod_counter(header_bh, 1, sudirty ? (u64)-1 : 0);
	nilfs_mdt_mark_dirty(sufile);
}

/**
@@ -500,72 +462,28 @@ int nilfs_sufile_get_ncleansegs(struct inode *sufile, unsigned long *nsegsp)
	return ret;
}

/**
 * nilfs_sufile_set_error - mark a segment as erroneous
 * @sufile: inode of segment usage file
 * @segnum: segment number
 *
 * Description: nilfs_sufile_set_error() marks the segment specified by
 * @segnum as erroneous. The error segment will never be used again.
 *
 * Return Value: On success, 0 is returned. On error, one of the following
 * negative error codes is returned.
 *
 * %-EIO - I/O error.
 *
 * %-ENOMEM - Insufficient amount of memory available.
 *
 * %-EINVAL - Invalid segment usage number.
 */
int nilfs_sufile_set_error(struct inode *sufile, __u64 segnum)
void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum,
			       struct buffer_head *header_bh,
			       struct buffer_head *su_bh)
{
	struct buffer_head *header_bh, *su_bh;
	struct nilfs_segment_usage *su;
	struct nilfs_sufile_header *header;
	void *kaddr;
	int ret;

	if (unlikely(segnum >= nilfs_sufile_get_nsegments(sufile))) {
		printk(KERN_WARNING "%s: invalid segment number: %llu\n",
		       __func__, (unsigned long long)segnum);
		return -EINVAL;
	}
	down_write(&NILFS_MDT(sufile)->mi_sem);

	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
	if (ret < 0)
		goto out_sem;
	ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &su_bh);
	if (ret < 0)
		goto out_header;
	int suclean;

	kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr);
	if (nilfs_segment_usage_error(su)) {
		kunmap_atomic(kaddr, KM_USER0);
		brelse(su_bh);
		goto out_header;
		return;
	}

	suclean = nilfs_segment_usage_clean(su);
	nilfs_segment_usage_set_error(su);
	kunmap_atomic(kaddr, KM_USER0);
	brelse(su_bh);

	kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
	header = nilfs_sufile_block_get_header(sufile, header_bh, kaddr);
	le64_add_cpu(&header->sh_ndirtysegs, -1);
	kunmap_atomic(kaddr, KM_USER0);
	nilfs_mdt_mark_buffer_dirty(header_bh);
	if (suclean)
		nilfs_sufile_mod_counter(header_bh, -1, 0);
	nilfs_mdt_mark_buffer_dirty(su_bh);
	nilfs_mdt_mark_dirty(sufile);
	brelse(su_bh);

 out_header:
	brelse(header_bh);

 out_sem:
	up_write(&NILFS_MDT(sufile)->mi_sem);
	return ret;
}

/**
@@ -625,7 +543,7 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum,
			si[i + j].sui_nblocks = le32_to_cpu(su->su_nblocks);
			si[i + j].sui_flags = le32_to_cpu(su->su_flags) &
				~(1UL << NILFS_SEGMENT_USAGE_ACTIVE);
			if (nilfs_segment_is_active(nilfs, segnum + i + j))
			if (nilfs_segment_is_active(nilfs, segnum + j))
				si[i + j].sui_flags |=
					(1UL << NILFS_SEGMENT_USAGE_ACTIVE);
		}
+75 −4
Original line number Diff line number Diff line
@@ -36,9 +36,6 @@ static inline unsigned long nilfs_sufile_get_nsegments(struct inode *sufile)
}

int nilfs_sufile_alloc(struct inode *, __u64 *);
int nilfs_sufile_cancel_free(struct inode *, __u64);
int nilfs_sufile_freev(struct inode *, __u64 *, size_t);
int nilfs_sufile_free(struct inode *, __u64);
int nilfs_sufile_get_segment_usage(struct inode *, __u64,
				   struct nilfs_segment_usage **,
				   struct buffer_head **);
@@ -46,9 +43,83 @@ void nilfs_sufile_put_segment_usage(struct inode *, __u64,
				    struct buffer_head *);
int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *);
int nilfs_sufile_get_ncleansegs(struct inode *, unsigned long *);
int nilfs_sufile_set_error(struct inode *, __u64);
ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, struct nilfs_suinfo *,
				size_t);

int nilfs_sufile_update(struct inode *, __u64, int,
			void (*dofunc)(struct inode *, __u64,
				       struct buffer_head *,
				       struct buffer_head *));
void nilfs_sufile_do_cancel_free(struct inode *, __u64, struct buffer_head *,
				 struct buffer_head *);
void nilfs_sufile_do_scrap(struct inode *, __u64, struct buffer_head *,
			   struct buffer_head *);
void nilfs_sufile_do_free(struct inode *, __u64, struct buffer_head *,
			  struct buffer_head *);
void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *,
			       struct buffer_head *);

/**
 * nilfs_sufile_cancel_free -
 * @sufile: inode of segment usage file
 * @segnum: segment number
 *
 * Description:
 *
 * Return Value: On success, 0 is returned. On error, one of the following
 * negative error codes is returned.
 *
 * %-EIO - I/O error.
 *
 * %-ENOMEM - Insufficient amount of memory available.
 */
static inline int nilfs_sufile_cancel_free(struct inode *sufile, __u64 segnum)
{
	return nilfs_sufile_update(sufile, segnum, 0,
				   nilfs_sufile_do_cancel_free);
}

/**
 * nilfs_sufile_scrap - make a segment garbage
 * @sufile: inode of segment usage file
 * @segnum: segment number to be freed
 */
static inline int nilfs_sufile_scrap(struct inode *sufile, __u64 segnum)
{
	return nilfs_sufile_update(sufile, segnum, 1, nilfs_sufile_do_scrap);
}

/**
 * nilfs_sufile_free - free segment
 * @sufile: inode of segment usage file
 * @segnum: segment number to be freed
 */
static inline int nilfs_sufile_free(struct inode *sufile, __u64 segnum)
{
	return nilfs_sufile_update(sufile, segnum, 0, nilfs_sufile_do_free);
}

/**
 * nilfs_sufile_set_error - mark a segment as erroneous
 * @sufile: inode of segment usage file
 * @segnum: segment number
 *
 * Description: nilfs_sufile_set_error() marks the segment specified by
 * @segnum as erroneous. The error segment will never be used again.
 *
 * Return Value: On success, 0 is returned. On error, one of the following
 * negative error codes is returned.
 *
 * %-EIO - I/O error.
 *
 * %-ENOMEM - Insufficient amount of memory available.
 *
 * %-EINVAL - Invalid segment usage number.
 */
static inline int nilfs_sufile_set_error(struct inode *sufile, __u64 segnum)
{
	return nilfs_sufile_update(sufile, segnum, 0,
				   nilfs_sufile_do_set_error);
}

#endif	/* _NILFS_SUFILE_H */
Loading