linux/fs/ocfs2/filecheck.c
<<
>>
Prefs
   1/* -*- mode: c; c-basic-offset: 8; -*-
   2 * vim: noexpandtab sw=8 ts=8 sts=0:
   3 *
   4 * filecheck.c
   5 *
   6 * Code which implements online file check.
   7 *
   8 * Copyright (C) 2016 SuSE.  All rights reserved.
   9 *
  10 * This program is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU General Public
  12 * License as published by the Free Software Foundation, version 2.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * General Public License for more details.
  18 */
  19
  20#include <linux/list.h>
  21#include <linux/spinlock.h>
  22#include <linux/module.h>
  23#include <linux/slab.h>
  24#include <linux/kmod.h>
  25#include <linux/fs.h>
  26#include <linux/kobject.h>
  27#include <linux/sysfs.h>
  28#include <linux/sysctl.h>
  29#include <cluster/masklog.h>
  30
  31#include "ocfs2.h"
  32#include "ocfs2_fs.h"
  33#include "stackglue.h"
  34#include "inode.h"
  35
  36#include "filecheck.h"
  37
  38
  39/* File check error strings,
  40 * must correspond with error number in header file.
  41 */
  42static const char * const ocfs2_filecheck_errs[] = {
  43        "SUCCESS",
  44        "FAILED",
  45        "INPROGRESS",
  46        "READONLY",
  47        "INJBD",
  48        "INVALIDINO",
  49        "BLOCKECC",
  50        "BLOCKNO",
  51        "VALIDFLAG",
  52        "GENERATION",
  53        "UNSUPPORTED"
  54};
  55
  56static DEFINE_SPINLOCK(ocfs2_filecheck_sysfs_lock);
  57static LIST_HEAD(ocfs2_filecheck_sysfs_list);
  58
  59struct ocfs2_filecheck {
  60        struct list_head fc_head;       /* File check entry list head */
  61        spinlock_t fc_lock;
  62        unsigned int fc_max;    /* Maximum number of entry in list */
  63        unsigned int fc_size;   /* Current entry count in list */
  64        unsigned int fc_done;   /* Finished entry count in list */
  65};
  66
  67struct ocfs2_filecheck_sysfs_entry {    /* sysfs entry per mounting */
  68        struct list_head fs_list;
  69        atomic_t fs_count;
  70        struct super_block *fs_sb;
  71        struct kset *fs_devicekset;
  72        struct kset *fs_fcheckkset;
  73        struct ocfs2_filecheck *fs_fcheck;
  74};
  75
  76#define OCFS2_FILECHECK_MAXSIZE         100
  77#define OCFS2_FILECHECK_MINSIZE         10
  78
  79/* File check operation type */
  80enum {
  81        OCFS2_FILECHECK_TYPE_CHK = 0,   /* Check a file(inode) */
  82        OCFS2_FILECHECK_TYPE_FIX,       /* Fix a file(inode) */
  83        OCFS2_FILECHECK_TYPE_SET = 100  /* Set entry list maximum size */
  84};
  85
  86struct ocfs2_filecheck_entry {
  87        struct list_head fe_list;
  88        unsigned long fe_ino;
  89        unsigned int fe_type;
  90        unsigned int fe_done:1;
  91        unsigned int fe_status:31;
  92};
  93
  94struct ocfs2_filecheck_args {
  95        unsigned int fa_type;
  96        union {
  97                unsigned long fa_ino;
  98                unsigned int fa_len;
  99        };
 100};
 101
 102static const char *
 103ocfs2_filecheck_error(int errno)
 104{
 105        if (!errno)
 106                return ocfs2_filecheck_errs[errno];
 107
 108        BUG_ON(errno < OCFS2_FILECHECK_ERR_START ||
 109               errno > OCFS2_FILECHECK_ERR_END);
 110        return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1];
 111}
 112
 113static ssize_t ocfs2_filecheck_show(struct kobject *kobj,
 114                                    struct kobj_attribute *attr,
 115                                    char *buf);
 116static ssize_t ocfs2_filecheck_store(struct kobject *kobj,
 117                                     struct kobj_attribute *attr,
 118                                     const char *buf, size_t count);
 119static struct kobj_attribute ocfs2_attr_filecheck_chk =
 120                                        __ATTR(check, S_IRUSR | S_IWUSR,
 121                                        ocfs2_filecheck_show,
 122                                        ocfs2_filecheck_store);
 123static struct kobj_attribute ocfs2_attr_filecheck_fix =
 124                                        __ATTR(fix, S_IRUSR | S_IWUSR,
 125                                        ocfs2_filecheck_show,
 126                                        ocfs2_filecheck_store);
 127static struct kobj_attribute ocfs2_attr_filecheck_set =
 128                                        __ATTR(set, S_IRUSR | S_IWUSR,
 129                                        ocfs2_filecheck_show,
 130                                        ocfs2_filecheck_store);
 131
 132static int ocfs2_filecheck_sysfs_wait(atomic_t *p)
 133{
 134        schedule();
 135        return 0;
 136}
 137
 138static void
 139ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry)
 140{
 141        struct ocfs2_filecheck_entry *p;
 142
 143        if (!atomic_dec_and_test(&entry->fs_count))
 144                wait_on_atomic_t(&entry->fs_count, ocfs2_filecheck_sysfs_wait,
 145                                 TASK_UNINTERRUPTIBLE);
 146
 147        spin_lock(&entry->fs_fcheck->fc_lock);
 148        while (!list_empty(&entry->fs_fcheck->fc_head)) {
 149                p = list_first_entry(&entry->fs_fcheck->fc_head,
 150                                     struct ocfs2_filecheck_entry, fe_list);
 151                list_del(&p->fe_list);
 152                BUG_ON(!p->fe_done); /* To free a undone file check entry */
 153                kfree(p);
 154        }
 155        spin_unlock(&entry->fs_fcheck->fc_lock);
 156
 157        kset_unregister(entry->fs_fcheckkset);
 158        kset_unregister(entry->fs_devicekset);
 159        kfree(entry->fs_fcheck);
 160        kfree(entry);
 161}
 162
 163static void
 164ocfs2_filecheck_sysfs_add(struct ocfs2_filecheck_sysfs_entry *entry)
 165{
 166        spin_lock(&ocfs2_filecheck_sysfs_lock);
 167        list_add_tail(&entry->fs_list, &ocfs2_filecheck_sysfs_list);
 168        spin_unlock(&ocfs2_filecheck_sysfs_lock);
 169}
 170
 171static int ocfs2_filecheck_sysfs_del(const char *devname)
 172{
 173        struct ocfs2_filecheck_sysfs_entry *p;
 174
 175        spin_lock(&ocfs2_filecheck_sysfs_lock);
 176        list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) {
 177                if (!strcmp(p->fs_sb->s_id, devname)) {
 178                        list_del(&p->fs_list);
 179                        spin_unlock(&ocfs2_filecheck_sysfs_lock);
 180                        ocfs2_filecheck_sysfs_free(p);
 181                        return 0;
 182                }
 183        }
 184        spin_unlock(&ocfs2_filecheck_sysfs_lock);
 185        return 1;
 186}
 187
 188static void
 189ocfs2_filecheck_sysfs_put(struct ocfs2_filecheck_sysfs_entry *entry)
 190{
 191        if (atomic_dec_and_test(&entry->fs_count))
 192                wake_up_atomic_t(&entry->fs_count);
 193}
 194
 195static struct ocfs2_filecheck_sysfs_entry *
 196ocfs2_filecheck_sysfs_get(const char *devname)
 197{
 198        struct ocfs2_filecheck_sysfs_entry *p = NULL;
 199
 200        spin_lock(&ocfs2_filecheck_sysfs_lock);
 201        list_for_each_entry(p, &ocfs2_filecheck_sysfs_list, fs_list) {
 202                if (!strcmp(p->fs_sb->s_id, devname)) {
 203                        atomic_inc(&p->fs_count);
 204                        spin_unlock(&ocfs2_filecheck_sysfs_lock);
 205                        return p;
 206                }
 207        }
 208        spin_unlock(&ocfs2_filecheck_sysfs_lock);
 209        return NULL;
 210}
 211
 212int ocfs2_filecheck_create_sysfs(struct super_block *sb)
 213{
 214        int ret = 0;
 215        struct kset *device_kset = NULL;
 216        struct kset *fcheck_kset = NULL;
 217        struct ocfs2_filecheck *fcheck = NULL;
 218        struct ocfs2_filecheck_sysfs_entry *entry = NULL;
 219        struct attribute **attrs = NULL;
 220        struct attribute_group attrgp;
 221
 222        if (!ocfs2_kset)
 223                return -ENOMEM;
 224
 225        attrs = kmalloc(sizeof(struct attribute *) * 4, GFP_NOFS);
 226        if (!attrs) {
 227                ret = -ENOMEM;
 228                goto error;
 229        } else {
 230                attrs[0] = &ocfs2_attr_filecheck_chk.attr;
 231                attrs[1] = &ocfs2_attr_filecheck_fix.attr;
 232                attrs[2] = &ocfs2_attr_filecheck_set.attr;
 233                attrs[3] = NULL;
 234                memset(&attrgp, 0, sizeof(attrgp));
 235                attrgp.attrs = attrs;
 236        }
 237
 238        fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS);
 239        if (!fcheck) {
 240                ret = -ENOMEM;
 241                goto error;
 242        } else {
 243                INIT_LIST_HEAD(&fcheck->fc_head);
 244                spin_lock_init(&fcheck->fc_lock);
 245                fcheck->fc_max = OCFS2_FILECHECK_MINSIZE;
 246                fcheck->fc_size = 0;
 247                fcheck->fc_done = 0;
 248        }
 249
 250        if (strlen(sb->s_id) <= 0) {
 251                mlog(ML_ERROR,
 252                "Cannot get device basename when create filecheck sysfs\n");
 253                ret = -ENODEV;
 254                goto error;
 255        }
 256
 257        device_kset = kset_create_and_add(sb->s_id, NULL, &ocfs2_kset->kobj);
 258        if (!device_kset) {
 259                ret = -ENOMEM;
 260                goto error;
 261        }
 262
 263        fcheck_kset = kset_create_and_add("filecheck", NULL,
 264                                          &device_kset->kobj);
 265        if (!fcheck_kset) {
 266                ret = -ENOMEM;
 267                goto error;
 268        }
 269
 270        ret = sysfs_create_group(&fcheck_kset->kobj, &attrgp);
 271        if (ret)
 272                goto error;
 273
 274        entry = kmalloc(sizeof(struct ocfs2_filecheck_sysfs_entry), GFP_NOFS);
 275        if (!entry) {
 276                ret = -ENOMEM;
 277                goto error;
 278        } else {
 279                atomic_set(&entry->fs_count, 1);
 280                entry->fs_sb = sb;
 281                entry->fs_devicekset = device_kset;
 282                entry->fs_fcheckkset = fcheck_kset;
 283                entry->fs_fcheck = fcheck;
 284                ocfs2_filecheck_sysfs_add(entry);
 285        }
 286
 287        kfree(attrs);
 288        return 0;
 289
 290error:
 291        kfree(attrs);
 292        kfree(entry);
 293        kfree(fcheck);
 294        kset_unregister(fcheck_kset);
 295        kset_unregister(device_kset);
 296        return ret;
 297}
 298
 299int ocfs2_filecheck_remove_sysfs(struct super_block *sb)
 300{
 301        return ocfs2_filecheck_sysfs_del(sb->s_id);
 302}
 303
 304static int
 305ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
 306                              unsigned int count);
 307static int
 308ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent,
 309                           unsigned int len)
 310{
 311        int ret;
 312
 313        if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE))
 314                return -EINVAL;
 315
 316        spin_lock(&ent->fs_fcheck->fc_lock);
 317        if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) {
 318                mlog(ML_ERROR,
 319                "Cannot set online file check maximum entry number "
 320                "to %u due to too many pending entries(%u)\n",
 321                len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done);
 322                ret = -EBUSY;
 323        } else {
 324                if (len < ent->fs_fcheck->fc_size)
 325                        BUG_ON(!ocfs2_filecheck_erase_entries(ent,
 326                                ent->fs_fcheck->fc_size - len));
 327
 328                ent->fs_fcheck->fc_max = len;
 329                ret = 0;
 330        }
 331        spin_unlock(&ent->fs_fcheck->fc_lock);
 332
 333        return ret;
 334}
 335
 336#define OCFS2_FILECHECK_ARGS_LEN        24
 337static int
 338ocfs2_filecheck_args_get_long(const char *buf, size_t count,
 339                              unsigned long *val)
 340{
 341        char buffer[OCFS2_FILECHECK_ARGS_LEN];
 342
 343        memcpy(buffer, buf, count);
 344        buffer[count] = '\0';
 345
 346        if (kstrtoul(buffer, 0, val))
 347                return 1;
 348
 349        return 0;
 350}
 351
 352static int
 353ocfs2_filecheck_type_parse(const char *name, unsigned int *type)
 354{
 355        if (!strncmp(name, "fix", 4))
 356                *type = OCFS2_FILECHECK_TYPE_FIX;
 357        else if (!strncmp(name, "check", 6))
 358                *type = OCFS2_FILECHECK_TYPE_CHK;
 359        else if (!strncmp(name, "set", 4))
 360                *type = OCFS2_FILECHECK_TYPE_SET;
 361        else
 362                return 1;
 363
 364        return 0;
 365}
 366
 367static int
 368ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count,
 369                           struct ocfs2_filecheck_args *args)
 370{
 371        unsigned long val = 0;
 372        unsigned int type;
 373
 374        /* too short/long args length */
 375        if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN))
 376                return 1;
 377
 378        if (ocfs2_filecheck_type_parse(name, &type))
 379                return 1;
 380        if (ocfs2_filecheck_args_get_long(buf, count, &val))
 381                return 1;
 382
 383        if (val <= 0)
 384                return 1;
 385
 386        args->fa_type = type;
 387        if (type == OCFS2_FILECHECK_TYPE_SET)
 388                args->fa_len = (unsigned int)val;
 389        else
 390                args->fa_ino = val;
 391
 392        return 0;
 393}
 394
 395static ssize_t ocfs2_filecheck_show(struct kobject *kobj,
 396                                    struct kobj_attribute *attr,
 397                                    char *buf)
 398{
 399
 400        ssize_t ret = 0, total = 0, remain = PAGE_SIZE;
 401        unsigned int type;
 402        struct ocfs2_filecheck_entry *p;
 403        struct ocfs2_filecheck_sysfs_entry *ent;
 404
 405        if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
 406                return -EINVAL;
 407
 408        ent = ocfs2_filecheck_sysfs_get(kobj->parent->name);
 409        if (!ent) {
 410                mlog(ML_ERROR,
 411                "Cannot get the corresponding entry via device basename %s\n",
 412                kobj->name);
 413                return -ENODEV;
 414        }
 415
 416        if (type == OCFS2_FILECHECK_TYPE_SET) {
 417                spin_lock(&ent->fs_fcheck->fc_lock);
 418                total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max);
 419                spin_unlock(&ent->fs_fcheck->fc_lock);
 420                goto exit;
 421        }
 422
 423        ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n");
 424        total += ret;
 425        remain -= ret;
 426        spin_lock(&ent->fs_fcheck->fc_lock);
 427        list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
 428                if (p->fe_type != type)
 429                        continue;
 430
 431                ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n",
 432                               p->fe_ino, p->fe_done,
 433                               ocfs2_filecheck_error(p->fe_status));
 434                if (ret < 0) {
 435                        total = ret;
 436                        break;
 437                }
 438                if (ret == remain) {
 439                        /* snprintf() didn't fit */
 440                        total = -E2BIG;
 441                        break;
 442                }
 443                total += ret;
 444                remain -= ret;
 445        }
 446        spin_unlock(&ent->fs_fcheck->fc_lock);
 447
 448exit:
 449        ocfs2_filecheck_sysfs_put(ent);
 450        return total;
 451}
 452
 453static int
 454ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent)
 455{
 456        struct ocfs2_filecheck_entry *p;
 457
 458        list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
 459                if (p->fe_done) {
 460                        list_del(&p->fe_list);
 461                        kfree(p);
 462                        ent->fs_fcheck->fc_size--;
 463                        ent->fs_fcheck->fc_done--;
 464                        return 1;
 465                }
 466        }
 467
 468        return 0;
 469}
 470
 471static int
 472ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
 473                              unsigned int count)
 474{
 475        unsigned int i = 0;
 476        unsigned int ret = 0;
 477
 478        while (i++ < count) {
 479                if (ocfs2_filecheck_erase_entry(ent))
 480                        ret++;
 481                else
 482                        break;
 483        }
 484
 485        return (ret == count ? 1 : 0);
 486}
 487
 488static void
 489ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
 490                           struct ocfs2_filecheck_entry *entry)
 491{
 492        entry->fe_done = 1;
 493        spin_lock(&ent->fs_fcheck->fc_lock);
 494        ent->fs_fcheck->fc_done++;
 495        spin_unlock(&ent->fs_fcheck->fc_lock);
 496}
 497
 498static unsigned int
 499ocfs2_filecheck_handle(struct super_block *sb,
 500                       unsigned long ino, unsigned int flags)
 501{
 502        unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS;
 503        struct inode *inode = NULL;
 504        int rc;
 505
 506        inode = ocfs2_iget(OCFS2_SB(sb), ino, flags, 0);
 507        if (IS_ERR(inode)) {
 508                rc = (int)(-(long)inode);
 509                if (rc >= OCFS2_FILECHECK_ERR_START &&
 510                    rc < OCFS2_FILECHECK_ERR_END)
 511                        ret = rc;
 512                else
 513                        ret = OCFS2_FILECHECK_ERR_FAILED;
 514        } else
 515                iput(inode);
 516
 517        return ret;
 518}
 519
 520static void
 521ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
 522                             struct ocfs2_filecheck_entry *entry)
 523{
 524        if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
 525                entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb,
 526                                entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
 527        else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
 528                entry->fe_status = ocfs2_filecheck_handle(ent->fs_sb,
 529                                entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
 530        else
 531                entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
 532
 533        ocfs2_filecheck_done_entry(ent, entry);
 534}
 535
 536static ssize_t ocfs2_filecheck_store(struct kobject *kobj,
 537                                     struct kobj_attribute *attr,
 538                                     const char *buf, size_t count)
 539{
 540        struct ocfs2_filecheck_args args;
 541        struct ocfs2_filecheck_entry *entry;
 542        struct ocfs2_filecheck_sysfs_entry *ent;
 543        ssize_t ret = 0;
 544
 545        if (count == 0)
 546                return count;
 547
 548        if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args)) {
 549                mlog(ML_ERROR, "Invalid arguments for online file check\n");
 550                return -EINVAL;
 551        }
 552
 553        ent = ocfs2_filecheck_sysfs_get(kobj->parent->name);
 554        if (!ent) {
 555                mlog(ML_ERROR,
 556                "Cannot get the corresponding entry via device basename %s\n",
 557                kobj->parent->name);
 558                return -ENODEV;
 559        }
 560
 561        if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) {
 562                ret = ocfs2_filecheck_adjust_max(ent, args.fa_len);
 563                goto exit;
 564        }
 565
 566        entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS);
 567        if (!entry) {
 568                ret = -ENOMEM;
 569                goto exit;
 570        }
 571
 572        spin_lock(&ent->fs_fcheck->fc_lock);
 573        if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
 574            (ent->fs_fcheck->fc_done == 0)) {
 575                mlog(ML_ERROR,
 576                "Cannot do more file check "
 577                "since file check queue(%u) is full now\n",
 578                ent->fs_fcheck->fc_max);
 579                ret = -EBUSY;
 580                kfree(entry);
 581        } else {
 582                if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
 583                    (ent->fs_fcheck->fc_done > 0)) {
 584                        /* Delete the oldest entry which was done,
 585                         * make sure the entry size in list does
 586                         * not exceed maximum value
 587                         */
 588                        BUG_ON(!ocfs2_filecheck_erase_entry(ent));
 589                }
 590
 591                entry->fe_ino = args.fa_ino;
 592                entry->fe_type = args.fa_type;
 593                entry->fe_done = 0;
 594                entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS;
 595                list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head);
 596                ent->fs_fcheck->fc_size++;
 597        }
 598        spin_unlock(&ent->fs_fcheck->fc_lock);
 599
 600        if (!ret)
 601                ocfs2_filecheck_handle_entry(ent, entry);
 602
 603exit:
 604        ocfs2_filecheck_sysfs_put(ent);
 605        return (!ret ? count : ret);
 606}
 607