/* * GPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 only, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is included * in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU General Public License * version 2 along with this program; If not, see * http://www.gnu.org/licenses/gpl-2.0.html * * GPL HEADER END */ /* * Copyright (c) 2017, Intel Corporation. */ /* * lustre/obdclass/scrub.c * * The OI scrub is used for checking and (re)building Object Index files * that are usually backend special. Here are some general scrub related * functions that can be shared by different backends for OI scrub. * * Author: Fan Yong */ #define DEBUG_SUBSYSTEM S_LFSCK #include #include #include static inline struct dt_device *scrub_obj2dev(struct dt_object *obj) { return container_of0(obj->do_lu.lo_dev, struct dt_device, dd_lu_dev); } static void scrub_file_to_cpu(struct scrub_file *des, struct scrub_file *src) { memcpy(des->sf_uuid, src->sf_uuid, 16); des->sf_flags = le64_to_cpu(src->sf_flags); des->sf_magic = le32_to_cpu(src->sf_magic); des->sf_status = le16_to_cpu(src->sf_status); des->sf_param = le16_to_cpu(src->sf_param); des->sf_time_last_complete = le64_to_cpu(src->sf_time_last_complete); des->sf_time_latest_start = le64_to_cpu(src->sf_time_latest_start); des->sf_time_last_checkpoint = le64_to_cpu(src->sf_time_last_checkpoint); des->sf_pos_latest_start = le64_to_cpu(src->sf_pos_latest_start); des->sf_pos_last_checkpoint = le64_to_cpu(src->sf_pos_last_checkpoint); des->sf_pos_first_inconsistent = le64_to_cpu(src->sf_pos_first_inconsistent); des->sf_items_checked = le64_to_cpu(src->sf_items_checked); des->sf_items_updated = le64_to_cpu(src->sf_items_updated); des->sf_items_failed = le64_to_cpu(src->sf_items_failed); des->sf_items_updated_prior = le64_to_cpu(src->sf_items_updated_prior); des->sf_run_time = le32_to_cpu(src->sf_run_time); des->sf_success_count = le32_to_cpu(src->sf_success_count); des->sf_oi_count = le16_to_cpu(src->sf_oi_count); des->sf_internal_flags = le16_to_cpu(src->sf_internal_flags); memcpy(des->sf_oi_bitmap, src->sf_oi_bitmap, SCRUB_OI_BITMAP_SIZE); } static void scrub_file_to_le(struct scrub_file *des, struct scrub_file *src) { memcpy(des->sf_uuid, src->sf_uuid, 16); des->sf_flags = cpu_to_le64(src->sf_flags); des->sf_magic = cpu_to_le32(src->sf_magic); des->sf_status = cpu_to_le16(src->sf_status); des->sf_param = cpu_to_le16(src->sf_param); des->sf_time_last_complete = cpu_to_le64(src->sf_time_last_complete); des->sf_time_latest_start = cpu_to_le64(src->sf_time_latest_start); des->sf_time_last_checkpoint = cpu_to_le64(src->sf_time_last_checkpoint); des->sf_pos_latest_start = cpu_to_le64(src->sf_pos_latest_start); des->sf_pos_last_checkpoint = cpu_to_le64(src->sf_pos_last_checkpoint); des->sf_pos_first_inconsistent = cpu_to_le64(src->sf_pos_first_inconsistent); des->sf_items_checked = cpu_to_le64(src->sf_items_checked); des->sf_items_updated = cpu_to_le64(src->sf_items_updated); des->sf_items_failed = cpu_to_le64(src->sf_items_failed); des->sf_items_updated_prior = cpu_to_le64(src->sf_items_updated_prior); des->sf_run_time = cpu_to_le32(src->sf_run_time); des->sf_success_count = cpu_to_le32(src->sf_success_count); des->sf_oi_count = cpu_to_le16(src->sf_oi_count); des->sf_internal_flags = cpu_to_le16(src->sf_internal_flags); memcpy(des->sf_oi_bitmap, src->sf_oi_bitmap, SCRUB_OI_BITMAP_SIZE); } void scrub_file_init(struct lustre_scrub *scrub, __u8 *uuid) { struct scrub_file *sf = &scrub->os_file; memset(sf, 0, sizeof(*sf)); memcpy(sf->sf_uuid, uuid, 16); sf->sf_magic = SCRUB_MAGIC_V1; sf->sf_status = SS_INIT; } EXPORT_SYMBOL(scrub_file_init); void scrub_file_reset(struct lustre_scrub *scrub, __u8 *uuid, __u64 flags) { struct scrub_file *sf = &scrub->os_file; CDEBUG(D_LFSCK, "%s: reset OI scrub file, old flags = " "%#llx, add flags = %#llx\n", scrub->os_name, sf->sf_flags, flags); memcpy(sf->sf_uuid, uuid, 16); sf->sf_status = SS_INIT; sf->sf_flags |= flags; sf->sf_flags &= ~SF_AUTO; sf->sf_run_time = 0; sf->sf_time_latest_start = 0; sf->sf_time_last_checkpoint = 0; sf->sf_pos_latest_start = 0; sf->sf_pos_last_checkpoint = 0; sf->sf_pos_first_inconsistent = 0; sf->sf_items_checked = 0; sf->sf_items_updated = 0; sf->sf_items_failed = 0; sf->sf_items_noscrub = 0; sf->sf_items_igif = 0; if (!scrub->os_in_join) sf->sf_items_updated_prior = 0; } EXPORT_SYMBOL(scrub_file_reset); int scrub_file_load(const struct lu_env *env, struct lustre_scrub *scrub) { struct scrub_file *sf = &scrub->os_file; struct lu_buf buf = { .lb_buf = &scrub->os_file_disk, .lb_len = sizeof(scrub->os_file_disk) }; loff_t pos = 0; int rc; rc = dt_read(env, scrub->os_obj, &buf, &pos); /* failure */ if (rc < 0) { CERROR("%s: fail to load scrub file: rc = %d\n", scrub->os_name, rc); return rc; } /* empty */ if (!rc) return -ENOENT; /* corrupted */ if (rc < buf.lb_len) { CDEBUG(D_LFSCK, "%s: fail to load scrub file, " "expected = %d: rc = %d\n", scrub->os_name, (int)buf.lb_len, rc); return -EFAULT; } scrub_file_to_cpu(sf, &scrub->os_file_disk); if (sf->sf_magic != SCRUB_MAGIC_V1) { CDEBUG(D_LFSCK, "%s: invalid scrub magic 0x%x != 0x%x\n", scrub->os_name, sf->sf_magic, SCRUB_MAGIC_V1); return -EFAULT; } return 0; } EXPORT_SYMBOL(scrub_file_load); int scrub_file_store(const struct lu_env *env, struct lustre_scrub *scrub) { struct scrub_file *sf = &scrub->os_file_disk; struct dt_object *obj = scrub->os_obj; struct dt_device *dev = scrub_obj2dev(obj); struct lu_buf buf = { .lb_buf = sf, .lb_len = sizeof(*sf) }; struct thandle *th; loff_t pos = 0; int rc; ENTRY; /* Skip store under rdonly mode. */ if (dev->dd_rdonly) RETURN(0); scrub_file_to_le(sf, &scrub->os_file); th = dt_trans_create(env, dev); if (IS_ERR(th)) GOTO(log, rc = PTR_ERR(th)); rc = dt_declare_record_write(env, obj, &buf, pos, th); if (rc) GOTO(stop, rc); rc = dt_trans_start_local(env, dev, th); if (rc) GOTO(stop, rc); rc = dt_record_write(env, obj, &buf, &pos, th); GOTO(stop, rc); stop: dt_trans_stop(env, dev, th); log: if (rc) CERROR("%s: store scrub file: rc = %d\n", scrub->os_name, rc); else CDEBUG(D_LFSCK, "%s: store scrub file: rc = %d\n", scrub->os_name, rc); scrub->os_time_last_checkpoint = cfs_time_current(); scrub->os_time_next_checkpoint = scrub->os_time_last_checkpoint + cfs_time_seconds(SCRUB_CHECKPOINT_INTERVAL); return rc; } EXPORT_SYMBOL(scrub_file_store); int scrub_checkpoint(const struct lu_env *env, struct lustre_scrub *scrub) { struct scrub_file *sf = &scrub->os_file; int rc; if (likely(cfs_time_before(cfs_time_current(), scrub->os_time_next_checkpoint) || scrub->os_new_checked == 0)) return 0; CDEBUG(D_LFSCK, "%s: OI scrub checkpoint at pos %llu\n", scrub->os_name, scrub->os_pos_current); down_write(&scrub->os_rwsem); sf->sf_items_checked += scrub->os_new_checked; scrub->os_new_checked = 0; sf->sf_pos_last_checkpoint = scrub->os_pos_current; sf->sf_time_last_checkpoint = cfs_time_current_sec(); sf->sf_run_time += cfs_duration_sec(cfs_time_current() + HALF_SEC - scrub->os_time_last_checkpoint); rc = scrub_file_store(env, scrub); up_write(&scrub->os_rwsem); return rc; } EXPORT_SYMBOL(scrub_checkpoint); int scrub_start(int (*threadfn)(void *data), struct lustre_scrub *scrub, void *data, __u32 flags) { struct ptlrpc_thread *thread = &scrub->os_thread; struct l_wait_info lwi = { 0 }; struct task_struct *task; int rc; ENTRY; again: /* os_lock: sync status between stop and scrub thread */ spin_lock(&scrub->os_lock); if (thread_is_running(thread)) { spin_unlock(&scrub->os_lock); RETURN(-EALREADY); } if (unlikely(thread_is_stopping(thread))) { spin_unlock(&scrub->os_lock); l_wait_event(thread->t_ctl_waitq, thread_is_stopped(thread), &lwi); goto again; } spin_unlock(&scrub->os_lock); if (scrub->os_file.sf_status == SS_COMPLETED) { if (!(flags & SS_SET_FAILOUT)) flags |= SS_CLEAR_FAILOUT; if (!(flags & SS_SET_DRYRUN)) flags |= SS_CLEAR_DRYRUN; flags |= SS_RESET; } scrub->os_start_flags = flags; thread_set_flags(thread, 0); task = kthread_run(threadfn, data, "OI_scrub"); if (IS_ERR(task)) { rc = PTR_ERR(task); CERROR("%s: cannot start iteration thread: rc = %d\n", scrub->os_name, rc); RETURN(rc); } l_wait_event(thread->t_ctl_waitq, thread_is_running(thread) || thread_is_stopped(thread), &lwi); RETURN(0); } EXPORT_SYMBOL(scrub_start); void scrub_stop(struct lustre_scrub *scrub) { struct ptlrpc_thread *thread = &scrub->os_thread; struct l_wait_info lwi = { 0 }; /* os_lock: sync status between stop and scrub thread */ spin_lock(&scrub->os_lock); if (!thread_is_init(thread) && !thread_is_stopped(thread)) { thread_set_flags(thread, SVC_STOPPING); spin_unlock(&scrub->os_lock); wake_up_all(&thread->t_ctl_waitq); l_wait_event(thread->t_ctl_waitq, thread_is_stopped(thread), &lwi); /* Do not skip the last lock/unlock, which can guarantee that * the caller cannot return until the OI scrub thread exit. */ spin_lock(&scrub->os_lock); } spin_unlock(&scrub->os_lock); } EXPORT_SYMBOL(scrub_stop); const char *scrub_status_names[] = { "init", "scanning", "completed", "failed", "stopped", "paused", "crashed", NULL }; const char *scrub_flags_names[] = { "recreated", "inconsistent", "auto", "upgrade", NULL }; const char *scrub_param_names[] = { "failout", "dryrun", NULL }; static void scrub_bits_dump(struct seq_file *m, int bits, const char *names[], const char *prefix) { int flag; int i; seq_printf(m, "%s:%c", prefix, bits != 0 ? ' ' : '\n'); for (i = 0, flag = 1; bits != 0; i++, flag = 1 << i) { if (flag & bits) { bits &= ~flag; seq_printf(m, "%s%c", names[i], bits != 0 ? ',' : '\n'); } } } static void scrub_time_dump(struct seq_file *m, __u64 time, const char *prefix) { if (time != 0) seq_printf(m, "%s: %llu seconds\n", prefix, cfs_time_current_sec() - time); else seq_printf(m, "%s: N/A\n", prefix); } static void scrub_pos_dump(struct seq_file *m, __u64 pos, const char *prefix) { if (pos != 0) seq_printf(m, "%s: %llu\n", prefix, pos); else seq_printf(m, "%s: N/A\n", prefix); } void scrub_dump(struct seq_file *m, struct lustre_scrub *scrub) { struct scrub_file *sf = &scrub->os_file; __u64 checked; __u64 speed; down_read(&scrub->os_rwsem); seq_printf(m, "name: OI_scrub\n" "magic: 0x%x\n" "oi_files: %d\n" "status: %s\n", sf->sf_magic, (int)sf->sf_oi_count, scrub_status_names[sf->sf_status]); scrub_bits_dump(m, sf->sf_flags, scrub_flags_names, "flags"); scrub_bits_dump(m, sf->sf_param, scrub_param_names, "param"); scrub_time_dump(m, sf->sf_time_last_complete, "time_since_last_completed"); scrub_time_dump(m, sf->sf_time_latest_start, "time_since_latest_start"); scrub_time_dump(m, sf->sf_time_last_checkpoint, "time_since_last_checkpoint"); scrub_pos_dump(m, sf->sf_pos_latest_start, "latest_start_position"); scrub_pos_dump(m, sf->sf_pos_last_checkpoint, "last_checkpoint_position"); scrub_pos_dump(m, sf->sf_pos_first_inconsistent, "first_failure_position"); checked = sf->sf_items_checked + scrub->os_new_checked; seq_printf(m, "checked: %llu\n" "%s: %llu\n" "failed: %llu\n" "prior_%s: %llu\n" "noscrub: %llu\n" "igif: %llu\n" "success_count: %u\n", checked, sf->sf_param & SP_DRYRUN ? "inconsistent" : "updated", sf->sf_items_updated, sf->sf_items_failed, sf->sf_param & SP_DRYRUN ? "inconsistent" : "updated", sf->sf_items_updated_prior, sf->sf_items_noscrub, sf->sf_items_igif, sf->sf_success_count); speed = checked; if (thread_is_running(&scrub->os_thread)) { cfs_duration_t duration = cfs_time_current() - scrub->os_time_last_checkpoint; __u64 new_checked = msecs_to_jiffies(scrub->os_new_checked * MSEC_PER_SEC); __u32 rtime = sf->sf_run_time + cfs_duration_sec(duration + HALF_SEC); if (duration != 0) do_div(new_checked, duration); if (rtime != 0) do_div(speed, rtime); seq_printf(m, "run_time: %u seconds\n" "average_speed: %llu objects/sec\n" "real-time_speed: %llu objects/sec\n" "current_position: %llu\n" "scrub_in_prior: %s\n" "scrub_full_speed: %s\n" "partial_scan: %s\n", rtime, speed, new_checked, scrub->os_pos_current, scrub->os_in_prior ? "yes" : "no", scrub->os_full_speed ? "yes" : "no", scrub->os_partial_scan ? "yes" : "no"); } else { if (sf->sf_run_time != 0) do_div(speed, sf->sf_run_time); seq_printf(m, "run_time: %u seconds\n" "average_speed: %llu objects/sec\n" "real-time_speed: N/A\n" "current_position: N/A\n", sf->sf_run_time, speed); } up_read(&scrub->os_rwsem); } EXPORT_SYMBOL(scrub_dump);