nfsd: Use write gathering only with NFSv2
[safe/jmp/linux-2.6] / fs / ocfs2 / super.c
index 41bb019..79ff8d9 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/debugfs.h>
 #include <linux/mount.h>
 #include <linux/seq_file.h>
+#include <linux/quotaops.h>
 
 #define MLOG_MASK_PREFIX ML_SUPER
 #include <cluster/masklog.h>
@@ -51,6 +52,7 @@
 #include "ocfs1_fs_compat.h"
 
 #include "alloc.h"
+#include "blockcheck.h"
 #include "dlmglue.h"
 #include "export.h"
 #include "extent_map.h"
 #include "uptodate.h"
 #include "ver.h"
 #include "xattr.h"
+#include "quota.h"
 
 #include "buffer_head_io.h"
 
 static struct kmem_cache *ocfs2_inode_cachep = NULL;
+struct kmem_cache *ocfs2_dquot_cachep;
+struct kmem_cache *ocfs2_qf_chunk_cachep;
 
 /* OCFS2 needs to schedule several differnt types of work which
  * require cluster locking, disk I/O, recovery waits, etc. Since these
@@ -124,6 +129,9 @@ static int ocfs2_get_sector(struct super_block *sb,
 static void ocfs2_write_super(struct super_block *sb);
 static struct inode *ocfs2_alloc_inode(struct super_block *sb);
 static void ocfs2_destroy_inode(struct inode *inode);
+static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend);
+static int ocfs2_enable_quotas(struct ocfs2_super *osb);
+static void ocfs2_disable_quotas(struct ocfs2_super *osb);
 
 static const struct super_operations ocfs2_sops = {
        .statfs         = ocfs2_statfs,
@@ -137,6 +145,8 @@ static const struct super_operations ocfs2_sops = {
        .put_super      = ocfs2_put_super,
        .remount_fs     = ocfs2_remount,
        .show_options   = ocfs2_show_options,
+       .quota_read     = ocfs2_quota_read,
+       .quota_write    = ocfs2_quota_write,
 };
 
 enum {
@@ -160,6 +170,8 @@ enum {
        Opt_inode64,
        Opt_acl,
        Opt_noacl,
+       Opt_usrquota,
+       Opt_grpquota,
        Opt_err,
 };
 
@@ -184,9 +196,175 @@ static const match_table_t tokens = {
        {Opt_inode64, "inode64"},
        {Opt_acl, "acl"},
        {Opt_noacl, "noacl"},
+       {Opt_usrquota, "usrquota"},
+       {Opt_grpquota, "grpquota"},
        {Opt_err, NULL}
 };
 
+#ifdef CONFIG_DEBUG_FS
+static int ocfs2_osb_dump(struct ocfs2_super *osb, char *buf, int len)
+{
+       int out = 0;
+       int i;
+       struct ocfs2_cluster_connection *cconn = osb->cconn;
+       struct ocfs2_recovery_map *rm = osb->recovery_map;
+
+       out += snprintf(buf + out, len - out,
+                       "%10s => Id: %-s  Uuid: %-s  Gen: 0x%X  Label: %-s\n",
+                       "Device", osb->dev_str, osb->uuid_str,
+                       osb->fs_generation, osb->vol_label);
+
+       out += snprintf(buf + out, len - out,
+                       "%10s => State: %d  Flags: 0x%lX\n", "Volume",
+                       atomic_read(&osb->vol_state), osb->osb_flags);
+
+       out += snprintf(buf + out, len - out,
+                       "%10s => Block: %lu  Cluster: %d\n", "Sizes",
+                       osb->sb->s_blocksize, osb->s_clustersize);
+
+       out += snprintf(buf + out, len - out,
+                       "%10s => Compat: 0x%X  Incompat: 0x%X  "
+                       "ROcompat: 0x%X\n",
+                       "Features", osb->s_feature_compat,
+                       osb->s_feature_incompat, osb->s_feature_ro_compat);
+
+       out += snprintf(buf + out, len - out,
+                       "%10s => Opts: 0x%lX  AtimeQuanta: %u\n", "Mount",
+                       osb->s_mount_opt, osb->s_atime_quantum);
+
+       out += snprintf(buf + out, len - out,
+                       "%10s => Stack: %s  Name: %*s  Version: %d.%d\n",
+                       "Cluster",
+                       (*osb->osb_cluster_stack == '\0' ?
+                        "o2cb" : osb->osb_cluster_stack),
+                       cconn->cc_namelen, cconn->cc_name,
+                       cconn->cc_version.pv_major, cconn->cc_version.pv_minor);
+
+       spin_lock(&osb->dc_task_lock);
+       out += snprintf(buf + out, len - out,
+                       "%10s => Pid: %d  Count: %lu  WakeSeq: %lu  "
+                       "WorkSeq: %lu\n", "DownCnvt",
+                       task_pid_nr(osb->dc_task), osb->blocked_lock_count,
+                       osb->dc_wake_sequence, osb->dc_work_sequence);
+       spin_unlock(&osb->dc_task_lock);
+
+       spin_lock(&osb->osb_lock);
+       out += snprintf(buf + out, len - out, "%10s => Pid: %d  Nodes:",
+                       "Recovery",
+                       (osb->recovery_thread_task ?
+                        task_pid_nr(osb->recovery_thread_task) : -1));
+       if (rm->rm_used == 0)
+               out += snprintf(buf + out, len - out, " None\n");
+       else {
+               for (i = 0; i < rm->rm_used; i++)
+                       out += snprintf(buf + out, len - out, " %d",
+                                       rm->rm_entries[i]);
+               out += snprintf(buf + out, len - out, "\n");
+       }
+       spin_unlock(&osb->osb_lock);
+
+       out += snprintf(buf + out, len - out,
+                       "%10s => Pid: %d  Interval: %lu  Needs: %d\n", "Commit",
+                       task_pid_nr(osb->commit_task), osb->osb_commit_interval,
+                       atomic_read(&osb->needs_checkpoint));
+
+       out += snprintf(buf + out, len - out,
+                       "%10s => State: %d  NumTxns: %d  TxnId: %lu\n",
+                       "Journal", osb->journal->j_state,
+                       atomic_read(&osb->journal->j_num_trans),
+                       osb->journal->j_trans_id);
+
+       out += snprintf(buf + out, len - out,
+                       "%10s => GlobalAllocs: %d  LocalAllocs: %d  "
+                       "SubAllocs: %d  LAWinMoves: %d  SAExtends: %d\n",
+                       "Stats",
+                       atomic_read(&osb->alloc_stats.bitmap_data),
+                       atomic_read(&osb->alloc_stats.local_data),
+                       atomic_read(&osb->alloc_stats.bg_allocs),
+                       atomic_read(&osb->alloc_stats.moves),
+                       atomic_read(&osb->alloc_stats.bg_extends));
+
+       out += snprintf(buf + out, len - out,
+                       "%10s => State: %u  Descriptor: %llu  Size: %u bits  "
+                       "Default: %u bits\n",
+                       "LocalAlloc", osb->local_alloc_state,
+                       (unsigned long long)osb->la_last_gd,
+                       osb->local_alloc_bits, osb->local_alloc_default_bits);
+
+       spin_lock(&osb->osb_lock);
+       out += snprintf(buf + out, len - out,
+                       "%10s => Slot: %d  NumStolen: %d\n", "Steal",
+                       osb->s_inode_steal_slot,
+                       atomic_read(&osb->s_num_inodes_stolen));
+       spin_unlock(&osb->osb_lock);
+
+       out += snprintf(buf + out, len - out, "%10s => %3s  %10s\n",
+                       "Slots", "Num", "RecoGen");
+
+       for (i = 0; i < osb->max_slots; ++i) {
+               out += snprintf(buf + out, len - out,
+                               "%10s  %c %3d  %10d\n",
+                               " ",
+                               (i == osb->slot_num ? '*' : ' '),
+                               i, osb->slot_recovery_generations[i]);
+       }
+
+       return out;
+}
+
+static int ocfs2_osb_debug_open(struct inode *inode, struct file *file)
+{
+       struct ocfs2_super *osb = inode->i_private;
+       char *buf = NULL;
+
+       buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!buf)
+               goto bail;
+
+       i_size_write(inode, ocfs2_osb_dump(osb, buf, PAGE_SIZE));
+
+       file->private_data = buf;
+
+       return 0;
+bail:
+       return -ENOMEM;
+}
+
+static int ocfs2_debug_release(struct inode *inode, struct file *file)
+{
+       kfree(file->private_data);
+       return 0;
+}
+
+static ssize_t ocfs2_debug_read(struct file *file, char __user *buf,
+                               size_t nbytes, loff_t *ppos)
+{
+       return simple_read_from_buffer(buf, nbytes, ppos, file->private_data,
+                                      i_size_read(file->f_mapping->host));
+}
+#else
+static int ocfs2_osb_debug_open(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+static int ocfs2_debug_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+static ssize_t ocfs2_debug_read(struct file *file, char __user *buf,
+                               size_t nbytes, loff_t *ppos)
+{
+       return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static struct file_operations ocfs2_osb_debug_fops = {
+       .open =         ocfs2_osb_debug_open,
+       .release =      ocfs2_debug_release,
+       .read =         ocfs2_debug_read,
+       .llseek =       generic_file_llseek,
+};
+
 /*
  * write_super and sync_fs ripped right out of ext3.
  */
@@ -447,6 +625,12 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
 
        /* We're going to/from readonly mode. */
        if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
+               /* Disable quota accounting before remounting RO */
+               if (*flags & MS_RDONLY) {
+                       ret = ocfs2_susp_quotas(osb, 0);
+                       if (ret < 0)
+                               goto out;
+               }
                /* Lock here so the check of HARD_RO and the potential
                 * setting of SOFT_RO is atomic. */
                spin_lock(&osb->osb_lock);
@@ -482,6 +666,21 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
                }
 unlock_osb:
                spin_unlock(&osb->osb_lock);
+               /* Enable quota accounting after remounting RW */
+               if (!ret && !(*flags & MS_RDONLY)) {
+                       if (sb_any_quota_suspended(sb))
+                               ret = ocfs2_susp_quotas(osb, 1);
+                       else
+                               ret = ocfs2_enable_quotas(osb);
+                       if (ret < 0) {
+                               /* Return back changes... */
+                               spin_lock(&osb->osb_lock);
+                               sb->s_flags |= MS_RDONLY;
+                               osb->osb_flags |= OCFS2_OSB_SOFT_RO;
+                               spin_unlock(&osb->osb_lock);
+                               goto out;
+                       }
+               }
        }
 
        if (!ret) {
@@ -642,6 +841,131 @@ static int ocfs2_verify_userspace_stack(struct ocfs2_super *osb,
        return 0;
 }
 
+static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend)
+{
+       int type;
+       struct super_block *sb = osb->sb;
+       unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+                                            OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+       int status = 0;
+
+       for (type = 0; type < MAXQUOTAS; type++) {
+               if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
+                       continue;
+               if (unsuspend)
+                       status = vfs_quota_enable(
+                                       sb_dqopt(sb)->files[type],
+                                       type, QFMT_OCFS2,
+                                       DQUOT_SUSPENDED);
+               else
+                       status = vfs_quota_disable(sb, type,
+                                                  DQUOT_SUSPENDED);
+               if (status < 0)
+                       break;
+       }
+       if (status < 0)
+               mlog(ML_ERROR, "Failed to suspend/unsuspend quotas on "
+                    "remount (error = %d).\n", status);
+       return status;
+}
+
+static int ocfs2_enable_quotas(struct ocfs2_super *osb)
+{
+       struct inode *inode[MAXQUOTAS] = { NULL, NULL };
+       struct super_block *sb = osb->sb;
+       unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+                                            OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+       unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
+                                       LOCAL_GROUP_QUOTA_SYSTEM_INODE };
+       int status;
+       int type;
+
+       sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NEGATIVE_USAGE;
+       for (type = 0; type < MAXQUOTAS; type++) {
+               if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
+                       continue;
+               inode[type] = ocfs2_get_system_file_inode(osb, ino[type],
+                                                       osb->slot_num);
+               if (!inode[type]) {
+                       status = -ENOENT;
+                       goto out_quota_off;
+               }
+               status = vfs_quota_enable(inode[type], type, QFMT_OCFS2,
+                                               DQUOT_USAGE_ENABLED);
+               if (status < 0)
+                       goto out_quota_off;
+       }
+
+       for (type = 0; type < MAXQUOTAS; type++)
+               iput(inode[type]);
+       return 0;
+out_quota_off:
+       ocfs2_disable_quotas(osb);
+       for (type = 0; type < MAXQUOTAS; type++)
+               iput(inode[type]);
+       mlog_errno(status);
+       return status;
+}
+
+static void ocfs2_disable_quotas(struct ocfs2_super *osb)
+{
+       int type;
+       struct inode *inode;
+       struct super_block *sb = osb->sb;
+
+       /* We mostly ignore errors in this function because there's not much
+        * we can do when we see them */
+       for (type = 0; type < MAXQUOTAS; type++) {
+               if (!sb_has_quota_loaded(sb, type))
+                       continue;
+               inode = igrab(sb->s_dquot.files[type]);
+               /* Turn off quotas. This will remove all dquot structures from
+                * memory and so they will be automatically synced to global
+                * quota files */
+               vfs_quota_disable(sb, type, DQUOT_USAGE_ENABLED |
+                                           DQUOT_LIMITS_ENABLED);
+               if (!inode)
+                       continue;
+               iput(inode);
+       }
+}
+
+/* Handle quota on quotactl */
+static int ocfs2_quota_on(struct super_block *sb, int type, int format_id,
+                         char *path, int remount)
+{
+       unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
+                                            OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
+
+       if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
+               return -EINVAL;
+
+       if (remount)
+               return 0;       /* Just ignore it has been handled in
+                                * ocfs2_remount() */
+       return vfs_quota_enable(sb_dqopt(sb)->files[type], type,
+                                   format_id, DQUOT_LIMITS_ENABLED);
+}
+
+/* Handle quota off quotactl */
+static int ocfs2_quota_off(struct super_block *sb, int type, int remount)
+{
+       if (remount)
+               return 0;       /* Ignore now and handle later in
+                                * ocfs2_remount() */
+       return vfs_quota_disable(sb, type, DQUOT_LIMITS_ENABLED);
+}
+
+static struct quotactl_ops ocfs2_quotactl_ops = {
+       .quota_on       = ocfs2_quota_on,
+       .quota_off      = ocfs2_quota_off,
+       .quota_sync     = vfs_quota_sync,
+       .get_info       = vfs_get_dqinfo,
+       .set_info       = vfs_set_dqinfo,
+       .get_dqblk      = vfs_get_dqblk,
+       .set_dqblk      = vfs_set_dqblk,
+};
+
 static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct dentry *root;
@@ -684,6 +1008,22 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
        osb->osb_commit_interval = parsed_options.commit_interval;
        osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt);
        osb->local_alloc_bits = osb->local_alloc_default_bits;
+       if (osb->s_mount_opt & OCFS2_MOUNT_USRQUOTA &&
+           !OCFS2_HAS_RO_COMPAT_FEATURE(sb,
+                                        OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
+               status = -EINVAL;
+               mlog(ML_ERROR, "User quotas were requested, but this "
+                    "filesystem does not have the feature enabled.\n");
+               goto read_super_error;
+       }
+       if (osb->s_mount_opt & OCFS2_MOUNT_GRPQUOTA &&
+           !OCFS2_HAS_RO_COMPAT_FEATURE(sb,
+                                        OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
+               status = -EINVAL;
+               mlog(ML_ERROR, "Group quotas were requested, but this "
+                    "filesystem does not have the feature enabled.\n");
+               goto read_super_error;
+       }
 
        status = ocfs2_verify_userspace_stack(osb, &parsed_options);
        if (status)
@@ -750,6 +1090,16 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
                goto read_super_error;
        }
 
+       osb->osb_ctxt = debugfs_create_file("fs_state", S_IFREG|S_IRUSR,
+                                           osb->osb_debug_root,
+                                           osb,
+                                           &ocfs2_osb_debug_fops);
+       if (!osb->osb_ctxt) {
+               status = -EINVAL;
+               mlog_errno(status);
+               goto read_super_error;
+       }
+
        status = ocfs2_mount_volume(sb);
        if (osb->root_inode)
                inode = igrab(osb->root_inode);
@@ -788,6 +1138,28 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
        atomic_set(&osb->vol_state, VOLUME_MOUNTED);
        wake_up(&osb->osb_mount_event);
 
+       /* Now we can initialize quotas because we can afford to wait
+        * for cluster locks recovery now. That also means that truncation
+        * log recovery can happen but that waits for proper quota setup */
+       if (!(sb->s_flags & MS_RDONLY)) {
+               status = ocfs2_enable_quotas(osb);
+               if (status < 0) {
+                       /* We have to err-out specially here because
+                        * s_root is already set */
+                       mlog_errno(status);
+                       atomic_set(&osb->vol_state, VOLUME_DISABLED);
+                       wake_up(&osb->osb_mount_event);
+                       mlog_exit(status);
+                       return status;
+               }
+       }
+
+       ocfs2_complete_quota_recovery(osb);
+
+       /* Now we wake up again for processes waiting for quotas */
+       atomic_set(&osb->vol_state, VOLUME_MOUNTED_QUOTAS);
+       wake_up(&osb->osb_mount_event);
+
        mlog_exit(status);
        return status;
 
@@ -975,6 +1347,28 @@ static int ocfs2_parse_options(struct super_block *sb,
                case Opt_inode64:
                        mopt->mount_opt |= OCFS2_MOUNT_INODE64;
                        break;
+               case Opt_usrquota:
+                       /* We check only on remount, otherwise features
+                        * aren't yet initialized. */
+                       if (is_remount && !OCFS2_HAS_RO_COMPAT_FEATURE(sb,
+                           OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
+                               mlog(ML_ERROR, "User quota requested but "
+                                    "filesystem feature is not set\n");
+                               status = 0;
+                               goto bail;
+                       }
+                       mopt->mount_opt |= OCFS2_MOUNT_USRQUOTA;
+                       break;
+               case Opt_grpquota:
+                       if (is_remount && !OCFS2_HAS_RO_COMPAT_FEATURE(sb,
+                           OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
+                               mlog(ML_ERROR, "Group quota requested but "
+                                    "filesystem feature is not set\n");
+                               status = 0;
+                               goto bail;
+                       }
+                       mopt->mount_opt |= OCFS2_MOUNT_GRPQUOTA;
+                       break;
 #ifdef CONFIG_OCFS2_FS_POSIX_ACL
                case Opt_acl:
                        mopt->mount_opt |= OCFS2_MOUNT_POSIX_ACL;
@@ -1051,6 +1445,10 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
        if (osb->osb_cluster_stack[0])
                seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN,
                           osb->osb_cluster_stack);
+       if (opts & OCFS2_MOUNT_USRQUOTA)
+               seq_printf(s, ",usrquota");
+       if (opts & OCFS2_MOUNT_GRPQUOTA)
+               seq_printf(s, ",grpquota");
 
        if (opts & OCFS2_MOUNT_NOUSERXATTR)
                seq_printf(s, ",nouser_xattr");
@@ -1102,10 +1500,16 @@ static int __init ocfs2_init(void)
                mlog(ML_ERROR, "Unable to create ocfs2 debugfs root.\n");
        }
 
+       status = ocfs2_quota_setup();
+       if (status)
+               goto leave;
+
        ocfs2_set_locking_protocol();
 
+       status = register_quota_format(&ocfs2_quota_format);
 leave:
        if (status < 0) {
+               ocfs2_quota_shutdown();
                ocfs2_free_mem_caches();
                exit_ocfs2_uptodate_cache();
        }
@@ -1122,11 +1526,15 @@ static void __exit ocfs2_exit(void)
 {
        mlog_entry_void();
 
+       ocfs2_quota_shutdown();
+
        if (ocfs2_wq) {
                flush_workqueue(ocfs2_wq);
                destroy_workqueue(ocfs2_wq);
        }
 
+       unregister_quota_format(&ocfs2_quota_format);
+
        debugfs_remove(ocfs2_debugfs_root);
 
        ocfs2_free_mem_caches();
@@ -1242,8 +1650,27 @@ static int ocfs2_initialize_mem_caches(void)
                                       (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
                                                SLAB_MEM_SPREAD),
                                       ocfs2_inode_init_once);
-       if (!ocfs2_inode_cachep)
+       ocfs2_dquot_cachep = kmem_cache_create("ocfs2_dquot_cache",
+                                       sizeof(struct ocfs2_dquot),
+                                       0,
+                                       (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT|
+                                               SLAB_MEM_SPREAD),
+                                       NULL);
+       ocfs2_qf_chunk_cachep = kmem_cache_create("ocfs2_qf_chunk_cache",
+                                       sizeof(struct ocfs2_quota_chunk),
+                                       0,
+                                       (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD),
+                                       NULL);
+       if (!ocfs2_inode_cachep || !ocfs2_dquot_cachep ||
+           !ocfs2_qf_chunk_cachep) {
+               if (ocfs2_inode_cachep)
+                       kmem_cache_destroy(ocfs2_inode_cachep);
+               if (ocfs2_dquot_cachep)
+                       kmem_cache_destroy(ocfs2_dquot_cachep);
+               if (ocfs2_qf_chunk_cachep)
+                       kmem_cache_destroy(ocfs2_qf_chunk_cachep);
                return -ENOMEM;
+       }
 
        return 0;
 }
@@ -1252,8 +1679,15 @@ static void ocfs2_free_mem_caches(void)
 {
        if (ocfs2_inode_cachep)
                kmem_cache_destroy(ocfs2_inode_cachep);
-
        ocfs2_inode_cachep = NULL;
+
+       if (ocfs2_dquot_cachep)
+               kmem_cache_destroy(ocfs2_dquot_cachep);
+       ocfs2_dquot_cachep = NULL;
+
+       if (ocfs2_qf_chunk_cachep)
+               kmem_cache_destroy(ocfs2_qf_chunk_cachep);
+       ocfs2_qf_chunk_cachep = NULL;
 }
 
 static int ocfs2_get_sector(struct super_block *sb,
@@ -1277,6 +1711,13 @@ static int ocfs2_get_sector(struct super_block *sb,
        unlock_buffer(*bh);
        ll_rw_block(READ, 1, bh);
        wait_on_buffer(*bh);
+       if (!buffer_uptodate(*bh)) {
+               mlog_errno(-EIO);
+               brelse(*bh);
+               *bh = NULL;
+               return -EIO;
+       }
+
        return 0;
 }
 
@@ -1353,6 +1794,10 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err)
        osb = OCFS2_SB(sb);
        BUG_ON(!osb);
 
+       debugfs_remove(osb->osb_ctxt);
+
+       ocfs2_disable_quotas(osb);
+
        ocfs2_shutdown_local_alloc(osb);
 
        ocfs2_truncate_log_shutdown(osb);
@@ -1463,6 +1908,8 @@ static int ocfs2_initialize_super(struct super_block *sb,
        sb->s_fs_info = osb;
        sb->s_op = &ocfs2_sops;
        sb->s_export_op = &ocfs2_export_ops;
+       sb->s_qcop = &ocfs2_quotactl_ops;
+       sb->dq_op = &ocfs2_quota_operations;
        sb->s_xattr = ocfs2_xattr_handlers;
        sb->s_time_gran = 1;
        sb->s_flags |= MS_NOATIME;
@@ -1471,6 +1918,12 @@ static int ocfs2_initialize_super(struct super_block *sb,
        bbits = le32_to_cpu(di->id2.i_super.s_blocksize_bits);
        sb->s_maxbytes = ocfs2_max_file_offset(bbits, cbits);
 
+       osb->osb_dx_mask = (1 << (cbits - bbits)) - 1;
+
+       for (i = 0; i < 3; i++)
+               osb->osb_dx_seed[i] = le32_to_cpu(di->id2.i_super.s_dx_seed[i]);
+       osb->osb_dx_seed[3] = le32_to_cpu(di->id2.i_super.s_uuid_hash);
+
        osb->sb = sb;
        /* Save off for ocfs2_rw_direct */
        osb->s_sectsize_bits = blksize_bits(sector_size);
@@ -1483,6 +1936,7 @@ static int ocfs2_initialize_super(struct super_block *sb,
        INIT_LIST_HEAD(&osb->blocked_lock_list);
        osb->blocked_lock_count = 0;
        spin_lock_init(&osb->osb_lock);
+       spin_lock_init(&osb->osb_xattr_lock);
        ocfs2_init_inode_steal_slot(osb);
 
        atomic_set(&osb->alloc_stats.moves, 0);
@@ -1623,6 +2077,9 @@ static int ocfs2_initialize_super(struct super_block *sb,
        INIT_WORK(&journal->j_recovery_work, ocfs2_complete_recovery);
        journal->j_state = OCFS2_JOURNAL_FREE;
 
+       INIT_WORK(&osb->dentry_lock_work, ocfs2_drop_dl_inodes);
+       osb->dentry_lock_list = NULL;
+
        /* get some pseudo constants for clustersize bits */
        osb->s_clustersize_bits =
                le32_to_cpu(di->id2.i_super.s_clustersize_bits);
@@ -1726,6 +2183,15 @@ static int ocfs2_verify_volume(struct ocfs2_dinode *di,
 
        if (memcmp(di->i_signature, OCFS2_SUPER_BLOCK_SIGNATURE,
                   strlen(OCFS2_SUPER_BLOCK_SIGNATURE)) == 0) {
+               /* We have to do a raw check of the feature here */
+               if (le32_to_cpu(di->id2.i_super.s_feature_incompat) &
+                   OCFS2_FEATURE_INCOMPAT_META_ECC) {
+                       status = ocfs2_block_check_validate(bh->b_data,
+                                                           bh->b_size,
+                                                           &di->i_check);
+                       if (status)
+                               goto out;
+               }
                status = -EINVAL;
                if ((1 << le32_to_cpu(di->id2.i_super.s_blocksize_bits)) != blksz) {
                        mlog(ML_ERROR, "found superblock with incorrect block "
@@ -1767,6 +2233,7 @@ static int ocfs2_verify_volume(struct ocfs2_dinode *di,
                }
        }
 
+out:
        mlog_exit(status);
        return status;
 }
@@ -1845,6 +2312,12 @@ static int ocfs2_check_volume(struct ocfs2_super *osb)
         * lock, and it's marked as dirty, set the bit in the recover
         * map and launch a recovery thread for it. */
        status = ocfs2_mark_dead_nodes(osb);
+       if (status < 0) {
+               mlog_errno(status);
+               goto finally;
+       }
+
+       status = ocfs2_compute_replay_slots(osb);
        if (status < 0)
                mlog_errno(status);