| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright 2023 Red Hat
- */
- #include <linux/atomic.h>
- #include <linux/bitops.h>
- #include <linux/completion.h>
- #include <linux/delay.h>
- #include <linux/device-mapper.h>
- #include <linux/err.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/spinlock.h>
- #include "admin-state.h"
- #include "block-map.h"
- #include "completion.h"
- #include "constants.h"
- #include "data-vio.h"
- #include "dedupe.h"
- #include "dump.h"
- #include "encodings.h"
- #include "errors.h"
- #include "flush.h"
- #include "io-submitter.h"
- #include "logger.h"
- #include "memory-alloc.h"
- #include "message-stats.h"
- #include "recovery-journal.h"
- #include "repair.h"
- #include "slab-depot.h"
- #include "status-codes.h"
- #include "string-utils.h"
- #include "thread-device.h"
- #include "thread-registry.h"
- #include "thread-utils.h"
- #include "types.h"
- #include "vdo.h"
- #include "vio.h"
- enum admin_phases {
- GROW_LOGICAL_PHASE_START,
- GROW_LOGICAL_PHASE_GROW_BLOCK_MAP,
- GROW_LOGICAL_PHASE_END,
- GROW_LOGICAL_PHASE_ERROR,
- GROW_PHYSICAL_PHASE_START,
- GROW_PHYSICAL_PHASE_COPY_SUMMARY,
- GROW_PHYSICAL_PHASE_UPDATE_COMPONENTS,
- GROW_PHYSICAL_PHASE_USE_NEW_SLABS,
- GROW_PHYSICAL_PHASE_END,
- GROW_PHYSICAL_PHASE_ERROR,
- LOAD_PHASE_START,
- LOAD_PHASE_LOAD_DEPOT,
- LOAD_PHASE_MAKE_DIRTY,
- LOAD_PHASE_PREPARE_TO_ALLOCATE,
- LOAD_PHASE_SCRUB_SLABS,
- LOAD_PHASE_DATA_REDUCTION,
- LOAD_PHASE_FINISHED,
- LOAD_PHASE_DRAIN_JOURNAL,
- LOAD_PHASE_WAIT_FOR_READ_ONLY,
- PRE_LOAD_PHASE_START,
- PRE_LOAD_PHASE_LOAD_COMPONENTS,
- PRE_LOAD_PHASE_END,
- PREPARE_GROW_PHYSICAL_PHASE_START,
- RESUME_PHASE_START,
- RESUME_PHASE_ALLOW_READ_ONLY_MODE,
- RESUME_PHASE_DEDUPE,
- RESUME_PHASE_DEPOT,
- RESUME_PHASE_JOURNAL,
- RESUME_PHASE_BLOCK_MAP,
- RESUME_PHASE_LOGICAL_ZONES,
- RESUME_PHASE_PACKER,
- RESUME_PHASE_FLUSHER,
- RESUME_PHASE_DATA_VIOS,
- RESUME_PHASE_END,
- SUSPEND_PHASE_START,
- SUSPEND_PHASE_PACKER,
- SUSPEND_PHASE_DATA_VIOS,
- SUSPEND_PHASE_DEDUPE,
- SUSPEND_PHASE_FLUSHES,
- SUSPEND_PHASE_LOGICAL_ZONES,
- SUSPEND_PHASE_BLOCK_MAP,
- SUSPEND_PHASE_JOURNAL,
- SUSPEND_PHASE_DEPOT,
- SUSPEND_PHASE_READ_ONLY_WAIT,
- SUSPEND_PHASE_WRITE_SUPER_BLOCK,
- SUSPEND_PHASE_END,
- };
- static const char * const ADMIN_PHASE_NAMES[] = {
- "GROW_LOGICAL_PHASE_START",
- "GROW_LOGICAL_PHASE_GROW_BLOCK_MAP",
- "GROW_LOGICAL_PHASE_END",
- "GROW_LOGICAL_PHASE_ERROR",
- "GROW_PHYSICAL_PHASE_START",
- "GROW_PHYSICAL_PHASE_COPY_SUMMARY",
- "GROW_PHYSICAL_PHASE_UPDATE_COMPONENTS",
- "GROW_PHYSICAL_PHASE_USE_NEW_SLABS",
- "GROW_PHYSICAL_PHASE_END",
- "GROW_PHYSICAL_PHASE_ERROR",
- "LOAD_PHASE_START",
- "LOAD_PHASE_LOAD_DEPOT",
- "LOAD_PHASE_MAKE_DIRTY",
- "LOAD_PHASE_PREPARE_TO_ALLOCATE",
- "LOAD_PHASE_SCRUB_SLABS",
- "LOAD_PHASE_DATA_REDUCTION",
- "LOAD_PHASE_FINISHED",
- "LOAD_PHASE_DRAIN_JOURNAL",
- "LOAD_PHASE_WAIT_FOR_READ_ONLY",
- "PRE_LOAD_PHASE_START",
- "PRE_LOAD_PHASE_LOAD_COMPONENTS",
- "PRE_LOAD_PHASE_END",
- "PREPARE_GROW_PHYSICAL_PHASE_START",
- "RESUME_PHASE_START",
- "RESUME_PHASE_ALLOW_READ_ONLY_MODE",
- "RESUME_PHASE_DEDUPE",
- "RESUME_PHASE_DEPOT",
- "RESUME_PHASE_JOURNAL",
- "RESUME_PHASE_BLOCK_MAP",
- "RESUME_PHASE_LOGICAL_ZONES",
- "RESUME_PHASE_PACKER",
- "RESUME_PHASE_FLUSHER",
- "RESUME_PHASE_DATA_VIOS",
- "RESUME_PHASE_END",
- "SUSPEND_PHASE_START",
- "SUSPEND_PHASE_PACKER",
- "SUSPEND_PHASE_DATA_VIOS",
- "SUSPEND_PHASE_DEDUPE",
- "SUSPEND_PHASE_FLUSHES",
- "SUSPEND_PHASE_LOGICAL_ZONES",
- "SUSPEND_PHASE_BLOCK_MAP",
- "SUSPEND_PHASE_JOURNAL",
- "SUSPEND_PHASE_DEPOT",
- "SUSPEND_PHASE_READ_ONLY_WAIT",
- "SUSPEND_PHASE_WRITE_SUPER_BLOCK",
- "SUSPEND_PHASE_END",
- };
- /* If we bump this, update the arrays below */
- #define TABLE_VERSION 4
- /* arrays for handling different table versions */
- static const u8 REQUIRED_ARGC[] = { 10, 12, 9, 7, 6 };
- /* pool name no longer used. only here for verification of older versions */
- static const u8 POOL_NAME_ARG_INDEX[] = { 8, 10, 8 };
- /*
- * Track in-use instance numbers using a flat bit array.
- *
- * O(n) run time isn't ideal, but if we have 1000 VDO devices in use simultaneously we still only
- * need to scan 16 words, so it's not likely to be a big deal compared to other resource usage.
- */
- /*
- * This minimum size for the bit array creates a numbering space of 0-999, which allows
- * successive starts of the same volume to have different instance numbers in any
- * reasonably-sized test. Changing instances on restart allows vdoMonReport to detect that
- * the ephemeral stats have reset to zero.
- */
- #define BIT_COUNT_MINIMUM 1000
- /* Grow the bit array by this many bits when needed */
- #define BIT_COUNT_INCREMENT 100
- struct instance_tracker {
- unsigned int bit_count;
- unsigned long *words;
- unsigned int count;
- unsigned int next;
- };
- static DEFINE_MUTEX(instances_lock);
- static struct instance_tracker instances;
- /**
- * free_device_config() - Free a device config created by parse_device_config().
- * @config: The config to free.
- */
- static void free_device_config(struct device_config *config)
- {
- if (config == NULL)
- return;
- if (config->owned_device != NULL)
- dm_put_device(config->owning_target, config->owned_device);
- vdo_free(config->parent_device_name);
- vdo_free(config->original_string);
- /* Reduce the chance a use-after-free (as in BZ 1669960) happens to work. */
- memset(config, 0, sizeof(*config));
- vdo_free(config);
- }
- /**
- * get_version_number() - Decide the version number from argv.
- *
- * @argc: The number of table values.
- * @argv: The array of table values.
- * @error_ptr: A pointer to return a error string in.
- * @version_ptr: A pointer to return the version.
- *
- * Return: VDO_SUCCESS or an error code.
- */
- static int get_version_number(int argc, char **argv, char **error_ptr,
- unsigned int *version_ptr)
- {
- /* version, if it exists, is in a form of V<n> */
- if (sscanf(argv[0], "V%u", version_ptr) == 1) {
- if (*version_ptr < 1 || *version_ptr > TABLE_VERSION) {
- *error_ptr = "Unknown version number detected";
- return VDO_BAD_CONFIGURATION;
- }
- } else {
- /* V0 actually has no version number in the table string */
- *version_ptr = 0;
- }
- /*
- * V0 and V1 have no optional parameters. There will always be a parameter for thread
- * config, even if it's a "." to show it's an empty list.
- */
- if (*version_ptr <= 1) {
- if (argc != REQUIRED_ARGC[*version_ptr]) {
- *error_ptr = "Incorrect number of arguments for version";
- return VDO_BAD_CONFIGURATION;
- }
- } else if (argc < REQUIRED_ARGC[*version_ptr]) {
- *error_ptr = "Incorrect number of arguments for version";
- return VDO_BAD_CONFIGURATION;
- }
- if (*version_ptr != TABLE_VERSION) {
- vdo_log_warning("Detected version mismatch between kernel module and tools kernel: %d, tool: %d",
- TABLE_VERSION, *version_ptr);
- vdo_log_warning("Please consider upgrading management tools to match kernel.");
- }
- return VDO_SUCCESS;
- }
- /* Free a list of non-NULL string pointers, and then the list itself. */
- static void free_string_array(char **string_array)
- {
- unsigned int offset;
- for (offset = 0; string_array[offset] != NULL; offset++)
- vdo_free(string_array[offset]);
- vdo_free(string_array);
- }
- /*
- * Split the input string into substrings, separated at occurrences of the indicated character,
- * returning a null-terminated list of string pointers.
- *
- * The string pointers and the pointer array itself should both be freed with vdo_free() when no
- * longer needed. This can be done with vdo_free_string_array (below) if the pointers in the array
- * are not changed. Since the array and copied strings are allocated by this function, it may only
- * be used in contexts where allocation is permitted.
- *
- * Empty substrings are not ignored; that is, returned substrings may be empty strings if the
- * separator occurs twice in a row.
- */
- static int split_string(const char *string, char separator, char ***substring_array_ptr)
- {
- unsigned int current_substring = 0, substring_count = 1;
- const char *s;
- char **substrings;
- int result;
- ptrdiff_t length;
- for (s = string; *s != 0; s++) {
- if (*s == separator)
- substring_count++;
- }
- result = vdo_allocate(substring_count + 1, char *, "string-splitting array",
- &substrings);
- if (result != VDO_SUCCESS)
- return result;
- for (s = string; *s != 0; s++) {
- if (*s == separator) {
- ptrdiff_t length = s - string;
- result = vdo_allocate(length + 1, char, "split string",
- &substrings[current_substring]);
- if (result != VDO_SUCCESS) {
- free_string_array(substrings);
- return result;
- }
- /*
- * Trailing NUL is already in place after allocation; deal with the zero or
- * more non-NUL bytes in the string.
- */
- if (length > 0)
- memcpy(substrings[current_substring], string, length);
- string = s + 1;
- current_substring++;
- BUG_ON(current_substring >= substring_count);
- }
- }
- /* Process final string, with no trailing separator. */
- BUG_ON(current_substring != (substring_count - 1));
- length = strlen(string);
- result = vdo_allocate(length + 1, char, "split string",
- &substrings[current_substring]);
- if (result != VDO_SUCCESS) {
- free_string_array(substrings);
- return result;
- }
- memcpy(substrings[current_substring], string, length);
- current_substring++;
- /* substrings[current_substring] is NULL already */
- *substring_array_ptr = substrings;
- return VDO_SUCCESS;
- }
- /*
- * Join the input substrings into one string, joined with the indicated character, returning a
- * string. array_length is a bound on the number of valid elements in substring_array, in case it
- * is not NULL-terminated.
- */
- static int join_strings(char **substring_array, size_t array_length, char separator,
- char **string_ptr)
- {
- size_t string_length = 0;
- size_t i;
- int result;
- char *output, *current_position;
- for (i = 0; (i < array_length) && (substring_array[i] != NULL); i++)
- string_length += strlen(substring_array[i]) + 1;
- result = vdo_allocate(string_length, char, __func__, &output);
- if (result != VDO_SUCCESS)
- return result;
- current_position = &output[0];
- for (i = 0; (i < array_length) && (substring_array[i] != NULL); i++) {
- current_position = vdo_append_to_buffer(current_position,
- output + string_length, "%s",
- substring_array[i]);
- *current_position = separator;
- current_position++;
- }
- /* We output one too many separators; replace the last with a zero byte. */
- if (current_position != output)
- *(current_position - 1) = '\0';
- *string_ptr = output;
- return VDO_SUCCESS;
- }
- /**
- * parse_bool() - Parse a two-valued option into a bool.
- * @bool_str: The string value to convert to a bool.
- * @true_str: The string value which should be converted to true.
- * @false_str: The string value which should be converted to false.
- * @bool_ptr: A pointer to return the bool value in.
- *
- * Return: VDO_SUCCESS or an error if bool_str is neither true_str nor false_str.
- */
- static inline int __must_check parse_bool(const char *bool_str, const char *true_str,
- const char *false_str, bool *bool_ptr)
- {
- bool value = false;
- if (strcmp(bool_str, true_str) == 0)
- value = true;
- else if (strcmp(bool_str, false_str) == 0)
- value = false;
- else
- return VDO_BAD_CONFIGURATION;
- *bool_ptr = value;
- return VDO_SUCCESS;
- }
- /**
- * process_one_thread_config_spec() - Process one component of a thread parameter configuration
- * string and update the configuration data structure.
- * @thread_param_type: The type of thread specified.
- * @count: The thread count requested.
- * @config: The configuration data structure to update.
- *
- * If the thread count requested is invalid, a message is logged and -EINVAL returned. If the
- * thread name is unknown, a message is logged but no error is returned.
- *
- * Return: VDO_SUCCESS or -EINVAL
- */
- static int process_one_thread_config_spec(const char *thread_param_type,
- unsigned int count,
- struct thread_count_config *config)
- {
- /* Handle limited thread parameters */
- if (strcmp(thread_param_type, "bioRotationInterval") == 0) {
- if (count == 0) {
- vdo_log_error("thread config string error: 'bioRotationInterval' of at least 1 is required");
- return -EINVAL;
- } else if (count > VDO_BIO_ROTATION_INTERVAL_LIMIT) {
- vdo_log_error("thread config string error: 'bioRotationInterval' cannot be higher than %d",
- VDO_BIO_ROTATION_INTERVAL_LIMIT);
- return -EINVAL;
- }
- config->bio_rotation_interval = count;
- return VDO_SUCCESS;
- }
- if (strcmp(thread_param_type, "logical") == 0) {
- if (count > MAX_VDO_LOGICAL_ZONES) {
- vdo_log_error("thread config string error: at most %d 'logical' threads are allowed",
- MAX_VDO_LOGICAL_ZONES);
- return -EINVAL;
- }
- config->logical_zones = count;
- return VDO_SUCCESS;
- }
- if (strcmp(thread_param_type, "physical") == 0) {
- if (count > MAX_VDO_PHYSICAL_ZONES) {
- vdo_log_error("thread config string error: at most %d 'physical' threads are allowed",
- MAX_VDO_PHYSICAL_ZONES);
- return -EINVAL;
- }
- config->physical_zones = count;
- return VDO_SUCCESS;
- }
- /* Handle other thread count parameters */
- if (count > MAXIMUM_VDO_THREADS) {
- vdo_log_error("thread config string error: at most %d '%s' threads are allowed",
- MAXIMUM_VDO_THREADS, thread_param_type);
- return -EINVAL;
- }
- if (strcmp(thread_param_type, "hash") == 0) {
- config->hash_zones = count;
- return VDO_SUCCESS;
- }
- if (strcmp(thread_param_type, "cpu") == 0) {
- if (count == 0) {
- vdo_log_error("thread config string error: at least one 'cpu' thread required");
- return -EINVAL;
- }
- config->cpu_threads = count;
- return VDO_SUCCESS;
- }
- if (strcmp(thread_param_type, "ack") == 0) {
- config->bio_ack_threads = count;
- return VDO_SUCCESS;
- }
- if (strcmp(thread_param_type, "bio") == 0) {
- if (count == 0) {
- vdo_log_error("thread config string error: at least one 'bio' thread required");
- return -EINVAL;
- }
- config->bio_threads = count;
- return VDO_SUCCESS;
- }
- /*
- * Don't fail, just log. This will handle version mismatches between user mode tools and
- * kernel.
- */
- vdo_log_info("unknown thread parameter type \"%s\"", thread_param_type);
- return VDO_SUCCESS;
- }
- /**
- * parse_one_thread_config_spec() - Parse one component of a thread parameter configuration string
- * and update the configuration data structure.
- * @spec: The thread parameter specification string.
- * @config: The configuration data to be updated.
- */
- static int parse_one_thread_config_spec(const char *spec,
- struct thread_count_config *config)
- {
- unsigned int count;
- char **fields;
- int result;
- result = split_string(spec, '=', &fields);
- if (result != VDO_SUCCESS)
- return result;
- if ((fields[0] == NULL) || (fields[1] == NULL) || (fields[2] != NULL)) {
- vdo_log_error("thread config string error: expected thread parameter assignment, saw \"%s\"",
- spec);
- free_string_array(fields);
- return -EINVAL;
- }
- result = kstrtouint(fields[1], 10, &count);
- if (result) {
- vdo_log_error("thread config string error: integer value needed, found \"%s\"",
- fields[1]);
- free_string_array(fields);
- return result;
- }
- result = process_one_thread_config_spec(fields[0], count, config);
- free_string_array(fields);
- return result;
- }
- /**
- * parse_thread_config_string() - Parse the configuration string passed and update the specified
- * counts and other parameters of various types of threads to be
- * created.
- * @string: Thread parameter configuration string.
- * @config: The thread configuration data to update.
- *
- * The configuration string should contain one or more comma-separated specs of the form
- * "typename=number"; the supported type names are "cpu", "ack", "bio", "bioRotationInterval",
- * "logical", "physical", and "hash".
- *
- * If an error occurs during parsing of a single key/value pair, we deem it serious enough to stop
- * further parsing.
- *
- * This function can't set the "reason" value the caller wants to pass back, because we'd want to
- * format it to say which field was invalid, and we can't allocate the "reason" strings
- * dynamically. So if an error occurs, we'll log the details and pass back an error.
- *
- * Return: VDO_SUCCESS or -EINVAL or -ENOMEM
- */
- static int parse_thread_config_string(const char *string,
- struct thread_count_config *config)
- {
- int result = VDO_SUCCESS;
- char **specs;
- if (strcmp(".", string) != 0) {
- unsigned int i;
- result = split_string(string, ',', &specs);
- if (result != VDO_SUCCESS)
- return result;
- for (i = 0; specs[i] != NULL; i++) {
- result = parse_one_thread_config_spec(specs[i], config);
- if (result != VDO_SUCCESS)
- break;
- }
- free_string_array(specs);
- }
- return result;
- }
- /**
- * process_one_key_value_pair() - Process one component of an optional parameter string and update
- * the configuration data structure.
- * @key: The optional parameter key name.
- * @value: The optional parameter value.
- * @config: The configuration data structure to update.
- *
- * If the value requested is invalid, a message is logged and -EINVAL returned. If the key is
- * unknown, a message is logged but no error is returned.
- *
- * Return: VDO_SUCCESS or -EINVAL
- */
- static int process_one_key_value_pair(const char *key, unsigned int value,
- struct device_config *config)
- {
- /* Non thread optional parameters */
- if (strcmp(key, "maxDiscard") == 0) {
- if (value == 0) {
- vdo_log_error("optional parameter error: at least one max discard block required");
- return -EINVAL;
- }
- /* Max discard sectors in blkdev_issue_discard is UINT_MAX >> 9 */
- if (value > (UINT_MAX / VDO_BLOCK_SIZE)) {
- vdo_log_error("optional parameter error: at most %d max discard blocks are allowed",
- UINT_MAX / VDO_BLOCK_SIZE);
- return -EINVAL;
- }
- config->max_discard_blocks = value;
- return VDO_SUCCESS;
- }
- /* Handles unknown key names */
- return process_one_thread_config_spec(key, value, &config->thread_counts);
- }
- /**
- * parse_one_key_value_pair() - Parse one key/value pair and update the configuration data
- * structure.
- * @key: The optional key name.
- * @value: The optional value.
- * @config: The configuration data to be updated.
- *
- * Return: VDO_SUCCESS or error.
- */
- static int parse_one_key_value_pair(const char *key, const char *value,
- struct device_config *config)
- {
- unsigned int count;
- int result;
- if (strcmp(key, "deduplication") == 0)
- return parse_bool(value, "on", "off", &config->deduplication);
- if (strcmp(key, "compression") == 0)
- return parse_bool(value, "on", "off", &config->compression);
- /* The remaining arguments must have integral values. */
- result = kstrtouint(value, 10, &count);
- if (result) {
- vdo_log_error("optional config string error: integer value needed, found \"%s\"",
- value);
- return result;
- }
- return process_one_key_value_pair(key, count, config);
- }
- /**
- * parse_key_value_pairs() - Parse all key/value pairs from a list of arguments.
- * @argc: The total number of arguments in list.
- * @argv: The list of key/value pairs.
- * @config: The device configuration data to update.
- *
- * If an error occurs during parsing of a single key/value pair, we deem it serious enough to stop
- * further parsing.
- *
- * This function can't set the "reason" value the caller wants to pass back, because we'd want to
- * format it to say which field was invalid, and we can't allocate the "reason" strings
- * dynamically. So if an error occurs, we'll log the details and return the error.
- *
- * Return: VDO_SUCCESS or error
- */
- static int parse_key_value_pairs(int argc, char **argv, struct device_config *config)
- {
- int result = VDO_SUCCESS;
- while (argc) {
- result = parse_one_key_value_pair(argv[0], argv[1], config);
- if (result != VDO_SUCCESS)
- break;
- argc -= 2;
- argv += 2;
- }
- return result;
- }
- /**
- * parse_optional_arguments() - Parse the configuration string passed in for optional arguments.
- * @arg_set: The structure holding the arguments to parse.
- * @error_ptr: Pointer to a buffer to hold the error string.
- * @config: Pointer to device configuration data to update.
- *
- * For V0/V1 configurations, there will only be one optional parameter; the thread configuration.
- * The configuration string should contain one or more comma-separated specs of the form
- * "typename=number"; the supported type names are "cpu", "ack", "bio", "bioRotationInterval",
- * "logical", "physical", and "hash".
- *
- * For V2 configurations and beyond, there could be any number of arguments. They should contain
- * one or more key/value pairs separated by a space.
- *
- * Return: VDO_SUCCESS or error
- */
- static int parse_optional_arguments(struct dm_arg_set *arg_set, char **error_ptr,
- struct device_config *config)
- {
- int result = VDO_SUCCESS;
- if (config->version == 0 || config->version == 1) {
- result = parse_thread_config_string(arg_set->argv[0],
- &config->thread_counts);
- if (result != VDO_SUCCESS) {
- *error_ptr = "Invalid thread-count configuration";
- return VDO_BAD_CONFIGURATION;
- }
- } else {
- if ((arg_set->argc % 2) != 0) {
- *error_ptr = "Odd number of optional arguments given but they should be <key> <value> pairs";
- return VDO_BAD_CONFIGURATION;
- }
- result = parse_key_value_pairs(arg_set->argc, arg_set->argv, config);
- if (result != VDO_SUCCESS) {
- *error_ptr = "Invalid optional argument configuration";
- return VDO_BAD_CONFIGURATION;
- }
- }
- return result;
- }
- /**
- * handle_parse_error() - Handle a parsing error.
- * @config: The config to free.
- * @error_ptr: A place to store a constant string about the error.
- * @error_str: A constant string to store in error_ptr.
- */
- static void handle_parse_error(struct device_config *config, char **error_ptr,
- char *error_str)
- {
- free_device_config(config);
- *error_ptr = error_str;
- }
- /**
- * parse_device_config() - Convert the dmsetup table into a struct device_config.
- * @argc: The number of table values.
- * @argv: The array of table values.
- * @ti: The target structure for this table.
- * @config_ptr: A pointer to return the allocated config.
- *
- * Return: VDO_SUCCESS or an error code.
- */
- static int parse_device_config(int argc, char **argv, struct dm_target *ti,
- struct device_config **config_ptr)
- {
- bool enable_512e;
- size_t logical_bytes = to_bytes(ti->len);
- struct dm_arg_set arg_set;
- char **error_ptr = &ti->error;
- struct device_config *config = NULL;
- int result;
- if ((logical_bytes % VDO_BLOCK_SIZE) != 0) {
- handle_parse_error(config, error_ptr,
- "Logical size must be a multiple of 4096");
- return VDO_BAD_CONFIGURATION;
- }
- if (argc == 0) {
- handle_parse_error(config, error_ptr, "Incorrect number of arguments");
- return VDO_BAD_CONFIGURATION;
- }
- result = vdo_allocate(1, struct device_config, "device_config", &config);
- if (result != VDO_SUCCESS) {
- handle_parse_error(config, error_ptr,
- "Could not allocate config structure");
- return VDO_BAD_CONFIGURATION;
- }
- config->owning_target = ti;
- config->logical_blocks = logical_bytes / VDO_BLOCK_SIZE;
- INIT_LIST_HEAD(&config->config_list);
- /* Save the original string. */
- result = join_strings(argv, argc, ' ', &config->original_string);
- if (result != VDO_SUCCESS) {
- handle_parse_error(config, error_ptr, "Could not populate string");
- return VDO_BAD_CONFIGURATION;
- }
- vdo_log_info("table line: %s", config->original_string);
- config->thread_counts = (struct thread_count_config) {
- .bio_ack_threads = 1,
- .bio_threads = DEFAULT_VDO_BIO_SUBMIT_QUEUE_COUNT,
- .bio_rotation_interval = DEFAULT_VDO_BIO_SUBMIT_QUEUE_ROTATE_INTERVAL,
- .cpu_threads = 1,
- .logical_zones = 0,
- .physical_zones = 0,
- .hash_zones = 0,
- };
- config->max_discard_blocks = 1;
- config->deduplication = true;
- config->compression = false;
- arg_set.argc = argc;
- arg_set.argv = argv;
- result = get_version_number(argc, argv, error_ptr, &config->version);
- if (result != VDO_SUCCESS) {
- /* get_version_number sets error_ptr itself. */
- handle_parse_error(config, error_ptr, *error_ptr);
- return result;
- }
- /* Move the arg pointer forward only if the argument was there. */
- if (config->version >= 1)
- dm_shift_arg(&arg_set);
- result = vdo_duplicate_string(dm_shift_arg(&arg_set), "parent device name",
- &config->parent_device_name);
- if (result != VDO_SUCCESS) {
- handle_parse_error(config, error_ptr,
- "Could not copy parent device name");
- return VDO_BAD_CONFIGURATION;
- }
- /* Get the physical blocks, if known. */
- if (config->version >= 1) {
- result = kstrtoull(dm_shift_arg(&arg_set), 10, &config->physical_blocks);
- if (result != VDO_SUCCESS) {
- handle_parse_error(config, error_ptr,
- "Invalid physical block count");
- return VDO_BAD_CONFIGURATION;
- }
- }
- /* Get the logical block size and validate */
- result = parse_bool(dm_shift_arg(&arg_set), "512", "4096", &enable_512e);
- if (result != VDO_SUCCESS) {
- handle_parse_error(config, error_ptr, "Invalid logical block size");
- return VDO_BAD_CONFIGURATION;
- }
- config->logical_block_size = (enable_512e ? 512 : 4096);
- /* Skip past the two no longer used read cache options. */
- if (config->version <= 1)
- dm_consume_args(&arg_set, 2);
- /* Get the page cache size. */
- result = kstrtouint(dm_shift_arg(&arg_set), 10, &config->cache_size);
- if (result != VDO_SUCCESS) {
- handle_parse_error(config, error_ptr,
- "Invalid block map page cache size");
- return VDO_BAD_CONFIGURATION;
- }
- /* Get the block map era length. */
- result = kstrtouint(dm_shift_arg(&arg_set), 10, &config->block_map_maximum_age);
- if (result != VDO_SUCCESS) {
- handle_parse_error(config, error_ptr, "Invalid block map maximum age");
- return VDO_BAD_CONFIGURATION;
- }
- /* Skip past the no longer used MD RAID5 optimization mode */
- if (config->version <= 2)
- dm_consume_args(&arg_set, 1);
- /* Skip past the no longer used write policy setting */
- if (config->version <= 3)
- dm_consume_args(&arg_set, 1);
- /* Skip past the no longer used pool name for older table lines */
- if (config->version <= 2) {
- /*
- * Make sure the enum to get the pool name from argv directly is still in sync with
- * the parsing of the table line.
- */
- if (&arg_set.argv[0] != &argv[POOL_NAME_ARG_INDEX[config->version]]) {
- handle_parse_error(config, error_ptr,
- "Pool name not in expected location");
- return VDO_BAD_CONFIGURATION;
- }
- dm_shift_arg(&arg_set);
- }
- /* Get the optional arguments and validate. */
- result = parse_optional_arguments(&arg_set, error_ptr, config);
- if (result != VDO_SUCCESS) {
- /* parse_optional_arguments sets error_ptr itself. */
- handle_parse_error(config, error_ptr, *error_ptr);
- return result;
- }
- /*
- * Logical, physical, and hash zone counts can all be zero; then we get one thread doing
- * everything, our older configuration. If any zone count is non-zero, the others must be
- * as well.
- */
- if (((config->thread_counts.logical_zones == 0) !=
- (config->thread_counts.physical_zones == 0)) ||
- ((config->thread_counts.physical_zones == 0) !=
- (config->thread_counts.hash_zones == 0))) {
- handle_parse_error(config, error_ptr,
- "Logical, physical, and hash zones counts must all be zero or all non-zero");
- return VDO_BAD_CONFIGURATION;
- }
- if (config->cache_size <
- (2 * MAXIMUM_VDO_USER_VIOS * config->thread_counts.logical_zones)) {
- handle_parse_error(config, error_ptr,
- "Insufficient block map cache for logical zones");
- return VDO_BAD_CONFIGURATION;
- }
- result = dm_get_device(ti, config->parent_device_name,
- dm_table_get_mode(ti->table), &config->owned_device);
- if (result != 0) {
- vdo_log_error("couldn't open device \"%s\": error %d",
- config->parent_device_name, result);
- handle_parse_error(config, error_ptr, "Unable to open storage device");
- return VDO_BAD_CONFIGURATION;
- }
- if (config->version == 0) {
- u64 device_size = bdev_nr_bytes(config->owned_device->bdev);
- config->physical_blocks = device_size / VDO_BLOCK_SIZE;
- }
- *config_ptr = config;
- return result;
- }
- static struct vdo *get_vdo_for_target(struct dm_target *ti)
- {
- return ((struct device_config *) ti->private)->vdo;
- }
- static int vdo_map_bio(struct dm_target *ti, struct bio *bio)
- {
- struct vdo *vdo = get_vdo_for_target(ti);
- struct vdo_work_queue *current_work_queue;
- const struct admin_state_code *code = vdo_get_admin_state_code(&vdo->admin.state);
- VDO_ASSERT_LOG_ONLY(code->normal, "vdo should not receive bios while in state %s",
- code->name);
- /* Count all incoming bios. */
- vdo_count_bios(&vdo->stats.bios_in, bio);
- /* Handle empty bios. Empty flush bios are not associated with a vio. */
- if ((bio_op(bio) == REQ_OP_FLUSH) || ((bio->bi_opf & REQ_PREFLUSH) != 0)) {
- vdo_launch_flush(vdo, bio);
- return DM_MAPIO_SUBMITTED;
- }
- /* This could deadlock, */
- current_work_queue = vdo_get_current_work_queue();
- BUG_ON((current_work_queue != NULL) &&
- (vdo == vdo_get_work_queue_owner(current_work_queue)->vdo));
- vdo_launch_bio(vdo->data_vio_pool, bio);
- return DM_MAPIO_SUBMITTED;
- }
- static void vdo_io_hints(struct dm_target *ti, struct queue_limits *limits)
- {
- struct vdo *vdo = get_vdo_for_target(ti);
- limits->logical_block_size = vdo->device_config->logical_block_size;
- limits->physical_block_size = VDO_BLOCK_SIZE;
- /* The minimum io size for random io */
- limits->io_min = VDO_BLOCK_SIZE;
- /* The optimal io size for streamed/sequential io */
- limits->io_opt = VDO_BLOCK_SIZE;
- /*
- * Sets the maximum discard size that will be passed into VDO. This value comes from a
- * table line value passed in during dmsetup create.
- *
- * The value 1024 is the largest usable value on HD systems. A 2048 sector discard on a
- * busy HD system takes 31 seconds. We should use a value no higher than 1024, which takes
- * 15 to 16 seconds on a busy HD system. However, using large values results in 120 second
- * blocked task warnings in kernel logs. In order to avoid these warnings, we choose to
- * use the smallest reasonable value.
- *
- * The value is used by dm-thin to determine whether to pass down discards. The block layer
- * splits large discards on this boundary when this is set.
- */
- limits->max_hw_discard_sectors =
- (vdo->device_config->max_discard_blocks * VDO_SECTORS_PER_BLOCK);
- /*
- * Force discards to not begin or end with a partial block by stating the granularity is
- * 4k.
- */
- limits->discard_granularity = VDO_BLOCK_SIZE;
- }
- static int vdo_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn,
- void *data)
- {
- struct device_config *config = get_vdo_for_target(ti)->device_config;
- return fn(ti, config->owned_device, 0,
- config->physical_blocks * VDO_SECTORS_PER_BLOCK, data);
- }
- /*
- * Status line is:
- * <device> <operating mode> <in recovery> <index state> <compression state>
- * <used physical blocks> <total physical blocks>
- */
- static void vdo_status(struct dm_target *ti, status_type_t status_type,
- unsigned int status_flags, char *result, unsigned int maxlen)
- {
- struct vdo *vdo = get_vdo_for_target(ti);
- struct vdo_statistics *stats;
- struct device_config *device_config;
- /* N.B.: The DMEMIT macro uses the variables named "sz", "result", "maxlen". */
- int sz = 0;
- switch (status_type) {
- case STATUSTYPE_INFO:
- /* Report info for dmsetup status */
- mutex_lock(&vdo->stats_mutex);
- vdo_fetch_statistics(vdo, &vdo->stats_buffer);
- stats = &vdo->stats_buffer;
- DMEMIT("/dev/%pg %s %s %s %s %llu %llu",
- vdo_get_backing_device(vdo), stats->mode,
- stats->in_recovery_mode ? "recovering" : "-",
- vdo_get_dedupe_index_state_name(vdo->hash_zones),
- vdo_get_compressing(vdo) ? "online" : "offline",
- stats->data_blocks_used + stats->overhead_blocks_used,
- stats->physical_blocks);
- mutex_unlock(&vdo->stats_mutex);
- break;
- case STATUSTYPE_TABLE:
- /* Report the string actually specified in the beginning. */
- device_config = (struct device_config *) ti->private;
- DMEMIT("%s", device_config->original_string);
- break;
- case STATUSTYPE_IMA:
- /* FIXME: We ought to be more detailed here, but this is what thin does. */
- *result = '\0';
- break;
- }
- }
- static block_count_t __must_check get_underlying_device_block_count(const struct vdo *vdo)
- {
- return bdev_nr_bytes(vdo_get_backing_device(vdo)) / VDO_BLOCK_SIZE;
- }
- static int __must_check process_vdo_message_locked(struct vdo *vdo, unsigned int argc,
- char **argv)
- {
- if ((argc == 2) && (strcasecmp(argv[0], "compression") == 0)) {
- if (strcasecmp(argv[1], "on") == 0) {
- vdo_set_compressing(vdo, true);
- return 0;
- }
- if (strcasecmp(argv[1], "off") == 0) {
- vdo_set_compressing(vdo, false);
- return 0;
- }
- vdo_log_warning("invalid argument '%s' to dmsetup compression message",
- argv[1]);
- return -EINVAL;
- }
- vdo_log_warning("unrecognized dmsetup message '%s' received", argv[0]);
- return -EINVAL;
- }
- /*
- * If the message is a dump, just do it. Otherwise, check that no other message is being processed,
- * and only proceed if so.
- * Returns -EBUSY if another message is being processed
- */
- static int __must_check process_vdo_message(struct vdo *vdo, unsigned int argc,
- char **argv)
- {
- int result;
- /*
- * All messages which may be processed in parallel with other messages should be handled
- * here before the atomic check below. Messages which should be exclusive should be
- * processed in process_vdo_message_locked().
- */
- /* Dump messages should always be processed */
- if (strcasecmp(argv[0], "dump") == 0)
- return vdo_dump(vdo, argc, argv, "dmsetup message");
- if (argc == 1) {
- if (strcasecmp(argv[0], "dump-on-shutdown") == 0) {
- vdo->dump_on_shutdown = true;
- return 0;
- }
- /* Index messages should always be processed */
- if ((strcasecmp(argv[0], "index-close") == 0) ||
- (strcasecmp(argv[0], "index-create") == 0) ||
- (strcasecmp(argv[0], "index-disable") == 0) ||
- (strcasecmp(argv[0], "index-enable") == 0))
- return vdo_message_dedupe_index(vdo->hash_zones, argv[0]);
- }
- if (atomic_cmpxchg(&vdo->processing_message, 0, 1) != 0)
- return -EBUSY;
- result = process_vdo_message_locked(vdo, argc, argv);
- /* Pairs with the implicit barrier in cmpxchg just above */
- smp_wmb();
- atomic_set(&vdo->processing_message, 0);
- return result;
- }
- static int vdo_message(struct dm_target *ti, unsigned int argc, char **argv,
- char *result_buffer, unsigned int maxlen)
- {
- struct registered_thread allocating_thread, instance_thread;
- struct vdo *vdo;
- int result;
- if (argc == 0) {
- vdo_log_warning("unspecified dmsetup message");
- return -EINVAL;
- }
- vdo = get_vdo_for_target(ti);
- vdo_register_allocating_thread(&allocating_thread, NULL);
- vdo_register_thread_device_id(&instance_thread, &vdo->instance);
- /*
- * Must be done here so we don't map return codes. The code in dm-ioctl expects a 1 for a
- * return code to look at the buffer and see if it is full or not.
- */
- if ((argc == 1) && (strcasecmp(argv[0], "stats") == 0)) {
- vdo_write_stats(vdo, result_buffer, maxlen);
- result = 1;
- } else if ((argc == 1) && (strcasecmp(argv[0], "config") == 0)) {
- vdo_write_config(vdo, &result_buffer, &maxlen);
- result = 1;
- } else {
- result = vdo_status_to_errno(process_vdo_message(vdo, argc, argv));
- }
- vdo_unregister_thread_device_id();
- vdo_unregister_allocating_thread();
- return result;
- }
- static void configure_target_capabilities(struct dm_target *ti)
- {
- ti->discards_supported = 1;
- ti->flush_supported = true;
- ti->num_discard_bios = 1;
- ti->num_flush_bios = 1;
- /*
- * If this value changes, please make sure to update the value for max_discard_sectors
- * accordingly.
- */
- BUG_ON(dm_set_target_max_io_len(ti, VDO_SECTORS_PER_BLOCK) != 0);
- }
- /*
- * Implements vdo_filter_fn.
- */
- static bool vdo_uses_device(struct vdo *vdo, const void *context)
- {
- const struct device_config *config = context;
- return vdo_get_backing_device(vdo)->bd_dev == config->owned_device->bdev->bd_dev;
- }
- /**
- * get_thread_id_for_phase() - Get the thread id for the current phase of the admin operation in
- * progress.
- * @vdo: The vdo.
- */
- static thread_id_t __must_check get_thread_id_for_phase(struct vdo *vdo)
- {
- switch (vdo->admin.phase) {
- case RESUME_PHASE_PACKER:
- case RESUME_PHASE_FLUSHER:
- case SUSPEND_PHASE_PACKER:
- case SUSPEND_PHASE_FLUSHES:
- return vdo->thread_config.packer_thread;
- case RESUME_PHASE_DATA_VIOS:
- case SUSPEND_PHASE_DATA_VIOS:
- return vdo->thread_config.cpu_thread;
- case LOAD_PHASE_DRAIN_JOURNAL:
- case RESUME_PHASE_JOURNAL:
- case SUSPEND_PHASE_JOURNAL:
- return vdo->thread_config.journal_thread;
- default:
- return vdo->thread_config.admin_thread;
- }
- }
- static struct vdo_completion *prepare_admin_completion(struct vdo *vdo,
- vdo_action_fn callback,
- vdo_action_fn error_handler)
- {
- struct vdo_completion *completion = &vdo->admin.completion;
- /*
- * We can't use vdo_prepare_completion_for_requeue() here because we don't want to reset
- * any error in the completion.
- */
- completion->callback = callback;
- completion->error_handler = error_handler;
- completion->callback_thread_id = get_thread_id_for_phase(vdo);
- completion->requeue = true;
- return completion;
- }
- /**
- * advance_phase() - Increment the phase of the current admin operation and prepare the admin
- * completion to run on the thread for the next phase.
- * @vdo: The vdo on which an admin operation is being performed.
- *
- * Return: The current phase.
- */
- static u32 advance_phase(struct vdo *vdo)
- {
- u32 phase = vdo->admin.phase++;
- vdo->admin.completion.callback_thread_id = get_thread_id_for_phase(vdo);
- vdo->admin.completion.requeue = true;
- return phase;
- }
- /*
- * Perform an administrative operation (load, suspend, grow logical, or grow physical). This method
- * should not be called from vdo threads.
- */
- static int perform_admin_operation(struct vdo *vdo, u32 starting_phase,
- vdo_action_fn callback, vdo_action_fn error_handler,
- const char *type)
- {
- int result;
- struct vdo_administrator *admin = &vdo->admin;
- if (atomic_cmpxchg(&admin->busy, 0, 1) != 0) {
- return vdo_log_error_strerror(VDO_COMPONENT_BUSY,
- "Can't start %s operation, another operation is already in progress",
- type);
- }
- admin->phase = starting_phase;
- reinit_completion(&admin->callback_sync);
- vdo_reset_completion(&admin->completion);
- vdo_launch_completion(prepare_admin_completion(vdo, callback, error_handler));
- /*
- * Using the "interruptible" interface means that Linux will not log a message when we wait
- * for more than 120 seconds.
- */
- while (wait_for_completion_interruptible(&admin->callback_sync)) {
- /* However, if we get a signal in a user-mode process, we could spin... */
- fsleep(1000);
- }
- result = admin->completion.result;
- /* pairs with implicit barrier in cmpxchg above */
- smp_wmb();
- atomic_set(&admin->busy, 0);
- return result;
- }
- /* Assert that we are operating on the correct thread for the current phase. */
- static void assert_admin_phase_thread(struct vdo *vdo, const char *what)
- {
- VDO_ASSERT_LOG_ONLY(vdo_get_callback_thread_id() == get_thread_id_for_phase(vdo),
- "%s on correct thread for %s", what,
- ADMIN_PHASE_NAMES[vdo->admin.phase]);
- }
- /**
- * finish_operation_callback() - Callback to finish an admin operation.
- * @completion: The admin_completion.
- */
- static void finish_operation_callback(struct vdo_completion *completion)
- {
- struct vdo_administrator *admin = &completion->vdo->admin;
- vdo_finish_operation(&admin->state, completion->result);
- complete(&admin->callback_sync);
- }
- /**
- * decode_from_super_block() - Decode the VDO state from the super block and validate that it is
- * correct.
- * @vdo: The vdo being loaded.
- *
- * On error from this method, the component states must be destroyed explicitly. If this method
- * returns successfully, the component states must not be destroyed.
- *
- * Return: VDO_SUCCESS or an error.
- */
- static int __must_check decode_from_super_block(struct vdo *vdo)
- {
- const struct device_config *config = vdo->device_config;
- int result;
- result = vdo_decode_component_states(vdo->super_block.buffer, &vdo->geometry,
- &vdo->states);
- if (result != VDO_SUCCESS)
- return result;
- vdo_set_state(vdo, vdo->states.vdo.state);
- vdo->load_state = vdo->states.vdo.state;
- /*
- * If the device config specifies a larger logical size than was recorded in the super
- * block, just accept it.
- */
- if (vdo->states.vdo.config.logical_blocks < config->logical_blocks) {
- vdo_log_warning("Growing logical size: a logical size of %llu blocks was specified, but that differs from the %llu blocks configured in the vdo super block",
- (unsigned long long) config->logical_blocks,
- (unsigned long long) vdo->states.vdo.config.logical_blocks);
- vdo->states.vdo.config.logical_blocks = config->logical_blocks;
- }
- result = vdo_validate_component_states(&vdo->states, vdo->geometry.nonce,
- config->physical_blocks,
- config->logical_blocks);
- if (result != VDO_SUCCESS)
- return result;
- vdo->layout = vdo->states.layout;
- return VDO_SUCCESS;
- }
- /**
- * decode_vdo() - Decode the component data portion of a super block and fill in the corresponding
- * portions of the vdo being loaded.
- * @vdo: The vdo being loaded.
- *
- * This will also allocate the recovery journal and slab depot. If this method is called with an
- * asynchronous layer (i.e. a thread config which specifies at least one base thread), the block
- * map and packer will be constructed as well.
- *
- * Return: VDO_SUCCESS or an error.
- */
- static int __must_check decode_vdo(struct vdo *vdo)
- {
- block_count_t maximum_age, journal_length;
- struct partition *partition;
- int result;
- result = decode_from_super_block(vdo);
- if (result != VDO_SUCCESS) {
- vdo_destroy_component_states(&vdo->states);
- return result;
- }
- maximum_age = vdo_convert_maximum_age(vdo->device_config->block_map_maximum_age);
- journal_length =
- vdo_get_recovery_journal_length(vdo->states.vdo.config.recovery_journal_size);
- if (maximum_age > (journal_length / 2)) {
- return vdo_log_error_strerror(VDO_BAD_CONFIGURATION,
- "maximum age: %llu exceeds limit %llu",
- (unsigned long long) maximum_age,
- (unsigned long long) (journal_length / 2));
- }
- if (maximum_age == 0) {
- return vdo_log_error_strerror(VDO_BAD_CONFIGURATION,
- "maximum age must be greater than 0");
- }
- result = vdo_enable_read_only_entry(vdo);
- if (result != VDO_SUCCESS)
- return result;
- partition = vdo_get_known_partition(&vdo->layout,
- VDO_RECOVERY_JOURNAL_PARTITION);
- result = vdo_decode_recovery_journal(vdo->states.recovery_journal,
- vdo->states.vdo.nonce, vdo, partition,
- vdo->states.vdo.complete_recoveries,
- vdo->states.vdo.config.recovery_journal_size,
- &vdo->recovery_journal);
- if (result != VDO_SUCCESS)
- return result;
- partition = vdo_get_known_partition(&vdo->layout, VDO_SLAB_SUMMARY_PARTITION);
- result = vdo_decode_slab_depot(vdo->states.slab_depot, vdo, partition,
- &vdo->depot);
- if (result != VDO_SUCCESS)
- return result;
- result = vdo_decode_block_map(vdo->states.block_map,
- vdo->states.vdo.config.logical_blocks, vdo,
- vdo->recovery_journal, vdo->states.vdo.nonce,
- vdo->device_config->cache_size, maximum_age,
- &vdo->block_map);
- if (result != VDO_SUCCESS)
- return result;
- result = vdo_make_physical_zones(vdo, &vdo->physical_zones);
- if (result != VDO_SUCCESS)
- return result;
- /* The logical zones depend on the physical zones already existing. */
- result = vdo_make_logical_zones(vdo, &vdo->logical_zones);
- if (result != VDO_SUCCESS)
- return result;
- return vdo_make_hash_zones(vdo, &vdo->hash_zones);
- }
- /**
- * pre_load_callback() - Callback to initiate a pre-load, registered in vdo_initialize().
- * @completion: The admin completion.
- */
- static void pre_load_callback(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- int result;
- assert_admin_phase_thread(vdo, __func__);
- switch (advance_phase(vdo)) {
- case PRE_LOAD_PHASE_START:
- result = vdo_start_operation(&vdo->admin.state,
- VDO_ADMIN_STATE_PRE_LOADING);
- if (result != VDO_SUCCESS) {
- vdo_continue_completion(completion, result);
- return;
- }
- vdo_load_super_block(vdo, completion);
- return;
- case PRE_LOAD_PHASE_LOAD_COMPONENTS:
- vdo_continue_completion(completion, decode_vdo(vdo));
- return;
- case PRE_LOAD_PHASE_END:
- break;
- default:
- vdo_set_completion_result(completion, UDS_BAD_STATE);
- }
- finish_operation_callback(completion);
- }
- static void release_instance(unsigned int instance)
- {
- mutex_lock(&instances_lock);
- if (instance >= instances.bit_count) {
- VDO_ASSERT_LOG_ONLY(false,
- "instance number %u must be less than bit count %u",
- instance, instances.bit_count);
- } else if (test_bit(instance, instances.words) == 0) {
- VDO_ASSERT_LOG_ONLY(false, "instance number %u must be allocated", instance);
- } else {
- __clear_bit(instance, instances.words);
- instances.count -= 1;
- }
- mutex_unlock(&instances_lock);
- }
- static void set_device_config(struct dm_target *ti, struct vdo *vdo,
- struct device_config *config)
- {
- list_del_init(&config->config_list);
- list_add_tail(&config->config_list, &vdo->device_config_list);
- config->vdo = vdo;
- ti->private = config;
- configure_target_capabilities(ti);
- }
- static int vdo_initialize(struct dm_target *ti, unsigned int instance,
- struct device_config *config)
- {
- struct vdo *vdo;
- int result;
- u64 block_size = VDO_BLOCK_SIZE;
- u64 logical_size = to_bytes(ti->len);
- block_count_t logical_blocks = logical_size / block_size;
- vdo_log_info("loading device '%s'", vdo_get_device_name(ti));
- vdo_log_debug("Logical block size = %llu", (u64) config->logical_block_size);
- vdo_log_debug("Logical blocks = %llu", logical_blocks);
- vdo_log_debug("Physical block size = %llu", (u64) block_size);
- vdo_log_debug("Physical blocks = %llu", config->physical_blocks);
- vdo_log_debug("Block map cache blocks = %u", config->cache_size);
- vdo_log_debug("Block map maximum age = %u", config->block_map_maximum_age);
- vdo_log_debug("Deduplication = %s", (config->deduplication ? "on" : "off"));
- vdo_log_debug("Compression = %s", (config->compression ? "on" : "off"));
- vdo = vdo_find_matching(vdo_uses_device, config);
- if (vdo != NULL) {
- vdo_log_error("Existing vdo already uses device %s",
- vdo->device_config->parent_device_name);
- ti->error = "Cannot share storage device with already-running VDO";
- return VDO_BAD_CONFIGURATION;
- }
- result = vdo_make(instance, config, &ti->error, &vdo);
- if (result != VDO_SUCCESS) {
- vdo_log_error("Could not create VDO device. (VDO error %d, message %s)",
- result, ti->error);
- vdo_destroy(vdo);
- return result;
- }
- result = perform_admin_operation(vdo, PRE_LOAD_PHASE_START, pre_load_callback,
- finish_operation_callback, "pre-load");
- if (result != VDO_SUCCESS) {
- ti->error = ((result == VDO_INVALID_ADMIN_STATE) ?
- "Pre-load is only valid immediately after initialization" :
- "Cannot load metadata from device");
- vdo_log_error("Could not start VDO device. (VDO error %d, message %s)",
- result, ti->error);
- vdo_destroy(vdo);
- return result;
- }
- set_device_config(ti, vdo, config);
- vdo->device_config = config;
- return VDO_SUCCESS;
- }
- /* Implements vdo_filter_fn. */
- static bool __must_check vdo_is_named(struct vdo *vdo, const void *context)
- {
- struct dm_target *ti = vdo->device_config->owning_target;
- const char *device_name = vdo_get_device_name(ti);
- return strcmp(device_name, context) == 0;
- }
- /**
- * get_bit_array_size() - Return the number of bytes needed to store a bit array of the specified
- * capacity in an array of unsigned longs.
- * @bit_count: The number of bits the array must hold.
- *
- * Return: the number of bytes needed for the array representation.
- */
- static size_t get_bit_array_size(unsigned int bit_count)
- {
- /* Round up to a multiple of the word size and convert to a byte count. */
- return (BITS_TO_LONGS(bit_count) * sizeof(unsigned long));
- }
- /**
- * grow_bit_array() - Re-allocate the bitmap word array so there will more instance numbers that
- * can be allocated.
- *
- * Since the array is initially NULL, this also initializes the array the first time we allocate an
- * instance number.
- *
- * Return: VDO_SUCCESS or an error code from the allocation
- */
- static int grow_bit_array(void)
- {
- unsigned int new_count = max(instances.bit_count + BIT_COUNT_INCREMENT,
- (unsigned int) BIT_COUNT_MINIMUM);
- unsigned long *new_words;
- int result;
- result = vdo_reallocate_memory(instances.words,
- get_bit_array_size(instances.bit_count),
- get_bit_array_size(new_count),
- "instance number bit array", &new_words);
- if (result != VDO_SUCCESS)
- return result;
- instances.bit_count = new_count;
- instances.words = new_words;
- return VDO_SUCCESS;
- }
- /**
- * allocate_instance() - Allocate an instance number.
- * @instance_ptr: A point to hold the instance number
- *
- * Return: VDO_SUCCESS or an error code
- *
- * This function must be called while holding the instances lock.
- */
- static int allocate_instance(unsigned int *instance_ptr)
- {
- unsigned int instance;
- int result;
- /* If there are no unallocated instances, grow the bit array. */
- if (instances.count >= instances.bit_count) {
- result = grow_bit_array();
- if (result != VDO_SUCCESS)
- return result;
- }
- /*
- * There must be a zero bit somewhere now. Find it, starting just after the last instance
- * allocated.
- */
- instance = find_next_zero_bit(instances.words, instances.bit_count,
- instances.next);
- if (instance >= instances.bit_count) {
- /* Nothing free after next, so wrap around to instance zero. */
- instance = find_first_zero_bit(instances.words, instances.bit_count);
- result = VDO_ASSERT(instance < instances.bit_count,
- "impossibly, no zero bit found");
- if (result != VDO_SUCCESS)
- return result;
- }
- __set_bit(instance, instances.words);
- instances.count++;
- instances.next = instance + 1;
- *instance_ptr = instance;
- return VDO_SUCCESS;
- }
- static int construct_new_vdo_registered(struct dm_target *ti, unsigned int argc,
- char **argv, unsigned int instance)
- {
- int result;
- struct device_config *config;
- result = parse_device_config(argc, argv, ti, &config);
- if (result != VDO_SUCCESS) {
- vdo_log_error_strerror(result, "parsing failed: %s", ti->error);
- release_instance(instance);
- return -EINVAL;
- }
- /* Beyond this point, the instance number will be cleaned up for us if needed */
- result = vdo_initialize(ti, instance, config);
- if (result != VDO_SUCCESS) {
- release_instance(instance);
- free_device_config(config);
- return vdo_status_to_errno(result);
- }
- return VDO_SUCCESS;
- }
- static int construct_new_vdo(struct dm_target *ti, unsigned int argc, char **argv)
- {
- int result;
- unsigned int instance;
- struct registered_thread instance_thread;
- mutex_lock(&instances_lock);
- result = allocate_instance(&instance);
- mutex_unlock(&instances_lock);
- if (result != VDO_SUCCESS)
- return -ENOMEM;
- vdo_register_thread_device_id(&instance_thread, &instance);
- result = construct_new_vdo_registered(ti, argc, argv, instance);
- vdo_unregister_thread_device_id();
- return result;
- }
- /**
- * check_may_grow_physical() - Callback to check that we're not in recovery mode, used in
- * vdo_prepare_to_grow_physical().
- * @completion: The admin completion.
- */
- static void check_may_grow_physical(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- assert_admin_phase_thread(vdo, __func__);
- /* These checks can only be done from a vdo thread. */
- if (vdo_is_read_only(vdo))
- vdo_set_completion_result(completion, VDO_READ_ONLY);
- if (vdo_in_recovery_mode(vdo))
- vdo_set_completion_result(completion, VDO_RETRY_AFTER_REBUILD);
- finish_operation_callback(completion);
- }
- static block_count_t get_partition_size(struct layout *layout, enum partition_id id)
- {
- return vdo_get_known_partition(layout, id)->count;
- }
- /**
- * grow_layout() - Make the layout for growing a vdo.
- * @vdo: The vdo preparing to grow.
- * @old_size: The current size of the vdo.
- * @new_size: The size to which the vdo will be grown.
- *
- * Return: VDO_SUCCESS or an error code.
- */
- static int grow_layout(struct vdo *vdo, block_count_t old_size, block_count_t new_size)
- {
- int result;
- block_count_t min_new_size;
- if (vdo->next_layout.size == new_size) {
- /* We are already prepared to grow to the new size, so we're done. */
- return VDO_SUCCESS;
- }
- /* Make a copy completion if there isn't one */
- if (vdo->partition_copier == NULL) {
- vdo->partition_copier = dm_kcopyd_client_create(NULL);
- if (IS_ERR(vdo->partition_copier)) {
- result = PTR_ERR(vdo->partition_copier);
- vdo->partition_copier = NULL;
- return result;
- }
- }
- /* Free any unused preparation. */
- vdo_uninitialize_layout(&vdo->next_layout);
- /*
- * Make a new layout with the existing partition sizes for everything but the slab depot
- * partition.
- */
- result = vdo_initialize_layout(new_size, vdo->layout.start,
- get_partition_size(&vdo->layout,
- VDO_BLOCK_MAP_PARTITION),
- get_partition_size(&vdo->layout,
- VDO_RECOVERY_JOURNAL_PARTITION),
- get_partition_size(&vdo->layout,
- VDO_SLAB_SUMMARY_PARTITION),
- &vdo->next_layout);
- if (result != VDO_SUCCESS) {
- dm_kcopyd_client_destroy(vdo_forget(vdo->partition_copier));
- return result;
- }
- /* Ensure the new journal and summary are entirely within the added blocks. */
- min_new_size = (old_size +
- get_partition_size(&vdo->next_layout,
- VDO_SLAB_SUMMARY_PARTITION) +
- get_partition_size(&vdo->next_layout,
- VDO_RECOVERY_JOURNAL_PARTITION));
- if (min_new_size > new_size) {
- /* Copying the journal and summary would destroy some old metadata. */
- vdo_uninitialize_layout(&vdo->next_layout);
- dm_kcopyd_client_destroy(vdo_forget(vdo->partition_copier));
- return VDO_INCREMENT_TOO_SMALL;
- }
- return VDO_SUCCESS;
- }
- static int prepare_to_grow_physical(struct vdo *vdo, block_count_t new_physical_blocks)
- {
- int result;
- block_count_t current_physical_blocks = vdo->states.vdo.config.physical_blocks;
- vdo_log_info("Preparing to resize physical to %llu",
- (unsigned long long) new_physical_blocks);
- VDO_ASSERT_LOG_ONLY((new_physical_blocks > current_physical_blocks),
- "New physical size is larger than current physical size");
- result = perform_admin_operation(vdo, PREPARE_GROW_PHYSICAL_PHASE_START,
- check_may_grow_physical,
- finish_operation_callback,
- "prepare grow-physical");
- if (result != VDO_SUCCESS)
- return result;
- result = grow_layout(vdo, current_physical_blocks, new_physical_blocks);
- if (result != VDO_SUCCESS)
- return result;
- result = vdo_prepare_to_grow_slab_depot(vdo->depot,
- vdo_get_known_partition(&vdo->next_layout,
- VDO_SLAB_DEPOT_PARTITION));
- if (result != VDO_SUCCESS) {
- vdo_uninitialize_layout(&vdo->next_layout);
- return result;
- }
- vdo_log_info("Done preparing to resize physical");
- return VDO_SUCCESS;
- }
- /**
- * validate_new_device_config() - Check whether a new device config represents a valid modification
- * to an existing config.
- * @to_validate: The new config to validate.
- * @config: The existing config.
- * @may_grow: Set to true if growing the logical and physical size of the vdo is currently
- * permitted.
- * @error_ptr: A pointer to hold the reason for any error.
- *
- * Return: VDO_SUCCESS or an error.
- */
- static int validate_new_device_config(struct device_config *to_validate,
- struct device_config *config, bool may_grow,
- char **error_ptr)
- {
- if (to_validate->owning_target->begin != config->owning_target->begin) {
- *error_ptr = "Starting sector cannot change";
- return VDO_PARAMETER_MISMATCH;
- }
- if (to_validate->logical_block_size != config->logical_block_size) {
- *error_ptr = "Logical block size cannot change";
- return VDO_PARAMETER_MISMATCH;
- }
- if (to_validate->logical_blocks < config->logical_blocks) {
- *error_ptr = "Can't shrink VDO logical size";
- return VDO_PARAMETER_MISMATCH;
- }
- if (to_validate->cache_size != config->cache_size) {
- *error_ptr = "Block map cache size cannot change";
- return VDO_PARAMETER_MISMATCH;
- }
- if (to_validate->block_map_maximum_age != config->block_map_maximum_age) {
- *error_ptr = "Block map maximum age cannot change";
- return VDO_PARAMETER_MISMATCH;
- }
- if (memcmp(&to_validate->thread_counts, &config->thread_counts,
- sizeof(struct thread_count_config)) != 0) {
- *error_ptr = "Thread configuration cannot change";
- return VDO_PARAMETER_MISMATCH;
- }
- if (to_validate->physical_blocks < config->physical_blocks) {
- *error_ptr = "Removing physical storage from a VDO is not supported";
- return VDO_NOT_IMPLEMENTED;
- }
- if (!may_grow && (to_validate->physical_blocks > config->physical_blocks)) {
- *error_ptr = "VDO physical size may not grow in current state";
- return VDO_NOT_IMPLEMENTED;
- }
- return VDO_SUCCESS;
- }
- static int prepare_to_modify(struct dm_target *ti, struct device_config *config,
- struct vdo *vdo)
- {
- int result;
- bool may_grow = (vdo_get_admin_state(vdo) != VDO_ADMIN_STATE_PRE_LOADED);
- result = validate_new_device_config(config, vdo->device_config, may_grow,
- &ti->error);
- if (result != VDO_SUCCESS)
- return -EINVAL;
- if (config->logical_blocks > vdo->device_config->logical_blocks) {
- block_count_t logical_blocks = vdo->states.vdo.config.logical_blocks;
- vdo_log_info("Preparing to resize logical to %llu",
- (unsigned long long) config->logical_blocks);
- VDO_ASSERT_LOG_ONLY((config->logical_blocks > logical_blocks),
- "New logical size is larger than current size");
- result = vdo_prepare_to_grow_block_map(vdo->block_map,
- config->logical_blocks);
- if (result != VDO_SUCCESS) {
- ti->error = "Device vdo_prepare_to_grow_logical failed";
- return result;
- }
- vdo_log_info("Done preparing to resize logical");
- }
- if (config->physical_blocks > vdo->device_config->physical_blocks) {
- result = prepare_to_grow_physical(vdo, config->physical_blocks);
- if (result != VDO_SUCCESS) {
- if (result == VDO_PARAMETER_MISMATCH) {
- /*
- * If we don't trap this case, vdo_status_to_errno() will remap
- * it to -EIO, which is misleading and ahistorical.
- */
- result = -EINVAL;
- }
- if (result == VDO_TOO_MANY_SLABS)
- ti->error = "Device vdo_prepare_to_grow_physical failed (specified physical size too big based on formatted slab size)";
- else
- ti->error = "Device vdo_prepare_to_grow_physical failed";
- return result;
- }
- }
- if (strcmp(config->parent_device_name, vdo->device_config->parent_device_name) != 0) {
- const char *device_name = vdo_get_device_name(config->owning_target);
- vdo_log_info("Updating backing device of %s from %s to %s", device_name,
- vdo->device_config->parent_device_name,
- config->parent_device_name);
- }
- return VDO_SUCCESS;
- }
- static int update_existing_vdo(const char *device_name, struct dm_target *ti,
- unsigned int argc, char **argv, struct vdo *vdo)
- {
- int result;
- struct device_config *config;
- result = parse_device_config(argc, argv, ti, &config);
- if (result != VDO_SUCCESS)
- return -EINVAL;
- vdo_log_info("preparing to modify device '%s'", device_name);
- result = prepare_to_modify(ti, config, vdo);
- if (result != VDO_SUCCESS) {
- free_device_config(config);
- return vdo_status_to_errno(result);
- }
- set_device_config(ti, vdo, config);
- return VDO_SUCCESS;
- }
- static int vdo_ctr(struct dm_target *ti, unsigned int argc, char **argv)
- {
- int result;
- struct registered_thread allocating_thread, instance_thread;
- const char *device_name;
- struct vdo *vdo;
- vdo_register_allocating_thread(&allocating_thread, NULL);
- device_name = vdo_get_device_name(ti);
- vdo = vdo_find_matching(vdo_is_named, device_name);
- if (vdo == NULL) {
- result = construct_new_vdo(ti, argc, argv);
- } else {
- vdo_register_thread_device_id(&instance_thread, &vdo->instance);
- result = update_existing_vdo(device_name, ti, argc, argv, vdo);
- vdo_unregister_thread_device_id();
- }
- vdo_unregister_allocating_thread();
- return result;
- }
- static void vdo_dtr(struct dm_target *ti)
- {
- struct device_config *config = ti->private;
- struct vdo *vdo = vdo_forget(config->vdo);
- list_del_init(&config->config_list);
- if (list_empty(&vdo->device_config_list)) {
- const char *device_name;
- /* This was the last config referencing the VDO. Free it. */
- unsigned int instance = vdo->instance;
- struct registered_thread allocating_thread, instance_thread;
- vdo_register_thread_device_id(&instance_thread, &instance);
- vdo_register_allocating_thread(&allocating_thread, NULL);
- device_name = vdo_get_device_name(ti);
- vdo_log_info("stopping device '%s'", device_name);
- if (vdo->dump_on_shutdown)
- vdo_dump_all(vdo, "device shutdown");
- vdo_destroy(vdo_forget(vdo));
- vdo_log_info("device '%s' stopped", device_name);
- vdo_unregister_thread_device_id();
- vdo_unregister_allocating_thread();
- release_instance(instance);
- } else if (config == vdo->device_config) {
- /*
- * The VDO still references this config. Give it a reference to a config that isn't
- * being destroyed.
- */
- vdo->device_config = list_first_entry(&vdo->device_config_list,
- struct device_config, config_list);
- }
- free_device_config(config);
- ti->private = NULL;
- }
- static void vdo_presuspend(struct dm_target *ti)
- {
- get_vdo_for_target(ti)->suspend_type =
- (dm_noflush_suspending(ti) ? VDO_ADMIN_STATE_SUSPENDING : VDO_ADMIN_STATE_SAVING);
- }
- /**
- * write_super_block_for_suspend() - Update the VDO state and save the super block.
- * @completion: The admin completion
- */
- static void write_super_block_for_suspend(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- switch (vdo_get_state(vdo)) {
- case VDO_DIRTY:
- case VDO_NEW:
- vdo_set_state(vdo, VDO_CLEAN);
- break;
- case VDO_CLEAN:
- case VDO_READ_ONLY_MODE:
- case VDO_FORCE_REBUILD:
- case VDO_RECOVERING:
- case VDO_REBUILD_FOR_UPGRADE:
- break;
- case VDO_REPLAYING:
- default:
- vdo_continue_completion(completion, UDS_BAD_STATE);
- return;
- }
- vdo_save_components(vdo, completion);
- }
- /**
- * suspend_callback() - Callback to initiate a suspend, registered in vdo_postsuspend().
- * @completion: The sub-task completion.
- */
- static void suspend_callback(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- struct admin_state *state = &vdo->admin.state;
- int result;
- assert_admin_phase_thread(vdo, __func__);
- switch (advance_phase(vdo)) {
- case SUSPEND_PHASE_START:
- if (vdo_get_admin_state_code(state)->quiescent) {
- /* Already suspended */
- break;
- }
- vdo_continue_completion(completion,
- vdo_start_operation(state, vdo->suspend_type));
- return;
- case SUSPEND_PHASE_PACKER:
- /*
- * If the VDO was already resumed from a prior suspend while read-only, some of the
- * components may not have been resumed. By setting a read-only error here, we
- * guarantee that the result of this suspend will be VDO_READ_ONLY and not
- * VDO_INVALID_ADMIN_STATE in that case.
- */
- if (vdo_in_read_only_mode(vdo))
- vdo_set_completion_result(completion, VDO_READ_ONLY);
- vdo_drain_packer(vdo->packer, completion);
- return;
- case SUSPEND_PHASE_DATA_VIOS:
- drain_data_vio_pool(vdo->data_vio_pool, completion);
- return;
- case SUSPEND_PHASE_DEDUPE:
- vdo_drain_hash_zones(vdo->hash_zones, completion);
- return;
- case SUSPEND_PHASE_FLUSHES:
- vdo_drain_flusher(vdo->flusher, completion);
- return;
- case SUSPEND_PHASE_LOGICAL_ZONES:
- /*
- * Attempt to flush all I/O before completing post suspend work. We believe a
- * suspended device is expected to have persisted all data written before the
- * suspend, even if it hasn't been flushed yet.
- */
- result = vdo_synchronous_flush(vdo);
- if (result != VDO_SUCCESS)
- vdo_enter_read_only_mode(vdo, result);
- vdo_drain_logical_zones(vdo->logical_zones,
- vdo_get_admin_state_code(state), completion);
- return;
- case SUSPEND_PHASE_BLOCK_MAP:
- vdo_drain_block_map(vdo->block_map, vdo_get_admin_state_code(state),
- completion);
- return;
- case SUSPEND_PHASE_JOURNAL:
- vdo_drain_recovery_journal(vdo->recovery_journal,
- vdo_get_admin_state_code(state), completion);
- return;
- case SUSPEND_PHASE_DEPOT:
- vdo_drain_slab_depot(vdo->depot, vdo_get_admin_state_code(state),
- completion);
- return;
- case SUSPEND_PHASE_READ_ONLY_WAIT:
- vdo_wait_until_not_entering_read_only_mode(completion);
- return;
- case SUSPEND_PHASE_WRITE_SUPER_BLOCK:
- if (vdo_is_state_suspending(state) || (completion->result != VDO_SUCCESS)) {
- /* If we didn't save the VDO or there was an error, we're done. */
- break;
- }
- write_super_block_for_suspend(completion);
- return;
- case SUSPEND_PHASE_END:
- break;
- default:
- vdo_set_completion_result(completion, UDS_BAD_STATE);
- }
- finish_operation_callback(completion);
- }
- static void vdo_postsuspend(struct dm_target *ti)
- {
- struct vdo *vdo = get_vdo_for_target(ti);
- struct registered_thread instance_thread;
- const char *device_name;
- int result;
- vdo_register_thread_device_id(&instance_thread, &vdo->instance);
- device_name = vdo_get_device_name(vdo->device_config->owning_target);
- vdo_log_info("suspending device '%s'", device_name);
- /*
- * It's important to note any error here does not actually stop device-mapper from
- * suspending the device. All this work is done post suspend.
- */
- result = perform_admin_operation(vdo, SUSPEND_PHASE_START, suspend_callback,
- suspend_callback, "suspend");
- if ((result == VDO_SUCCESS) || (result == VDO_READ_ONLY)) {
- /*
- * Treat VDO_READ_ONLY as a success since a read-only suspension still leaves the
- * VDO suspended.
- */
- vdo_log_info("device '%s' suspended", device_name);
- } else if (result == VDO_INVALID_ADMIN_STATE) {
- vdo_log_error("Suspend invoked while in unexpected state: %s",
- vdo_get_admin_state(vdo)->name);
- } else {
- vdo_log_error_strerror(result, "Suspend of device '%s' failed",
- device_name);
- }
- vdo_unregister_thread_device_id();
- }
- /**
- * was_new() - Check whether the vdo was new when it was loaded.
- * @vdo: The vdo to query.
- *
- * Return: true if the vdo was new.
- */
- static bool was_new(const struct vdo *vdo)
- {
- return (vdo->load_state == VDO_NEW);
- }
- /**
- * requires_repair() - Check whether a vdo requires recovery or rebuild.
- * @vdo: The vdo to query.
- *
- * Return: true if the vdo must be repaired.
- */
- static bool __must_check requires_repair(const struct vdo *vdo)
- {
- switch (vdo_get_state(vdo)) {
- case VDO_DIRTY:
- case VDO_FORCE_REBUILD:
- case VDO_REPLAYING:
- case VDO_REBUILD_FOR_UPGRADE:
- return true;
- default:
- return false;
- }
- }
- /**
- * get_load_type() - Determine how the slab depot was loaded.
- * @vdo: The vdo.
- *
- * Return: How the depot was loaded.
- */
- static enum slab_depot_load_type get_load_type(struct vdo *vdo)
- {
- if (vdo_state_requires_read_only_rebuild(vdo->load_state))
- return VDO_SLAB_DEPOT_REBUILD_LOAD;
- if (vdo_state_requires_recovery(vdo->load_state))
- return VDO_SLAB_DEPOT_RECOVERY_LOAD;
- return VDO_SLAB_DEPOT_NORMAL_LOAD;
- }
- /**
- * load_callback() - Callback to do the destructive parts of loading a VDO.
- * @completion: The sub-task completion.
- */
- static void load_callback(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- int result;
- assert_admin_phase_thread(vdo, __func__);
- switch (advance_phase(vdo)) {
- case LOAD_PHASE_START:
- result = vdo_start_operation(&vdo->admin.state, VDO_ADMIN_STATE_LOADING);
- if (result != VDO_SUCCESS) {
- vdo_continue_completion(completion, result);
- return;
- }
- /* Prepare the recovery journal for new entries. */
- vdo_open_recovery_journal(vdo->recovery_journal, vdo->depot,
- vdo->block_map);
- vdo_allow_read_only_mode_entry(completion);
- return;
- case LOAD_PHASE_LOAD_DEPOT:
- vdo_set_dedupe_state_normal(vdo->hash_zones);
- if (vdo_is_read_only(vdo)) {
- /*
- * In read-only mode we don't use the allocator and it may not even be
- * readable, so don't bother trying to load it.
- */
- vdo_set_completion_result(completion, VDO_READ_ONLY);
- break;
- }
- if (requires_repair(vdo)) {
- vdo_repair(completion);
- return;
- }
- vdo_load_slab_depot(vdo->depot,
- (was_new(vdo) ? VDO_ADMIN_STATE_FORMATTING :
- VDO_ADMIN_STATE_LOADING),
- completion, NULL);
- return;
- case LOAD_PHASE_MAKE_DIRTY:
- vdo_set_state(vdo, VDO_DIRTY);
- vdo_save_components(vdo, completion);
- return;
- case LOAD_PHASE_PREPARE_TO_ALLOCATE:
- vdo_initialize_block_map_from_journal(vdo->block_map,
- vdo->recovery_journal);
- vdo_prepare_slab_depot_to_allocate(vdo->depot, get_load_type(vdo),
- completion);
- return;
- case LOAD_PHASE_SCRUB_SLABS:
- if (vdo_state_requires_recovery(vdo->load_state))
- vdo_enter_recovery_mode(vdo);
- vdo_scrub_all_unrecovered_slabs(vdo->depot, completion);
- return;
- case LOAD_PHASE_DATA_REDUCTION:
- WRITE_ONCE(vdo->compressing, vdo->device_config->compression);
- if (vdo->device_config->deduplication) {
- /*
- * Don't try to load or rebuild the index first (and log scary error
- * messages) if this is known to be a newly-formatted volume.
- */
- vdo_start_dedupe_index(vdo->hash_zones, was_new(vdo));
- }
- vdo->allocations_allowed = false;
- fallthrough;
- case LOAD_PHASE_FINISHED:
- break;
- case LOAD_PHASE_DRAIN_JOURNAL:
- vdo_drain_recovery_journal(vdo->recovery_journal, VDO_ADMIN_STATE_SAVING,
- completion);
- return;
- case LOAD_PHASE_WAIT_FOR_READ_ONLY:
- /* Avoid an infinite loop */
- completion->error_handler = NULL;
- vdo->admin.phase = LOAD_PHASE_FINISHED;
- vdo_wait_until_not_entering_read_only_mode(completion);
- return;
- default:
- vdo_set_completion_result(completion, UDS_BAD_STATE);
- }
- finish_operation_callback(completion);
- }
- /**
- * handle_load_error() - Handle an error during the load operation.
- * @completion: The admin completion.
- *
- * If at all possible, brings the vdo online in read-only mode. This handler is registered in
- * vdo_preresume_registered().
- */
- static void handle_load_error(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- if (vdo_requeue_completion_if_needed(completion,
- vdo->thread_config.admin_thread))
- return;
- if (vdo_state_requires_read_only_rebuild(vdo->load_state) &&
- (vdo->admin.phase == LOAD_PHASE_MAKE_DIRTY)) {
- vdo_log_error_strerror(completion->result, "aborting load");
- vdo->admin.phase = LOAD_PHASE_DRAIN_JOURNAL;
- load_callback(vdo_forget(completion));
- return;
- }
- if ((completion->result == VDO_UNSUPPORTED_VERSION) &&
- (vdo->admin.phase == LOAD_PHASE_MAKE_DIRTY)) {
- vdo_log_error("Aborting load due to unsupported version");
- vdo->admin.phase = LOAD_PHASE_FINISHED;
- load_callback(completion);
- return;
- }
- vdo_log_error_strerror(completion->result,
- "Entering read-only mode due to load error");
- vdo->admin.phase = LOAD_PHASE_WAIT_FOR_READ_ONLY;
- vdo_enter_read_only_mode(vdo, completion->result);
- completion->result = VDO_READ_ONLY;
- load_callback(completion);
- }
- /**
- * write_super_block_for_resume() - Update the VDO state and save the super block.
- * @completion: The admin completion
- */
- static void write_super_block_for_resume(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- switch (vdo_get_state(vdo)) {
- case VDO_CLEAN:
- case VDO_NEW:
- vdo_set_state(vdo, VDO_DIRTY);
- vdo_save_components(vdo, completion);
- return;
- case VDO_DIRTY:
- case VDO_READ_ONLY_MODE:
- case VDO_FORCE_REBUILD:
- case VDO_RECOVERING:
- case VDO_REBUILD_FOR_UPGRADE:
- /* No need to write the super block in these cases */
- vdo_launch_completion(completion);
- return;
- case VDO_REPLAYING:
- default:
- vdo_continue_completion(completion, UDS_BAD_STATE);
- }
- }
- /**
- * resume_callback() - Callback to resume a VDO.
- * @completion: The admin completion.
- */
- static void resume_callback(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- int result;
- assert_admin_phase_thread(vdo, __func__);
- switch (advance_phase(vdo)) {
- case RESUME_PHASE_START:
- result = vdo_start_operation(&vdo->admin.state,
- VDO_ADMIN_STATE_RESUMING);
- if (result != VDO_SUCCESS) {
- vdo_continue_completion(completion, result);
- return;
- }
- write_super_block_for_resume(completion);
- return;
- case RESUME_PHASE_ALLOW_READ_ONLY_MODE:
- vdo_allow_read_only_mode_entry(completion);
- return;
- case RESUME_PHASE_DEDUPE:
- vdo_resume_hash_zones(vdo->hash_zones, completion);
- return;
- case RESUME_PHASE_DEPOT:
- vdo_resume_slab_depot(vdo->depot, completion);
- return;
- case RESUME_PHASE_JOURNAL:
- vdo_resume_recovery_journal(vdo->recovery_journal, completion);
- return;
- case RESUME_PHASE_BLOCK_MAP:
- vdo_resume_block_map(vdo->block_map, completion);
- return;
- case RESUME_PHASE_LOGICAL_ZONES:
- vdo_resume_logical_zones(vdo->logical_zones, completion);
- return;
- case RESUME_PHASE_PACKER:
- {
- bool was_enabled = vdo_get_compressing(vdo);
- bool enable = vdo->device_config->compression;
- if (enable != was_enabled)
- WRITE_ONCE(vdo->compressing, enable);
- vdo_log_info("compression is %s", (enable ? "enabled" : "disabled"));
- vdo_resume_packer(vdo->packer, completion);
- return;
- }
- case RESUME_PHASE_FLUSHER:
- vdo_resume_flusher(vdo->flusher, completion);
- return;
- case RESUME_PHASE_DATA_VIOS:
- resume_data_vio_pool(vdo->data_vio_pool, completion);
- return;
- case RESUME_PHASE_END:
- break;
- default:
- vdo_set_completion_result(completion, UDS_BAD_STATE);
- }
- finish_operation_callback(completion);
- }
- /**
- * grow_logical_callback() - Callback to initiate a grow logical.
- * @completion: The admin completion.
- *
- * Registered in perform_grow_logical().
- */
- static void grow_logical_callback(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- int result;
- assert_admin_phase_thread(vdo, __func__);
- switch (advance_phase(vdo)) {
- case GROW_LOGICAL_PHASE_START:
- if (vdo_is_read_only(vdo)) {
- vdo_log_error_strerror(VDO_READ_ONLY,
- "Can't grow logical size of a read-only VDO");
- vdo_set_completion_result(completion, VDO_READ_ONLY);
- break;
- }
- result = vdo_start_operation(&vdo->admin.state,
- VDO_ADMIN_STATE_SUSPENDED_OPERATION);
- if (result != VDO_SUCCESS) {
- vdo_continue_completion(completion, result);
- return;
- }
- vdo->states.vdo.config.logical_blocks = vdo->block_map->next_entry_count;
- vdo_save_components(vdo, completion);
- return;
- case GROW_LOGICAL_PHASE_GROW_BLOCK_MAP:
- vdo_grow_block_map(vdo->block_map, completion);
- return;
- case GROW_LOGICAL_PHASE_END:
- break;
- case GROW_LOGICAL_PHASE_ERROR:
- vdo_enter_read_only_mode(vdo, completion->result);
- break;
- default:
- vdo_set_completion_result(completion, UDS_BAD_STATE);
- }
- finish_operation_callback(completion);
- }
- /**
- * handle_logical_growth_error() - Handle an error during the grow physical process.
- * @completion: The admin completion.
- */
- static void handle_logical_growth_error(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- if (vdo->admin.phase == GROW_LOGICAL_PHASE_GROW_BLOCK_MAP) {
- /*
- * We've failed to write the new size in the super block, so set our in memory
- * config back to the old size.
- */
- vdo->states.vdo.config.logical_blocks = vdo->block_map->entry_count;
- vdo_abandon_block_map_growth(vdo->block_map);
- }
- vdo->admin.phase = GROW_LOGICAL_PHASE_ERROR;
- grow_logical_callback(completion);
- }
- /**
- * perform_grow_logical() - Grow the logical size of the vdo.
- * @vdo: The vdo to grow.
- * @new_logical_blocks: The size to which the vdo should be grown.
- *
- * Context: This method may only be called when the vdo has been suspended and must not be called
- * from a base thread.
- *
- * Return: VDO_SUCCESS or an error.
- */
- static int perform_grow_logical(struct vdo *vdo, block_count_t new_logical_blocks)
- {
- int result;
- if (vdo->device_config->logical_blocks == new_logical_blocks) {
- /*
- * A table was loaded for which we prepared to grow, but a table without that
- * growth was what we are resuming with.
- */
- vdo_abandon_block_map_growth(vdo->block_map);
- return VDO_SUCCESS;
- }
- vdo_log_info("Resizing logical to %llu",
- (unsigned long long) new_logical_blocks);
- if (vdo->block_map->next_entry_count != new_logical_blocks)
- return VDO_PARAMETER_MISMATCH;
- result = perform_admin_operation(vdo, GROW_LOGICAL_PHASE_START,
- grow_logical_callback,
- handle_logical_growth_error, "grow logical");
- if (result != VDO_SUCCESS)
- return result;
- vdo_log_info("Logical blocks now %llu", (unsigned long long) new_logical_blocks);
- return VDO_SUCCESS;
- }
- static void copy_callback(int read_err, unsigned long write_err, void *context)
- {
- struct vdo_completion *completion = context;
- int result = (((read_err == 0) && (write_err == 0)) ? VDO_SUCCESS : -EIO);
- vdo_continue_completion(completion, result);
- }
- static void partition_to_region(struct partition *partition, struct vdo *vdo,
- struct dm_io_region *region)
- {
- physical_block_number_t pbn = partition->offset - vdo->geometry.bio_offset;
- *region = (struct dm_io_region) {
- .bdev = vdo_get_backing_device(vdo),
- .sector = pbn * VDO_SECTORS_PER_BLOCK,
- .count = partition->count * VDO_SECTORS_PER_BLOCK,
- };
- }
- /**
- * copy_partition() - Copy a partition from the location specified in the current layout to that in
- * the next layout.
- * @vdo: The vdo preparing to grow.
- * @id: The ID of the partition to copy.
- * @parent: The completion to notify when the copy is complete.
- */
- static void copy_partition(struct vdo *vdo, enum partition_id id,
- struct vdo_completion *parent)
- {
- struct dm_io_region read_region, write_regions[1];
- struct partition *from = vdo_get_known_partition(&vdo->layout, id);
- struct partition *to = vdo_get_known_partition(&vdo->next_layout, id);
- partition_to_region(from, vdo, &read_region);
- partition_to_region(to, vdo, &write_regions[0]);
- dm_kcopyd_copy(vdo->partition_copier, &read_region, 1, write_regions, 0,
- copy_callback, parent);
- }
- /**
- * grow_physical_callback() - Callback to initiate a grow physical.
- * @completion: The admin completion.
- *
- * Registered in perform_grow_physical().
- */
- static void grow_physical_callback(struct vdo_completion *completion)
- {
- struct vdo *vdo = completion->vdo;
- int result;
- assert_admin_phase_thread(vdo, __func__);
- switch (advance_phase(vdo)) {
- case GROW_PHYSICAL_PHASE_START:
- if (vdo_is_read_only(vdo)) {
- vdo_log_error_strerror(VDO_READ_ONLY,
- "Can't grow physical size of a read-only VDO");
- vdo_set_completion_result(completion, VDO_READ_ONLY);
- break;
- }
- result = vdo_start_operation(&vdo->admin.state,
- VDO_ADMIN_STATE_SUSPENDED_OPERATION);
- if (result != VDO_SUCCESS) {
- vdo_continue_completion(completion, result);
- return;
- }
- /* Copy the journal into the new layout. */
- copy_partition(vdo, VDO_RECOVERY_JOURNAL_PARTITION, completion);
- return;
- case GROW_PHYSICAL_PHASE_COPY_SUMMARY:
- copy_partition(vdo, VDO_SLAB_SUMMARY_PARTITION, completion);
- return;
- case GROW_PHYSICAL_PHASE_UPDATE_COMPONENTS:
- vdo_uninitialize_layout(&vdo->layout);
- vdo->layout = vdo->next_layout;
- vdo_forget(vdo->next_layout.head);
- vdo->states.vdo.config.physical_blocks = vdo->layout.size;
- vdo_update_slab_depot_size(vdo->depot);
- vdo_save_components(vdo, completion);
- return;
- case GROW_PHYSICAL_PHASE_USE_NEW_SLABS:
- vdo_use_new_slabs(vdo->depot, completion);
- return;
- case GROW_PHYSICAL_PHASE_END:
- vdo->depot->summary_origin =
- vdo_get_known_partition(&vdo->layout,
- VDO_SLAB_SUMMARY_PARTITION)->offset;
- vdo->recovery_journal->origin =
- vdo_get_known_partition(&vdo->layout,
- VDO_RECOVERY_JOURNAL_PARTITION)->offset;
- break;
- case GROW_PHYSICAL_PHASE_ERROR:
- vdo_enter_read_only_mode(vdo, completion->result);
- break;
- default:
- vdo_set_completion_result(completion, UDS_BAD_STATE);
- }
- vdo_uninitialize_layout(&vdo->next_layout);
- finish_operation_callback(completion);
- }
- /**
- * handle_physical_growth_error() - Handle an error during the grow physical process.
- * @completion: The sub-task completion.
- */
- static void handle_physical_growth_error(struct vdo_completion *completion)
- {
- completion->vdo->admin.phase = GROW_PHYSICAL_PHASE_ERROR;
- grow_physical_callback(completion);
- }
- /**
- * perform_grow_physical() - Grow the physical size of the vdo.
- * @vdo: The vdo to resize.
- * @new_physical_blocks: The new physical size in blocks.
- *
- * Context: This method may only be called when the vdo has been suspended and must not be called
- * from a base thread.
- *
- * Return: VDO_SUCCESS or an error.
- */
- static int perform_grow_physical(struct vdo *vdo, block_count_t new_physical_blocks)
- {
- int result;
- block_count_t new_depot_size, prepared_depot_size;
- block_count_t old_physical_blocks = vdo->states.vdo.config.physical_blocks;
- /* Skip any noop grows. */
- if (old_physical_blocks == new_physical_blocks)
- return VDO_SUCCESS;
- if (new_physical_blocks != vdo->next_layout.size) {
- /*
- * Either the VDO isn't prepared to grow, or it was prepared to grow to a different
- * size. Doing this check here relies on the fact that the call to this method is
- * done under the dmsetup message lock.
- */
- vdo_uninitialize_layout(&vdo->next_layout);
- vdo_abandon_new_slabs(vdo->depot);
- return VDO_PARAMETER_MISMATCH;
- }
- /* Validate that we are prepared to grow appropriately. */
- new_depot_size =
- vdo_get_known_partition(&vdo->next_layout, VDO_SLAB_DEPOT_PARTITION)->count;
- prepared_depot_size = (vdo->depot->new_slabs == NULL) ? 0 : vdo->depot->new_size;
- if (prepared_depot_size != new_depot_size)
- return VDO_PARAMETER_MISMATCH;
- result = perform_admin_operation(vdo, GROW_PHYSICAL_PHASE_START,
- grow_physical_callback,
- handle_physical_growth_error, "grow physical");
- if (result != VDO_SUCCESS)
- return result;
- vdo_log_info("Physical block count was %llu, now %llu",
- (unsigned long long) old_physical_blocks,
- (unsigned long long) new_physical_blocks);
- return VDO_SUCCESS;
- }
- /**
- * apply_new_vdo_configuration() - Attempt to make any configuration changes from the table being
- * resumed.
- * @vdo: The vdo being resumed.
- * @config: The new device configuration derived from the table with which the vdo is being
- * resumed.
- *
- * Return: VDO_SUCCESS or an error.
- */
- static int __must_check apply_new_vdo_configuration(struct vdo *vdo,
- struct device_config *config)
- {
- int result;
- result = perform_grow_logical(vdo, config->logical_blocks);
- if (result != VDO_SUCCESS) {
- vdo_log_error("grow logical operation failed, result = %d", result);
- return result;
- }
- result = perform_grow_physical(vdo, config->physical_blocks);
- if (result != VDO_SUCCESS)
- vdo_log_error("resize operation failed, result = %d", result);
- return result;
- }
- static int vdo_preresume_registered(struct dm_target *ti, struct vdo *vdo)
- {
- struct device_config *config = ti->private;
- const char *device_name = vdo_get_device_name(ti);
- block_count_t backing_blocks;
- int result;
- backing_blocks = get_underlying_device_block_count(vdo);
- if (backing_blocks < config->physical_blocks) {
- /* FIXME: can this still happen? */
- vdo_log_error("resume of device '%s' failed: backing device has %llu blocks but VDO physical size is %llu blocks",
- device_name, (unsigned long long) backing_blocks,
- (unsigned long long) config->physical_blocks);
- return -EINVAL;
- }
- if (vdo_get_admin_state(vdo) == VDO_ADMIN_STATE_PRE_LOADED) {
- vdo_log_info("starting device '%s'", device_name);
- result = perform_admin_operation(vdo, LOAD_PHASE_START, load_callback,
- handle_load_error, "load");
- if (result == VDO_UNSUPPORTED_VERSION) {
- /*
- * A component version is not supported. This can happen when the
- * recovery journal metadata is in an old version format. Abort the
- * load without saving the state.
- */
- vdo->suspend_type = VDO_ADMIN_STATE_SUSPENDING;
- perform_admin_operation(vdo, SUSPEND_PHASE_START,
- suspend_callback, suspend_callback,
- "suspend");
- return result;
- }
- if ((result != VDO_SUCCESS) && (result != VDO_READ_ONLY)) {
- /*
- * Something has gone very wrong. Make sure everything has drained and
- * leave the device in an unresumable state.
- */
- vdo_log_error_strerror(result,
- "Start failed, could not load VDO metadata");
- vdo->suspend_type = VDO_ADMIN_STATE_STOPPING;
- perform_admin_operation(vdo, SUSPEND_PHASE_START,
- suspend_callback, suspend_callback,
- "suspend");
- return result;
- }
- /* Even if the VDO is read-only, it is now able to handle read requests. */
- vdo_log_info("device '%s' started", device_name);
- }
- vdo_log_info("resuming device '%s'", device_name);
- /* If this fails, the VDO was not in a state to be resumed. This should never happen. */
- result = apply_new_vdo_configuration(vdo, config);
- BUG_ON(result == VDO_INVALID_ADMIN_STATE);
- /*
- * Now that we've tried to modify the vdo, the new config *is* the config, whether the
- * modifications worked or not.
- */
- vdo->device_config = config;
- /*
- * Any error here is highly unexpected and the state of the vdo is questionable, so we mark
- * it read-only in memory. Because we are suspended, the read-only state will not be
- * written to disk.
- */
- if (result != VDO_SUCCESS) {
- vdo_log_error_strerror(result,
- "Commit of modifications to device '%s' failed",
- device_name);
- vdo_enter_read_only_mode(vdo, result);
- return result;
- }
- if (vdo_get_admin_state(vdo)->normal) {
- /* The VDO was just started, so we don't need to resume it. */
- return VDO_SUCCESS;
- }
- result = perform_admin_operation(vdo, RESUME_PHASE_START, resume_callback,
- resume_callback, "resume");
- BUG_ON(result == VDO_INVALID_ADMIN_STATE);
- if (result == VDO_READ_ONLY) {
- /* Even if the vdo is read-only, it has still resumed. */
- result = VDO_SUCCESS;
- }
- if (result != VDO_SUCCESS)
- vdo_log_error("resume of device '%s' failed with error: %d", device_name,
- result);
- return result;
- }
- static int vdo_preresume(struct dm_target *ti)
- {
- struct registered_thread instance_thread;
- struct vdo *vdo = get_vdo_for_target(ti);
- int result;
- vdo_register_thread_device_id(&instance_thread, &vdo->instance);
- result = vdo_preresume_registered(ti, vdo);
- if ((result == VDO_PARAMETER_MISMATCH) || (result == VDO_INVALID_ADMIN_STATE) ||
- (result == VDO_UNSUPPORTED_VERSION))
- result = -EINVAL;
- vdo_unregister_thread_device_id();
- return vdo_status_to_errno(result);
- }
- static void vdo_resume(struct dm_target *ti)
- {
- struct registered_thread instance_thread;
- vdo_register_thread_device_id(&instance_thread,
- &get_vdo_for_target(ti)->instance);
- vdo_log_info("device '%s' resumed", vdo_get_device_name(ti));
- vdo_unregister_thread_device_id();
- }
- /*
- * If anything changes that affects how user tools will interact with vdo, update the version
- * number and make sure documentation about the change is complete so tools can properly update
- * their management code.
- */
- static struct target_type vdo_target_bio = {
- .features = DM_TARGET_SINGLETON,
- .name = "vdo",
- .version = { 9, 1, 0 },
- .module = THIS_MODULE,
- .ctr = vdo_ctr,
- .dtr = vdo_dtr,
- .io_hints = vdo_io_hints,
- .iterate_devices = vdo_iterate_devices,
- .map = vdo_map_bio,
- .message = vdo_message,
- .status = vdo_status,
- .presuspend = vdo_presuspend,
- .postsuspend = vdo_postsuspend,
- .preresume = vdo_preresume,
- .resume = vdo_resume,
- };
- static bool dm_registered;
- static void vdo_module_destroy(void)
- {
- vdo_log_debug("unloading");
- if (dm_registered)
- dm_unregister_target(&vdo_target_bio);
- VDO_ASSERT_LOG_ONLY(instances.count == 0,
- "should have no instance numbers still in use, but have %u",
- instances.count);
- vdo_free(instances.words);
- memset(&instances, 0, sizeof(struct instance_tracker));
- }
- static int __init vdo_init(void)
- {
- int result = 0;
- /* Memory tracking must be initialized first for accurate accounting. */
- vdo_memory_init();
- vdo_initialize_threads_mutex();
- vdo_initialize_thread_device_registry();
- vdo_initialize_device_registry_once();
- /* Add VDO errors to the set of errors registered by the indexer. */
- result = vdo_register_status_codes();
- if (result != VDO_SUCCESS) {
- vdo_log_error("vdo_register_status_codes failed %d", result);
- vdo_module_destroy();
- return result;
- }
- result = dm_register_target(&vdo_target_bio);
- if (result < 0) {
- vdo_log_error("dm_register_target failed %d", result);
- vdo_module_destroy();
- return result;
- }
- dm_registered = true;
- return result;
- }
- static void __exit vdo_exit(void)
- {
- vdo_module_destroy();
- /* Memory tracking cleanup must be done last. */
- vdo_memory_exit();
- }
- module_init(vdo_init);
- module_exit(vdo_exit);
- module_param_named(log_level, vdo_log_level, uint, 0644);
- MODULE_PARM_DESC(log_level, "Log level for log messages");
- MODULE_DESCRIPTION(DM_NAME " target for transparent deduplication");
- MODULE_AUTHOR("Red Hat, Inc.");
- MODULE_LICENSE("GPL");
|