1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025 |
- /*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2005-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- #include <sys/param.h>
- #include <sys/systm.h>
- #include <sys/bio.h>
- #include <sys/eventhandler.h>
- #include <sys/kernel.h>
- #include <sys/kthread.h>
- #include <sys/limits.h>
- #include <sys/lock.h>
- #include <sys/module.h>
- #include <sys/malloc.h>
- #include <sys/mount.h>
- #include <sys/mutex.h>
- #include <sys/proc.h>
- #include <sys/reboot.h>
- #include <sys/sbuf.h>
- #include <sys/sched.h>
- #include <sys/sysctl.h>
- #include <sys/taskqueue.h>
- #include <sys/vnode.h>
- #ifdef GJ_MEMDEBUG
- #include <sys/stack.h>
- #include <sys/kdb.h>
- #endif
- #include <vm/vm.h>
- #include <vm/vm_kern.h>
- #include <geom/geom.h>
- #include <geom/geom_dbg.h>
- #include <geom/journal/g_journal.h>
- FEATURE(geom_journal, "GEOM journaling support");
- /*
- * On-disk journal format:
- *
- * JH - Journal header
- * RH - Record header
- *
- * %%%%%% ****** +------+ +------+ ****** +------+ %%%%%%
- * % JH % * RH * | Data | | Data | ... * RH * | Data | ... % JH % ...
- * %%%%%% ****** +------+ +------+ ****** +------+ %%%%%%
- *
- */
- CTASSERT(sizeof(struct g_journal_header) <= 512);
- CTASSERT(sizeof(struct g_journal_record_header) <= 512);
- static MALLOC_DEFINE(M_JOURNAL, "journal_data", "GEOM_JOURNAL Data");
- static struct mtx g_journal_cache_mtx;
- MTX_SYSINIT(g_journal_cache, &g_journal_cache_mtx, "cache usage", MTX_DEF);
- const struct g_journal_desc *g_journal_filesystems[] = {
- &g_journal_ufs,
- NULL
- };
- SYSCTL_DECL(_kern_geom);
- int g_journal_debug = 0;
- static u_int g_journal_switch_time = 10;
- static u_int g_journal_force_switch = 70;
- static u_int g_journal_parallel_flushes = 16;
- static u_int g_journal_parallel_copies = 16;
- static u_int g_journal_accept_immediately = 64;
- static u_int g_journal_record_entries = GJ_RECORD_HEADER_NENTRIES;
- static u_int g_journal_do_optimize = 1;
- static SYSCTL_NODE(_kern_geom, OID_AUTO, journal,
- CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
- "GEOM_JOURNAL stuff");
- SYSCTL_INT(_kern_geom_journal, OID_AUTO, debug, CTLFLAG_RWTUN, &g_journal_debug, 0,
- "Debug level");
- SYSCTL_UINT(_kern_geom_journal, OID_AUTO, switch_time, CTLFLAG_RW,
- &g_journal_switch_time, 0, "Switch journals every N seconds");
- SYSCTL_UINT(_kern_geom_journal, OID_AUTO, force_switch, CTLFLAG_RW,
- &g_journal_force_switch, 0, "Force switch when journal is N% full");
- SYSCTL_UINT(_kern_geom_journal, OID_AUTO, parallel_flushes, CTLFLAG_RW,
- &g_journal_parallel_flushes, 0,
- "Number of flush I/O requests to send in parallel");
- SYSCTL_UINT(_kern_geom_journal, OID_AUTO, accept_immediately, CTLFLAG_RW,
- &g_journal_accept_immediately, 0,
- "Number of I/O requests accepted immediately");
- SYSCTL_UINT(_kern_geom_journal, OID_AUTO, parallel_copies, CTLFLAG_RW,
- &g_journal_parallel_copies, 0,
- "Number of copy I/O requests to send in parallel");
- static int
- g_journal_record_entries_sysctl(SYSCTL_HANDLER_ARGS)
- {
- u_int entries;
- int error;
- entries = g_journal_record_entries;
- error = sysctl_handle_int(oidp, &entries, 0, req);
- if (error != 0 || req->newptr == NULL)
- return (error);
- if (entries < 1 || entries > GJ_RECORD_HEADER_NENTRIES)
- return (EINVAL);
- g_journal_record_entries = entries;
- return (0);
- }
- SYSCTL_PROC(_kern_geom_journal, OID_AUTO, record_entries,
- CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
- g_journal_record_entries_sysctl, "I",
- "Maximum number of entries in one journal record");
- SYSCTL_UINT(_kern_geom_journal, OID_AUTO, optimize, CTLFLAG_RW,
- &g_journal_do_optimize, 0, "Try to combine bios on flush and copy");
- static u_long g_journal_cache_used = 0;
- static u_long g_journal_cache_limit = 64 * 1024 * 1024;
- static u_int g_journal_cache_divisor = 2;
- static u_int g_journal_cache_switch = 90;
- static u_int g_journal_cache_misses = 0;
- static u_int g_journal_cache_alloc_failures = 0;
- static u_long g_journal_cache_low = 0;
- static SYSCTL_NODE(_kern_geom_journal, OID_AUTO, cache,
- CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
- "GEOM_JOURNAL cache");
- SYSCTL_ULONG(_kern_geom_journal_cache, OID_AUTO, used, CTLFLAG_RD,
- &g_journal_cache_used, 0, "Number of allocated bytes");
- static int
- g_journal_cache_limit_sysctl(SYSCTL_HANDLER_ARGS)
- {
- u_long limit;
- int error;
- limit = g_journal_cache_limit;
- error = sysctl_handle_long(oidp, &limit, 0, req);
- if (error != 0 || req->newptr == NULL)
- return (error);
- g_journal_cache_limit = limit;
- g_journal_cache_low = (limit / 100) * g_journal_cache_switch;
- return (0);
- }
- SYSCTL_PROC(_kern_geom_journal_cache, OID_AUTO, limit,
- CTLTYPE_ULONG | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, NULL, 0,
- g_journal_cache_limit_sysctl, "I",
- "Maximum number of allocated bytes");
- SYSCTL_UINT(_kern_geom_journal_cache, OID_AUTO, divisor, CTLFLAG_RDTUN,
- &g_journal_cache_divisor, 0,
- "(kmem_size / kern.geom.journal.cache.divisor) == cache size");
- static int
- g_journal_cache_switch_sysctl(SYSCTL_HANDLER_ARGS)
- {
- u_int cswitch;
- int error;
- cswitch = g_journal_cache_switch;
- error = sysctl_handle_int(oidp, &cswitch, 0, req);
- if (error != 0 || req->newptr == NULL)
- return (error);
- if (cswitch > 100)
- return (EINVAL);
- g_journal_cache_switch = cswitch;
- g_journal_cache_low = (g_journal_cache_limit / 100) * cswitch;
- return (0);
- }
- SYSCTL_PROC(_kern_geom_journal_cache, OID_AUTO, switch,
- CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
- g_journal_cache_switch_sysctl, "I",
- "Force switch when we hit this percent of cache use");
- SYSCTL_UINT(_kern_geom_journal_cache, OID_AUTO, misses, CTLFLAG_RW,
- &g_journal_cache_misses, 0, "Number of cache misses");
- SYSCTL_UINT(_kern_geom_journal_cache, OID_AUTO, alloc_failures, CTLFLAG_RW,
- &g_journal_cache_alloc_failures, 0, "Memory allocation failures");
- static u_long g_journal_stats_bytes_skipped = 0;
- static u_long g_journal_stats_combined_ios = 0;
- static u_long g_journal_stats_switches = 0;
- static u_long g_journal_stats_wait_for_copy = 0;
- static u_long g_journal_stats_journal_full = 0;
- static u_long g_journal_stats_low_mem = 0;
- static SYSCTL_NODE(_kern_geom_journal, OID_AUTO, stats,
- CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
- "GEOM_JOURNAL statistics");
- SYSCTL_ULONG(_kern_geom_journal_stats, OID_AUTO, skipped_bytes, CTLFLAG_RW,
- &g_journal_stats_bytes_skipped, 0, "Number of skipped bytes");
- SYSCTL_ULONG(_kern_geom_journal_stats, OID_AUTO, combined_ios, CTLFLAG_RW,
- &g_journal_stats_combined_ios, 0, "Number of combined I/O requests");
- SYSCTL_ULONG(_kern_geom_journal_stats, OID_AUTO, switches, CTLFLAG_RW,
- &g_journal_stats_switches, 0, "Number of journal switches");
- SYSCTL_ULONG(_kern_geom_journal_stats, OID_AUTO, wait_for_copy, CTLFLAG_RW,
- &g_journal_stats_wait_for_copy, 0, "Wait for journal copy on switch");
- SYSCTL_ULONG(_kern_geom_journal_stats, OID_AUTO, journal_full, CTLFLAG_RW,
- &g_journal_stats_journal_full, 0,
- "Number of times journal was almost full.");
- SYSCTL_ULONG(_kern_geom_journal_stats, OID_AUTO, low_mem, CTLFLAG_RW,
- &g_journal_stats_low_mem, 0, "Number of times low_mem hook was called.");
- static g_taste_t g_journal_taste;
- static g_ctl_req_t g_journal_config;
- static g_dumpconf_t g_journal_dumpconf;
- static g_init_t g_journal_init;
- static g_fini_t g_journal_fini;
- struct g_class g_journal_class = {
- .name = G_JOURNAL_CLASS_NAME,
- .version = G_VERSION,
- .taste = g_journal_taste,
- .ctlreq = g_journal_config,
- .dumpconf = g_journal_dumpconf,
- .init = g_journal_init,
- .fini = g_journal_fini
- };
- static int g_journal_destroy(struct g_journal_softc *sc);
- static void g_journal_metadata_update(struct g_journal_softc *sc);
- static void g_journal_start_switcher(struct g_class *mp);
- static void g_journal_stop_switcher(void);
- static void g_journal_switch_wait(struct g_journal_softc *sc);
- #define GJ_SWITCHER_WORKING 0
- #define GJ_SWITCHER_DIE 1
- #define GJ_SWITCHER_DIED 2
- static struct proc *g_journal_switcher_proc = NULL;
- static int g_journal_switcher_state = GJ_SWITCHER_WORKING;
- static int g_journal_switcher_wokenup = 0;
- static int g_journal_sync_requested = 0;
- #ifdef GJ_MEMDEBUG
- struct meminfo {
- size_t mi_size;
- struct stack mi_stack;
- };
- #endif
- /*
- * We use our own malloc/realloc/free functions, so we can collect statistics
- * and force journal switch when we're running out of cache.
- */
- static void *
- gj_malloc(size_t size, int flags)
- {
- void *p;
- #ifdef GJ_MEMDEBUG
- struct meminfo *mi;
- #endif
- mtx_lock(&g_journal_cache_mtx);
- if (g_journal_cache_limit > 0 && !g_journal_switcher_wokenup &&
- g_journal_cache_used + size > g_journal_cache_low) {
- GJ_DEBUG(1, "No cache, waking up the switcher.");
- g_journal_switcher_wokenup = 1;
- wakeup(&g_journal_switcher_state);
- }
- if ((flags & M_NOWAIT) && g_journal_cache_limit > 0 &&
- g_journal_cache_used + size > g_journal_cache_limit) {
- mtx_unlock(&g_journal_cache_mtx);
- g_journal_cache_alloc_failures++;
- return (NULL);
- }
- g_journal_cache_used += size;
- mtx_unlock(&g_journal_cache_mtx);
- flags &= ~M_NOWAIT;
- #ifndef GJ_MEMDEBUG
- p = malloc(size, M_JOURNAL, flags | M_WAITOK);
- #else
- mi = malloc(sizeof(*mi) + size, M_JOURNAL, flags | M_WAITOK);
- p = (u_char *)mi + sizeof(*mi);
- mi->mi_size = size;
- stack_save(&mi->mi_stack);
- #endif
- return (p);
- }
- static void
- gj_free(void *p, size_t size)
- {
- #ifdef GJ_MEMDEBUG
- struct meminfo *mi;
- #endif
- KASSERT(p != NULL, ("p=NULL"));
- KASSERT(size > 0, ("size=0"));
- mtx_lock(&g_journal_cache_mtx);
- KASSERT(g_journal_cache_used >= size, ("Freeing too much?"));
- g_journal_cache_used -= size;
- mtx_unlock(&g_journal_cache_mtx);
- #ifdef GJ_MEMDEBUG
- mi = p = (void *)((u_char *)p - sizeof(*mi));
- if (mi->mi_size != size) {
- printf("GJOURNAL: Size mismatch! %zu != %zu\n", size,
- mi->mi_size);
- printf("GJOURNAL: Alloc backtrace:\n");
- stack_print(&mi->mi_stack);
- printf("GJOURNAL: Free backtrace:\n");
- kdb_backtrace();
- }
- #endif
- free(p, M_JOURNAL);
- }
- static void *
- gj_realloc(void *p, size_t size, size_t oldsize)
- {
- void *np;
- #ifndef GJ_MEMDEBUG
- mtx_lock(&g_journal_cache_mtx);
- g_journal_cache_used -= oldsize;
- g_journal_cache_used += size;
- mtx_unlock(&g_journal_cache_mtx);
- np = realloc(p, size, M_JOURNAL, M_WAITOK);
- #else
- np = gj_malloc(size, M_WAITOK);
- bcopy(p, np, MIN(oldsize, size));
- gj_free(p, oldsize);
- #endif
- return (np);
- }
- static void
- g_journal_check_overflow(struct g_journal_softc *sc)
- {
- off_t length, used;
- if ((sc->sc_active.jj_offset < sc->sc_inactive.jj_offset &&
- sc->sc_journal_offset >= sc->sc_inactive.jj_offset) ||
- (sc->sc_active.jj_offset > sc->sc_inactive.jj_offset &&
- sc->sc_journal_offset >= sc->sc_inactive.jj_offset &&
- sc->sc_journal_offset < sc->sc_active.jj_offset)) {
- panic("Journal overflow "
- "(id = %u joffset=%jd active=%jd inactive=%jd)",
- (unsigned)sc->sc_id,
- (intmax_t)sc->sc_journal_offset,
- (intmax_t)sc->sc_active.jj_offset,
- (intmax_t)sc->sc_inactive.jj_offset);
- }
- if (sc->sc_active.jj_offset < sc->sc_inactive.jj_offset) {
- length = sc->sc_inactive.jj_offset - sc->sc_active.jj_offset;
- used = sc->sc_journal_offset - sc->sc_active.jj_offset;
- } else {
- length = sc->sc_jend - sc->sc_active.jj_offset;
- length += sc->sc_inactive.jj_offset - sc->sc_jstart;
- if (sc->sc_journal_offset >= sc->sc_active.jj_offset)
- used = sc->sc_journal_offset - sc->sc_active.jj_offset;
- else {
- used = sc->sc_jend - sc->sc_active.jj_offset;
- used += sc->sc_journal_offset - sc->sc_jstart;
- }
- }
- /* Already woken up? */
- if (g_journal_switcher_wokenup)
- return;
- /*
- * If the active journal takes more than g_journal_force_switch percent
- * of free journal space, we force journal switch.
- */
- KASSERT(length > 0,
- ("length=%jd used=%jd active=%jd inactive=%jd joffset=%jd",
- (intmax_t)length, (intmax_t)used,
- (intmax_t)sc->sc_active.jj_offset,
- (intmax_t)sc->sc_inactive.jj_offset,
- (intmax_t)sc->sc_journal_offset));
- if ((used * 100) / length > g_journal_force_switch) {
- g_journal_stats_journal_full++;
- GJ_DEBUG(1, "Journal %s %jd%% full, forcing journal switch.",
- sc->sc_name, (used * 100) / length);
- mtx_lock(&g_journal_cache_mtx);
- g_journal_switcher_wokenup = 1;
- wakeup(&g_journal_switcher_state);
- mtx_unlock(&g_journal_cache_mtx);
- }
- }
- static void
- g_journal_orphan(struct g_consumer *cp)
- {
- struct g_journal_softc *sc;
- char name[256];
- int error;
- g_topology_assert();
- sc = cp->geom->softc;
- strlcpy(name, cp->provider->name, sizeof(name));
- GJ_DEBUG(0, "Lost provider %s.", name);
- if (sc == NULL)
- return;
- error = g_journal_destroy(sc);
- if (error == 0)
- GJ_DEBUG(0, "Journal %s destroyed.", name);
- else {
- GJ_DEBUG(0, "Cannot destroy journal %s (error=%d). "
- "Destroy it manually after last close.", sc->sc_name,
- error);
- }
- }
- static int
- g_journal_access(struct g_provider *pp, int acr, int acw, int ace)
- {
- struct g_journal_softc *sc;
- int dcw;
- g_topology_assert();
- GJ_DEBUG(2, "Access request for %s: r%dw%de%d.", pp->name,
- acr, acw, ace);
- dcw = pp->acw + acw;
- sc = pp->geom->softc;
- if (sc == NULL || (sc->sc_flags & GJF_DEVICE_DESTROY)) {
- if (acr <= 0 && acw <= 0 && ace <= 0)
- return (0);
- else
- return (ENXIO);
- }
- if (pp->acw == 0 && dcw > 0) {
- GJ_DEBUG(1, "Marking %s as dirty.", sc->sc_name);
- sc->sc_flags &= ~GJF_DEVICE_CLEAN;
- g_topology_unlock();
- g_journal_metadata_update(sc);
- g_topology_lock();
- } /* else if (pp->acw == 0 && dcw > 0 && JEMPTY(sc)) {
- GJ_DEBUG(1, "Marking %s as clean.", sc->sc_name);
- sc->sc_flags |= GJF_DEVICE_CLEAN;
- g_topology_unlock();
- g_journal_metadata_update(sc);
- g_topology_lock();
- } */
- return (0);
- }
- static void
- g_journal_header_encode(struct g_journal_header *hdr, u_char *data)
- {
- bcopy(GJ_HEADER_MAGIC, data, sizeof(GJ_HEADER_MAGIC));
- data += sizeof(GJ_HEADER_MAGIC);
- le32enc(data, hdr->jh_journal_id);
- data += 4;
- le32enc(data, hdr->jh_journal_next_id);
- }
- static int
- g_journal_header_decode(const u_char *data, struct g_journal_header *hdr)
- {
- bcopy(data, hdr->jh_magic, sizeof(hdr->jh_magic));
- data += sizeof(hdr->jh_magic);
- if (bcmp(hdr->jh_magic, GJ_HEADER_MAGIC, sizeof(GJ_HEADER_MAGIC)) != 0)
- return (EINVAL);
- hdr->jh_journal_id = le32dec(data);
- data += 4;
- hdr->jh_journal_next_id = le32dec(data);
- return (0);
- }
- static void
- g_journal_flush_cache(struct g_journal_softc *sc)
- {
- struct bintime bt;
- int error;
- if (sc->sc_bio_flush == 0)
- return;
- GJ_TIMER_START(1, &bt);
- if (sc->sc_bio_flush & GJ_FLUSH_JOURNAL) {
- error = g_io_flush(sc->sc_jconsumer);
- GJ_DEBUG(error == 0 ? 2 : 0, "Flush cache of %s: error=%d.",
- sc->sc_jconsumer->provider->name, error);
- }
- if (sc->sc_bio_flush & GJ_FLUSH_DATA) {
- /*
- * TODO: This could be called in parallel with the
- * previous call.
- */
- error = g_io_flush(sc->sc_dconsumer);
- GJ_DEBUG(error == 0 ? 2 : 0, "Flush cache of %s: error=%d.",
- sc->sc_dconsumer->provider->name, error);
- }
- GJ_TIMER_STOP(1, &bt, "Cache flush time");
- }
- static int
- g_journal_write_header(struct g_journal_softc *sc)
- {
- struct g_journal_header hdr;
- struct g_consumer *cp;
- u_char *buf;
- int error;
- cp = sc->sc_jconsumer;
- buf = gj_malloc(cp->provider->sectorsize, M_WAITOK);
- strlcpy(hdr.jh_magic, GJ_HEADER_MAGIC, sizeof(hdr.jh_magic));
- hdr.jh_journal_id = sc->sc_journal_id;
- hdr.jh_journal_next_id = sc->sc_journal_next_id;
- g_journal_header_encode(&hdr, buf);
- error = g_write_data(cp, sc->sc_journal_offset, buf,
- cp->provider->sectorsize);
- /* if (error == 0) */
- sc->sc_journal_offset += cp->provider->sectorsize;
- gj_free(buf, cp->provider->sectorsize);
- return (error);
- }
- /*
- * Every journal record has a header and data following it.
- * Functions below are used to decode the header before storing it to
- * little endian and to encode it after reading to system endianness.
- */
- static void
- g_journal_record_header_encode(struct g_journal_record_header *hdr,
- u_char *data)
- {
- struct g_journal_entry *ent;
- u_int i;
- bcopy(GJ_RECORD_HEADER_MAGIC, data, sizeof(GJ_RECORD_HEADER_MAGIC));
- data += sizeof(GJ_RECORD_HEADER_MAGIC);
- le32enc(data, hdr->jrh_journal_id);
- data += 8;
- le16enc(data, hdr->jrh_nentries);
- data += 2;
- bcopy(hdr->jrh_sum, data, sizeof(hdr->jrh_sum));
- data += 8;
- for (i = 0; i < hdr->jrh_nentries; i++) {
- ent = &hdr->jrh_entries[i];
- le64enc(data, ent->je_joffset);
- data += 8;
- le64enc(data, ent->je_offset);
- data += 8;
- le64enc(data, ent->je_length);
- data += 8;
- }
- }
- static int
- g_journal_record_header_decode(const u_char *data,
- struct g_journal_record_header *hdr)
- {
- struct g_journal_entry *ent;
- u_int i;
- bcopy(data, hdr->jrh_magic, sizeof(hdr->jrh_magic));
- data += sizeof(hdr->jrh_magic);
- if (strcmp(hdr->jrh_magic, GJ_RECORD_HEADER_MAGIC) != 0)
- return (EINVAL);
- hdr->jrh_journal_id = le32dec(data);
- data += 8;
- hdr->jrh_nentries = le16dec(data);
- data += 2;
- if (hdr->jrh_nentries > GJ_RECORD_HEADER_NENTRIES)
- return (EINVAL);
- bcopy(data, hdr->jrh_sum, sizeof(hdr->jrh_sum));
- data += 8;
- for (i = 0; i < hdr->jrh_nentries; i++) {
- ent = &hdr->jrh_entries[i];
- ent->je_joffset = le64dec(data);
- data += 8;
- ent->je_offset = le64dec(data);
- data += 8;
- ent->je_length = le64dec(data);
- data += 8;
- }
- return (0);
- }
- /*
- * Function reads metadata from a provider (via the given consumer), decodes
- * it to system endianness and verifies its correctness.
- */
- static int
- g_journal_metadata_read(struct g_consumer *cp, struct g_journal_metadata *md)
- {
- struct g_provider *pp;
- u_char *buf;
- int error;
- g_topology_assert();
- error = g_access(cp, 1, 0, 0);
- if (error != 0)
- return (error);
- pp = cp->provider;
- g_topology_unlock();
- /* Metadata is stored in last sector. */
- buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize,
- &error);
- g_topology_lock();
- g_access(cp, -1, 0, 0);
- if (buf == NULL) {
- GJ_DEBUG(1, "Cannot read metadata from %s (error=%d).",
- cp->provider->name, error);
- return (error);
- }
- /* Decode metadata. */
- error = journal_metadata_decode(buf, md);
- g_free(buf);
- /* Is this is gjournal provider at all? */
- if (strcmp(md->md_magic, G_JOURNAL_MAGIC) != 0)
- return (EINVAL);
- /*
- * Are we able to handle this version of metadata?
- * We only maintain backward compatibility.
- */
- if (md->md_version > G_JOURNAL_VERSION) {
- GJ_DEBUG(0,
- "Kernel module is too old to handle metadata from %s.",
- cp->provider->name);
- return (EINVAL);
- }
- /* Is checksum correct? */
- if (error != 0) {
- GJ_DEBUG(0, "MD5 metadata hash mismatch for provider %s.",
- cp->provider->name);
- return (error);
- }
- return (0);
- }
- /*
- * Two functions below are responsible for updating metadata.
- * Only metadata on the data provider is updated (we need to update
- * information about active journal in there).
- */
- static void
- g_journal_metadata_done(struct bio *bp)
- {
- /*
- * There is not much we can do on error except informing about it.
- */
- if (bp->bio_error != 0) {
- GJ_LOGREQ(0, bp, "Cannot update metadata (error=%d).",
- bp->bio_error);
- } else {
- GJ_LOGREQ(2, bp, "Metadata updated.");
- }
- gj_free(bp->bio_data, bp->bio_length);
- g_destroy_bio(bp);
- }
- static void
- g_journal_metadata_update(struct g_journal_softc *sc)
- {
- struct g_journal_metadata md;
- struct g_consumer *cp;
- struct bio *bp;
- u_char *sector;
- cp = sc->sc_dconsumer;
- sector = gj_malloc(cp->provider->sectorsize, M_WAITOK);
- strlcpy(md.md_magic, G_JOURNAL_MAGIC, sizeof(md.md_magic));
- md.md_version = G_JOURNAL_VERSION;
- md.md_id = sc->sc_id;
- md.md_type = sc->sc_orig_type;
- md.md_jstart = sc->sc_jstart;
- md.md_jend = sc->sc_jend;
- md.md_joffset = sc->sc_inactive.jj_offset;
- md.md_jid = sc->sc_journal_previous_id;
- md.md_flags = 0;
- if (sc->sc_flags & GJF_DEVICE_CLEAN)
- md.md_flags |= GJ_FLAG_CLEAN;
- if (sc->sc_flags & GJF_DEVICE_HARDCODED)
- strlcpy(md.md_provider, sc->sc_name, sizeof(md.md_provider));
- else
- bzero(md.md_provider, sizeof(md.md_provider));
- md.md_provsize = cp->provider->mediasize;
- journal_metadata_encode(&md, sector);
- /*
- * Flush the cache, so we know all data are on disk.
- * We write here informations like "journal is consistent", so we need
- * to be sure it is. Without BIO_FLUSH here, we can end up in situation
- * where metadata is stored on disk, but not all data.
- */
- g_journal_flush_cache(sc);
- bp = g_alloc_bio();
- bp->bio_offset = cp->provider->mediasize - cp->provider->sectorsize;
- bp->bio_length = cp->provider->sectorsize;
- bp->bio_data = sector;
- bp->bio_cmd = BIO_WRITE;
- if (!(sc->sc_flags & GJF_DEVICE_DESTROY)) {
- bp->bio_done = g_journal_metadata_done;
- g_io_request(bp, cp);
- } else {
- bp->bio_done = NULL;
- g_io_request(bp, cp);
- biowait(bp, "gjmdu");
- g_journal_metadata_done(bp);
- }
- /*
- * Be sure metadata reached the disk.
- */
- g_journal_flush_cache(sc);
- }
- /*
- * This is where the I/O request comes from the GEOM.
- */
- static void
- g_journal_start(struct bio *bp)
- {
- struct g_journal_softc *sc;
- sc = bp->bio_to->geom->softc;
- GJ_LOGREQ(3, bp, "Request received.");
- switch (bp->bio_cmd) {
- case BIO_READ:
- case BIO_WRITE:
- mtx_lock(&sc->sc_mtx);
- bioq_insert_tail(&sc->sc_regular_queue, bp);
- wakeup(sc);
- mtx_unlock(&sc->sc_mtx);
- return;
- case BIO_GETATTR:
- if (strcmp(bp->bio_attribute, "GJOURNAL::provider") == 0) {
- strlcpy(bp->bio_data, bp->bio_to->name, bp->bio_length);
- bp->bio_completed = strlen(bp->bio_to->name) + 1;
- g_io_deliver(bp, 0);
- return;
- }
- /* FALLTHROUGH */
- case BIO_SPEEDUP:
- case BIO_DELETE:
- default:
- g_io_deliver(bp, EOPNOTSUPP);
- return;
- }
- }
- static void
- g_journal_std_done(struct bio *bp)
- {
- struct g_journal_softc *sc;
- sc = bp->bio_from->geom->softc;
- mtx_lock(&sc->sc_mtx);
- bioq_insert_tail(&sc->sc_back_queue, bp);
- wakeup(sc);
- mtx_unlock(&sc->sc_mtx);
- }
- static struct bio *
- g_journal_new_bio(off_t start, off_t end, off_t joffset, u_char *data,
- int flags)
- {
- struct bio *bp;
- bp = g_alloc_bio();
- bp->bio_offset = start;
- bp->bio_joffset = joffset;
- bp->bio_length = end - start;
- bp->bio_cmd = BIO_WRITE;
- bp->bio_done = g_journal_std_done;
- if (data == NULL)
- bp->bio_data = NULL;
- else {
- bp->bio_data = gj_malloc(bp->bio_length, flags);
- if (bp->bio_data != NULL)
- bcopy(data, bp->bio_data, bp->bio_length);
- }
- return (bp);
- }
- #define g_journal_insert_bio(head, bp, flags) \
- g_journal_insert((head), (bp)->bio_offset, \
- (bp)->bio_offset + (bp)->bio_length, (bp)->bio_joffset, \
- (bp)->bio_data, flags)
- /*
- * The function below does a lot more than just inserting bio to the queue.
- * It keeps the queue sorted by offset and ensures that there are no doubled
- * data (it combines bios where ranges overlap).
- *
- * The function returns the number of bios inserted (as bio can be splitted).
- */
- static int
- g_journal_insert(struct bio **head, off_t nstart, off_t nend, off_t joffset,
- u_char *data, int flags)
- {
- struct bio *nbp, *cbp, *pbp;
- off_t cstart, cend;
- u_char *tmpdata;
- int n;
- GJ_DEBUG(3, "INSERT(%p): (%jd, %jd, %jd)", *head, nstart, nend,
- joffset);
- n = 0;
- pbp = NULL;
- GJQ_FOREACH(*head, cbp) {
- cstart = cbp->bio_offset;
- cend = cbp->bio_offset + cbp->bio_length;
- if (nstart >= cend) {
- /*
- * +-------------+
- * | |
- * | current | +-------------+
- * | bio | | |
- * | | | new |
- * +-------------+ | bio |
- * | |
- * +-------------+
- */
- GJ_DEBUG(3, "INSERT(%p): 1", *head);
- } else if (nend <= cstart) {
- /*
- * +-------------+
- * | |
- * +-------------+ | current |
- * | | | bio |
- * | new | | |
- * | bio | +-------------+
- * | |
- * +-------------+
- */
- nbp = g_journal_new_bio(nstart, nend, joffset, data,
- flags);
- if (pbp == NULL)
- *head = nbp;
- else
- pbp->bio_next = nbp;
- nbp->bio_next = cbp;
- n++;
- GJ_DEBUG(3, "INSERT(%p): 2 (nbp=%p pbp=%p)", *head, nbp,
- pbp);
- goto end;
- } else if (nstart <= cstart && nend >= cend) {
- /*
- * +-------------+ +-------------+
- * | current bio | | current bio |
- * +---+-------------+---+ +-------------+---+
- * | | | | | | |
- * | | | | | | |
- * | +-------------+ | +-------------+ |
- * | new bio | | new bio |
- * +---------------------+ +-----------------+
- *
- * +-------------+ +-------------+
- * | current bio | | current bio |
- * +---+-------------+ +-------------+
- * | | | | |
- * | | | | |
- * | +-------------+ +-------------+
- * | new bio | | new bio |
- * +-----------------+ +-------------+
- */
- g_journal_stats_bytes_skipped += cbp->bio_length;
- cbp->bio_offset = nstart;
- cbp->bio_joffset = joffset;
- cbp->bio_length = cend - nstart;
- if (cbp->bio_data != NULL) {
- gj_free(cbp->bio_data, cend - cstart);
- cbp->bio_data = NULL;
- }
- if (data != NULL) {
- cbp->bio_data = gj_malloc(cbp->bio_length,
- flags);
- if (cbp->bio_data != NULL) {
- bcopy(data, cbp->bio_data,
- cbp->bio_length);
- }
- data += cend - nstart;
- }
- joffset += cend - nstart;
- nstart = cend;
- GJ_DEBUG(3, "INSERT(%p): 3 (cbp=%p)", *head, cbp);
- } else if (nstart > cstart && nend >= cend) {
- /*
- * +-----------------+ +-------------+
- * | current bio | | current bio |
- * | +-------------+ | +---------+---+
- * | | | | | | |
- * | | | | | | |
- * +---+-------------+ +---+---------+ |
- * | new bio | | new bio |
- * +-------------+ +-------------+
- */
- g_journal_stats_bytes_skipped += cend - nstart;
- nbp = g_journal_new_bio(nstart, cend, joffset, data,
- flags);
- nbp->bio_next = cbp->bio_next;
- cbp->bio_next = nbp;
- cbp->bio_length = nstart - cstart;
- if (cbp->bio_data != NULL) {
- cbp->bio_data = gj_realloc(cbp->bio_data,
- cbp->bio_length, cend - cstart);
- }
- if (data != NULL)
- data += cend - nstart;
- joffset += cend - nstart;
- nstart = cend;
- n++;
- GJ_DEBUG(3, "INSERT(%p): 4 (cbp=%p)", *head, cbp);
- } else if (nstart > cstart && nend < cend) {
- /*
- * +---------------------+
- * | current bio |
- * | +-------------+ |
- * | | | |
- * | | | |
- * +---+-------------+---+
- * | new bio |
- * +-------------+
- */
- g_journal_stats_bytes_skipped += nend - nstart;
- nbp = g_journal_new_bio(nstart, nend, joffset, data,
- flags);
- nbp->bio_next = cbp->bio_next;
- cbp->bio_next = nbp;
- if (cbp->bio_data == NULL)
- tmpdata = NULL;
- else
- tmpdata = cbp->bio_data + nend - cstart;
- nbp = g_journal_new_bio(nend, cend,
- cbp->bio_joffset + nend - cstart, tmpdata, flags);
- nbp->bio_next = ((struct bio *)cbp->bio_next)->bio_next;
- ((struct bio *)cbp->bio_next)->bio_next = nbp;
- cbp->bio_length = nstart - cstart;
- if (cbp->bio_data != NULL) {
- cbp->bio_data = gj_realloc(cbp->bio_data,
- cbp->bio_length, cend - cstart);
- }
- n += 2;
- GJ_DEBUG(3, "INSERT(%p): 5 (cbp=%p)", *head, cbp);
- goto end;
- } else if (nstart <= cstart && nend < cend) {
- /*
- * +-----------------+ +-------------+
- * | current bio | | current bio |
- * +-------------+ | +---+---------+ |
- * | | | | | | |
- * | | | | | | |
- * +-------------+---+ | +---------+---+
- * | new bio | | new bio |
- * +-------------+ +-------------+
- */
- g_journal_stats_bytes_skipped += nend - nstart;
- nbp = g_journal_new_bio(nstart, nend, joffset, data,
- flags);
- if (pbp == NULL)
- *head = nbp;
- else
- pbp->bio_next = nbp;
- nbp->bio_next = cbp;
- cbp->bio_offset = nend;
- cbp->bio_length = cend - nend;
- cbp->bio_joffset += nend - cstart;
- tmpdata = cbp->bio_data;
- if (tmpdata != NULL) {
- cbp->bio_data = gj_malloc(cbp->bio_length,
- flags);
- if (cbp->bio_data != NULL) {
- bcopy(tmpdata + nend - cstart,
- cbp->bio_data, cbp->bio_length);
- }
- gj_free(tmpdata, cend - cstart);
- }
- n++;
- GJ_DEBUG(3, "INSERT(%p): 6 (cbp=%p)", *head, cbp);
- goto end;
- }
- if (nstart == nend)
- goto end;
- pbp = cbp;
- }
- nbp = g_journal_new_bio(nstart, nend, joffset, data, flags);
- if (pbp == NULL)
- *head = nbp;
- else
- pbp->bio_next = nbp;
- nbp->bio_next = NULL;
- n++;
- GJ_DEBUG(3, "INSERT(%p): 8 (nbp=%p pbp=%p)", *head, nbp, pbp);
- end:
- if (g_journal_debug >= 3) {
- GJQ_FOREACH(*head, cbp) {
- GJ_DEBUG(3, "ELEMENT: %p (%jd, %jd, %jd, %p)", cbp,
- (intmax_t)cbp->bio_offset,
- (intmax_t)cbp->bio_length,
- (intmax_t)cbp->bio_joffset, cbp->bio_data);
- }
- GJ_DEBUG(3, "INSERT(%p): DONE %d", *head, n);
- }
- return (n);
- }
- /*
- * The function combines neighbour bios trying to squeeze as much data as
- * possible into one bio.
- *
- * The function returns the number of bios combined (negative value).
- */
- static int
- g_journal_optimize(struct bio *head)
- {
- struct bio *cbp, *pbp;
- int n;
- n = 0;
- pbp = NULL;
- GJQ_FOREACH(head, cbp) {
- /* Skip bios which has to be read first. */
- if (cbp->bio_data == NULL) {
- pbp = NULL;
- continue;
- }
- /* There is no previous bio yet. */
- if (pbp == NULL) {
- pbp = cbp;
- continue;
- }
- /* Is this a neighbour bio? */
- if (pbp->bio_offset + pbp->bio_length != cbp->bio_offset) {
- /* Be sure that bios queue is sorted. */
- KASSERT(pbp->bio_offset + pbp->bio_length < cbp->bio_offset,
- ("poffset=%jd plength=%jd coffset=%jd",
- (intmax_t)pbp->bio_offset,
- (intmax_t)pbp->bio_length,
- (intmax_t)cbp->bio_offset));
- pbp = cbp;
- continue;
- }
- /* Be sure we don't end up with too big bio. */
- if (pbp->bio_length + cbp->bio_length > maxphys) {
- pbp = cbp;
- continue;
- }
- /* Ok, we can join bios. */
- GJ_LOGREQ(4, pbp, "Join: ");
- GJ_LOGREQ(4, cbp, "and: ");
- pbp->bio_data = gj_realloc(pbp->bio_data,
- pbp->bio_length + cbp->bio_length, pbp->bio_length);
- bcopy(cbp->bio_data, pbp->bio_data + pbp->bio_length,
- cbp->bio_length);
- gj_free(cbp->bio_data, cbp->bio_length);
- pbp->bio_length += cbp->bio_length;
- pbp->bio_next = cbp->bio_next;
- g_destroy_bio(cbp);
- cbp = pbp;
- g_journal_stats_combined_ios++;
- n--;
- GJ_LOGREQ(4, pbp, "Got: ");
- }
- return (n);
- }
- /*
- * TODO: Update comment.
- * These are functions responsible for copying one portion of data from journal
- * to the destination provider.
- * The order goes like this:
- * 1. Read the header, which contains informations about data blocks
- * following it.
- * 2. Read the data blocks from the journal.
- * 3. Write the data blocks on the data provider.
- *
- * g_journal_copy_start()
- * g_journal_copy_done() - got finished write request, logs potential errors.
- */
- /*
- * When there is no data in cache, this function is used to read it.
- */
- static void
- g_journal_read_first(struct g_journal_softc *sc, struct bio *bp)
- {
- struct bio *cbp;
- /*
- * We were short in memory, so data was freed.
- * In that case we need to read it back from journal.
- */
- cbp = g_alloc_bio();
- cbp->bio_cflags = bp->bio_cflags;
- cbp->bio_parent = bp;
- cbp->bio_offset = bp->bio_joffset;
- cbp->bio_length = bp->bio_length;
- cbp->bio_data = gj_malloc(bp->bio_length, M_WAITOK);
- cbp->bio_cmd = BIO_READ;
- cbp->bio_done = g_journal_std_done;
- GJ_LOGREQ(4, cbp, "READ FIRST");
- g_io_request(cbp, sc->sc_jconsumer);
- g_journal_cache_misses++;
- }
- static void
- g_journal_copy_send(struct g_journal_softc *sc)
- {
- struct bio *bioq, *bp, *lbp;
- bioq = lbp = NULL;
- mtx_lock(&sc->sc_mtx);
- for (; sc->sc_copy_in_progress < g_journal_parallel_copies;) {
- bp = GJQ_FIRST(sc->sc_inactive.jj_queue);
- if (bp == NULL)
- break;
- GJQ_REMOVE(sc->sc_inactive.jj_queue, bp);
- sc->sc_copy_in_progress++;
- GJQ_INSERT_AFTER(bioq, bp, lbp);
- lbp = bp;
- }
- mtx_unlock(&sc->sc_mtx);
- if (g_journal_do_optimize)
- sc->sc_copy_in_progress += g_journal_optimize(bioq);
- while ((bp = GJQ_FIRST(bioq)) != NULL) {
- GJQ_REMOVE(bioq, bp);
- GJQ_INSERT_HEAD(sc->sc_copy_queue, bp);
- bp->bio_cflags = GJ_BIO_COPY;
- if (bp->bio_data == NULL)
- g_journal_read_first(sc, bp);
- else {
- bp->bio_joffset = 0;
- GJ_LOGREQ(4, bp, "SEND");
- g_io_request(bp, sc->sc_dconsumer);
- }
- }
- }
- static void
- g_journal_copy_start(struct g_journal_softc *sc)
- {
- /*
- * Remember in metadata that we're starting to copy journaled data
- * to the data provider.
- * In case of power failure, we will copy these data once again on boot.
- */
- if (!sc->sc_journal_copying) {
- sc->sc_journal_copying = 1;
- GJ_DEBUG(1, "Starting copy of journal.");
- g_journal_metadata_update(sc);
- }
- g_journal_copy_send(sc);
- }
- /*
- * Data block has been read from the journal provider.
- */
- static int
- g_journal_copy_read_done(struct bio *bp)
- {
- struct g_journal_softc *sc;
- struct g_consumer *cp;
- struct bio *pbp;
- KASSERT(bp->bio_cflags == GJ_BIO_COPY,
- ("Invalid bio (%d != %d).", bp->bio_cflags, GJ_BIO_COPY));
- sc = bp->bio_from->geom->softc;
- pbp = bp->bio_parent;
- if (bp->bio_error != 0) {
- GJ_DEBUG(0, "Error while reading data from %s (error=%d).",
- bp->bio_to->name, bp->bio_error);
- /*
- * We will not be able to deliver WRITE request as well.
- */
- gj_free(bp->bio_data, bp->bio_length);
- g_destroy_bio(pbp);
- g_destroy_bio(bp);
- sc->sc_copy_in_progress--;
- return (1);
- }
- pbp->bio_data = bp->bio_data;
- cp = sc->sc_dconsumer;
- g_io_request(pbp, cp);
- GJ_LOGREQ(4, bp, "READ DONE");
- g_destroy_bio(bp);
- return (0);
- }
- /*
- * Data block has been written to the data provider.
- */
- static void
- g_journal_copy_write_done(struct bio *bp)
- {
- struct g_journal_softc *sc;
- KASSERT(bp->bio_cflags == GJ_BIO_COPY,
- ("Invalid bio (%d != %d).", bp->bio_cflags, GJ_BIO_COPY));
- sc = bp->bio_from->geom->softc;
- sc->sc_copy_in_progress--;
- if (bp->bio_error != 0) {
- GJ_LOGREQ(0, bp, "[copy] Error while writing data (error=%d)",
- bp->bio_error);
- }
- GJQ_REMOVE(sc->sc_copy_queue, bp);
- gj_free(bp->bio_data, bp->bio_length);
- GJ_LOGREQ(4, bp, "DONE");
- g_destroy_bio(bp);
- if (sc->sc_copy_in_progress == 0) {
- /*
- * This was the last write request for this journal.
- */
- GJ_DEBUG(1, "Data has been copied.");
- sc->sc_journal_copying = 0;
- }
- }
- static void g_journal_flush_done(struct bio *bp);
- /*
- * Flush one record onto active journal provider.
- */
- static void
- g_journal_flush(struct g_journal_softc *sc)
- {
- struct g_journal_record_header hdr;
- struct g_journal_entry *ent;
- struct g_provider *pp;
- struct bio **bioq;
- struct bio *bp, *fbp, *pbp;
- off_t joffset;
- u_char *data, hash[16];
- MD5_CTX ctx;
- u_int i;
- if (sc->sc_current_count == 0)
- return;
- pp = sc->sc_jprovider;
- GJ_VALIDATE_OFFSET(sc->sc_journal_offset, sc);
- joffset = sc->sc_journal_offset;
- GJ_DEBUG(2, "Storing %d journal entries on %s at %jd.",
- sc->sc_current_count, pp->name, (intmax_t)joffset);
- /*
- * Store 'journal id', so we know to which journal this record belongs.
- */
- hdr.jrh_journal_id = sc->sc_journal_id;
- /* Could be less than g_journal_record_entries if called due timeout. */
- hdr.jrh_nentries = MIN(sc->sc_current_count, g_journal_record_entries);
- strlcpy(hdr.jrh_magic, GJ_RECORD_HEADER_MAGIC, sizeof(hdr.jrh_magic));
- bioq = &sc->sc_active.jj_queue;
- GJQ_LAST(sc->sc_flush_queue, pbp);
- fbp = g_alloc_bio();
- fbp->bio_parent = NULL;
- fbp->bio_cflags = GJ_BIO_JOURNAL;
- fbp->bio_offset = -1;
- fbp->bio_joffset = joffset;
- fbp->bio_length = pp->sectorsize;
- fbp->bio_cmd = BIO_WRITE;
- fbp->bio_done = g_journal_std_done;
- GJQ_INSERT_AFTER(sc->sc_flush_queue, fbp, pbp);
- pbp = fbp;
- fbp->bio_to = pp;
- GJ_LOGREQ(4, fbp, "FLUSH_OUT");
- joffset += pp->sectorsize;
- sc->sc_flush_count++;
- if (sc->sc_flags & GJF_DEVICE_CHECKSUM)
- MD5Init(&ctx);
- for (i = 0; i < hdr.jrh_nentries; i++) {
- bp = sc->sc_current_queue;
- KASSERT(bp != NULL, ("NULL bp"));
- bp->bio_to = pp;
- GJ_LOGREQ(4, bp, "FLUSHED");
- sc->sc_current_queue = bp->bio_next;
- bp->bio_next = NULL;
- sc->sc_current_count--;
- /* Add to the header. */
- ent = &hdr.jrh_entries[i];
- ent->je_offset = bp->bio_offset;
- ent->je_joffset = joffset;
- ent->je_length = bp->bio_length;
- data = bp->bio_data;
- if (sc->sc_flags & GJF_DEVICE_CHECKSUM)
- MD5Update(&ctx, data, ent->je_length);
- g_reset_bio(bp);
- bp->bio_cflags = GJ_BIO_JOURNAL;
- bp->bio_offset = ent->je_offset;
- bp->bio_joffset = ent->je_joffset;
- bp->bio_length = ent->je_length;
- bp->bio_data = data;
- bp->bio_cmd = BIO_WRITE;
- bp->bio_done = g_journal_std_done;
- GJQ_INSERT_AFTER(sc->sc_flush_queue, bp, pbp);
- pbp = bp;
- bp->bio_to = pp;
- GJ_LOGREQ(4, bp, "FLUSH_OUT");
- joffset += bp->bio_length;
- sc->sc_flush_count++;
- /*
- * Add request to the active sc_journal_queue queue.
- * This is our cache. After journal switch we don't have to
- * read the data from the inactive journal, because we keep
- * it in memory.
- */
- g_journal_insert(bioq, ent->je_offset,
- ent->je_offset + ent->je_length, ent->je_joffset, data,
- M_NOWAIT);
- }
- /*
- * After all requests, store valid header.
- */
- data = gj_malloc(pp->sectorsize, M_WAITOK);
- if (sc->sc_flags & GJF_DEVICE_CHECKSUM) {
- MD5Final(hash, &ctx);
- bcopy(hash, hdr.jrh_sum, sizeof(hdr.jrh_sum));
- }
- g_journal_record_header_encode(&hdr, data);
- fbp->bio_data = data;
- sc->sc_journal_offset = joffset;
- g_journal_check_overflow(sc);
- }
- /*
- * Flush request finished.
- */
- static void
- g_journal_flush_done(struct bio *bp)
- {
- struct g_journal_softc *sc;
- struct g_consumer *cp;
- KASSERT((bp->bio_cflags & GJ_BIO_MASK) == GJ_BIO_JOURNAL,
- ("Invalid bio (%d != %d).", bp->bio_cflags, GJ_BIO_JOURNAL));
- cp = bp->bio_from;
- sc = cp->geom->softc;
- sc->sc_flush_in_progress--;
- if (bp->bio_error != 0) {
- GJ_LOGREQ(0, bp, "[flush] Error while writing data (error=%d)",
- bp->bio_error);
- }
- gj_free(bp->bio_data, bp->bio_length);
- GJ_LOGREQ(4, bp, "DONE");
- g_destroy_bio(bp);
- }
- static void g_journal_release_delayed(struct g_journal_softc *sc);
- static void
- g_journal_flush_send(struct g_journal_softc *sc)
- {
- struct g_consumer *cp;
- struct bio *bioq, *bp, *lbp;
- cp = sc->sc_jconsumer;
- bioq = lbp = NULL;
- while (sc->sc_flush_in_progress < g_journal_parallel_flushes) {
- /* Send one flush request to the active journal. */
- bp = GJQ_FIRST(sc->sc_flush_queue);
- if (bp != NULL) {
- GJQ_REMOVE(sc->sc_flush_queue, bp);
- sc->sc_flush_count--;
- bp->bio_offset = bp->bio_joffset;
- bp->bio_joffset = 0;
- sc->sc_flush_in_progress++;
- GJQ_INSERT_AFTER(bioq, bp, lbp);
- lbp = bp;
- }
- /* Try to release delayed requests. */
- g_journal_release_delayed(sc);
- /* If there are no requests to flush, leave. */
- if (GJQ_FIRST(sc->sc_flush_queue) == NULL)
- break;
- }
- if (g_journal_do_optimize)
- sc->sc_flush_in_progress += g_journal_optimize(bioq);
- while ((bp = GJQ_FIRST(bioq)) != NULL) {
- GJQ_REMOVE(bioq, bp);
- GJ_LOGREQ(3, bp, "Flush request send");
- g_io_request(bp, cp);
- }
- }
- static void
- g_journal_add_current(struct g_journal_softc *sc, struct bio *bp)
- {
- int n;
- GJ_LOGREQ(4, bp, "CURRENT %d", sc->sc_current_count);
- n = g_journal_insert_bio(&sc->sc_current_queue, bp, M_WAITOK);
- sc->sc_current_count += n;
- n = g_journal_optimize(sc->sc_current_queue);
- sc->sc_current_count += n;
- /*
- * For requests which are added to the current queue we deliver
- * response immediately.
- */
- bp->bio_completed = bp->bio_length;
- g_io_deliver(bp, 0);
- if (sc->sc_current_count >= g_journal_record_entries) {
- /*
- * Let's flush one record onto active journal provider.
- */
- g_journal_flush(sc);
- }
- }
- static void
- g_journal_release_delayed(struct g_journal_softc *sc)
- {
- struct bio *bp;
- for (;;) {
- /* The flush queue is full, exit. */
- if (sc->sc_flush_count >= g_journal_accept_immediately)
- return;
- bp = bioq_takefirst(&sc->sc_delayed_queue);
- if (bp == NULL)
- return;
- sc->sc_delayed_count--;
- g_journal_add_current(sc, bp);
- }
- }
- /*
- * Add I/O request to the current queue. If we have enough requests for one
- * journal record we flush them onto active journal provider.
- */
- static void
- g_journal_add_request(struct g_journal_softc *sc, struct bio *bp)
- {
- /*
- * The flush queue is full, we need to delay the request.
- */
- if (sc->sc_delayed_count > 0 ||
- sc->sc_flush_count >= g_journal_accept_immediately) {
- GJ_LOGREQ(4, bp, "DELAYED");
- bioq_insert_tail(&sc->sc_delayed_queue, bp);
- sc->sc_delayed_count++;
- return;
- }
- KASSERT(TAILQ_EMPTY(&sc->sc_delayed_queue.queue),
- ("DELAYED queue not empty."));
- g_journal_add_current(sc, bp);
- }
- static void g_journal_read_done(struct bio *bp);
- /*
- * Try to find requested data in cache.
- */
- static struct bio *
- g_journal_read_find(struct bio *head, int sorted, struct bio *pbp, off_t ostart,
- off_t oend)
- {
- off_t cstart, cend;
- struct bio *bp;
- GJQ_FOREACH(head, bp) {
- if (bp->bio_offset == -1)
- continue;
- cstart = MAX(ostart, bp->bio_offset);
- cend = MIN(oend, bp->bio_offset + bp->bio_length);
- if (cend <= ostart)
- continue;
- else if (cstart >= oend) {
- if (!sorted)
- continue;
- else {
- bp = NULL;
- break;
- }
- }
- if (bp->bio_data == NULL)
- break;
- GJ_DEBUG(3, "READ(%p): (%jd, %jd) (bp=%p)", head, cstart, cend,
- bp);
- bcopy(bp->bio_data + cstart - bp->bio_offset,
- pbp->bio_data + cstart - pbp->bio_offset, cend - cstart);
- pbp->bio_completed += cend - cstart;
- if (pbp->bio_completed == pbp->bio_length) {
- /*
- * Cool, the whole request was in cache, deliver happy
- * message.
- */
- g_io_deliver(pbp, 0);
- return (pbp);
- }
- break;
- }
- return (bp);
- }
- /*
- * This function is used for collecting data on read.
- * The complexity is because parts of the data can be stored in four different
- * places:
- * - in memory - the data not yet send to the active journal provider
- * - in the active journal
- * - in the inactive journal
- * - in the data provider
- */
- static void
- g_journal_read(struct g_journal_softc *sc, struct bio *pbp, off_t ostart,
- off_t oend)
- {
- struct bio *bp, *nbp, *head;
- off_t cstart, cend;
- u_int i, sorted = 0;
- GJ_DEBUG(3, "READ: (%jd, %jd)", ostart, oend);
- cstart = cend = -1;
- bp = NULL;
- head = NULL;
- for (i = 1; i <= 5; i++) {
- switch (i) {
- case 1: /* Not-yet-send data. */
- head = sc->sc_current_queue;
- sorted = 1;
- break;
- case 2: /* Skip flush queue as they are also in active queue */
- continue;
- case 3: /* Active journal. */
- head = sc->sc_active.jj_queue;
- sorted = 1;
- break;
- case 4: /* Inactive journal. */
- /*
- * XXX: Here could be a race with g_journal_lowmem().
- */
- head = sc->sc_inactive.jj_queue;
- sorted = 1;
- break;
- case 5: /* In-flight to the data provider. */
- head = sc->sc_copy_queue;
- sorted = 0;
- break;
- default:
- panic("gjournal %s: i=%d", __func__, i);
- }
- bp = g_journal_read_find(head, sorted, pbp, ostart, oend);
- if (bp == pbp) { /* Got the whole request. */
- GJ_DEBUG(2, "Got the whole request from %u.", i);
- return;
- } else if (bp != NULL) {
- cstart = MAX(ostart, bp->bio_offset);
- cend = MIN(oend, bp->bio_offset + bp->bio_length);
- GJ_DEBUG(2, "Got part of the request from %u (%jd-%jd).",
- i, (intmax_t)cstart, (intmax_t)cend);
- break;
- }
- }
- if (bp != NULL) {
- if (bp->bio_data == NULL) {
- nbp = g_duplicate_bio(pbp);
- nbp->bio_cflags = GJ_BIO_READ;
- nbp->bio_data =
- pbp->bio_data + cstart - pbp->bio_offset;
- nbp->bio_offset =
- bp->bio_joffset + cstart - bp->bio_offset;
- nbp->bio_length = cend - cstart;
- nbp->bio_done = g_journal_read_done;
- g_io_request(nbp, sc->sc_jconsumer);
- }
- /*
- * If we don't have the whole request yet, call g_journal_read()
- * recursively.
- */
- if (ostart < cstart)
- g_journal_read(sc, pbp, ostart, cstart);
- if (oend > cend)
- g_journal_read(sc, pbp, cend, oend);
- } else {
- /*
- * No data in memory, no data in journal.
- * Its time for asking data provider.
- */
- GJ_DEBUG(3, "READ(data): (%jd, %jd)", ostart, oend);
- nbp = g_duplicate_bio(pbp);
- nbp->bio_cflags = GJ_BIO_READ;
- nbp->bio_data = pbp->bio_data + ostart - pbp->bio_offset;
- nbp->bio_offset = ostart;
- nbp->bio_length = oend - ostart;
- nbp->bio_done = g_journal_read_done;
- g_io_request(nbp, sc->sc_dconsumer);
- /* We have the whole request, return here. */
- return;
- }
- }
- /*
- * Function responsible for handling finished READ requests.
- * Actually, g_std_done() could be used here, the only difference is that we
- * log error.
- */
- static void
- g_journal_read_done(struct bio *bp)
- {
- struct bio *pbp;
- KASSERT(bp->bio_cflags == GJ_BIO_READ,
- ("Invalid bio (%d != %d).", bp->bio_cflags, GJ_BIO_READ));
- pbp = bp->bio_parent;
- pbp->bio_inbed++;
- pbp->bio_completed += bp->bio_length;
- if (bp->bio_error != 0) {
- if (pbp->bio_error == 0)
- pbp->bio_error = bp->bio_error;
- GJ_DEBUG(0, "Error while reading data from %s (error=%d).",
- bp->bio_to->name, bp->bio_error);
- }
- g_destroy_bio(bp);
- if (pbp->bio_children == pbp->bio_inbed &&
- pbp->bio_completed == pbp->bio_length) {
- /* We're done. */
- g_io_deliver(pbp, 0);
- }
- }
- /*
- * Deactive current journal and active next one.
- */
- static void
- g_journal_switch(struct g_journal_softc *sc)
- {
- struct g_provider *pp;
- if (JEMPTY(sc)) {
- GJ_DEBUG(3, "No need for %s switch.", sc->sc_name);
- pp = LIST_FIRST(&sc->sc_geom->provider);
- if (!(sc->sc_flags & GJF_DEVICE_CLEAN) && pp->acw == 0) {
- sc->sc_flags |= GJF_DEVICE_CLEAN;
- GJ_DEBUG(1, "Marking %s as clean.", sc->sc_name);
- g_journal_metadata_update(sc);
- }
- } else {
- GJ_DEBUG(3, "Switching journal %s.", sc->sc_geom->name);
- pp = sc->sc_jprovider;
- sc->sc_journal_previous_id = sc->sc_journal_id;
- sc->sc_journal_id = sc->sc_journal_next_id;
- sc->sc_journal_next_id = arc4random();
- GJ_VALIDATE_OFFSET(sc->sc_journal_offset, sc);
- g_journal_write_header(sc);
- sc->sc_inactive.jj_offset = sc->sc_active.jj_offset;
- sc->sc_inactive.jj_queue = sc->sc_active.jj_queue;
- sc->sc_active.jj_offset =
- sc->sc_journal_offset - pp->sectorsize;
- sc->sc_active.jj_queue = NULL;
- /*
- * Switch is done, start copying data from the (now) inactive
- * journal to the data provider.
- */
- g_journal_copy_start(sc);
- }
- mtx_lock(&sc->sc_mtx);
- sc->sc_flags &= ~GJF_DEVICE_SWITCH;
- mtx_unlock(&sc->sc_mtx);
- }
- static void
- g_journal_initialize(struct g_journal_softc *sc)
- {
- sc->sc_journal_id = arc4random();
- sc->sc_journal_next_id = arc4random();
- sc->sc_journal_previous_id = sc->sc_journal_id;
- sc->sc_journal_offset = sc->sc_jstart;
- sc->sc_inactive.jj_offset = sc->sc_jstart;
- g_journal_write_header(sc);
- sc->sc_active.jj_offset = sc->sc_jstart;
- }
- static void
- g_journal_mark_as_dirty(struct g_journal_softc *sc)
- {
- const struct g_journal_desc *desc;
- int i;
- GJ_DEBUG(1, "Marking file system %s as dirty.", sc->sc_name);
- for (i = 0; (desc = g_journal_filesystems[i]) != NULL; i++)
- desc->jd_dirty(sc->sc_dconsumer);
- }
- /*
- * Function read record header from the given journal.
- * It is very similar to g_read_data(9), but it doesn't allocate memory for bio
- * and data on every call.
- */
- static int
- g_journal_sync_read(struct g_consumer *cp, struct bio *bp, off_t offset,
- void *data)
- {
- int error;
- g_reset_bio(bp);
- bp->bio_cmd = BIO_READ;
- bp->bio_done = NULL;
- bp->bio_offset = offset;
- bp->bio_length = cp->provider->sectorsize;
- bp->bio_data = data;
- g_io_request(bp, cp);
- error = biowait(bp, "gjs_read");
- return (error);
- }
- #if 0
- /*
- * Function is called when we start the journal device and we detect that
- * one of the journals was not fully copied.
- * The purpose of this function is to read all records headers from journal
- * and placed them in the inactive queue, so we can start journal
- * synchronization process and the journal provider itself.
- * Design decision was taken to not synchronize the whole journal here as it
- * can take too much time. Reading headers only and delaying synchronization
- * process until after journal provider is started should be the best choice.
- */
- #endif
- static void
- g_journal_sync(struct g_journal_softc *sc)
- {
- struct g_journal_record_header rhdr;
- struct g_journal_entry *ent;
- struct g_journal_header jhdr;
- struct g_consumer *cp;
- struct bio *bp, *fbp, *tbp;
- off_t joffset, offset;
- u_char *buf, sum[16];
- uint64_t id;
- MD5_CTX ctx;
- int error, found, i;
- found = 0;
- fbp = NULL;
- cp = sc->sc_jconsumer;
- bp = g_alloc_bio();
- buf = gj_malloc(cp->provider->sectorsize, M_WAITOK);
- offset = joffset = sc->sc_inactive.jj_offset = sc->sc_journal_offset;
- GJ_DEBUG(2, "Looking for termination at %jd.", (intmax_t)joffset);
- /*
- * Read and decode first journal header.
- */
- error = g_journal_sync_read(cp, bp, offset, buf);
- if (error != 0) {
- GJ_DEBUG(0, "Error while reading journal header from %s.",
- cp->provider->name);
- goto end;
- }
- error = g_journal_header_decode(buf, &jhdr);
- if (error != 0) {
- GJ_DEBUG(0, "Cannot decode journal header from %s.",
- cp->provider->name);
- goto end;
- }
- id = sc->sc_journal_id;
- if (jhdr.jh_journal_id != sc->sc_journal_id) {
- GJ_DEBUG(1, "Journal ID mismatch at %jd (0x%08x != 0x%08x).",
- (intmax_t)offset, (u_int)jhdr.jh_journal_id, (u_int)id);
- goto end;
- }
- offset += cp->provider->sectorsize;
- id = sc->sc_journal_next_id = jhdr.jh_journal_next_id;
- for (;;) {
- /*
- * If the biggest record won't fit, look for a record header or
- * journal header from the beginning.
- */
- GJ_VALIDATE_OFFSET(offset, sc);
- error = g_journal_sync_read(cp, bp, offset, buf);
- if (error != 0) {
- /*
- * Not good. Having an error while reading header
- * means, that we cannot read next headers and in
- * consequence we cannot find termination.
- */
- GJ_DEBUG(0,
- "Error while reading record header from %s.",
- cp->provider->name);
- break;
- }
- error = g_journal_record_header_decode(buf, &rhdr);
- if (error != 0) {
- GJ_DEBUG(2, "Not a record header at %jd (error=%d).",
- (intmax_t)offset, error);
- /*
- * This is not a record header.
- * If we are lucky, this is next journal header.
- */
- error = g_journal_header_decode(buf, &jhdr);
- if (error != 0) {
- GJ_DEBUG(1, "Not a journal header at %jd (error=%d).",
- (intmax_t)offset, error);
- /*
- * Nope, this is not journal header, which
- * basically means that journal is not
- * terminated properly.
- */
- error = ENOENT;
- break;
- }
- /*
- * Ok. This is header of _some_ journal. Now we need to
- * verify if this is header of the _next_ journal.
- */
- if (jhdr.jh_journal_id != id) {
- GJ_DEBUG(1, "Journal ID mismatch at %jd "
- "(0x%08x != 0x%08x).", (intmax_t)offset,
- (u_int)jhdr.jh_journal_id, (u_int)id);
- error = ENOENT;
- break;
- }
- /* Found termination. */
- found++;
- GJ_DEBUG(1, "Found termination at %jd (id=0x%08x).",
- (intmax_t)offset, (u_int)id);
- sc->sc_active.jj_offset = offset;
- sc->sc_journal_offset =
- offset + cp->provider->sectorsize;
- sc->sc_journal_id = id;
- id = sc->sc_journal_next_id = jhdr.jh_journal_next_id;
- while ((tbp = fbp) != NULL) {
- fbp = tbp->bio_next;
- GJ_LOGREQ(3, tbp, "Adding request.");
- g_journal_insert_bio(&sc->sc_inactive.jj_queue,
- tbp, M_WAITOK);
- }
- /* Skip journal's header. */
- offset += cp->provider->sectorsize;
- continue;
- }
- /* Skip record's header. */
- offset += cp->provider->sectorsize;
- /*
- * Add information about every record entry to the inactive
- * queue.
- */
- if (sc->sc_flags & GJF_DEVICE_CHECKSUM)
- MD5Init(&ctx);
- for (i = 0; i < rhdr.jrh_nentries; i++) {
- ent = &rhdr.jrh_entries[i];
- GJ_DEBUG(3, "Insert entry: %jd %jd.",
- (intmax_t)ent->je_offset, (intmax_t)ent->je_length);
- g_journal_insert(&fbp, ent->je_offset,
- ent->je_offset + ent->je_length, ent->je_joffset,
- NULL, M_WAITOK);
- if (sc->sc_flags & GJF_DEVICE_CHECKSUM) {
- u_char *buf2;
- /*
- * TODO: Should use faster function (like
- * g_journal_sync_read()).
- */
- buf2 = g_read_data(cp, offset, ent->je_length,
- NULL);
- if (buf2 == NULL)
- GJ_DEBUG(0, "Cannot read data at %jd.",
- (intmax_t)offset);
- else {
- MD5Update(&ctx, buf2, ent->je_length);
- g_free(buf2);
- }
- }
- /* Skip entry's data. */
- offset += ent->je_length;
- }
- if (sc->sc_flags & GJF_DEVICE_CHECKSUM) {
- MD5Final(sum, &ctx);
- if (bcmp(sum, rhdr.jrh_sum, sizeof(rhdr.jrh_sum)) != 0) {
- GJ_DEBUG(0, "MD5 hash mismatch at %jd!",
- (intmax_t)offset);
- }
- }
- }
- end:
- gj_free(bp->bio_data, cp->provider->sectorsize);
- g_destroy_bio(bp);
- /* Remove bios from unterminated journal. */
- while ((tbp = fbp) != NULL) {
- fbp = tbp->bio_next;
- g_destroy_bio(tbp);
- }
- if (found < 1 && joffset > 0) {
- GJ_DEBUG(0, "Journal on %s is broken/corrupted. Initializing.",
- sc->sc_name);
- while ((tbp = sc->sc_inactive.jj_queue) != NULL) {
- sc->sc_inactive.jj_queue = tbp->bio_next;
- g_destroy_bio(tbp);
- }
- g_journal_initialize(sc);
- g_journal_mark_as_dirty(sc);
- } else {
- GJ_DEBUG(0, "Journal %s consistent.", sc->sc_name);
- g_journal_copy_start(sc);
- }
- }
- /*
- * Wait for requests.
- * If we have requests in the current queue, flush them after 3 seconds from the
- * last flush. In this way we don't wait forever (or for journal switch) with
- * storing not full records on journal.
- */
- static void
- g_journal_wait(struct g_journal_softc *sc, time_t last_write)
- {
- int error, timeout;
- GJ_DEBUG(3, "%s: enter", __func__);
- if (sc->sc_current_count == 0) {
- if (g_journal_debug < 2)
- msleep(sc, &sc->sc_mtx, PRIBIO | PDROP, "gj:work", 0);
- else {
- /*
- * If we have debug turned on, show number of elements
- * in various queues.
- */
- for (;;) {
- error = msleep(sc, &sc->sc_mtx, PRIBIO,
- "gj:work", hz * 3);
- if (error == 0) {
- mtx_unlock(&sc->sc_mtx);
- break;
- }
- GJ_DEBUG(3, "Report: current count=%d",
- sc->sc_current_count);
- GJ_DEBUG(3, "Report: flush count=%d",
- sc->sc_flush_count);
- GJ_DEBUG(3, "Report: flush in progress=%d",
- sc->sc_flush_in_progress);
- GJ_DEBUG(3, "Report: copy in progress=%d",
- sc->sc_copy_in_progress);
- GJ_DEBUG(3, "Report: delayed=%d",
- sc->sc_delayed_count);
- }
- }
- GJ_DEBUG(3, "%s: exit 1", __func__);
- return;
- }
- /*
- * Flush even not full records every 3 seconds.
- */
- timeout = (last_write + 3 - time_second) * hz;
- if (timeout <= 0) {
- mtx_unlock(&sc->sc_mtx);
- g_journal_flush(sc);
- g_journal_flush_send(sc);
- GJ_DEBUG(3, "%s: exit 2", __func__);
- return;
- }
- error = msleep(sc, &sc->sc_mtx, PRIBIO | PDROP, "gj:work", timeout);
- if (error == EWOULDBLOCK)
- g_journal_flush_send(sc);
- GJ_DEBUG(3, "%s: exit 3", __func__);
- }
- /*
- * Worker thread.
- */
- static void
- g_journal_worker(void *arg)
- {
- struct g_journal_softc *sc;
- struct g_geom *gp;
- struct g_provider *pp;
- struct bio *bp;
- time_t last_write;
- int type;
- thread_lock(curthread);
- sched_prio(curthread, PRIBIO);
- thread_unlock(curthread);
- sc = arg;
- type = 0; /* gcc */
- if (sc->sc_flags & GJF_DEVICE_CLEAN) {
- GJ_DEBUG(0, "Journal %s clean.", sc->sc_name);
- g_journal_initialize(sc);
- } else {
- g_journal_sync(sc);
- }
- /*
- * Check if we can use BIO_FLUSH.
- */
- sc->sc_bio_flush = 0;
- if (g_io_flush(sc->sc_jconsumer) == 0) {
- sc->sc_bio_flush |= GJ_FLUSH_JOURNAL;
- GJ_DEBUG(1, "BIO_FLUSH supported by %s.",
- sc->sc_jconsumer->provider->name);
- } else {
- GJ_DEBUG(0, "BIO_FLUSH not supported by %s.",
- sc->sc_jconsumer->provider->name);
- }
- if (sc->sc_jconsumer != sc->sc_dconsumer) {
- if (g_io_flush(sc->sc_dconsumer) == 0) {
- sc->sc_bio_flush |= GJ_FLUSH_DATA;
- GJ_DEBUG(1, "BIO_FLUSH supported by %s.",
- sc->sc_dconsumer->provider->name);
- } else {
- GJ_DEBUG(0, "BIO_FLUSH not supported by %s.",
- sc->sc_dconsumer->provider->name);
- }
- }
- gp = sc->sc_geom;
- g_topology_lock();
- pp = g_new_providerf(gp, "%s.journal", sc->sc_name);
- pp->mediasize = sc->sc_mediasize;
- /*
- * There could be a problem when data provider and journal providers
- * have different sectorsize, but such scenario is prevented on journal
- * creation.
- */
- pp->sectorsize = sc->sc_sectorsize;
- g_error_provider(pp, 0);
- g_topology_unlock();
- last_write = time_second;
- if (sc->sc_rootmount != NULL) {
- GJ_DEBUG(1, "root_mount_rel %p", sc->sc_rootmount);
- root_mount_rel(sc->sc_rootmount);
- sc->sc_rootmount = NULL;
- }
- for (;;) {
- /* Get first request from the queue. */
- mtx_lock(&sc->sc_mtx);
- bp = bioq_first(&sc->sc_back_queue);
- if (bp != NULL)
- type = (bp->bio_cflags & GJ_BIO_MASK);
- if (bp == NULL) {
- bp = bioq_first(&sc->sc_regular_queue);
- if (bp != NULL)
- type = GJ_BIO_REGULAR;
- }
- if (bp == NULL) {
- try_switch:
- if ((sc->sc_flags & GJF_DEVICE_SWITCH) ||
- (sc->sc_flags & GJF_DEVICE_DESTROY)) {
- if (sc->sc_current_count > 0) {
- mtx_unlock(&sc->sc_mtx);
- g_journal_flush(sc);
- g_journal_flush_send(sc);
- continue;
- }
- if (sc->sc_flush_in_progress > 0)
- goto sleep;
- if (sc->sc_copy_in_progress > 0)
- goto sleep;
- }
- if (sc->sc_flags & GJF_DEVICE_SWITCH) {
- mtx_unlock(&sc->sc_mtx);
- g_journal_switch(sc);
- wakeup(&sc->sc_journal_copying);
- continue;
- }
- if (sc->sc_flags & GJF_DEVICE_DESTROY) {
- GJ_DEBUG(1, "Shutting down worker "
- "thread for %s.", gp->name);
- sc->sc_worker = NULL;
- wakeup(&sc->sc_worker);
- mtx_unlock(&sc->sc_mtx);
- kproc_exit(0);
- }
- sleep:
- g_journal_wait(sc, last_write);
- continue;
- }
- /*
- * If we're in switch process, we need to delay all new
- * write requests until its done.
- */
- if ((sc->sc_flags & GJF_DEVICE_SWITCH) &&
- type == GJ_BIO_REGULAR && bp->bio_cmd == BIO_WRITE) {
- GJ_LOGREQ(2, bp, "WRITE on SWITCH");
- goto try_switch;
- }
- if (type == GJ_BIO_REGULAR)
- bioq_remove(&sc->sc_regular_queue, bp);
- else
- bioq_remove(&sc->sc_back_queue, bp);
- mtx_unlock(&sc->sc_mtx);
- switch (type) {
- case GJ_BIO_REGULAR:
- /* Regular request. */
- switch (bp->bio_cmd) {
- case BIO_READ:
- g_journal_read(sc, bp, bp->bio_offset,
- bp->bio_offset + bp->bio_length);
- break;
- case BIO_WRITE:
- last_write = time_second;
- g_journal_add_request(sc, bp);
- g_journal_flush_send(sc);
- break;
- default:
- panic("Invalid bio_cmd (%d).", bp->bio_cmd);
- }
- break;
- case GJ_BIO_COPY:
- switch (bp->bio_cmd) {
- case BIO_READ:
- if (g_journal_copy_read_done(bp))
- g_journal_copy_send(sc);
- break;
- case BIO_WRITE:
- g_journal_copy_write_done(bp);
- g_journal_copy_send(sc);
- break;
- default:
- panic("Invalid bio_cmd (%d).", bp->bio_cmd);
- }
- break;
- case GJ_BIO_JOURNAL:
- g_journal_flush_done(bp);
- g_journal_flush_send(sc);
- break;
- case GJ_BIO_READ:
- default:
- panic("Invalid bio (%d).", type);
- }
- }
- }
- static void
- g_journal_destroy_event(void *arg, int flags __unused)
- {
- struct g_journal_softc *sc;
- g_topology_assert();
- sc = arg;
- g_journal_destroy(sc);
- }
- static void
- g_journal_timeout(void *arg)
- {
- struct g_journal_softc *sc;
- sc = arg;
- GJ_DEBUG(0, "Timeout. Journal %s cannot be completed.",
- sc->sc_geom->name);
- g_post_event(g_journal_destroy_event, sc, M_NOWAIT, NULL);
- }
- static struct g_geom *
- g_journal_create(struct g_class *mp, struct g_provider *pp,
- const struct g_journal_metadata *md)
- {
- struct g_journal_softc *sc;
- struct g_geom *gp;
- struct g_consumer *cp;
- int error;
- sc = NULL; /* gcc */
- g_topology_assert();
- /*
- * There are two possibilities:
- * 1. Data and both journals are on the same provider.
- * 2. Data and journals are all on separated providers.
- */
- /* Look for journal device with the same ID. */
- LIST_FOREACH(gp, &mp->geom, geom) {
- sc = gp->softc;
- if (sc == NULL)
- continue;
- if (sc->sc_id == md->md_id)
- break;
- }
- if (gp == NULL)
- sc = NULL;
- else if (sc != NULL && (sc->sc_type & md->md_type) != 0) {
- GJ_DEBUG(1, "Journal device %u already configured.", sc->sc_id);
- return (NULL);
- }
- if (md->md_type == 0 || (md->md_type & ~GJ_TYPE_COMPLETE) != 0) {
- GJ_DEBUG(0, "Invalid type on %s.", pp->name);
- return (NULL);
- }
- if (md->md_type & GJ_TYPE_DATA) {
- GJ_DEBUG(0, "Journal %u: %s contains data.", md->md_id,
- pp->name);
- }
- if (md->md_type & GJ_TYPE_JOURNAL) {
- GJ_DEBUG(0, "Journal %u: %s contains journal.", md->md_id,
- pp->name);
- }
- if (sc == NULL) {
- /* Action geom. */
- sc = malloc(sizeof(*sc), M_JOURNAL, M_WAITOK | M_ZERO);
- sc->sc_id = md->md_id;
- sc->sc_type = 0;
- sc->sc_flags = 0;
- sc->sc_worker = NULL;
- gp = g_new_geomf(mp, "gjournal %u", sc->sc_id);
- gp->start = g_journal_start;
- gp->orphan = g_journal_orphan;
- gp->access = g_journal_access;
- gp->softc = sc;
- gp->flags |= G_GEOM_VOLATILE_BIO;
- sc->sc_geom = gp;
- mtx_init(&sc->sc_mtx, "gjournal", NULL, MTX_DEF);
- bioq_init(&sc->sc_back_queue);
- bioq_init(&sc->sc_regular_queue);
- bioq_init(&sc->sc_delayed_queue);
- sc->sc_delayed_count = 0;
- sc->sc_current_queue = NULL;
- sc->sc_current_count = 0;
- sc->sc_flush_queue = NULL;
- sc->sc_flush_count = 0;
- sc->sc_flush_in_progress = 0;
- sc->sc_copy_queue = NULL;
- sc->sc_copy_in_progress = 0;
- sc->sc_inactive.jj_queue = NULL;
- sc->sc_active.jj_queue = NULL;
- sc->sc_rootmount = root_mount_hold("GJOURNAL");
- GJ_DEBUG(1, "root_mount_hold %p", sc->sc_rootmount);
- callout_init(&sc->sc_callout, 1);
- if (md->md_type != GJ_TYPE_COMPLETE) {
- /*
- * Journal and data are on separate providers.
- * At this point we have only one of them.
- * We setup a timeout in case the other part will not
- * appear, so we won't wait forever.
- */
- callout_reset(&sc->sc_callout, 5 * hz,
- g_journal_timeout, sc);
- }
- }
- /* Remember type of the data provider. */
- if (md->md_type & GJ_TYPE_DATA)
- sc->sc_orig_type = md->md_type;
- sc->sc_type |= md->md_type;
- cp = NULL;
- if (md->md_type & GJ_TYPE_DATA) {
- if (md->md_flags & GJ_FLAG_CLEAN)
- sc->sc_flags |= GJF_DEVICE_CLEAN;
- if (md->md_flags & GJ_FLAG_CHECKSUM)
- sc->sc_flags |= GJF_DEVICE_CHECKSUM;
- cp = g_new_consumer(gp);
- error = g_attach(cp, pp);
- KASSERT(error == 0, ("Cannot attach to %s (error=%d).",
- pp->name, error));
- error = g_access(cp, 1, 1, 1);
- if (error != 0) {
- GJ_DEBUG(0, "Cannot access %s (error=%d).", pp->name,
- error);
- g_journal_destroy(sc);
- return (NULL);
- }
- sc->sc_dconsumer = cp;
- sc->sc_mediasize = pp->mediasize - pp->sectorsize;
- sc->sc_sectorsize = pp->sectorsize;
- sc->sc_jstart = md->md_jstart;
- sc->sc_jend = md->md_jend;
- if (md->md_provider[0] != '\0')
- sc->sc_flags |= GJF_DEVICE_HARDCODED;
- sc->sc_journal_offset = md->md_joffset;
- sc->sc_journal_id = md->md_jid;
- sc->sc_journal_previous_id = md->md_jid;
- }
- if (md->md_type & GJ_TYPE_JOURNAL) {
- if (cp == NULL) {
- cp = g_new_consumer(gp);
- error = g_attach(cp, pp);
- KASSERT(error == 0, ("Cannot attach to %s (error=%d).",
- pp->name, error));
- error = g_access(cp, 1, 1, 1);
- if (error != 0) {
- GJ_DEBUG(0, "Cannot access %s (error=%d).",
- pp->name, error);
- g_journal_destroy(sc);
- return (NULL);
- }
- } else {
- /*
- * Journal is on the same provider as data, which means
- * that data provider ends where journal starts.
- */
- sc->sc_mediasize = md->md_jstart;
- }
- sc->sc_jconsumer = cp;
- }
- /* Start switcher kproc if needed. */
- if (g_journal_switcher_proc == NULL)
- g_journal_start_switcher(mp);
- if ((sc->sc_type & GJ_TYPE_COMPLETE) != GJ_TYPE_COMPLETE) {
- /* Journal is not complete yet. */
- return (gp);
- } else {
- /* Journal complete, cancel timeout. */
- callout_drain(&sc->sc_callout);
- }
- error = kproc_create(g_journal_worker, sc, &sc->sc_worker, 0, 0,
- "g_journal %s", sc->sc_name);
- if (error != 0) {
- GJ_DEBUG(0, "Cannot create worker thread for %s.journal.",
- sc->sc_name);
- g_journal_destroy(sc);
- return (NULL);
- }
- return (gp);
- }
- static void
- g_journal_destroy_consumer(void *arg, int flags __unused)
- {
- struct g_consumer *cp;
- g_topology_assert();
- cp = arg;
- g_detach(cp);
- g_destroy_consumer(cp);
- }
- static int
- g_journal_destroy(struct g_journal_softc *sc)
- {
- struct g_geom *gp;
- struct g_provider *pp;
- struct g_consumer *cp;
- g_topology_assert();
- if (sc == NULL)
- return (ENXIO);
- gp = sc->sc_geom;
- pp = LIST_FIRST(&gp->provider);
- if (pp != NULL) {
- if (pp->acr != 0 || pp->acw != 0 || pp->ace != 0) {
- GJ_DEBUG(1, "Device %s is still open (r%dw%de%d).",
- pp->name, pp->acr, pp->acw, pp->ace);
- return (EBUSY);
- }
- g_error_provider(pp, ENXIO);
- g_journal_flush(sc);
- g_journal_flush_send(sc);
- g_journal_switch(sc);
- }
- sc->sc_flags |= (GJF_DEVICE_DESTROY | GJF_DEVICE_CLEAN);
- g_topology_unlock();
- if (sc->sc_rootmount != NULL) {
- GJ_DEBUG(1, "root_mount_rel %p", sc->sc_rootmount);
- root_mount_rel(sc->sc_rootmount);
- sc->sc_rootmount = NULL;
- }
- callout_drain(&sc->sc_callout);
- mtx_lock(&sc->sc_mtx);
- wakeup(sc);
- while (sc->sc_worker != NULL)
- msleep(&sc->sc_worker, &sc->sc_mtx, PRIBIO, "gj:destroy", 0);
- mtx_unlock(&sc->sc_mtx);
- if (pp != NULL) {
- GJ_DEBUG(1, "Marking %s as clean.", sc->sc_name);
- g_journal_metadata_update(sc);
- g_topology_lock();
- g_wither_provider(pp, ENXIO);
- } else {
- g_topology_lock();
- }
- mtx_destroy(&sc->sc_mtx);
- if (sc->sc_current_count != 0) {
- GJ_DEBUG(0, "Warning! Number of current requests %d.",
- sc->sc_current_count);
- }
- gp->softc = NULL;
- LIST_FOREACH(cp, &gp->consumer, consumer) {
- if (cp->acr + cp->acw + cp->ace > 0)
- g_access(cp, -1, -1, -1);
- /*
- * We keep all consumers open for writing, so if I'll detach
- * and destroy consumer here, I'll get providers for taste, so
- * journal will be started again.
- * Sending an event here, prevents this from happening.
- */
- g_post_event(g_journal_destroy_consumer, cp, M_WAITOK, NULL);
- }
- g_wither_geom(gp, ENXIO);
- free(sc, M_JOURNAL);
- return (0);
- }
- static void
- g_journal_taste_orphan(struct g_consumer *cp)
- {
- KASSERT(1 == 0, ("%s called while tasting %s.", __func__,
- cp->provider->name));
- }
- static struct g_geom *
- g_journal_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
- {
- struct g_journal_metadata md;
- struct g_consumer *cp;
- struct g_geom *gp;
- int error;
- g_topology_assert();
- g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name);
- GJ_DEBUG(2, "Tasting %s.", pp->name);
- if (pp->geom->class == mp)
- return (NULL);
- gp = g_new_geomf(mp, "journal:taste");
- /* This orphan function should be never called. */
- gp->orphan = g_journal_taste_orphan;
- cp = g_new_consumer(gp);
- cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
- error = g_attach(cp, pp);
- if (error == 0) {
- error = g_journal_metadata_read(cp, &md);
- g_detach(cp);
- }
- g_destroy_consumer(cp);
- g_destroy_geom(gp);
- if (error != 0)
- return (NULL);
- gp = NULL;
- if (md.md_provider[0] != '\0' &&
- !g_compare_names(md.md_provider, pp->name))
- return (NULL);
- if (md.md_provsize != 0 && md.md_provsize != pp->mediasize)
- return (NULL);
- if (g_journal_debug >= 2)
- journal_metadata_dump(&md);
- gp = g_journal_create(mp, pp, &md);
- return (gp);
- }
- static struct g_journal_softc *
- g_journal_find_device(struct g_class *mp, const char *name)
- {
- struct g_journal_softc *sc;
- struct g_geom *gp;
- struct g_provider *pp;
- if (strncmp(name, _PATH_DEV, 5) == 0)
- name += 5;
- LIST_FOREACH(gp, &mp->geom, geom) {
- sc = gp->softc;
- if (sc == NULL)
- continue;
- if (sc->sc_flags & GJF_DEVICE_DESTROY)
- continue;
- if ((sc->sc_type & GJ_TYPE_COMPLETE) != GJ_TYPE_COMPLETE)
- continue;
- pp = LIST_FIRST(&gp->provider);
- if (strcmp(sc->sc_name, name) == 0)
- return (sc);
- if (pp != NULL && strcmp(pp->name, name) == 0)
- return (sc);
- }
- return (NULL);
- }
- static void
- g_journal_ctl_destroy(struct gctl_req *req, struct g_class *mp)
- {
- struct g_journal_softc *sc;
- const char *name;
- char param[16];
- int *nargs;
- int error, i;
- g_topology_assert();
- nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
- if (nargs == NULL) {
- gctl_error(req, "No '%s' argument.", "nargs");
- return;
- }
- if (*nargs <= 0) {
- gctl_error(req, "Missing device(s).");
- return;
- }
- for (i = 0; i < *nargs; i++) {
- snprintf(param, sizeof(param), "arg%d", i);
- name = gctl_get_asciiparam(req, param);
- if (name == NULL) {
- gctl_error(req, "No 'arg%d' argument.", i);
- return;
- }
- sc = g_journal_find_device(mp, name);
- if (sc == NULL) {
- gctl_error(req, "No such device: %s.", name);
- return;
- }
- error = g_journal_destroy(sc);
- if (error != 0) {
- gctl_error(req, "Cannot destroy device %s (error=%d).",
- LIST_FIRST(&sc->sc_geom->provider)->name, error);
- return;
- }
- }
- }
- static void
- g_journal_ctl_sync(struct gctl_req *req __unused, struct g_class *mp __unused)
- {
- g_topology_assert();
- g_topology_unlock();
- g_journal_sync_requested++;
- wakeup(&g_journal_switcher_state);
- while (g_journal_sync_requested > 0)
- tsleep(&g_journal_sync_requested, PRIBIO, "j:sreq", hz / 2);
- g_topology_lock();
- }
- static void
- g_journal_config(struct gctl_req *req, struct g_class *mp, const char *verb)
- {
- uint32_t *version;
- g_topology_assert();
- version = gctl_get_paraml(req, "version", sizeof(*version));
- if (version == NULL) {
- gctl_error(req, "No '%s' argument.", "version");
- return;
- }
- if (*version != G_JOURNAL_VERSION) {
- gctl_error(req, "Userland and kernel parts are out of sync.");
- return;
- }
- if (strcmp(verb, "destroy") == 0 || strcmp(verb, "stop") == 0) {
- g_journal_ctl_destroy(req, mp);
- return;
- } else if (strcmp(verb, "sync") == 0) {
- g_journal_ctl_sync(req, mp);
- return;
- }
- gctl_error(req, "Unknown verb.");
- }
- static void
- g_journal_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
- struct g_consumer *cp, struct g_provider *pp)
- {
- struct g_journal_softc *sc;
- g_topology_assert();
- sc = gp->softc;
- if (sc == NULL)
- return;
- if (pp != NULL) {
- /* Nothing here. */
- } else if (cp != NULL) {
- int first = 1;
- sbuf_printf(sb, "%s<Role>", indent);
- if (cp == sc->sc_dconsumer) {
- sbuf_cat(sb, "Data");
- first = 0;
- }
- if (cp == sc->sc_jconsumer) {
- if (!first)
- sbuf_cat(sb, ",");
- sbuf_cat(sb, "Journal");
- }
- sbuf_cat(sb, "</Role>\n");
- if (cp == sc->sc_jconsumer) {
- sbuf_printf(sb, "<Jstart>%jd</Jstart>\n",
- (intmax_t)sc->sc_jstart);
- sbuf_printf(sb, "<Jend>%jd</Jend>\n",
- (intmax_t)sc->sc_jend);
- }
- } else {
- sbuf_printf(sb, "%s<ID>%u</ID>\n", indent, (u_int)sc->sc_id);
- }
- }
- static eventhandler_tag g_journal_event_shutdown = NULL;
- static eventhandler_tag g_journal_event_lowmem = NULL;
- static void
- g_journal_shutdown_post_sync(void *arg, int howto)
- {
- struct g_class *mp;
- struct g_geom *gp, *gp2;
- if ((howto & RB_NOSYNC) != 0)
- return;
- mp = arg;
- g_topology_lock();
- LIST_FOREACH_SAFE(gp, &mp->geom, geom, gp2) {
- if (gp->softc == NULL)
- continue;
- GJ_DEBUG(0, "Shutting down geom %s.", gp->name);
- g_journal_destroy(gp->softc);
- }
- g_topology_unlock();
- }
- /*
- * Free cached requests from inactive queue in case of low memory.
- * We free GJ_FREE_AT_ONCE elements at once.
- */
- #define GJ_FREE_AT_ONCE 4
- static void
- g_journal_lowmem(void *arg, int howto __unused)
- {
- struct g_journal_softc *sc;
- struct g_class *mp;
- struct g_geom *gp;
- struct bio *bp;
- u_int nfree = GJ_FREE_AT_ONCE;
- g_journal_stats_low_mem++;
- mp = arg;
- g_topology_lock();
- LIST_FOREACH(gp, &mp->geom, geom) {
- sc = gp->softc;
- if (sc == NULL || (sc->sc_flags & GJF_DEVICE_DESTROY))
- continue;
- mtx_lock(&sc->sc_mtx);
- for (bp = sc->sc_inactive.jj_queue; nfree > 0 && bp != NULL;
- nfree--, bp = bp->bio_next) {
- /*
- * This is safe to free the bio_data, because:
- * 1. If bio_data is NULL it will be read from the
- * inactive journal.
- * 2. If bp is sent down, it is first removed from the
- * inactive queue, so it's impossible to free the
- * data from under in-flight bio.
- * On the other hand, freeing elements from the active
- * queue, is not safe.
- */
- if (bp->bio_data != NULL) {
- GJ_DEBUG(2, "Freeing data from %s.",
- sc->sc_name);
- gj_free(bp->bio_data, bp->bio_length);
- bp->bio_data = NULL;
- }
- }
- mtx_unlock(&sc->sc_mtx);
- if (nfree == 0)
- break;
- }
- g_topology_unlock();
- }
- static void g_journal_switcher(void *arg);
- static void
- g_journal_init(struct g_class *mp)
- {
- /* Pick a conservative value if provided value sucks. */
- if (g_journal_cache_divisor <= 0 ||
- (vm_kmem_size / g_journal_cache_divisor == 0)) {
- g_journal_cache_divisor = 5;
- }
- if (g_journal_cache_limit > 0) {
- g_journal_cache_limit = vm_kmem_size / g_journal_cache_divisor;
- g_journal_cache_low =
- (g_journal_cache_limit / 100) * g_journal_cache_switch;
- }
- g_journal_event_shutdown = EVENTHANDLER_REGISTER(shutdown_post_sync,
- g_journal_shutdown_post_sync, mp, EVENTHANDLER_PRI_FIRST);
- if (g_journal_event_shutdown == NULL)
- GJ_DEBUG(0, "Warning! Cannot register shutdown event.");
- g_journal_event_lowmem = EVENTHANDLER_REGISTER(vm_lowmem,
- g_journal_lowmem, mp, EVENTHANDLER_PRI_FIRST);
- if (g_journal_event_lowmem == NULL)
- GJ_DEBUG(0, "Warning! Cannot register lowmem event.");
- }
- static void
- g_journal_fini(struct g_class *mp)
- {
- if (g_journal_event_shutdown != NULL) {
- EVENTHANDLER_DEREGISTER(shutdown_post_sync,
- g_journal_event_shutdown);
- }
- if (g_journal_event_lowmem != NULL)
- EVENTHANDLER_DEREGISTER(vm_lowmem, g_journal_event_lowmem);
- if (g_journal_switcher_proc != NULL)
- g_journal_stop_switcher();
- }
- DECLARE_GEOM_CLASS(g_journal_class, g_journal);
- static const struct g_journal_desc *
- g_journal_find_desc(const char *fstype)
- {
- const struct g_journal_desc *desc;
- int i;
- for (desc = g_journal_filesystems[i = 0]; desc != NULL;
- desc = g_journal_filesystems[++i]) {
- if (strcmp(desc->jd_fstype, fstype) == 0)
- break;
- }
- return (desc);
- }
- static void
- g_journal_switch_wait(struct g_journal_softc *sc)
- {
- struct bintime bt;
- mtx_assert(&sc->sc_mtx, MA_OWNED);
- if (g_journal_debug >= 2) {
- if (sc->sc_flush_in_progress > 0) {
- GJ_DEBUG(2, "%d requests flushing.",
- sc->sc_flush_in_progress);
- }
- if (sc->sc_copy_in_progress > 0) {
- GJ_DEBUG(2, "%d requests copying.",
- sc->sc_copy_in_progress);
- }
- if (sc->sc_flush_count > 0) {
- GJ_DEBUG(2, "%d requests to flush.",
- sc->sc_flush_count);
- }
- if (sc->sc_delayed_count > 0) {
- GJ_DEBUG(2, "%d requests delayed.",
- sc->sc_delayed_count);
- }
- }
- g_journal_stats_switches++;
- if (sc->sc_copy_in_progress > 0)
- g_journal_stats_wait_for_copy++;
- GJ_TIMER_START(1, &bt);
- sc->sc_flags &= ~GJF_DEVICE_BEFORE_SWITCH;
- sc->sc_flags |= GJF_DEVICE_SWITCH;
- wakeup(sc);
- while (sc->sc_flags & GJF_DEVICE_SWITCH) {
- msleep(&sc->sc_journal_copying, &sc->sc_mtx, PRIBIO,
- "gj:switch", 0);
- }
- GJ_TIMER_STOP(1, &bt, "Switch time of %s", sc->sc_name);
- }
- static void
- g_journal_do_switch(struct g_class *classp)
- {
- struct g_journal_softc *sc;
- const struct g_journal_desc *desc;
- struct g_geom *gp;
- struct mount *mp;
- struct bintime bt;
- char *mountpoint;
- int error, save;
- g_topology_lock();
- LIST_FOREACH(gp, &classp->geom, geom) {
- sc = gp->softc;
- if (sc == NULL)
- continue;
- if (sc->sc_flags & GJF_DEVICE_DESTROY)
- continue;
- if ((sc->sc_type & GJ_TYPE_COMPLETE) != GJ_TYPE_COMPLETE)
- continue;
- mtx_lock(&sc->sc_mtx);
- sc->sc_flags |= GJF_DEVICE_BEFORE_SWITCH;
- mtx_unlock(&sc->sc_mtx);
- }
- g_topology_unlock();
- mtx_lock(&mountlist_mtx);
- TAILQ_FOREACH(mp, &mountlist, mnt_list) {
- if (mp->mnt_gjprovider == NULL)
- continue;
- if (mp->mnt_flag & MNT_RDONLY)
- continue;
- desc = g_journal_find_desc(mp->mnt_stat.f_fstypename);
- if (desc == NULL)
- continue;
- if (vfs_busy(mp, MBF_NOWAIT | MBF_MNTLSTLOCK))
- continue;
- /* mtx_unlock(&mountlist_mtx) was done inside vfs_busy() */
- g_topology_lock();
- sc = g_journal_find_device(classp, mp->mnt_gjprovider);
- g_topology_unlock();
- if (sc == NULL) {
- GJ_DEBUG(0, "Cannot find journal geom for %s.",
- mp->mnt_gjprovider);
- goto next;
- } else if (JEMPTY(sc)) {
- mtx_lock(&sc->sc_mtx);
- sc->sc_flags &= ~GJF_DEVICE_BEFORE_SWITCH;
- mtx_unlock(&sc->sc_mtx);
- GJ_DEBUG(3, "No need for %s switch.", sc->sc_name);
- goto next;
- }
- mountpoint = mp->mnt_stat.f_mntonname;
- error = vn_start_write(NULL, &mp, V_WAIT);
- if (error != 0) {
- GJ_DEBUG(0, "vn_start_write(%s) failed (error=%d).",
- mountpoint, error);
- goto next;
- }
- save = curthread_pflags_set(TDP_SYNCIO);
- GJ_TIMER_START(1, &bt);
- vfs_periodic(mp, MNT_NOWAIT);
- GJ_TIMER_STOP(1, &bt, "Msync time of %s", mountpoint);
- GJ_TIMER_START(1, &bt);
- error = VFS_SYNC(mp, MNT_NOWAIT);
- if (error == 0)
- GJ_TIMER_STOP(1, &bt, "Sync time of %s", mountpoint);
- else {
- GJ_DEBUG(0, "Cannot sync file system %s (error=%d).",
- mountpoint, error);
- }
- curthread_pflags_restore(save);
- vn_finished_write(mp);
- if (error != 0)
- goto next;
- /*
- * Send BIO_FLUSH before freezing the file system, so it can be
- * faster after the freeze.
- */
- GJ_TIMER_START(1, &bt);
- g_journal_flush_cache(sc);
- GJ_TIMER_STOP(1, &bt, "BIO_FLUSH time of %s", sc->sc_name);
- GJ_TIMER_START(1, &bt);
- error = vfs_write_suspend(mp, VS_SKIP_UNMOUNT);
- GJ_TIMER_STOP(1, &bt, "Suspend time of %s", mountpoint);
- if (error != 0) {
- GJ_DEBUG(0, "Cannot suspend file system %s (error=%d).",
- mountpoint, error);
- goto next;
- }
- error = desc->jd_clean(mp);
- if (error != 0)
- goto next;
- mtx_lock(&sc->sc_mtx);
- g_journal_switch_wait(sc);
- mtx_unlock(&sc->sc_mtx);
- vfs_write_resume(mp, 0);
- next:
- mtx_lock(&mountlist_mtx);
- vfs_unbusy(mp);
- }
- mtx_unlock(&mountlist_mtx);
- sc = NULL;
- for (;;) {
- g_topology_lock();
- LIST_FOREACH(gp, &g_journal_class.geom, geom) {
- sc = gp->softc;
- if (sc == NULL)
- continue;
- mtx_lock(&sc->sc_mtx);
- if ((sc->sc_type & GJ_TYPE_COMPLETE) == GJ_TYPE_COMPLETE &&
- !(sc->sc_flags & GJF_DEVICE_DESTROY) &&
- (sc->sc_flags & GJF_DEVICE_BEFORE_SWITCH)) {
- break;
- }
- mtx_unlock(&sc->sc_mtx);
- sc = NULL;
- }
- g_topology_unlock();
- if (sc == NULL)
- break;
- mtx_assert(&sc->sc_mtx, MA_OWNED);
- g_journal_switch_wait(sc);
- mtx_unlock(&sc->sc_mtx);
- }
- }
- static void
- g_journal_start_switcher(struct g_class *mp)
- {
- int error __diagused;
- g_topology_assert();
- MPASS(g_journal_switcher_proc == NULL);
- g_journal_switcher_state = GJ_SWITCHER_WORKING;
- error = kproc_create(g_journal_switcher, mp, &g_journal_switcher_proc,
- 0, 0, "g_journal switcher");
- KASSERT(error == 0, ("Cannot create switcher thread."));
- }
- static void
- g_journal_stop_switcher(void)
- {
- g_topology_assert();
- MPASS(g_journal_switcher_proc != NULL);
- g_journal_switcher_state = GJ_SWITCHER_DIE;
- wakeup(&g_journal_switcher_state);
- while (g_journal_switcher_state != GJ_SWITCHER_DIED)
- tsleep(&g_journal_switcher_state, PRIBIO, "jfini:wait", hz / 5);
- GJ_DEBUG(1, "Switcher died.");
- g_journal_switcher_proc = NULL;
- }
- /*
- * TODO: Kill switcher thread on last geom destruction?
- */
- static void
- g_journal_switcher(void *arg)
- {
- struct g_class *mp;
- struct bintime bt;
- int error;
- mp = arg;
- curthread->td_pflags |= TDP_NORUNNINGBUF;
- for (;;) {
- g_journal_switcher_wokenup = 0;
- error = tsleep(&g_journal_switcher_state, PRIBIO, "jsw:wait",
- g_journal_switch_time * hz);
- if (g_journal_switcher_state == GJ_SWITCHER_DIE) {
- g_journal_switcher_state = GJ_SWITCHER_DIED;
- GJ_DEBUG(1, "Switcher exiting.");
- wakeup(&g_journal_switcher_state);
- kproc_exit(0);
- }
- if (error == 0 && g_journal_sync_requested == 0) {
- GJ_DEBUG(1, "Out of cache, force switch (used=%jd "
- "limit=%jd).", (intmax_t)g_journal_cache_used,
- (intmax_t)g_journal_cache_limit);
- }
- GJ_TIMER_START(1, &bt);
- g_journal_do_switch(mp);
- GJ_TIMER_STOP(1, &bt, "Entire switch time");
- if (g_journal_sync_requested > 0) {
- g_journal_sync_requested = 0;
- wakeup(&g_journal_sync_requested);
- }
- }
- }
|