?? rrc_raid5.c
字號:
/* RAID5 driver for reconfiguration */#include "raidreconf.h"#include "rrc_common.h"/* private contextual data for this driver */typedef struct { int algorithm; /* which parity computation */ int raid_disks; /* # disks in RAID */ int data_disks; /* # data disks in RAID */ int chunk_size; /* bytes/chunk */ rrc_disk_t *disks; /* disk info array */ int *disk_id_idx; /* direct lookup of disk_id */ unsigned long tot_chunks; /* chunks in this RAID */ unsigned long cur_block; /* current block */ unsigned long tot_blocks; /* blocks to copy */ unsigned long blocks_per_chunk; /* should always be 1 */ unsigned long *disk_total_blocks; /* # blocks for each disk */} raid5_driver_private_t;/* function prototypes */static void *emalloc (int size);static unsigned long raid5_compute_sector (unsigned long, unsigned int *, unsigned int *, raid5_driver_private_t *);static unsigned long compute_blocknr (raid5_driver_private_t *, int, unsigned long);/* initialize this driver */static const char *raid5_initialize (void *context, md_cfg_entry_t *cfg, rrc_disk_t *cfgdisks, unsigned long *blocks){ int d; unsigned long max_disk_id = 0; raid5_driver_private_t *ctx = (raid5_driver_private_t *) context; if (cfg->array.param.level != 5) return ("Wrong level for RAID-5 Driver"); if (cfg->array.param.nr_disks < 3) return "Too few disks for RAID5"; ctx->raid_disks = cfg->array.param.nr_disks; ctx->data_disks = ctx->raid_disks-1; ctx->chunk_size = cfg->array.param.chunk_size; ctx->algorithm = cfg->array.param.layout; ctx->cur_block = 0; ctx->disks = cfgdisks; /* sum the total blocks in the array */ ctx->tot_chunks = 0; for (d = 0; d < ctx->raid_disks; ++d) ctx->tot_chunks += ctx->disks[d].chunks; /* RAID5 uses one disk's worth of data for parity, so subtract it */ ctx->tot_chunks -= ctx->disks[0].chunks; /* set up the disk_id -> disks[] mapping */ for (d = 0; d < ctx->raid_disks; ++d) if (ctx->disks[d].disk_id > max_disk_id) max_disk_id = ctx->disks[d].disk_id; if (max_disk_id > 0) { ctx->disk_id_idx = emalloc (sizeof (int) * (max_disk_id + 1)); for (d = 0; d < ctx->raid_disks; ++d) ctx->disk_id_idx[ctx->disks[d].disk_id] = d; } ctx->blocks_per_chunk = (cfg->array.param.chunk_size / MD_BLK_SIZ) / reconf_block_size; *blocks = ctx->blocks_per_chunk * ctx->tot_chunks; /* we only want to actually copy the MIN blocks of the arrays */ if (source_blocks && sink_blocks) ctx->tot_blocks = MIN (source_blocks, sink_blocks); else ctx->tot_blocks = *blocks; ctx->disk_total_blocks = emalloc (sizeof (unsigned long) * ctx->raid_disks); for (d = 0; d < ctx->raid_disks; ++d) ctx->disk_total_blocks[d] = cfgdisks[d].chunks * (ctx->chunk_size / MD_BLK_SIZ) / reconf_block_size; return NULL;}/* fill the wish_list with wishes for blocks we wish to write */static driver_status_traid5_request_blocks (void *context){ raid5_driver_private_t *ctx = (raid5_driver_private_t *) context; while (ctx->cur_block < ctx->tot_blocks) { if (!can_wish_again ()) return LDR_INCOMPLETE; insert_wish (ctx->cur_block); ++ctx->cur_block; } return LDR_DONE;}/* update the super-block, tell the kernel about any new disks, and** start the array*/static const char *raid5_update_super (void *context){ unsigned long d; int mdfile; int rc; mdu_param_t mdpar; printf ("Updating superblocks...\n"); if (analyze_sb (&ver, mkraid, new_md_cfg, 1, 0, 1)) { fprintf (stderr, "Error analyzing superblock.\n"); return "RAID-5 Superblock analysis error"; } /* Tell the kernel about this */ mdfile = open (new_md_cfg->md_name, O_RDONLY); if (mdfile < 0) { static char grump[1024]; snprintf (grump, sizeof (grump), "can't open %s (%s).", new_md_cfg->md_name, strerror (errno)); fprintf (stderr, "%s\n", grump); return (grump); } /* turn off the clean state bit, ** so the parity blocks will be recalculated ** "Unclean! Unclean!" */ new_md_cfg->array.param.state &= ~(1 << MD_SB_CLEAN); rc = ioctl (mdfile, SET_ARRAY_INFO, (unsigned long) &new_md_cfg->array.param); if (rc) { fprintf (stderr, "Failed setting array info for device %s\n", new_md_cfg->md_name); return "RAID-5 Superblock info error"; } printf ("Array is updated with kernel.\n"); for (d = 0; d != new_md_cfg->array.param.nr_disks; d++) { rc = ioctl (mdfile, ADD_NEW_DISK, (unsigned long) (new_md_cfg->array.disks + d)); if (rc) { static char grump[1024]; snprintf (grump, sizeof (grump), "Failed adding disk %lu to array.", d); fprintf (stderr, "%s\n", grump); return (grump); } } close (mdfile); printf ("Disks re-inserted in array... " "Hold on while starting the array...\n"); mdfile = open (new_md_cfg->md_name, O_RDWR); if (mdfile < 0) { static char grump[1024]; snprintf (grump, sizeof (grump), "can't open %s (%s).", new_md_cfg->md_name, strerror (errno)); fprintf (stderr, "%s\n", grump); return (grump); } /* Now run the array ! */ memset (&mdpar, 0, sizeof (mdpar)); mdpar.personality = RAID5; mdpar.chunk_size = new_md_cfg->array.param.chunk_size; rc = ioctl (mdfile, RUN_ARRAY, (unsigned long) &mdpar); close (mdfile); if (rc) { switch (errno) { case EBUSY: printf ("Array %s is already running\n", new_md_cfg->md_name); break; default: perror(new_md_cfg->md_name); } return "RAID-5 Superblock update error"; } return 0;}/* the following func is lifted (and edited) from the kernel sources *//* * Input: a 'big' sector number, * Output: index of the data and parity disk, and the sector # in them. */static unsigned longraid5_compute_sector (unsigned long r_sector, unsigned int *dd_idx, unsigned int *pd_idx, raid5_driver_private_t *ctx){ unsigned long stripe; unsigned long chunk_number; unsigned int chunk_offset;# ifdef TESTING# define PU(V) printf ("%s = %lu ", #V, V)# else# define PU(V)# endif /* TESTING */ /* First compute the information on this sector */ /* * Compute the chunk number and the sector offset inside the chunk */ chunk_number = r_sector / ctx->blocks_per_chunk; chunk_offset = r_sector % ctx->blocks_per_chunk; PU (r_sector); PU (chunk_number); PU (chunk_offset); /* * Compute the stripe number */ stripe = chunk_number / ctx->data_disks; PU (stripe); /* * Compute the data disk and parity disk indexes inside the stripe */ *dd_idx = chunk_number % ctx->data_disks; /* * Select the parity disk based on the user selected algorithm. *//*********************************************************** if (ctx->level == 4) *pd_idx = ctx->data_disks; else ***********************************************************/ switch (ctx->algorithm) { case RAID5_ALGORITHM_LEFT_ASYMMETRIC: *pd_idx = ctx->data_disks - stripe % ctx->raid_disks; if (*dd_idx >= *pd_idx) (*dd_idx)++; break; case RAID5_ALGORITHM_RIGHT_ASYMMETRIC: *pd_idx = stripe % ctx->raid_disks; if (*dd_idx >= *pd_idx) (*dd_idx)++; break; case RAID5_ALGORITHM_LEFT_SYMMETRIC: *pd_idx = ctx->data_disks - stripe % ctx->raid_disks; *dd_idx = (*pd_idx + 1 + *dd_idx) % ctx->raid_disks; break; case RAID5_ALGORITHM_RIGHT_SYMMETRIC: *pd_idx = stripe % ctx->raid_disks; *dd_idx = (*pd_idx + 1 + *dd_idx) % ctx->raid_disks; break; default: fprintf (stderr, "raid5: unsupported algorithm %d\n", ctx->algorithm); } PU (*dd_idx); PU (*pd_idx); /* * Finally, compute the new sector number */# ifdef TESTING printf ("old sector # = %lu, new sector # = %lu\n", r_sector, stripe * blocks_per_chunk + chunk_offset);# endif return stripe * ctx->blocks_per_chunk + chunk_offset;}static const char *raid5_map_global_to_local (void *context, unsigned long gblock, int *diskid, unsigned long *lblock){ raid5_driver_private_t *ctx = (raid5_driver_private_t *) context; int disk_idx, parity_id;# define ROUTINE "raid5_map_global_to_local" *lblock = raid5_compute_sector (gblock, &disk_idx, &parity_id, ctx); if (*lblock >= ctx->disk_total_blocks[disk_idx]) { fprintf (stderr, "\n%s: disk %d block out of range: %lu (%lu) gblock = %lu\n", ROUTINE, disk_idx, *lblock, ctx->disk_total_blocks[disk_idx], gblock); abort (); } *diskid = ctx->disks[disk_idx].disk_id; return (NULL);# undef ROUTINE}/* the following func is lifted from the kernel sources also */static unsigned longcompute_blocknr (raid5_driver_private_t *ctx, int i, unsigned long dblock){ int raid_disks = ctx->raid_disks, data_disks = raid_disks - 1; unsigned long new_sector = dblock; unsigned long stripe = new_sector / ctx->blocks_per_chunk; int chunk_offset = new_sector % ctx->blocks_per_chunk; int chunk_number, pd_idx; unsigned long r_sector, blocknr; /* compute the parity disk index "pd_idx" */ pd_idx = stripe % raid_disks; if (ctx->algorithm == RAID5_ALGORITHM_LEFT_ASYMMETRIC || ctx->algorithm == RAID5_ALGORITHM_LEFT_SYMMETRIC) pd_idx = data_disks - pd_idx; if (pd_idx == i) /* parity block, flag it */ return (ULONG_MAX); /* adjust the data disk index "i" */ switch (ctx->algorithm) { case RAID5_ALGORITHM_LEFT_ASYMMETRIC: case RAID5_ALGORITHM_RIGHT_ASYMMETRIC: if (i > pd_idx) i--; break; case RAID5_ALGORITHM_LEFT_SYMMETRIC: case RAID5_ALGORITHM_RIGHT_SYMMETRIC: if (i < pd_idx) i += raid_disks; i -= (pd_idx + 1); break; default: fprintf (stderr, "raid5: unsupported algorithm %d\n", ctx->algorithm); } /* calculate the new sector & block number */ chunk_number = stripe * data_disks + i; r_sector = chunk_number * ctx->blocks_per_chunk + chunk_offset; blocknr = r_sector; return blocknr;}/* return the global block number from a sink disk id and disk-block */static unsigned longraid5_map_local_to_global (void *context, int disk_idx, unsigned long dblock){ raid5_driver_private_t *ctx = (raid5_driver_private_t *) context; int disk_id = ctx->disks[ctx->disk_id_idx[disk_idx]].disk_id; return compute_blocknr (ctx, disk_id, dblock);}/* for each block above gblock, mark as free */static voidraid5_free_blocks_above_gblock (void *context, unsigned long gblock){ raid5_driver_private_t *ctx = (raid5_driver_private_t *) context; unsigned long block, dblock; unsigned long tot_blocks = ctx->tot_chunks * ctx->blocks_per_chunk; for (block = gblock; block < tot_blocks; ++block) { int diskid; raid5_map_global_to_local (ctx, block, &diskid, &dblock); unchecked_mark_disk_block_free (diskid, dblock); }}static voidraid5_unfree_all_blocks (void *context){ raid5_driver_private_t *ctx = (raid5_driver_private_t *) context; int disk; for (disk = 0; disk < ctx->raid_disks; ++disk) { unsigned long block; for (block = 0; block < ctx->disk_total_blocks[disk]; ++block) mark_disk_block_unfree (ctx->disks[disk].disk_id, block); }}level_driver_t *new_raid5_driver (void){ level_driver_t *drv = malloc (sizeof (level_driver_t)); raid5_driver_private_t *ctx = malloc (sizeof (raid5_driver_private_t)); if (!drv || !ctx) return (0); drv->initialize = raid5_initialize; drv->request_blocks = raid5_request_blocks; drv->update_super = raid5_update_super; drv->map_global_to_local = raid5_map_global_to_local; drv->map_local_to_global = raid5_map_local_to_global; drv->free_blocks_above_gblock = raid5_free_blocks_above_gblock; drv->unfree_all_blocks = raid5_unfree_all_blocks; drv->priv = ctx; ctx->algorithm = -1; ctx->raid_disks = 0; ctx->data_disks = 0; ctx->chunk_size = 0; ctx->disks = NULL; ctx->tot_chunks = 0; ctx->blocks_per_chunk = 0; ctx->disk_total_blocks = NULL; return drv;}/* die on error from malloc */static void *emalloc (int size){ void *ret; ret = malloc ((size_t) size); if (ret == NULL) { fprintf (stderr, "Out of memory: abort ()\n"); abort (); } return (ret);}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -