1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-25 01:34:38 +03:00
lvm2/lib/activate/dev_manager.c

2380 lines
47 KiB
C
Raw Normal View History

/*
2004-03-30 23:35:44 +04:00
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
*
2004-03-30 23:35:44 +04:00
* 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 General Public License v.2.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
2002-11-18 17:01:16 +03:00
#include "lib.h"
2003-09-18 00:35:57 +04:00
#include "str_list.h"
#include "dev_manager.h"
2002-02-25 19:53:12 +03:00
#include "lvm-string.h"
#include "fs.h"
#include "defaults.h"
2004-09-16 22:40:56 +04:00
#include "segtype.h"
2004-05-05 01:25:57 +04:00
#include "display.h"
2003-11-13 21:47:22 +03:00
#include "toolcontext.h"
2004-05-05 01:25:57 +04:00
#include "targets.h"
#include "config.h"
#include <limits.h>
#include <dirent.h>
/*
* Algorithm
* ---------
*
* 1) Examine dm directory, and store details of active mapped devices
* in the VG. Indexed by lvid-layer. (_scan_existing_devices)
*
* 2) Build lists of visible devices that need to be left in each state:
* active, reloaded, suspended.
*
* 3) Run through these lists and set the appropriate marks on each device
* and its dependencies.
*
* 4) Add layers not marked active to remove_list for removal at the end.
*
* 5) Remove unmarked layers from core.
*
* 6) Activate remaining layers, recursing to handle dependedncies and
* skipping any that already exist unless they are marked as needing
* reloading.
*
* 7) Remove layers in the remove_list. (_remove_old_layers)
*
*/
#define MAX_TARGET_PARAMSIZE 50000
enum {
ACTIVE = 0,
RELOAD = 1,
VISIBLE = 2,
READWRITE = 3,
SUSPENDED = 4,
NOPROPAGATE = 5,
TOPLEVEL = 6,
REMOVE = 7,
RESUME_IMMEDIATE = 8
};
2002-03-16 01:59:12 +03:00
typedef enum {
ACTIVATE,
DEACTIVATE,
2002-03-16 01:59:12 +03:00
SUSPEND,
RESUME
} action_t;
2002-03-16 01:59:12 +03:00
struct dev_layer {
char *name;
int flags;
/*
* Setup the dm_task.
*/
2002-03-19 02:25:50 +03:00
int (*populate) (struct dev_manager * dm,
struct dm_task * dmt, struct dev_layer * dl);
2002-02-25 17:46:57 +03:00
struct dm_info info;
2002-03-19 02:25:50 +03:00
/* lvid plus layer */
const char *dlid;
2002-03-19 02:25:50 +03:00
struct logical_volume *lv;
/*
* Devices that must be created before this one can be created.
* Reloads get propagated to this list. Holds str_lists.
*/
2002-03-15 00:17:30 +03:00
struct list pre_create;
/* Inverse of pre_create */
struct list pre_suspend;
};
struct dl_list {
struct list list;
struct dev_layer *dl;
};
static const char *stripe_filler = NULL;
struct dev_manager {
struct dm_pool *mem;
2004-05-05 01:25:57 +04:00
struct cmd_context *cmd;
const char *stripe_filler;
2004-05-05 01:25:57 +04:00
void *target_state;
uint32_t pvmove_mirror_count;
char *vg_name;
/*
* list of struct lv_list, contains lvs that we wish to
* be active after execution.
*/
struct list active_list;
/*
* Layers that need reloading.
*/
struct list reload_list;
/*
* Layers that need suspending.
*/
struct list suspend_list;
/*
* Layers that will need removing after activation.
*/
struct list remove_list;
struct dm_hash_table *layers;
};
/*
* Functions to manage the flags.
*/
2002-03-15 00:17:30 +03:00
static inline int _get_flag(struct dev_layer *dl, int bit)
{
return (dl->flags & (1 << bit)) ? 1 : 0;
}
2002-03-15 00:17:30 +03:00
static inline void _set_flag(struct dev_layer *dl, int bit)
{
dl->flags |= (1 << bit);
}
2002-03-15 00:17:30 +03:00
static inline void _clear_flag(struct dev_layer *dl, int bit)
{
dl->flags &= ~(1 << bit);
}
static char *_build_dlid(struct dm_pool *mem, const char *lvid, const char *layer)
2002-03-19 02:25:50 +03:00
{
char *dlid;
size_t len;
2002-03-19 02:25:50 +03:00
if (!layer)
layer = "";
len = strlen(lvid) + strlen(layer) + 2;
if (!(dlid = dm_pool_alloc(mem, len))) {
2002-03-19 02:25:50 +03:00
stack;
return NULL;
}
sprintf(dlid, "%s%s%s", lvid, (*layer) ? "-" : "", layer);
return dlid;
}
2002-02-25 17:46:57 +03:00
/*
* Low level device-layer operations.
*/
static struct dm_task *_setup_task(const char *name, const char *uuid,
2003-04-30 19:26:25 +04:00
uint32_t *event_nr, int task)
2002-02-25 17:46:57 +03:00
{
struct dm_task *dmt;
if (!(dmt = dm_task_create(task))) {
stack;
return NULL;
}
if (name)
dm_task_set_name(dmt, name);
if (uuid && *uuid)
dm_task_set_uuid(dmt, uuid);
2003-04-30 19:26:25 +04:00
if (event_nr)
dm_task_set_event_nr(dmt, *event_nr);
2002-02-25 17:46:57 +03:00
return dmt;
}
static int _info_run(const char *name, const char *uuid, struct dm_info *info,
int mknodes, int with_open_count, struct dm_pool *mem,
char **uuid_out)
2002-03-15 00:17:30 +03:00
{
int r = 0;
struct dm_task *dmt;
2002-03-19 02:25:50 +03:00
const char *u;
int dmtask;
2002-03-15 00:17:30 +03:00
dmtask = mknodes ? DM_DEVICE_MKNODES : DM_DEVICE_INFO;
if (!(dmt = _setup_task(name, uuid, 0, dmtask))) {
2002-03-15 00:17:30 +03:00
stack;
return 0;
}
if (!with_open_count)
if (!dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
2002-03-15 00:17:30 +03:00
if (!dm_task_run(dmt)) {
stack;
goto out;
}
if (!dm_task_get_info(dmt, info)) {
stack;
goto out;
}
2002-03-19 02:25:50 +03:00
if (info->exists && uuid_out) {
if (!(u = dm_task_get_uuid(dmt))) {
stack;
goto out;
}
*uuid_out = dm_pool_strdup(mem, u);
2002-03-19 02:25:50 +03:00
}
2002-03-15 00:17:30 +03:00
r = 1;
out:
dm_task_destroy(dmt);
return r;
}
2002-02-25 17:46:57 +03:00
static int _info(const char *name, const char *uuid, int mknodes,
int with_open_count, struct dm_info *info,
struct dm_pool *mem, char **uuid_out)
{
2004-05-05 01:25:57 +04:00
if (!mknodes && uuid && *uuid &&
_info_run(NULL, uuid, info, 0, with_open_count, mem, uuid_out) &&
info->exists)
return 1;
if (name)
return _info_run(name, NULL, info, mknodes, with_open_count,
mem, uuid_out);
return 0;
}
int dev_manager_info(const char *name, const char *uuid,
int with_mknodes, int with_open_count, struct dm_info *info)
{
return _info(name, uuid, with_mknodes, with_open_count, info, NULL, NULL);
}
2002-05-22 18:03:45 +04:00
/* FIXME Interface must cope with multiple targets */
static int _status_run(const char *name, const char *uuid,
unsigned long long *s, unsigned long long *l,
char **t, uint32_t t_size, char **p, uint32_t p_size)
{
int r = 0;
struct dm_task *dmt;
void *next = NULL;
uint64_t start, length;
char *type = NULL;
char *params = NULL;
2003-04-30 19:26:25 +04:00
if (!(dmt = _setup_task(name, uuid, 0, DM_DEVICE_STATUS))) {
stack;
return 0;
}
if (!dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
if (!dm_task_run(dmt)) {
stack;
goto out;
}
do {
next = dm_get_next_target(dmt, next, &start, &length,
&type, &params);
2002-05-22 18:03:45 +04:00
if (type) {
*s = start;
*l = length;
/* Make sure things are null terminated */
strncpy(*t, type, t_size);
2002-05-22 18:03:45 +04:00
(*t)[t_size - 1] = '\0';
strncpy(*p, params, p_size);
2002-05-22 18:03:45 +04:00
(*p)[p_size - 1] = '\0';
r = 1;
2002-05-22 18:03:45 +04:00
/* FIXME Cope with multiple targets! */
break;
}
2002-05-22 18:03:45 +04:00
} while (next);
out:
dm_task_destroy(dmt);
return r;
}
static int _status(const char *name, const char *uuid,
unsigned long long *start, unsigned long long *length,
char **type, uint32_t type_size, char **params,
uint32_t param_size) __attribute__ ((unused));
static int _status(const char *name, const char *uuid,
unsigned long long *start, unsigned long long *length,
char **type, uint32_t type_size, char **params,
uint32_t param_size)
{
if (uuid && *uuid && _status_run(NULL, uuid, start, length, type,
type_size, params, param_size)
&& *params)
return 1;
if (name && _status_run(name, NULL, start, length, type, type_size,
2002-05-22 18:03:45 +04:00
params, param_size))
return 1;
2002-05-22 18:03:45 +04:00
return 0;
}
static int _percent_run(struct dev_manager *dm, const char *name,
const char *uuid,
const char *target_type, int wait,
struct logical_volume *lv, float *percent,
2003-04-30 19:26:25 +04:00
uint32_t *event_nr)
{
int r = 0;
struct dm_task *dmt;
2003-04-30 19:26:25 +04:00
struct dm_info info;
void *next = NULL;
uint64_t start, length;
char *type = NULL;
char *params = NULL;
struct list *segh = &lv->segments;
struct lv_segment *seg = NULL;
2004-05-05 01:25:57 +04:00
struct segment_type *segtype;
uint64_t total_numerator = 0, total_denominator = 0;
*percent = -1;
2003-04-30 19:26:25 +04:00
if (!(dmt = _setup_task(name, uuid, event_nr,
2004-05-05 01:25:57 +04:00
wait ? DM_DEVICE_WAITEVENT : DM_DEVICE_STATUS))) {
stack;
return 0;
}
if (!dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
if (!dm_task_run(dmt)) {
stack;
goto out;
}
2003-04-30 19:26:25 +04:00
if (!dm_task_get_info(dmt, &info) || !info.exists) {
stack;
goto out;
}
if (event_nr)
*event_nr = info.event_nr;
do {
next = dm_get_next_target(dmt, next, &start, &length, &type,
&params);
if (lv) {
if (!(segh = list_next(&lv->segments, segh))) {
log_error("Number of segments in active LV %s "
"does not match metadata", lv->name);
goto out;
}
seg = list_item(segh, struct lv_segment);
}
if (!type || !params || strcmp(type, target_type))
continue;
2004-05-05 01:25:57 +04:00
if (!(segtype = get_segtype_from_string(dm->cmd, type)))
2003-04-30 19:26:25 +04:00
continue;
2004-05-05 01:25:57 +04:00
if (segtype->ops->target_percent &&
!segtype->ops->target_percent(&dm->target_state, dm->mem,
dm->cmd->cft, seg, params,
&total_numerator,
&total_denominator,
percent)) {
stack;
goto out;
}
2004-05-05 01:25:57 +04:00
} while (next);
if (lv && (segh = list_next(&lv->segments, segh))) {
log_error("Number of segments in active LV %s does not "
"match metadata", lv->name);
goto out;
}
if (total_denominator)
*percent = (float) total_numerator *100 / total_denominator;
2004-05-05 01:25:57 +04:00
else if (*percent < 0)
2003-04-30 19:26:25 +04:00
*percent = 100;
2003-08-20 19:48:27 +04:00
log_debug("LV percent: %f", *percent);
r = 1;
out:
dm_task_destroy(dmt);
return r;
}
static int _percent(struct dev_manager *dm, const char *name, const char *uuid,
const char *target_type, int wait,
struct logical_volume *lv, float *percent,
uint32_t *event_nr)
{
2003-04-30 19:26:25 +04:00
if (uuid && *uuid
&& _percent_run(dm, NULL, uuid, target_type, wait, lv, percent,
event_nr))
return 1;
if (name && _percent_run(dm, name, NULL, target_type, wait, lv, percent,
2003-04-30 19:26:25 +04:00
event_nr))
return 1;
return 0;
}
static int _rename(struct dev_manager *dm, struct dev_layer *dl, char *newname)
2002-03-19 02:25:50 +03:00
{
int r = 1;
struct dm_task *dmt;
char *vgname, *lvname, *layer;
if (!split_dm_name(dm->mem, dl->name, &vgname, &lvname, &layer)) {
log_error("Couldn't split up dm layer name %s", dl->name);
return 0;
}
2002-03-19 02:25:50 +03:00
log_verbose("Renaming %s to %s", dl->name, newname);
2003-04-30 19:26:25 +04:00
if (!(dmt = _setup_task(dl->name, NULL, 0, DM_DEVICE_RENAME))) {
2002-03-19 02:25:50 +03:00
stack;
return 0;
}
if (!dm_task_set_newname(dmt, newname)) {
stack;
r = 0;
goto out;
}
if (!dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
if (!(r = dm_task_run(dmt))) {
2002-03-19 02:25:50 +03:00
log_error("Couldn't rename device '%s'.", dl->name);
goto out;
}
2002-03-19 02:25:50 +03:00
if (r && _get_flag(dl, VISIBLE))
fs_rename_lv(dl->lv, newname, lvname);
2002-03-19 02:25:50 +03:00
dl->name = newname;
out:
dm_task_destroy(dmt);
return r;
}
static int _suspend_or_resume(const char *name, action_t suspend)
{
int r;
struct dm_task *dmt;
int sus = (suspend == SUSPEND) ? 1 : 0;
int task = sus ? DM_DEVICE_SUSPEND : DM_DEVICE_RESUME;
log_very_verbose("%s %s", sus ? "Suspending" : "Resuming", name);
if (!(dmt = _setup_task(name, NULL, 0, task))) {
stack;
return 0;
}
if (!dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
if (!(r = dm_task_run(dmt)))
log_error("Couldn't %s device '%s'", sus ? "suspend" : "resume",
name);
dm_task_destroy(dmt);
return r;
}
static int _suspend(struct dev_layer *dl)
{
if (!dl->info.exists || dl->info.suspended)
return 1;
if (!_suspend_or_resume(dl->name, SUSPEND)) {
stack;
return 0;
}
dl->info.suspended = 1;
return 1;
}
static int _resume(struct dev_layer *dl)
{
if (!dl->info.exists || !dl->info.suspended)
return 1;
if (!_suspend_or_resume(dl->name, RESUME)) {
stack;
return 0;
}
dl->info.suspended = 0;
return 1;
}
static int _load(struct dev_manager *dm, struct dev_layer *dl, int task)
2002-02-25 17:46:57 +03:00
{
2002-03-19 02:25:50 +03:00
int r = 1;
2002-02-25 17:46:57 +03:00
struct dm_task *dmt;
log_verbose("Loading %s", dl->name);
if (!(dmt = _setup_task(task == DM_DEVICE_CREATE ? dl->name : NULL,
2003-04-30 19:26:25 +04:00
dl->dlid, 0, task))) {
2002-02-25 17:46:57 +03:00
stack;
return 0;
}
/*
* Populate the table.
*/
if (!dl->populate(dm, dmt, dl)) {
2002-03-15 00:17:30 +03:00
log_error("Couldn't populate device '%s'.", dl->name);
2002-03-19 02:25:50 +03:00
r = 0;
goto out;
2002-02-25 17:46:57 +03:00
}
2002-03-26 16:41:37 +03:00
/*
* Do we want a specific device number ?
2002-03-26 16:41:37 +03:00
*/
if (dl->lv->major >= 0 && _get_flag(dl, VISIBLE)) {
if (!dm_task_set_major(dmt, dl->lv->major)) {
log_error("Failed to set major number for %s to %d "
"during activation.", dl->name,
dl->lv->major);
goto out;
} else
log_very_verbose("Set major number for %s to %d.",
dl->name, dl->lv->major);
}
2002-03-26 16:41:37 +03:00
if (dl->lv->minor >= 0 && _get_flag(dl, VISIBLE)) {
if (!dm_task_set_minor(dmt, dl->lv->minor)) {
log_error("Failed to set minor number for %s to %d "
"during activation.", dl->name,
dl->lv->minor);
goto out;
} else
log_very_verbose("Set minor number for %s to %d.",
dl->name, dl->lv->minor);
}
if (!_get_flag(dl, READWRITE)) {
if (!dm_task_set_ro(dmt)) {
log_error("Failed to set %s read-only during "
"activation.", dl->name);
goto out;
} else
log_very_verbose("Activating %s read-only", dl->name);
}
if (!dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
if (!(r = dm_task_run(dmt))) {
2002-03-15 00:17:30 +03:00
log_error("Couldn't load device '%s'.", dl->name);
if ((dl->lv->minor >= 0 || dl->lv->major >= 0) &&
_get_flag(dl, VISIBLE))
2004-05-05 01:25:57 +04:00
log_error("Perhaps the persistent device number "
"%d:%d is already in use?",
dl->lv->major, dl->lv->minor);
}
2002-03-15 00:17:30 +03:00
if (!dm_task_get_info(dmt, &dl->info)) {
stack;
2002-03-19 02:25:50 +03:00
r = 0;
goto out;
2002-03-15 00:17:30 +03:00
}
if (!dl->info.exists || !dl->info.live_table) {
stack;
r = 0;
goto out;
}
if (_get_flag(dl, RESUME_IMMEDIATE) && dl->info.suspended &&
!_resume(dl)) {
stack;
r = 0;
goto out;
}
log_very_verbose("Activated %s %s %03u:%03u", dl->name,
dl->dlid, dl->info.major, dl->info.minor);
if (r && _get_flag(dl, VISIBLE))
fs_add_lv(dl->lv, dl->name);
_clear_flag(dl, RELOAD);
2002-03-16 01:59:12 +03:00
2002-03-19 02:25:50 +03:00
out:
dm_task_destroy(dmt);
2002-02-25 17:46:57 +03:00
return r;
}
static int _remove(struct dev_layer *dl)
{
int r;
struct dm_task *dmt;
2002-03-16 01:59:12 +03:00
if (_get_flag(dl, VISIBLE))
log_verbose("Removing %s", dl->name);
2002-03-16 01:59:12 +03:00
else
log_very_verbose("Removing %s", dl->name);
2002-03-16 01:59:12 +03:00
2003-04-30 19:26:25 +04:00
if (!(dmt = _setup_task(dl->name, NULL, 0, DM_DEVICE_REMOVE))) {
2002-02-25 17:46:57 +03:00
stack;
return 0;
}
if (!dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
2002-03-16 01:59:12 +03:00
/* Suppress error message if it's still in use - we'll log it later */
log_suppress(1);
if ((r = dm_task_run(dmt)))
2002-03-15 00:17:30 +03:00
dl->info.exists = 0;
2002-02-25 17:46:57 +03:00
2002-03-16 01:59:12 +03:00
log_suppress(0);
2002-02-25 17:46:57 +03:00
dm_task_destroy(dmt);
if (r && _get_flag(dl, VISIBLE))
fs_del_lv(dl->lv);
2002-03-16 01:59:12 +03:00
_clear_flag(dl, ACTIVE);
2002-02-25 17:46:57 +03:00
return r;
}
2002-02-25 18:19:53 +03:00
/*
* The functions that populate the table in a dm_task as part of
* a create/reload.
*/
/*
* Emit a target for a given segment.
* FIXME: tidy this function.
*/
static int _emit_target_line(struct dev_manager *dm, struct dm_task *dmt,
struct lv_segment *seg, char *params,
size_t paramsize)
2002-02-25 18:19:53 +03:00
{
uint64_t esize = seg->lv->vg->extent_size;
2004-05-05 01:25:57 +04:00
int w = 0;
const char *target = NULL;
2004-05-05 01:25:57 +04:00
int r;
2002-02-25 18:19:53 +03:00
2004-05-05 01:25:57 +04:00
if (!seg->segtype->ops->compose_target_line) {
log_error("_emit_target: Internal error: Can't handle "
2004-05-05 01:25:57 +04:00
"segment type %s", seg->segtype->name);
return 0;
}
if ((r = seg->segtype->ops->compose_target_line(dm, dm->mem,
dm->cmd->cft,
&dm->target_state, seg,
params, paramsize,
&target, &w,
&dm->
pvmove_mirror_count)) <=
0) {
stack;
return r;
}
log_debug("Adding target: %" PRIu64 " %" PRIu64 " %s %s",
esize * seg->le, esize * seg->len, target, params);
if (!dm_task_add_target(dmt, esize * seg->le, esize * seg->len,
target, params)) {
stack;
return 0;
2002-02-25 18:19:53 +03:00
}
2004-05-05 01:25:57 +04:00
return 1;
}
int build_dev_string(struct dev_manager *dm, char *dlid, char *devbuf,
size_t bufsize, const char *desc)
2005-08-04 05:15:30 +04:00
{
struct dev_layer *dl;
if (!(dl = dm_hash_lookup(dm->layers, dlid))) {
2005-08-04 05:15:30 +04:00
log_error("%s device layer %s missing from hash",
desc, dlid);
return 0;
}
if (!dm_format_dev(devbuf, bufsize, dl->info.major,
dl->info.minor)) {
log_error("Failed to format %s device number for %s as dm "
"target (%u,%u)",
desc, dlid, dl->info.major, dl->info.minor);
return 0;
}
return 1;
}
2004-05-05 01:25:57 +04:00
int compose_areas_line(struct dev_manager *dm, struct lv_segment *seg,
char *params, size_t paramsize, int *pos, int start_area,
int areas)
{
uint32_t s;
int tw = 0;
const char *trailing_space;
uint64_t esize = seg->lv->vg->extent_size;
char devbuf[10];
for (s = start_area; s < areas; s++, *pos += tw) {
trailing_space = (areas - s - 1) ? " " : "";
2005-06-01 20:51:55 +04:00
if ((seg_type(seg, s) == AREA_PV &&
(!seg_pvseg(seg, s) ||
!seg_pv(seg, s) ||
!seg_dev(seg, s))) ||
(seg_type(seg, s) == AREA_LV && !seg_lv(seg, s)))
2004-05-05 01:25:57 +04:00
tw = lvm_snprintf(params + *pos, paramsize - *pos,
"%s 0%s", dm->stripe_filler,
trailing_space);
2005-06-01 20:51:55 +04:00
else if (seg_type(seg, s) == AREA_PV)
2004-05-05 01:25:57 +04:00
tw = lvm_snprintf(params + *pos, paramsize - *pos,
2002-04-16 18:42:20 +04:00
"%s %" PRIu64 "%s",
2005-06-01 20:51:55 +04:00
dev_name(seg_dev(seg, s)),
(seg_pv(seg, s)->pe_start +
(esize * seg_pe(seg, s))),
trailing_space);
else if (seg_type(seg, s) == AREA_LV) {
if (!build_dev_string(dm, seg_lv(seg, s)->lvid.s, devbuf,
sizeof(devbuf), "LV")) {
2005-08-04 05:15:30 +04:00
stack;
return 0;
}
2004-05-05 01:25:57 +04:00
tw = lvm_snprintf(params + *pos, paramsize - *pos,
"%s %" PRIu64 "%s", devbuf,
2005-06-01 20:51:55 +04:00
esize * seg_le(seg, s),
trailing_space);
} else {
log_error("Internal error: Unassigned area found in LV %s.",
2005-08-04 05:15:30 +04:00
seg->lv->name);
return 0;
}
2004-05-05 01:25:57 +04:00
if (tw < 0) {
stack;
return -1;
}
2002-02-25 18:19:53 +03:00
}
return 1;
}
static int _emit_target(struct dev_manager *dm, struct dm_task *dmt,
struct lv_segment *seg)
{
char *params;
size_t paramsize = 4096;
int ret;
do {
if (!(params = dm_malloc(paramsize))) {
log_error("Insufficient space for target parameters.");
return 0;
}
ret = _emit_target_line(dm, dmt, seg, params, paramsize);
dm_free(params);
if (!ret)
stack;
if (ret >= 0)
return ret;
2004-05-05 01:25:57 +04:00
log_debug("Insufficient space in params[%" PRIsize_t
"] for target parameters.", paramsize);
paramsize *= 2;
} while (paramsize < MAX_TARGET_PARAMSIZE);
log_error("Target parameter size too big. Aborting.");
return 0;
2002-02-25 18:19:53 +03:00
}
static int _populate_vanilla(struct dev_manager *dm,
struct dm_task *dmt, struct dev_layer *dl)
2002-02-25 18:19:53 +03:00
{
2002-11-18 17:01:16 +03:00
struct lv_segment *seg;
2002-02-25 18:19:53 +03:00
struct logical_volume *lv = dl->lv;
dm->pvmove_mirror_count = 0u;
2005-06-01 20:51:55 +04:00
list_iterate_items(seg, &lv->segments) {
if (!_emit_target(dm, dmt, seg)) {
2002-02-25 18:19:53 +03:00
log_error("Unable to build table for '%s'", lv->name);
return 0;
}
}
return 1;
}
static int _populate_origin(struct dev_manager *dm,
struct dm_task *dmt, struct dev_layer *dl)
2002-02-25 18:19:53 +03:00
{
char *real;
2002-03-15 00:17:30 +03:00
char params[PATH_MAX + 32];
if (!(real = _build_dlid(dm->mem, dl->lv->lvid.s, "real"))) {
stack;
return 0;
}
if (!build_dev_string(dm, real, params, sizeof(params), "origin")) {
2005-08-04 05:15:30 +04:00
stack;
return 0;
}
2002-02-25 18:19:53 +03:00
2002-03-15 00:17:30 +03:00
log_debug("Adding target: 0 %" PRIu64 " snapshot-origin %s",
dl->lv->size, params);
if (!dm_task_add_target(dmt, UINT64_C(0), dl->lv->size,
"snapshot-origin", params)) {
2002-03-15 00:17:30 +03:00
stack;
return 0;
}
2002-02-25 18:19:53 +03:00
return 1;
}
static int _populate_snapshot(struct dev_manager *dm,
struct dm_task *dmt, struct dev_layer *dl)
2002-02-25 18:19:53 +03:00
{
char *origin, *cow;
2002-03-15 00:17:30 +03:00
char params[PATH_MAX * 2 + 32];
2005-04-07 16:39:44 +04:00
struct lv_segment *snap_seg;
char devbufo[10], devbufc[10];
uint64_t size;
2005-04-07 16:39:44 +04:00
if (!(snap_seg = find_cow(dl->lv))) {
2002-03-19 02:25:50 +03:00
log_error("Couldn't find snapshot for '%s'.", dl->lv->name);
return 0;
}
2005-04-07 16:39:44 +04:00
if (!(origin = _build_dlid(dm->mem, snap_seg->origin->lvid.s,
"real"))) {
stack;
return 0;
}
2002-02-25 18:19:53 +03:00
2005-04-07 16:39:44 +04:00
if (!(cow = _build_dlid(dm->mem, snap_seg->cow->lvid.s, "cow"))) {
stack;
return 0;
}
2002-02-25 18:19:53 +03:00
if (!build_dev_string(dm, origin, devbufo, sizeof(devbufo), "origin")) {
2005-08-04 05:15:30 +04:00
stack;
return 0;
}
if (!build_dev_string(dm, cow, devbufc, sizeof(devbufc), "cow")) {
2005-08-04 05:15:30 +04:00
stack;
return 0;
}
2003-09-18 00:35:57 +04:00
if (lvm_snprintf(params, sizeof(params), "%s %s P %d",
2005-04-07 16:39:44 +04:00
devbufo, devbufc, snap_seg->chunk_size) == -1) {
2002-03-15 00:17:30 +03:00
stack;
return 0;
}
2002-02-25 18:19:53 +03:00
2005-04-07 16:39:44 +04:00
size = (uint64_t) snap_seg->len * snap_seg->origin->vg->extent_size;
log_debug("Adding target: 0 %" PRIu64 " snapshot %s", size, params);
if (!dm_task_add_target(dmt, UINT64_C(0), size, "snapshot", params)) {
2002-03-15 00:17:30 +03:00
stack;
return 0;
}
2002-02-25 18:19:53 +03:00
return 1;
}
/*
* dev_manager implementation.
*/
2004-05-05 01:25:57 +04:00
struct dev_manager *dev_manager_create(struct cmd_context *cmd,
const char *vg_name)
{
struct dm_pool *mem;
struct dev_manager *dm;
if (!(mem = dm_pool_create("dev_manager", 16 * 1024))) {
stack;
return NULL;
}
if (!(dm = dm_pool_alloc(mem, sizeof(*dm)))) {
stack;
goto bad;
}
2004-05-05 01:25:57 +04:00
dm->cmd = cmd;
dm->mem = mem;
2004-05-05 01:25:57 +04:00
if (!stripe_filler) {
2004-05-05 01:25:57 +04:00
stripe_filler = find_config_str(cmd->cft->root,
"activation/missing_stripe_filler",
DEFAULT_STRIPE_FILLER);
}
dm->stripe_filler = stripe_filler;
if (!(dm->vg_name = dm_pool_strdup(dm->mem, vg_name))) {
stack;
goto bad;
}
if (!(dm->layers = dm_hash_create(32))) {
stack;
goto bad;
}
list_init(&dm->active_list);
list_init(&dm->reload_list);
list_init(&dm->remove_list);
list_init(&dm->suspend_list);
2004-05-05 01:25:57 +04:00
dm->target_state = NULL;
return dm;
2002-03-15 00:17:30 +03:00
bad:
dm_pool_destroy(mem);
return NULL;
}
void dev_manager_destroy(struct dev_manager *dm)
{
dm_hash_destroy(dm->layers);
dm_pool_destroy(dm->mem);
}
2002-05-22 18:03:45 +04:00
int dev_manager_snapshot_percent(struct dev_manager *dm,
struct logical_volume *lv, float *percent)
{
char *name;
/*
* Build a name for the top layer.
*/
if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, NULL))) {
stack;
return 0;
}
/*
* Try and get some info on this device.
*/
log_debug("Getting device status percentage for %s", name);
if (!(_percent(dm, name, lv->lvid.s, "snapshot", 0, NULL, percent,
NULL))) {
2002-05-22 18:03:45 +04:00
stack;
return 0;
}
/* FIXME dm_pool_free ? */
2002-05-22 18:03:45 +04:00
/* If the snapshot isn't available, percent will be -1 */
return 1;
}
2003-04-30 19:26:25 +04:00
/* FIXME Merge with snapshot_percent, auto-detecting target type */
/* FIXME Cope with more than one target */
int dev_manager_mirror_percent(struct dev_manager *dm,
struct logical_volume *lv, int wait,
float *percent, uint32_t *event_nr)
{
char *name;
/*
* Build a name for the top layer.
*/
if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, NULL))) {
2003-04-30 19:26:25 +04:00
stack;
return 0;
}
/* FIXME dm_pool_free ? */
2003-04-30 19:26:25 +04:00
log_debug("Getting device mirror status percentage for %s", name);
if (!(_percent(dm, name, lv->lvid.s, "mirror", wait, lv, percent,
event_nr))) {
2003-04-30 19:26:25 +04:00
stack;
return 0;
}
return 1;
}
2002-03-19 02:25:50 +03:00
static struct dev_layer *_create_dev(struct dev_manager *dm, char *name,
const char *dlid)
{
struct dev_layer *dl;
2002-03-19 02:25:50 +03:00
char *uuid;
if (!(dl = dm_pool_zalloc(dm->mem, sizeof(*dl)))) {
stack;
return NULL;
}
dl->name = name;
log_debug("Getting device info for %s", dl->name);
if (!_info(dl->name, dlid, 0, 0, &dl->info, dm->mem, &uuid)) {
2002-02-25 17:46:57 +03:00
stack;
return NULL;
}
2002-03-19 02:25:50 +03:00
if (dl->info.exists)
dl->dlid = uuid;
else
dl->dlid = dlid;
list_init(&dl->pre_create);
list_init(&dl->pre_suspend);
if (!dm_hash_insert(dm->layers, dl->dlid, dl)) {
2002-03-15 00:17:30 +03:00
stack;
return NULL;
}
return dl;
}
static inline int _read_only_lv(struct logical_volume *lv)
{
2002-03-26 16:41:37 +03:00
return (!(lv->vg->status & LVM_WRITE) || !(lv->status & LVM_WRITE));
}
2002-03-15 00:17:30 +03:00
static struct dev_layer *_create_layer(struct dev_manager *dm,
const char *layer,
struct logical_volume *lv)
{
2002-03-19 02:25:50 +03:00
char *name, *dlid;
2002-03-15 00:17:30 +03:00
struct dev_layer *dl;
if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, layer))) {
2002-03-15 00:17:30 +03:00
stack;
return NULL;
}
2002-03-19 02:25:50 +03:00
if (!(dlid = _build_dlid(dm->mem, lv->lvid.s, layer))) {
stack;
return NULL;
}
if (!(dl = dm_hash_lookup(dm->layers, dlid)) &&
2002-03-19 02:25:50 +03:00
!(dl = _create_dev(dm, name, dlid))) {
stack;
return NULL;
}
2002-03-15 00:17:30 +03:00
dl->lv = lv;
if (!_read_only_lv(lv))
_set_flag(dl, READWRITE);
2002-03-15 00:17:30 +03:00
return dl;
}
/*
* Finds the specified layer.
*/
2002-02-25 17:46:57 +03:00
static struct dev_layer *_lookup(struct dev_manager *dm,
2002-03-19 02:25:50 +03:00
const char *lvid, const char *layer)
{
2002-03-19 02:25:50 +03:00
char *dlid;
struct dev_layer *dl;
2002-03-19 02:25:50 +03:00
if (!(dlid = _build_dlid(dm->mem, lvid, layer))) {
stack;
return NULL;
}
dl = dm_hash_lookup(dm->layers, dlid);
dm_pool_free(dm->mem, dlid);
return dl;
}
static int _expand_vanilla(struct dev_manager *dm, struct logical_volume *lv,
int was_origin)
{
/*
* only one layer.
*/
struct dev_layer *dl, *dlr;
struct lv_segment *seg;
uint32_t s;
2002-03-15 00:17:30 +03:00
if (!(dl = _create_layer(dm, NULL, lv))) {
stack;
return 0;
}
dl->populate = _populate_vanilla;
if (lv->status & VISIBLE_LV) {
_set_flag(dl, VISIBLE);
_set_flag(dl, TOPLEVEL);
}
if (lv->status & PVMOVE)
_set_flag(dl, TOPLEVEL);
/* Add dependencies for any LVs that segments refer to */
2005-06-01 20:51:55 +04:00
list_iterate_items(seg, &lv->segments) {
// When do we need? _set_flag(dl, REMOVE) on the log?
2005-06-01 20:51:55 +04:00
if (seg->log_lv &&
!str_list_add(dm->mem, &dl->pre_create,
_build_dlid(dm->mem, seg->log_lv->lvid.s,
NULL))) {
stack;
return 0;
}
for (s = 0; s < seg->area_count; s++) {
2005-06-01 20:51:55 +04:00
if (seg_type(seg, s) != AREA_LV)
continue;
2003-09-18 00:35:57 +04:00
if (!str_list_add(dm->mem, &dl->pre_create,
_build_dlid(dm->mem,
2005-06-01 20:51:55 +04:00
seg_lv(seg, s)->
2004-05-05 01:25:57 +04:00
lvid.s, NULL))) {
stack;
return 0;
}
// ? if (seg_lv(seg, s)->status & PVMOVE)
_set_flag(dl, NOPROPAGATE);
// When do we need? _set_flag(dl, REMOVE)
}
}
if (!was_origin)
return 1;
/* Deactivating the last snapshot */
if (!(dlr = _create_layer(dm, "real", lv))) {
stack;
return 0;
}
dlr->populate = _populate_vanilla;
_clear_flag(dlr, VISIBLE);
_clear_flag(dlr, TOPLEVEL);
_set_flag(dlr, REMOVE);
2004-05-05 01:25:57 +04:00
/* add the dependency on the real device */
2003-09-18 00:35:57 +04:00
if (!str_list_add(dm->mem, &dl->pre_create,
dm_pool_strdup(dm->mem, dlr->dlid))) {
stack;
return 0;
}
return 1;
}
static int _expand_origin_real(struct dev_manager *dm,
struct logical_volume *lv)
{
struct dev_layer *dl;
const char *real_dlid;
2002-03-15 00:17:30 +03:00
if (!(dl = _create_layer(dm, "real", lv))) {
stack;
return 0;
}
dl->populate = _populate_vanilla;
_clear_flag(dl, VISIBLE);
_clear_flag(dl, TOPLEVEL);
/* Size changes must take effect before tables using it are reloaded */
_set_flag(dl, RESUME_IMMEDIATE);
2002-03-19 02:25:50 +03:00
real_dlid = dl->dlid;
2002-03-15 00:17:30 +03:00
if (!(dl = _create_layer(dm, NULL, lv))) {
stack;
return 0;
}
dl->populate = _populate_origin;
_set_flag(dl, VISIBLE);
_set_flag(dl, TOPLEVEL);
/* add the dependency on the real device */
2003-09-18 00:35:57 +04:00
if (!str_list_add(dm->mem, &dl->pre_create,
dm_pool_strdup(dm->mem, real_dlid))) {
stack;
return 0;
}
return 1;
}
static int _expand_origin(struct dev_manager *dm, struct logical_volume *lv)
{
struct logical_volume *active;
2005-04-07 16:39:44 +04:00
struct lv_segment *snap_seg;
2005-06-01 20:51:55 +04:00
struct lv_list *lvl;
/*
* We only need to create an origin layer if one of our
* snapshots is in the active list
*/
2005-06-01 20:51:55 +04:00
list_iterate_items(lvl, &dm->active_list) {
active = lvl->lv;
2005-04-07 16:39:44 +04:00
if ((snap_seg = find_cow(active)) && (snap_seg->origin == lv))
return _expand_origin_real(dm, lv);
}
/*
* We're deactivating the last snapshot
*/
return _expand_vanilla(dm, lv, 1);
}
static int _expand_snapshot(struct dev_manager *dm, struct logical_volume *lv,
2005-04-07 16:39:44 +04:00
struct lv_segment *snap_seg)
{
/*
* snapshot(org, cow)
* cow
*/
struct dev_layer *dl;
const char *cow_dlid;
2002-03-15 00:17:30 +03:00
if (!(dl = _create_layer(dm, "cow", lv))) {
stack;
return 0;
}
dl->populate = _populate_vanilla;
_clear_flag(dl, VISIBLE);
_clear_flag(dl, TOPLEVEL);
2003-09-16 20:08:05 +04:00
_set_flag(dl, READWRITE);
2002-03-19 02:25:50 +03:00
cow_dlid = dl->dlid;
2002-03-15 00:17:30 +03:00
if (!(dl = _create_layer(dm, NULL, lv))) {
stack;
return 0;
}
dl->populate = _populate_snapshot;
_set_flag(dl, VISIBLE);
_set_flag(dl, TOPLEVEL);
/* add the dependency on the cow device */
2003-09-18 00:35:57 +04:00
if (!str_list_add(dm->mem, &dl->pre_create,
dm_pool_strdup(dm->mem, cow_dlid))) {
stack;
return 0;
}
/* add the dependency on the real origin device */
2003-09-18 00:35:57 +04:00
if (!str_list_add(dm->mem, &dl->pre_create,
2005-04-07 16:39:44 +04:00
_build_dlid(dm->mem, snap_seg->origin->lvid.s,
"real"))) {
stack;
return 0;
}
/* add the dependency on the visible origin device */
2005-04-07 16:39:44 +04:00
if (!str_list_add(dm->mem, &dl->pre_suspend,
snap_seg->origin->lvid.s)) {
stack;
return 0;
}
return 1;
}
/*
* Inserts the appropriate dev_layers for a logical volume.
*/
static int _expand_lv(struct dev_manager *dm, struct logical_volume *lv)
{
2005-04-07 16:39:44 +04:00
struct lv_segment *snap_seg;
/*
* FIXME: this doesn't cope with recursive snapshots yet.
*/
if ((snap_seg = find_cow(lv))) {
if (lv->vg->status & CLUSTERED) {
log_error("Clustered snapshots are not yet supported");
return 0;
}
2005-04-07 16:39:44 +04:00
return _expand_snapshot(dm, lv, snap_seg);
} else if (lv_is_origin(lv)) {
if (lv->vg->status & CLUSTERED) {
log_error("Clustered snapshots are not yet supported");
return 0;
}
return _expand_origin(dm, lv);
}
return _expand_vanilla(dm, lv, 0);
}
/*
* Clears the mark bit on all layers.
*/
static void _clear_marks(struct dev_manager *dm, int flag)
{
struct dm_hash_node *hn;
struct dev_layer *dl;
dm_hash_iterate(hn, dm->layers) {
dl = dm_hash_get_data(dm->layers, hn);
_clear_flag(dl, flag);
}
}
/*
* Propogates marks via the pre_create dependency list.
*/
static int _trace_layer_marks(struct dev_manager *dm, struct dev_layer *dl,
int flag)
{
2005-06-01 20:51:55 +04:00
struct str_list *strl;
const char *dlid;
struct dev_layer *dep;
2005-06-01 20:51:55 +04:00
list_iterate_items(strl, &dl->pre_create) {
dlid = strl->str;
if (!(dep = dm_hash_lookup(dm->layers, dlid))) {
2002-03-19 02:25:50 +03:00
log_error("Couldn't find device layer '%s'.", dlid);
return 0;
}
if (_get_flag(dep, flag))
continue;
/* FIXME Only propagate LV ACTIVE dependencies for now */
if ((flag != ACTIVE) && _get_flag(dl, NOPROPAGATE))
continue;
_set_flag(dep, flag);
if (!_trace_layer_marks(dm, dep, flag)) {
stack;
return 0;
}
}
return 1;
}
/*
* Calls _trace_single for every marked layer.
*/
static int _trace_all_marks(struct dev_manager *dm, int flag)
{
struct dm_hash_node *hn;
struct dev_layer *dl;
dm_hash_iterate(hn, dm->layers) {
dl = dm_hash_get_data(dm->layers, hn);
if (_get_flag(dl, flag) && !_trace_layer_marks(dm, dl, flag)) {
stack;
return 0;
}
}
return 1;
}
/*
* Marks the top layers, then traces these through the
* dependencies.
*/
static int _mark_lvs(struct dev_manager *dm, struct list *lvs, int flag)
{
2005-06-01 20:51:55 +04:00
struct lv_list *lvl;
struct dev_layer *dl;
2005-06-01 20:51:55 +04:00
list_iterate_items(lvl, lvs) {
if (lvl->lv->status & SNAPSHOT)
2005-04-07 16:39:44 +04:00
continue;
2005-06-01 20:51:55 +04:00
if (!(dl = _lookup(dm, lvl->lv->lvid.s, NULL))) {
stack;
return 0;
}
_set_flag(dl, flag);
}
if (!_trace_all_marks(dm, flag)) {
stack;
return 0;
}
return 1;
}
static int _suspend_parents(struct dev_manager *dm, struct dev_layer *dl)
{
2005-06-01 20:51:55 +04:00
struct str_list *strl;
struct dev_layer *dep;
const char *dlid;
2005-06-01 20:51:55 +04:00
list_iterate_items(strl, &dl->pre_suspend) {
dlid = strl->str;
if (!(dep = dm_hash_lookup(dm->layers, dlid))) {
log_debug("_suspend_parents couldn't find device "
"layer '%s' - skipping.", dlid);
continue;
}
if (!strcmp(dep->dlid, dl->dlid)) {
log_error("BUG: pre-suspend loop detected (%s)", dlid);
return 0;
}
if (!_suspend_parents(dm, dep)) {
stack;
return 0;
}
if (dep->info.exists & !_suspend(dep)) {
stack;
return 0;
}
}
return 1;
}
static int _resume_with_deps(struct dev_manager *dm, struct dev_layer *dl)
{
2005-06-01 20:51:55 +04:00
struct str_list *strl;
struct dev_layer *dep;
const char *dlid;
2005-06-01 20:51:55 +04:00
list_iterate_items(strl, &dl->pre_create) {
dlid = strl->str;
if (!(dep = dm_hash_lookup(dm->layers, dlid))) {
log_debug("_resume_with_deps couldn't find device "
"layer '%s' - skipping.", dlid);
continue;
}
if (!strcmp(dep->dlid, dl->dlid)) {
log_error("BUG: pre-create loop detected (%s)", dlid);
return 0;
}
if (!_resume_with_deps(dm, dep)) {
stack;
return 0;
}
}
if (dl->info.exists & !_get_flag(dl, SUSPENDED) && !_resume(dl)) {
stack;
return 0;
}
return 1;
}
/*
* Recurses through the tree, ensuring that devices are created
* in correct order.
*/
static int _create_rec(struct dev_manager *dm, struct dev_layer *dl)
{
2005-06-01 20:51:55 +04:00
struct str_list *strl;
struct dev_layer *dep;
const char *dlid;
char *newname, *suffix;
/* Suspend? */
if (_get_flag(dl, SUSPENDED) &&
(!_suspend_parents(dm, dl) || !_suspend(dl))) {
stack;
return 0;
}
2005-06-01 20:51:55 +04:00
list_iterate_items(strl, &dl->pre_create) {
dlid = strl->str;
if (!(dep = dm_hash_lookup(dm->layers, dlid))) {
2002-03-19 02:25:50 +03:00
log_error("Couldn't find device layer '%s'.", dlid);
return 0;
}
2002-03-19 02:25:50 +03:00
if (!strcmp(dep->dlid, dl->dlid)) {
log_error("BUG: pre-create loop detected (%s)", dlid);
return 0;
}
if (!_create_rec(dm, dep)) {
stack;
return 0;
}
2002-03-16 01:59:12 +03:00
}
2002-03-19 02:25:50 +03:00
/* Rename? */
if (dl->info.exists) {
if ((suffix = rindex(dl->dlid, '-')))
suffix++;
newname = build_dm_name(dm->mem, dm->vg_name, dl->lv->name,
suffix);
if (strcmp(newname, dl->name)) {
if (!_suspend_parents(dm, dl) ||
!_suspend(dl) || !_rename(dm, dl, newname)) {
2002-03-19 02:25:50 +03:00
stack;
return 0;
}
}
}
2002-03-16 01:59:12 +03:00
/* Create? */
if (!dl->info.exists) {
if (!_suspend_parents(dm, dl) ||
!_load(dm, dl, DM_DEVICE_CREATE)) {
stack;
return 0;
}
2002-03-16 01:59:12 +03:00
return 1;
}
/* Reload? */
if (_get_flag(dl, RELOAD) &&
(!_suspend_parents(dm, dl) || !_suspend(dl) ||
!_load(dm, dl, DM_DEVICE_RELOAD))) {
2002-03-16 01:59:12 +03:00
stack;
return 0;
}
return 1;
}
static int _build_all_layers(struct dev_manager *dm, struct volume_group *vg)
2002-02-25 19:53:12 +03:00
{
2005-06-01 20:51:55 +04:00
struct lv_list *lvl;
2002-02-25 19:53:12 +03:00
/*
* Build layers for complete vg.
*/
2005-06-01 20:51:55 +04:00
list_iterate_items(lvl, &vg->lvs) {
if (lvl->lv->status & SNAPSHOT)
2005-04-07 16:39:44 +04:00
continue;
2005-06-01 20:51:55 +04:00
if (!_expand_lv(dm, lvl->lv)) {
2002-02-25 19:53:12 +03:00
stack;
return 0;
}
}
return 1;
}
static int _fill_in_remove_list(struct dev_manager *dm)
{
struct dm_hash_node *hn;
struct dev_layer *dl;
struct dl_list *dll;
dm_hash_iterate(hn, dm->layers) {
dl = dm_hash_get_data(dm->layers, hn);
if (_get_flag(dl, REMOVE))
_clear_flag(dl, ACTIVE);
if (!_get_flag(dl, ACTIVE)) {
dll = dm_pool_alloc(dm->mem, sizeof(*dll));
if (!dll) {
stack;
return 0;
}
dll->dl = dl;
list_add(&dm->remove_list, &dll->list);
}
}
2002-02-25 19:53:12 +03:00
return 1;
}
static int _populate_pre_suspend_lists(struct dev_manager *dm)
{
struct dm_hash_node *hn;
struct dev_layer *dl;
2005-06-01 20:51:55 +04:00
struct str_list *strl;
const char *dlid;
struct dev_layer *dep;
dm_hash_iterate(hn, dm->layers) {
dl = dm_hash_get_data(dm->layers, hn);
2005-06-01 20:51:55 +04:00
list_iterate_items(strl, &dl->pre_suspend) {
dlid = strl->str;
if (!(dep = dm_hash_lookup(dm->layers, dlid))) {
log_debug("_populate_pre_suspend_lists: "
"Couldn't find device layer '%s' - "
"skipping.", dlid);
continue;
}
2004-05-05 01:25:57 +04:00
if (!str_list_add(dm->mem, &dep->pre_create, dl->dlid)) {
stack;
return 0;
}
}
2005-06-01 20:51:55 +04:00
list_iterate_items(strl, &dl->pre_create) {
dlid = strl->str;
if (!(dep = dm_hash_lookup(dm->layers, dlid))) {
log_debug("_populate_pre_suspend_lists: "
"Couldn't find device layer '%s' - "
"skipping.", dlid);
continue;
}
2004-05-05 01:25:57 +04:00
if (!str_list_add(dm->mem, &dep->pre_suspend, dl->dlid)) {
stack;
return 0;
}
}
}
return 1;
}
/*
* Layers are removed in a top-down manner.
*/
static int _remove_old_layers(struct dev_manager *dm)
{
int change;
2005-06-01 20:51:55 +04:00
struct dl_list *dll;
2002-03-16 01:59:12 +03:00
struct list *rh, *n;
struct dev_layer *dl;
do {
change = 0;
2002-03-15 00:17:30 +03:00
list_iterate_safe(rh, n, &dm->remove_list) {
dl = list_item(rh, struct dl_list)->dl;
2002-03-15 00:17:30 +03:00
if (!dl->info.exists) {
list_del(rh);
continue;
}
2002-03-16 01:59:12 +03:00
if (_remove(dl)) {
2002-03-15 00:17:30 +03:00
change = 1;
list_del(rh);
}
}
2002-03-15 00:17:30 +03:00
} while (change);
if (!list_empty(&dm->remove_list)) {
2005-06-01 20:51:55 +04:00
list_iterate_items(dll, &dm->remove_list)
log_error("Couldn't deactivate device %s", dll->dl->name);
return 0;
}
2002-02-25 17:46:57 +03:00
return 1;
}
/*
* The guts of the activation unit, this examines the device
* layers in the manager, and tries to issue the correct
* instructions to activate them in order.
*/
static int _execute(struct dev_manager *dm, struct volume_group *vg)
2002-02-25 17:46:57 +03:00
{
struct dm_hash_node *hn;
struct dev_layer *dl;
if (!_build_all_layers(dm, vg)) {
stack;
return 0;
}
/*
* Mark all layer that need reloading.
*/
_clear_marks(dm, RELOAD);
if (!_mark_lvs(dm, &dm->reload_list, RELOAD)) {
2002-02-25 17:46:57 +03:00
stack;
return 0;
}
/*
* Mark all layers that should be active.
*/
_clear_marks(dm, ACTIVE);
if (!_mark_lvs(dm, &dm->active_list, ACTIVE)) {
stack;
return 0;
}
/*
* Mark all layers that should be left suspended.
*/
_clear_marks(dm, SUSPENDED);
if (!_mark_lvs(dm, &dm->suspend_list, SUSPENDED)) {
stack;
return 0;
}
if (!_fill_in_remove_list(dm)) {
stack;
return 0;
}
if (!_populate_pre_suspend_lists(dm)) {
stack;
return 0;
}
/*
* Now only top level devices will be unmarked.
*/
dm_hash_iterate(hn, dm->layers) {
dl = dm_hash_get_data(dm->layers, hn);
if (_get_flag(dl, ACTIVE) && _get_flag(dl, TOPLEVEL))
if (!_create_rec(dm, dl)) {
stack;
return 0;
}
}
/* Resume devices */
dm_hash_iterate(hn, dm->layers) {
dl = dm_hash_get_data(dm->layers, hn);
if (!_resume_with_deps(dm, dl)) {
stack;
return 0;
}
}
if (!_remove_old_layers(dm)) {
stack;
return 0;
}
return 1;
}
/*
* ATM we decide which vg a layer belongs to by
* looking at the beginning of the device
* name.
*/
static int _belong_to_vg(const char *vgname, const char *name)
{
const char *v = vgname, *n = name;
while (*v) {
2002-03-26 16:41:37 +03:00
if ((*v != *n) || (*v == '-' && *(++n) != '-'))
return 0;
v++, n++;
}
2002-03-26 16:41:37 +03:00
if (*n == '-' && *(n + 1) != '-')
return 1;
else
return 0;
}
static int _add_existing_layer(struct dev_manager *dm, const char *name)
{
2002-03-15 00:17:30 +03:00
struct dev_layer *dl;
char *copy;
2002-03-15 00:17:30 +03:00
log_debug("Found existing layer '%s'", name);
if (!(copy = dm_pool_strdup(dm->mem, name))) {
stack;
return 0;
}
2002-03-19 02:25:50 +03:00
if (!(dl = _create_dev(dm, copy, ""))) {
stack;
return 0;
}
return 1;
}
static int _scan_existing_devices(struct dev_manager *dm)
{
int r = 0;
struct dm_names *names;
unsigned next = 0;
struct dm_task *dmt;
if (!(dmt = dm_task_create(DM_DEVICE_LIST)))
return 0;
if (!dm_task_run(dmt))
goto out;
if (!(names = dm_task_get_names(dmt)))
goto out;
r = 1;
if (!names->dev)
goto out;
do {
names = (void *) names + next;
if (_belong_to_vg(dm->vg_name, names->name) &&
!_add_existing_layer(dm, names->name)) {
stack;
r = 0;
break;
}
next = names->next;
} while (next);
out:
dm_task_destroy(dmt);
return r;
}
static int _add_lv(struct dm_pool *mem,
struct list *head, struct logical_volume *lv)
{
struct lv_list *lvl;
if (!(lvl = dm_pool_alloc(mem, sizeof(*lvl)))) {
stack;
return 0;
}
lvl->lv = lv;
list_add(head, &lvl->list);
return 1;
}
static int _add_lvs(struct dm_pool *mem,
struct list *head, struct logical_volume *origin)
{
2005-04-07 16:39:44 +04:00
struct lv_segment *snap_seg;
2005-06-01 20:51:55 +04:00
struct lv_list *lvl;
2005-06-01 20:51:55 +04:00
list_iterate_items(lvl, &origin->vg->lvs) {
if (lvl->lv->status & SNAPSHOT)
2005-04-07 16:39:44 +04:00
continue;
2005-06-01 20:51:55 +04:00
if ((snap_seg = find_cow(lvl->lv)) && snap_seg->origin == origin)
if (!_add_lv(mem, head, lvl->lv))
return 0;
}
return _add_lv(mem, head, origin);
}
static void _remove_lv(struct list *head, struct logical_volume *lv)
{
struct lv_list *lvl;
2005-06-01 20:51:55 +04:00
list_iterate_items(lvl, head) {
if (lvl->lv == lv) {
2005-06-01 20:51:55 +04:00
list_del(&lvl->list);
break;
}
}
}
2002-04-16 18:42:20 +04:00
static int _remove_lvs(struct dev_manager *dm, struct logical_volume *lv)
{
2002-04-16 18:42:20 +04:00
struct logical_volume *active, *old_origin;
2005-04-07 16:39:44 +04:00
struct lv_segment *snap_seg;
2005-06-01 20:51:55 +04:00
struct list *active_head;
struct lv_list *lvl;
2002-04-16 18:42:20 +04:00
active_head = &dm->active_list;
/* Remove any snapshots with given origin */
2005-06-01 20:51:55 +04:00
list_iterate_items(lvl, active_head) {
active = lvl->lv;
2005-04-07 16:39:44 +04:00
if ((snap_seg = find_cow(active)) && snap_seg->origin == lv) {
2002-04-16 18:42:20 +04:00
_remove_lv(active_head, active);
}
2002-04-16 18:42:20 +04:00
}
2002-04-16 18:42:20 +04:00
_remove_lv(active_head, lv);
2005-04-07 16:39:44 +04:00
if (!(snap_seg = find_cow(lv)))
2002-04-16 18:42:20 +04:00
return 1;
2005-04-07 16:39:44 +04:00
old_origin = snap_seg->origin;
2002-04-16 18:42:20 +04:00
/* Was this the last active snapshot with this origin? */
2005-06-01 20:51:55 +04:00
list_iterate_items(lvl, active_head) {
active = lvl->lv;
2005-04-07 16:39:44 +04:00
if ((snap_seg = find_cow(active)) &&
snap_seg->origin == old_origin) {
2002-04-16 18:42:20 +04:00
return 1;
}
}
2002-04-16 18:42:20 +04:00
return _add_lvs(dm->mem, &dm->reload_list, old_origin);
}
static int _remove_suspended_lvs(struct dev_manager *dm,
struct logical_volume *lv)
{
struct logical_volume *suspended;
2005-04-07 16:39:44 +04:00
struct lv_segment *snap_seg;
2005-06-01 20:51:55 +04:00
struct list *suspend_head;
struct lv_list *lvl;
suspend_head = &dm->suspend_list;
/* Remove from list any snapshots with given origin */
2005-06-01 20:51:55 +04:00
list_iterate_items(lvl, suspend_head) {
suspended = lvl->lv;
2005-04-07 16:39:44 +04:00
if ((snap_seg = find_cow(suspended)) &&
snap_seg->origin == lv) {
_remove_lv(suspend_head, suspended);
}
}
_remove_lv(suspend_head, lv);
return 1;
}
static int _targets_present(struct dev_manager *dm, struct list *lvs)
{
struct logical_volume *lv;
2005-06-01 20:51:55 +04:00
struct lv_list *lvl;
struct segment_type *segtype;
struct lv_segment *seg;
int snapshots = 0, mirrors = 0;
2005-06-01 20:51:55 +04:00
list_iterate_items(lvl, lvs) {
lv = lvl->lv;
if (!snapshots)
if (lv_is_cow(lv) || lv_is_origin(lv))
snapshots = 1;
if (!mirrors)
if (lv->status & PVMOVE)
mirrors = 1;
if (lv->status & VIRTUAL) {
2005-06-01 20:51:55 +04:00
list_iterate_items(seg, &lv->segments) {
if (seg->segtype->ops->target_present &&
2004-05-19 01:55:55 +04:00
!seg->segtype->ops->target_present()) {
log_error("Can't expand LV: %s target "
"support missing "
2004-05-19 01:55:55 +04:00
"from kernel?",
seg->segtype->name);
return 0;
}
}
}
}
if (mirrors) {
if (!(segtype = get_segtype_from_string(dm->cmd, "mirror"))) {
log_error("Can't expand LV: Mirror support "
"missing from tools?");
return 0;
}
if (!segtype->ops->target_present ||
!segtype->ops->target_present()) {
log_error("Can't expand LV: Mirror support missing "
"from kernel?");
return 0;
}
}
if (snapshots) {
if (!(segtype = get_segtype_from_string(dm->cmd, "snapshot"))) {
log_error("Can't expand LV: Snapshot support "
"missing from tools?");
return 0;
}
if (!segtype->ops->target_present ||
!segtype->ops->target_present()) {
log_error("Can't expand LV: Snapshot support missing "
"from kernel?");
return 0;
}
}
return 1;
}
2002-03-15 00:17:30 +03:00
static int _fill_in_active_list(struct dev_manager *dm, struct volume_group *vg)
{
2002-03-19 02:25:50 +03:00
char *dlid;
2005-06-01 20:51:55 +04:00
struct lv_list *lvl;
struct dev_layer *dl;
2005-06-01 20:51:55 +04:00
list_iterate_items(lvl, &vg->lvs) {
if (lvl->lv->status & SNAPSHOT)
2005-04-07 16:39:44 +04:00
continue;
2005-06-01 20:51:55 +04:00
if (!(dlid = _build_dlid(dm->mem, lvl->lv->lvid.s, NULL))) {
stack;
return 0;
}
dl = dm_hash_lookup(dm->layers, dlid);
dm_pool_free(dm->mem, dlid);
if (dl) {
2005-06-01 20:51:55 +04:00
log_debug("Found active lv %s%s", lvl->lv->name,
dl->info.suspended ? " (suspended)" : "");
2005-06-01 20:51:55 +04:00
if (!_add_lv(dm->mem, &dm->active_list, lvl->lv)) {
stack;
return 0;
}
if (dl->info.suspended) {
2005-06-01 20:51:55 +04:00
if (!_add_lv(dm->mem, &dm->suspend_list, lvl->lv)) {
stack;
return 0;
}
}
}
}
return 1;
}
static int _action(struct dev_manager *dm, struct logical_volume *lv,
action_t action)
{
if (!_scan_existing_devices(dm)) {
stack;
return 0;
}
if (!_fill_in_active_list(dm, lv->vg)) {
stack;
return 0;
}
if (action == ACTIVATE || action == DEACTIVATE)
/* Get into known state - remove from active list if present */
2002-04-16 18:42:20 +04:00
if (!_remove_lvs(dm, lv)) {
stack;
return 0;
}
2002-03-16 01:59:12 +03:00
if (action == ACTIVATE) {
/* Add to active & reload lists */
if (!_add_lvs(dm->mem, &dm->reload_list, lv) ||
!_add_lvs(dm->mem, &dm->active_list, lv)) {
2002-03-16 01:59:12 +03:00
stack;
return 0;
}
}
if (action == SUSPEND || action == RESUME || action == ACTIVATE)
/* Get into known state - remove from suspend list if present */
if (!_remove_suspended_lvs(dm, lv)) {
stack;
return 0;
}
if (action == SUSPEND) {
if (!_add_lvs(dm->mem, &dm->suspend_list, lv)) {
stack;
return 0;
}
}
if (!_targets_present(dm, &dm->active_list) ||
!_targets_present(dm, &dm->reload_list)) {
stack;
return 0;
}
if (!_execute(dm, lv->vg)) {
stack;
return 0;
}
return 1;
}
2002-03-16 01:59:12 +03:00
int dev_manager_activate(struct dev_manager *dm, struct logical_volume *lv)
{
return _action(dm, lv, ACTIVATE);
2002-03-16 01:59:12 +03:00
}
int dev_manager_suspend(struct dev_manager *dm, struct logical_volume *lv)
{
return _action(dm, lv, SUSPEND);
}
int dev_manager_lv_mknodes(const struct logical_volume *lv)
{
char *name;
if (!(name = build_dm_name(lv->vg->cmd->mem, lv->vg->name,
lv->name, NULL))) {
stack;
return 0;
}
return fs_add_lv(lv, name);
}
int dev_manager_lv_rmnodes(const struct logical_volume *lv)
{
return fs_del_lv(lv);
}
void dev_manager_exit(void)
{
dm_lib_exit();
}
2005-10-17 22:21:05 +04:00
/*
* New deactivation code
*/
static struct deptree *_create_partial_deptree(uint32_t major, uint32_t minor)
{
struct deptree *dtree;
if (!(dtree = dm_deptree_create())) {
log_error("partial deptree creation failed");
return NULL;
}
if (!dm_deptree_add_dev(dtree, major, minor)) {
log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to deptree",
major, minor);
dm_deptree_free(dtree);
return NULL;
}
return dtree;
}
static int _info_by_dev(uint32_t major, uint32_t minor, struct dm_info *info)
{
struct dm_task *dmt;
int r;
if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
log_error("_info_by_dev: dm_task creation failed");
return 0;
}
if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
log_error("_info_by_dev: Failed to set device number");
dm_task_destroy(dmt);
return 0;
}
if ((r = dm_task_run(dmt)))
r = dm_task_get_info(dmt, info);
dm_task_destroy(dmt);
return r;
}
static int _deactivate_node(const char *name, uint32_t major, uint32_t minor)
{
struct dm_task *dmt;
int r;
log_verbose("Removing %s (%" PRIu32 ":%" PRIu32 ")", name, major, minor);
if (!(dmt = dm_task_create(DM_DEVICE_REMOVE))) {
log_error("Deactivation dm_task creation failed for %s", name);
return 0;
}
if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) {
log_error("Failed to set device number for %s deactivation", name);
dm_task_destroy(dmt);
return 0;
}
if (!dm_task_no_open_count(dmt))
log_error("Failed to disable open_count");
r = dm_task_run(dmt);
dm_task_destroy(dmt);
return r;
}
static int _deactivate_children(struct deptree_node *dnode, const char *root_uuid)
{
void *handle = NULL;
struct deptree_node *child = dnode;
struct dm_info info;
const struct dm_info *dinfo;
const char *name;
const char *uuid;
2005-10-17 23:06:20 +04:00
while((child = dm_deptree_next_child(&handle, dnode, 0))) {
2005-10-17 22:21:05 +04:00
if (!(dinfo = dm_deptree_node_get_info(child))) {
stack;
continue;
}
2005-10-17 23:06:20 +04:00
if (!(name = dm_deptree_node_get_name(child))) {
2005-10-17 22:21:05 +04:00
stack;
continue;
}
2005-10-17 23:06:20 +04:00
if (!(uuid = dm_deptree_node_get_uuid(child))) {
2005-10-17 22:21:05 +04:00
stack;
continue;
}
/* Ignore if it doesn't belong to this VG */
if (strncmp(uuid, root_uuid, ID_LEN)) {
stack;
continue;
}
/* Refresh open_count */
if (!_info_by_dev(dinfo->major, dinfo->minor, &info) ||
!info.exists || info.open_count)
continue;
if (!_deactivate_node(name, info.major, info.minor)) {
log_error("Unable to deactivate %s (%" PRIu32
":%" PRIu32 ")", name, info.major,
info.minor);
continue;
}
if (dm_deptree_node_num_children(child, 0))
_deactivate_children(child, root_uuid);
}
return 1;
}
/*
* Deactivate LV and all devices it references that nothing else has open.
*/
int dev_manager_deactivate(struct dev_manager *dm, struct logical_volume *lv)
{
char *dlid, *name, *uuid;
struct dm_info info;
struct deptree *dtree;
struct deptree_node *dnode;
int r = 0;
if (!(name = build_dm_name(dm->mem, lv->vg->name, lv->name, NULL))) {
stack;
return 0;
}
if (!(dlid = _build_dlid(dm->mem, lv->lvid.s, ""))) {
stack;
return 0;
}
log_debug("Getting device info for %s [%s]", name, dlid);
2005-10-17 23:06:20 +04:00
if (!_info(name, dlid, 0, 1, &info, dm->mem, &uuid)) {
2005-10-17 22:21:05 +04:00
stack;
return 0;
}
if (!info.exists) {
/* FIXME Search for invisible devices left by previous incomplete removal */
log_debug("Ignoring deactivation request for inactive %s [%s]", name, dlid);
return 1;
}
if (!(dtree = _create_partial_deptree(info.major, info.minor))) {
stack;
return 0;
}
if (info.open_count) {
log_error("Unable to deactivate open (%" PRId32 ") device %s [%s]", info.open_count, name, dlid);
goto out;
}
if (!(dnode = dm_deptree_find_node(dtree, info.major, info.minor))) {
log_error("Lost dependency tree node for %s (%" PRIu32 ":%" PRIu32 ") [%s]",
name, info.major, info.minor, dlid);
goto out;
}
if (!_deactivate_node(name, info.major, info.minor)) {
stack;
goto out;
}
fs_del_lv(lv);
if (!_deactivate_children(dnode, uuid)) {
stack;
goto out;
}
r = 1;
out:
dm_deptree_free(dtree);
return r;
}