#include <linux/mount.h>
#include <linux/math64.h>
#include <linux/writeback.h>
+#include <linux/smp_lock.h>
#include "ubifs.h"
/*
out:
if (ui->dirty)
ubifs_release_dirty_inode_budget(c, ui);
+ else {
+ /* We've deleted something - clean the "no space" flags */
+ c->nospace = c->nospace_rp = 0;
+ smp_wmb();
+ }
clear_inode(inode);
}
if (!wait)
return 0;
- if (sb->s_flags & MS_RDONLY)
- return 0;
-
/*
* VFS calls '->sync_fs()' before synchronizing all dirty inodes and
* pages, so synchronize them first, then commit the journal. Strictly
* does not need to be synchronized by timer.
*/
c->jheads[GCHD].wbuf.dtype = UBI_LONGTERM;
- c->jheads[GCHD].wbuf.timeout = 0;
+ c->jheads[GCHD].wbuf.softlimit = ktime_set(0, 0);
return 0;
}
};
/**
+ * parse_standard_option - parse a standard mount option.
+ * @option: the option to parse
+ *
+ * Normally, standard mount options like "sync" are passed to file-systems as
+ * flags. However, when a "rootflags=" kernel boot parameter is used, they may
+ * be present in the options string. This function tries to deal with this
+ * situation and parse standard options. Returns 0 if the option was not
+ * recognized, and the corresponding integer flag if it was.
+ *
+ * UBIFS is only interested in the "sync" option, so do not check for anything
+ * else.
+ */
+ static int parse_standard_option(const char *option)
+ {
+ ubifs_msg("parse %s", option);
+ if (!strcmp(option, "sync"))
+ return MS_SYNCHRONOUS;
+ return 0;
+ }
+
+ /**
* ubifs_parse_options - parse mount parameters.
* @c: UBIFS file-system description object
* @options: parameters to parse
break;
}
default:
- ubifs_err("unrecognized mount option \"%s\" "
- "or missing value", p);
- return -EINVAL;
+ {
+ unsigned long flag;
+ struct super_block *sb = c->vfs_sb;
+
+ flag = parse_standard_option(p);
+ if (!flag) {
+ ubifs_err("unrecognized mount option \"%s\" "
+ "or missing value", p);
+ return -EINVAL;
+ }
+ sb->s_flags |= flag;
+ break;
+ }
}
}
if (!ubifs_compr_present(c->default_compr)) {
ubifs_err("'compressor \"%s\" is not compiled in",
ubifs_compr_name(c->default_compr));
+ err = -ENOTSUPP;
goto out_free;
}
for (i = 0; i < c->jhead_cnt; i++) {
ubifs_wbuf_sync(&c->jheads[i].wbuf);
- del_timer_sync(&c->jheads[i].wbuf.timer);
+ hrtimer_cancel(&c->jheads[i].wbuf.timer);
}
c->mst_node->flags &= ~cpu_to_le32(UBIFS_MST_DIRTY);
ubifs_msg("un-mount UBI device %d, volume %d", c->vi.ubi_num,
c->vi.vol_id);
+
+ lock_kernel();
+
/*
* The following asserts are only valid if there has not been a failure
* of the media. For example, there will be dirty inodes if we failed
if (c->jheads)
for (i = 0; i < c->jhead_cnt; i++) {
ubifs_wbuf_sync(&c->jheads[i].wbuf);
- del_timer_sync(&c->jheads[i].wbuf.timer);
+ hrtimer_cancel(&c->jheads[i].wbuf.timer);
}
/*
ubi_close_volume(c->ubi);
mutex_unlock(&c->umount_mutex);
kfree(c);
+
+ unlock_kernel();
}
static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
return err;
}
+ lock_kernel();
if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) {
if (c->ro_media) {
ubifs_msg("cannot re-mount due to prior errors");
+ unlock_kernel();
return -EROFS;
}
err = ubifs_remount_rw(c);
- if (err)
+ if (err) {
+ unlock_kernel();
return err;
+ }
} else if (!(sb->s_flags & MS_RDONLY) && (*flags & MS_RDONLY)) {
if (c->ro_media) {
ubifs_msg("cannot re-mount due to prior errors");
+ unlock_kernel();
return -EROFS;
}
ubifs_remount_ro(c);
}
ubifs_assert(c->lst.taken_empty_lebs > 0);
+ unlock_kernel();
return 0;
}
INIT_LIST_HEAD(&c->orph_list);
INIT_LIST_HEAD(&c->orph_new);
+ c->vfs_sb = sb;
c->highest_inum = UBIFS_FIRST_INO;
c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
if (err)
goto out_bdi;
- c->vfs_sb = sb;
-
sb->s_fs_info = c;
sb->s_magic = UBIFS_SUPER_MAGIC;
sb->s_blocksize = UBIFS_BLOCK_SIZE;
sb->s_blocksize_bits = UBIFS_BLOCK_SHIFT;
- sb->s_dev = c->vi.cdev;
sb->s_maxbytes = c->max_inode_sz = key_max_inode_size(c);
if (c->max_inode_sz > MAX_LFS_FILESIZE)
sb->s_maxbytes = c->max_inode_sz = MAX_LFS_FILESIZE;
static int sb_test(struct super_block *sb, void *data)
{
dev_t *dev = data;
+ struct ubifs_info *c = sb->s_fs_info;
- return sb->s_dev == *dev;
- }
-
- static int sb_set(struct super_block *sb, void *data)
- {
- dev_t *dev = data;
-
- sb->s_dev = *dev;
- return 0;
+ return c->vi.cdev == *dev;
}
static int ubifs_get_sb(struct file_system_type *fs_type, int flags,
dbg_gen("opened ubi%d_%d", vi.ubi_num, vi.vol_id);
- sb = sget(fs_type, &sb_test, &sb_set, &vi.cdev);
+ sb = sget(fs_type, &sb_test, &set_anon_super, &vi.cdev);
if (IS_ERR(sb)) {
err = PTR_ERR(sb);
goto out_close;
return 0;
out_deact:
- up_write(&sb->s_umount);
- deactivate_super(sb);
+ deactivate_locked_super(sb);
out_close:
ubi_close_volume(ubi);
return err;
}
- static void ubifs_kill_sb(struct super_block *sb)
- {
- generic_shutdown_super(sb);
- }
-
static struct file_system_type ubifs_fs_type = {
.name = "ubifs",
.owner = THIS_MODULE,
.get_sb = ubifs_get_sb,
- .kill_sb = ubifs_kill_sb
+ .kill_sb = kill_anon_super,
};
/*
#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/debugobjects.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
#include <asm/uaccess.h>
* Switch the timer base to the current CPU when possible.
*/
static inline struct hrtimer_clock_base *
-switch_hrtimer_base(struct hrtimer *timer, struct hrtimer_clock_base *base)
+switch_hrtimer_base(struct hrtimer *timer, struct hrtimer_clock_base *base,
+ int pinned)
{
struct hrtimer_clock_base *new_base;
struct hrtimer_cpu_base *new_cpu_base;
+ int cpu, preferred_cpu = -1;
+
+ cpu = smp_processor_id();
+#if defined(CONFIG_NO_HZ) && defined(CONFIG_SMP)
+ if (!pinned && get_sysctl_timer_migration() && idle_cpu(cpu)) {
+ preferred_cpu = get_nohz_load_balancer();
+ if (preferred_cpu >= 0)
+ cpu = preferred_cpu;
+ }
+#endif
- new_cpu_base = &__get_cpu_var(hrtimer_bases);
+again:
+ new_cpu_base = &per_cpu(hrtimer_bases, cpu);
new_base = &new_cpu_base->clock_base[base->index];
if (base != new_base) {
timer->base = NULL;
spin_unlock(&base->cpu_base->lock);
spin_lock(&new_base->cpu_base->lock);
+
+ /* Optimized away for NOHZ=n SMP=n */
+ if (cpu == preferred_cpu) {
+ /* Calculate clock monotonic expiry time */
+#ifdef CONFIG_HIGH_RES_TIMERS
+ ktime_t expires = ktime_sub(hrtimer_get_expires(timer),
+ new_base->offset);
+#else
+ ktime_t expires = hrtimer_get_expires(timer);
+#endif
+
+ /*
+ * Get the next event on target cpu from the
+ * clock events layer.
+ * This covers the highres=off nohz=on case as well.
+ */
+ ktime_t next = clockevents_get_next_event(cpu);
+
+ ktime_t delta = ktime_sub(expires, next);
+
+ /*
+ * We do not migrate the timer when it is expiring
+ * before the next event on the target cpu because
+ * we cannot reprogram the target cpu hardware and
+ * we would cause it to fire late.
+ */
+ if (delta.tv64 < 0) {
+ cpu = smp_processor_id();
+ spin_unlock(&new_base->cpu_base->lock);
+ spin_lock(&base->cpu_base->lock);
+ timer->base = base;
+ goto again;
+ }
+ }
timer->base = new_base;
}
return new_base;
return base;
}
-# define switch_hrtimer_base(t, b) (b)
+# define switch_hrtimer_base(t, b, p) (b)
#endif /* !CONFIG_SMP */
return res;
}
+ EXPORT_SYMBOL_GPL(ktime_add_safe);
+
#ifdef CONFIG_DEBUG_OBJECTS_TIMERS
static struct debug_obj_descr hrtimer_debug_descr;
ret = remove_hrtimer(timer, base);
/* Switch the timer base, if necessary: */
- new_base = switch_hrtimer_base(timer, base);
+ new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);
- if (mode == HRTIMER_MODE_REL) {
+ if (mode & HRTIMER_MODE_REL) {
tim = ktime_add_safe(tim, new_base->get_time());
/*
* CONFIG_TIME_LOW_RES is a temporary way for architectures