3
* (C) Copyright IBM Corp. 2003
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13
* the GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
38
* Function: safe to move
40
* Called to determine if it would be Ok to move
41
* the sepcified segment object.
43
static inline boolean safe_to_move(DISKSEG *seg)
45
LOGICALDISK *ld=get_logical_disk(seg);
46
DISK_PRIVATE_DATA *disk_pdata=NULL;
50
disk_pdata = get_gpt_disk_private_data(ld);
53
if ( i_can_modify(seg) == TRUE &&
54
seg->data_type == DATA_TYPE &&
55
(disk_pdata->flags & DISK_HAS_MOVE_PENDING) == 0 ) {
69
* Function: free a copy_job_t
71
* Called to free a copy_job_t
73
static inline void free_copy_job_t(copy_job_t *job)
83
* Function: alloc copy_job_t
85
* Called to allocate a copy_job_t
87
static inline copy_job_t * alloc_copy_job_t(void)
89
copy_job_t *job = calloc( 1, sizeof(copy_job_t) );
95
* Function: copy callback
97
* Called by gpt_move_start() to create an engine copy job that
98
* will be executed during a subsequent commit.
100
static int create_copy_job( DISKSEG *seg, // data segment to be moved
101
DISKSEG *trg, // move target segment
102
copy_job_t **copyjob ) // callers copy_job struct
105
DISK_PRIVATE_DATA *disk_pdata=NULL;
106
LOGICALDISK *ld=NULL;
107
copy_job_t *job=NULL;
113
ld = get_logical_disk(seg);
114
disk_pdata = get_gpt_disk_private_data(ld);
115
job = alloc_copy_job_t();
116
title = malloc(EVMS_NAME_SIZE*2+2);
120
sprintf(title, "Moving segment %s\n", seg->name);
122
job->description = "";
125
job->src.start = seg->start;
126
job->src.len = seg->size;
129
job->trg.start = trg->start;
130
job->trg.len = trg->size;
138
if (job) free_copy_job_t(job);
139
if (title) free(title);
149
* Function: create move target segment
151
* Called by our private function gpt_move_segment()
152
* to create a target storage_object_t that will
153
* be used to move the specified disk segment.
155
static int create_move_target( DISKSEG *seg, // source data segment
156
DISKSEG *freespace, // target freespace segment
157
DISKSEG **target, // where to return move target storage object
158
boolean testing ) // testing == TRUE if we just want to test
159
{ // if a move object could be created and
160
LOGICALDISK *ld=NULL; // dont actually want the object returned
162
SEG_PRIVATE_DATA *src_pdata=NULL;
163
SEG_PRIVATE_DATA *trg_pdata=NULL;
164
DISK_PRIVATE_DATA *disk_pdata=NULL;
173
ld = get_logical_disk(seg);
174
disk_pdata = get_gpt_disk_private_data(ld);
175
src_pdata = (SEG_PRIVATE_DATA *) seg->private_data;
178
if (ld && disk_pdata) {
182
trg = allocate_gpt_disk_segment( ld );
185
sprintf(trg->name, "%s_move_target", seg->name );
187
trg->data_type = DATA_TYPE;
188
trg->flags &= ~SOFLAG_DIRTY;
189
trg_pdata = (SEG_PRIVATE_DATA *) trg->private_data;
200
// calculate the starting lba
201
if (starts_on_cylinder_boundary(ld,seg->start)==TRUE) {
202
if (starts_on_cylinder_boundary(ld,freespace->start)==TRUE) {
203
start = freespace->start;
206
start = roundup_to_cylinder_boundary(ld,freespace->start)+1;
207
LOG_DEBUG("freespace doesn't start on cyl bdy ... rounding up\n");
211
sector_count_t sector_offset;
212
lba_t cylinder_start;
214
if (starts_on_cylinder_boundary(ld,freespace->start)==TRUE) {
215
cylinder_start = rounddown_to_cylinder_boundary(ld,seg->start);
216
sector_offset = seg->start - cylinder_start;
217
start = freespace->start + sector_offset;
220
start = freespace->start;
226
// now calculate the ending LBA ... always must end on a cyl boundary
227
end = trg->start + seg->size - 1;
228
if (ends_on_cylinder_boundary(ld,end)==FALSE) {
229
end = roundup_to_cylinder_boundary(ld,end);
232
if (end <= freespace->start+freespace->size-1 ) {
233
trg->size = end - trg->start + 1;
240
if (rc || testing==TRUE) {
241
if (trg) free_gpt_disk_segment(trg);
249
/* Function: validate move target
251
* Called to validate that the specified segment
252
* can be moved to the target freespace segment.
254
int gpt_validate_move_target( DISKSEG *src, DISKSEG *trg )
256
LOGICALDISK *ld=NULL;
258
SEG_PRIVATE_DATA *src_pdata=NULL;
259
SEG_PRIVATE_DATA *trg_pdata=NULL;
260
DISK_PRIVATE_DATA *disk_pdata=NULL;
267
if ( src->data_type == DATA_TYPE &&
268
trg->data_type == FREE_SPACE_TYPE ) {
270
ld = get_logical_disk(src);
271
disk_pdata = get_gpt_disk_private_data(ld);
272
src_pdata = (SEG_PRIVATE_DATA *) src->private_data;
273
trg_pdata = (SEG_PRIVATE_DATA *) trg->private_data;
275
if (ld && disk_pdata) {
281
rc = create_move_target( src, trg, &tseg, TRUE);
292
* Function: can move segment
294
* Check if the specified segment object can be moved
295
* elsewhere on the disk. Do so by ... checking each
296
* freespace segment we find ... stopping at the 1st
297
* freespace segment that could be used for the move.
299
int gpt_can_move_segment( DISKSEG *seg )
301
DISKSEG *freespace=NULL;
302
LOGICALDISK *ld=NULL;
308
if ( safe_to_move(seg) == TRUE ) {
310
ld = get_logical_disk(seg);
313
LIST_FOR_EACH( ld->parent_objects, iter, freespace ) {
315
if ( (freespace->data_type == FREE_SPACE_TYPE) &&
316
(freespace->size >= seg->size) ) {
318
rc = gpt_validate_move_target(seg,freespace);
336
* Function: gpt update segment info
338
* Called when finishing a move to update the partition
339
* table metadata info. This means we need to update
340
* the source segment info, i.e. where it is now located
343
static int gpt_update_segment_info( DISKSEG *src, DISKSEG *trg)
346
LOGICALDISK *ld=NULL;
352
ld = get_logical_disk(src);
354
// remove segments from the disks segment list cuz ...
355
// doing so will unregister the names and create freespace
356
// areas on the disk.
357
remove_gpt_segment_from_list( ld->parent_objects, src );
358
remove_gpt_segment_from_list( ld->parent_objects, trg );
360
// update source segment information
361
src->start = trg->start;
362
src->size = trg->size;
364
// reinsert the source segment
365
insert_gpt_segment_into_list( ld->parent_objects, src );
377
/* Function: GPT move segment
379
* Called as a seg mgr private function to move a data segment
380
* to a freespace area on the same disk.
382
int gpt_move_segment( DISKSEG *src, DISKSEG *freespace )
385
LOGICALDISK *ld=NULL;
387
SEG_PRIVATE_DATA *trg_pdata=NULL;
388
SEG_PRIVATE_DATA *src_pdata=NULL;
389
DISK_PRIVATE_DATA *disk_pdata=NULL;
390
copy_job_t *job=NULL;
394
if ( safe_to_move(src) == TRUE ) {
396
ld = get_logical_disk(src);
397
disk_pdata = get_gpt_disk_private_data(ld);
398
src_pdata = (SEG_PRIVATE_DATA *) src->private_data;
400
// create a move target segment
401
rc = create_move_target(src, freespace, &trg, FALSE);
402
if (rc==0) trg_pdata=(SEG_PRIVATE_DATA *)trg->private_data;
404
// create the engine copy job
406
rc = create_copy_job( src, trg, &job );
408
free_gpt_disk_segment( trg );
409
find_freespace_on_gpt_disk(ld);
413
// remove the freespace segment from the disk segment
414
// list and insert the new move-target data segment.
417
remove_gpt_segment_from_list( ld->parent_objects, freespace );
418
free_gpt_disk_segment( freespace );
420
insert_gpt_segment_into_ordered_list( ld->parent_objects, trg );
422
disk_pdata->flags |= DISK_HAS_MOVE_PENDING;
423
disk_pdata->copy_job = job;
424
src_pdata->move_target = trg;
425
src->flags |= SOFLAG_DIRTY;
426
find_freespace_on_gpt_disk(ld);
436
/* Function: do move segment finish
438
* Called to update partition tables after moving a segment.
440
static int do_move_segment_finish( DISKSEG *src,
446
LOGICALDISK *ld=NULL;
447
SEG_PRIVATE_DATA *src_pdata=NULL;
448
SEG_PRIVATE_DATA *trg_pdata=NULL;
449
DISK_PRIVATE_DATA *disk_pdata=NULL;
456
ld = get_logical_disk(src);
457
disk_pdata = get_gpt_disk_private_data(ld);
458
src_pdata = (SEG_PRIVATE_DATA *)src->private_data;
459
trg_pdata = (SEG_PRIVATE_DATA *)trg->private_data;
462
memcpy(&saved_seg, src, sizeof(DISKSEG));
463
rc = gpt_update_segment_info(src,trg);
465
rc = commit_guid_partition_tables( ld, trg, 1, FALSE );
467
rc = commit_guid_partition_tables( ld, trg, 2, FALSE );
471
memcpy(src, &saved_seg, sizeof(DISKSEG));
472
commit_guid_partition_tables( ld, src, 1, FALSE );
473
commit_guid_partition_tables( ld, src, 2, FALSE );
480
src_pdata->move_target = NULL;
481
free_gpt_disk_segment( trg );
482
find_freespace_on_gpt_disk(ld);
483
src->flags |= SOFLAG_NEEDS_ACTIVATE;
492
static int do_online_copy(storage_object_t *src, storage_object_t *tgt, copy_job_t *job)
495
dm_target_t *target=NULL;
499
rc = EngFncs->copy_setup(job);
501
LOG_SERIOUS("Error code %d when setting up a copy job: %s\n", rc, EngFncs->strerror(rc));
507
* Remap the source segment object to point to the mirror rather than the
510
rc = EngFncs->dm_get_targets(src, &target);
513
target->data.linear->major = job->mirror->dev_major;
514
target->data.linear->minor = job->mirror->dev_minor;
515
target->data.linear->start = 0;
517
rc = EngFncs->dm_load_targets(src, target);
519
EngFncs->dm_deallocate_targets(target);
522
EngFncs->dm_set_suspended_flag(TRUE);
524
rc = EngFncs->dm_suspend(src, TRUE);
526
rc = EngFncs->copy_start(job);
528
EngFncs->dm_clear_targets(src);
530
EngFncs->dm_suspend(src, FALSE);
533
LOG_SERIOUS("Error code %d when resuming object %s: %s\n",
534
rc, src->name, EngFncs->strerror(rc));
537
EngFncs->dm_set_suspended_flag(FALSE);
540
LOG_SERIOUS("Error code %d when loading the new mirror target for the object %s: %s\n",
541
rc, src->name, EngFncs->strerror(rc));
546
LOG_SERIOUS("Error code %d when getting the target for the object %s: %s\n",
547
rc, src->name, EngFncs->strerror(rc));
551
rc = EngFncs->copy_wait(job);
554
rc = do_move_segment_finish( src, tgt, rc, FALSE);
556
EngFncs->copy_cleanup(job);
562
static int do_offline_copy( storage_object_t *src, storage_object_t *tgt, copy_job_t *job )
568
rc = EngFncs->offline_copy( job);
570
rc = do_move_segment_finish( src, tgt, rc, TRUE);
578
* Function: gpt move commit
580
* Called during MOVE commit phase to commit a segment
581
* move to disk. This is done by calling the engine copy
582
* service and afterwards calling our own move finish
583
* routine that takes care of committing metadata changes
584
* or backing out changes.
586
int gpt_move_segment_commit( DISKSEG *src, DISKSEG *tgt, copy_job_t *job )
589
char * choices[] = {_("Yes"), _("No"), NULL};
591
logical_volume_t *vol=NULL;
595
if (EngFncs->can_online_copy()) {
596
rc = do_online_copy(src, tgt, job);
599
if (EngFncs->is_offline(src,&vol)==FALSE) {
600
LOG_DEBUG("Segment %s appears to be part of mounted volume %s\n", src->name, vol->name);
601
QUESTION( &answer, choices,
602
"Segment %s appears to be part of a mounted volume. The volume is %s.\n\n"
603
"Offline move can safely be used only with unmounted volumes. The volume may "
604
"become unuseable if you continue with the move.\n\n"
605
"Question: Would you like to continue and move segment %s?\n",
606
src->name, vol->name, src->name);
610
LOG_DEBUG("User is cancelling move\n");
614
rc = do_offline_copy(src, tgt, job);
618
src->flags &= ~SOFLAG_DIRTY; // always exit with dirty flag cleared