| /* |
| * Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved. |
| * |
| * This file is part of LVM2. |
| * |
| * This copyrighted material is made available to anyone wishing to use, |
| * modify, copy, or redistribute it subject to the terms and conditions |
| * of the GNU Lesser General Public License v.2.1. |
| * |
| * You should have received a copy of the GNU Lesser General Public License |
| * along with this program; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "lib.h" |
| #include "libdevmapper-event.h" |
| #include "dmeventd_lvm.h" |
| #include "activate.h" /* For TARGET_NAME* */ |
| |
| /* FIXME Reformat to 80 char lines. */ |
| |
| #define ME_IGNORE 0 |
| #define ME_INSYNC 1 |
| #define ME_FAILURE 2 |
| |
| struct dso_state { |
| struct dm_pool *mem; |
| char cmd_lvscan[512]; |
| char cmd_lvconvert[512]; |
| }; |
| |
| DM_EVENT_LOG_FN("mirr") |
| |
| static void _process_status_code(dm_status_mirror_health_t health, |
| uint32_t major, uint32_t minor, |
| const char *dev_type, int *r) |
| { |
| /* |
| * A => Alive - No failures |
| * D => Dead - A write failure occurred leaving mirror out-of-sync |
| * F => Flush failed. |
| * S => Sync - A sychronization failure occurred, mirror out-of-sync |
| * R => Read - A read failure occurred, mirror data unaffected |
| * U => Unclassified failure (bug) |
| */ |
| switch (health) { |
| case DM_STATUS_MIRROR_ALIVE: |
| return; |
| case DM_STATUS_MIRROR_FLUSH_FAILED: |
| log_error("%s device %u:%u flush failed.", |
| dev_type, major, minor); |
| *r = ME_FAILURE; |
| break; |
| case DM_STATUS_MIRROR_SYNC_FAILED: |
| log_error("%s device %u:%u sync failed.", |
| dev_type, major, minor); |
| break; |
| case DM_STATUS_MIRROR_READ_FAILED: |
| log_error("%s device %u:%u read failed.", |
| dev_type, major, minor); |
| break; |
| default: |
| log_error("%s device %u:%u has failed (%c).", |
| dev_type, major, minor, (char)health); |
| *r = ME_FAILURE; |
| break; |
| } |
| } |
| |
| static int _get_mirror_event(struct dso_state *state, char *params) |
| { |
| int r = ME_INSYNC; |
| unsigned i; |
| struct dm_status_mirror *ms; |
| |
| if (!dm_get_status_mirror(state->mem, params, &ms)) |
| goto_out; |
| |
| /* Check for bad mirror devices */ |
| for (i = 0; i < ms->dev_count; ++i) |
| _process_status_code(ms->devs[i].health, |
| ms->devs[i].major, ms->devs[i].minor, |
| i ? "Secondary mirror" : "Primary mirror", &r); |
| |
| /* Check for bad disk log device */ |
| for (i = 0; i < ms->log_count; ++i) |
| _process_status_code(ms->logs[i].health, |
| ms->logs[i].major, ms->logs[i].minor, |
| "Log", &r); |
| |
| /* Ignore if not in-sync */ |
| if ((r == ME_INSYNC) && (ms->insync_regions != ms->total_regions)) |
| r = ME_IGNORE; |
| |
| dm_pool_free(state->mem, ms); |
| |
| return r; |
| |
| out: |
| log_error("Unable to parse mirror status string."); |
| |
| return ME_IGNORE; |
| } |
| |
| static int _remove_failed_devices(const char *cmd_lvscan, const char *cmd_lvconvert) |
| { |
| int r; |
| |
| if (!dmeventd_lvm2_run_with_lock(cmd_lvscan)) |
| log_info("Re-scan of mirrored device failed."); |
| |
| /* if repair goes OK, report success even if lvscan has failed */ |
| r = dmeventd_lvm2_run_with_lock(cmd_lvconvert); |
| |
| log_info("Repair of mirrored device %s.", |
| (r) ? "finished successfully" : "failed"); |
| |
| return r; |
| } |
| |
| void process_event(struct dm_task *dmt, |
| enum dm_event_mask event __attribute__((unused)), |
| void **user) |
| { |
| struct dso_state *state = *user; |
| void *next = NULL; |
| uint64_t start, length; |
| char *target_type = NULL; |
| char *params; |
| const char *device = dm_task_get_name(dmt); |
| |
| do { |
| next = dm_get_next_target(dmt, next, &start, &length, |
| &target_type, ¶ms); |
| |
| if (!target_type) { |
| log_info("%s mapping lost.", device); |
| continue; |
| } |
| |
| if (strcmp(target_type, TARGET_NAME_MIRROR)) { |
| log_info("%s has unmirrored portion.", device); |
| continue; |
| } |
| |
| switch(_get_mirror_event(state, params)) { |
| case ME_INSYNC: |
| /* FIXME: all we really know is that this |
| _part_ of the device is in sync |
| Also, this is not an error |
| */ |
| log_notice("%s is now in-sync.", device); |
| break; |
| case ME_FAILURE: |
| log_error("Device failure in %s.", device); |
| if (!_remove_failed_devices(state->cmd_lvscan, |
| state->cmd_lvconvert)) |
| /* FIXME Why are all the error return codes unused? Get rid of them? */ |
| log_error("Failed to remove faulty devices in %s.", |
| device); |
| /* Should check before warning user that device is now linear |
| else |
| log_notice("%s is now a linear device.", |
| device); |
| */ |
| break; |
| case ME_IGNORE: |
| break; |
| default: |
| /* FIXME Provide value then! */ |
| log_info("Unknown event received."); |
| } |
| } while (next); |
| } |
| |
| int register_device(const char *device, |
| const char *uuid __attribute__((unused)), |
| int major __attribute__((unused)), |
| int minor __attribute__((unused)), |
| void **user) |
| { |
| struct dso_state *state; |
| |
| if (!dmeventd_lvm2_init_with_pool("mirror_state", state)) |
| goto_bad; |
| |
| if (!dmeventd_lvm2_command(state->mem, state->cmd_lvscan, sizeof(state->cmd_lvscan), |
| "lvscan --cache", device)) { |
| dmeventd_lvm2_exit_with_pool(state); |
| goto_bad; |
| } |
| |
| if (!dmeventd_lvm2_command(state->mem, state->cmd_lvconvert, sizeof(state->cmd_lvconvert), |
| "lvconvert --repair --use-policies", device)) { |
| dmeventd_lvm2_exit_with_pool(state); |
| goto_bad; |
| } |
| |
| *user = state; |
| |
| log_info("Monitoring mirror device %s for events.", device); |
| |
| return 1; |
| bad: |
| log_error("Failed to monitor mirror %s.", device); |
| |
| return 0; |
| } |
| |
| int unregister_device(const char *device, |
| const char *uuid __attribute__((unused)), |
| int major __attribute__((unused)), |
| int minor __attribute__((unused)), |
| void **user) |
| { |
| struct dso_state *state = *user; |
| |
| dmeventd_lvm2_exit_with_pool(state); |
| log_info("No longer monitoring mirror device %s for events.", |
| device); |
| |
| return 1; |
| } |