The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/vfs_trans.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: vfs_trans.c,v 1.23.4.1 2009/07/01 22:39:20 snj Exp $   */
    2 
    3 /*-
    4  * Copyright (c) 2007 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Juergen Hannken-Illjes.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   29  * POSSIBILITY OF SUCH DAMAGE.
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.23.4.1 2009/07/01 22:39:20 snj Exp $");
   34 
   35 /*
   36  * File system transaction operations.
   37  */
   38 
   39 #include "opt_ddb.h"
   40 
   41 #if defined(DDB)
   42 #define _LWP_API_PRIVATE        /* Need _lwp_getspecific_by_lwp() */
   43 #endif
   44 
   45 #include <sys/param.h>
   46 #include <sys/systm.h>
   47 #include <sys/kmem.h>
   48 #include <sys/mount.h>
   49 #include <sys/rwlock.h>
   50 #include <sys/vnode.h>
   51 #define _FSTRANS_API_PRIVATE
   52 #include <sys/fstrans.h>
   53 #include <sys/proc.h>
   54 
   55 #include <miscfs/specfs/specdev.h>
   56 #include <miscfs/syncfs/syncfs.h>
   57 
   58 struct fscow_handler {
   59         SLIST_ENTRY(fscow_handler) ch_list;
   60         int (*ch_func)(void *, struct buf *, bool);
   61         void *ch_arg;
   62 };
   63 struct fstrans_lwp_info {
   64         struct fstrans_lwp_info *fli_succ;
   65         struct mount *fli_mount;
   66         int fli_trans_cnt;
   67         int fli_cow_cnt;
   68         enum fstrans_lock_type fli_lock_type;
   69 };
   70 struct fstrans_mount_info {
   71         enum fstrans_state fmi_state;
   72         krwlock_t fmi_shared_lock;
   73         krwlock_t fmi_lazy_lock;
   74         krwlock_t fmi_cow_lock;
   75         SLIST_HEAD(, fscow_handler) fmi_cow_handler;
   76 };
   77 
   78 static specificdata_key_t lwp_data_key;
   79 static kmutex_t vfs_suspend_lock;       /* Serialize suspensions. */
   80 static pool_cache_t fstrans_cache;
   81 
   82 static void fstrans_lwp_dtor(void *);
   83 static struct fstrans_lwp_info *fstrans_get_lwp_info(struct mount *);
   84 
   85 /*
   86  * Initialize
   87  */
   88 void
   89 fstrans_init(void)
   90 {
   91         int error;
   92 
   93         error = lwp_specific_key_create(&lwp_data_key, fstrans_lwp_dtor);
   94         KASSERT(error == 0);
   95 
   96         mutex_init(&vfs_suspend_lock, MUTEX_DEFAULT, IPL_NONE);
   97         fstrans_cache = pool_cache_init(sizeof(struct fstrans_lwp_info), 0, 0,
   98             0, "fstrans", NULL, IPL_NONE, NULL, NULL, NULL);
   99 }
  100 
  101 /*
  102  * Deallocate lwp state
  103  */
  104 static void
  105 fstrans_lwp_dtor(void *arg)
  106 {
  107         struct fstrans_lwp_info *fli, *fli_next;
  108 
  109         for (fli = arg; fli; fli = fli_next) {
  110                 KASSERT(fli->fli_trans_cnt == 0);
  111                 KASSERT(fli->fli_cow_cnt == 0);
  112                 fli_next = fli->fli_succ;
  113                 pool_cache_put(fstrans_cache, fli);
  114         }
  115 }
  116 
  117 /*
  118  * Allocate mount state
  119  */
  120 int
  121 fstrans_mount(struct mount *mp)
  122 {
  123         struct fstrans_mount_info *new;
  124 
  125         if ((new = kmem_alloc(sizeof(*new), KM_SLEEP)) == NULL)
  126                 return ENOMEM;
  127         new->fmi_state = FSTRANS_NORMAL;
  128         rw_init(&new->fmi_lazy_lock);
  129         rw_init(&new->fmi_shared_lock);
  130         SLIST_INIT(&new->fmi_cow_handler);
  131         rw_init(&new->fmi_cow_lock);
  132 
  133         mp->mnt_transinfo = new;
  134         mp->mnt_iflag |= IMNT_HAS_TRANS;
  135 
  136         return 0;
  137 }
  138 
  139 /*
  140  * Deallocate mount state
  141  */
  142 void
  143 fstrans_unmount(struct mount *mp)
  144 {
  145         struct fstrans_mount_info *fmi;
  146         struct fscow_handler *hp;
  147 
  148         if ((fmi = mp->mnt_transinfo) == NULL)
  149                 return;
  150 
  151         KASSERT(fmi->fmi_state == FSTRANS_NORMAL);
  152         rw_destroy(&fmi->fmi_lazy_lock);
  153         rw_destroy(&fmi->fmi_shared_lock);
  154         rw_enter(&fmi->fmi_cow_lock, RW_WRITER);
  155         while ((hp = SLIST_FIRST(&fmi->fmi_cow_handler)) != NULL) {
  156                 SLIST_REMOVE(&fmi->fmi_cow_handler, hp, fscow_handler, ch_list);
  157                 kmem_free(hp, sizeof(*hp));
  158         }
  159         rw_exit(&fmi->fmi_cow_lock);
  160         rw_destroy(&fmi->fmi_cow_lock);
  161         kmem_free(fmi, sizeof(*fmi));
  162         mp->mnt_iflag &= ~IMNT_HAS_TRANS;
  163         mp->mnt_transinfo = NULL;
  164 }
  165 
  166 /*
  167  * Retrieve the per lwp info for this mount
  168  */
  169 static struct fstrans_lwp_info *
  170 fstrans_get_lwp_info(struct mount *mp)
  171 {
  172         struct fstrans_lwp_info *fli, *new_fli;
  173 
  174         new_fli = NULL;
  175         for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
  176                 if (fli->fli_mount == mp)
  177                         return fli;
  178                 else if (fli->fli_trans_cnt == 0 && fli->fli_cow_cnt == 0 &&
  179                     new_fli == NULL)
  180                         new_fli = fli;
  181         }
  182 
  183         if (new_fli == NULL) {
  184                 new_fli = pool_cache_get(fstrans_cache, PR_WAITOK);
  185                 new_fli->fli_trans_cnt = 0;
  186                 new_fli->fli_cow_cnt = 0;
  187                 new_fli->fli_succ = lwp_getspecific(lwp_data_key);
  188                 lwp_setspecific(lwp_data_key, new_fli);
  189         }
  190 
  191         KASSERT(new_fli->fli_trans_cnt == 0);
  192         KASSERT(new_fli->fli_cow_cnt == 0);
  193 
  194         new_fli->fli_mount = mp;
  195 
  196         return new_fli;
  197 }
  198 
  199 /*
  200  * Start a transaction.  If this thread already has a transaction on this
  201  * file system increment the reference counter.
  202  * A thread with an exclusive transaction lock may get a shared or lazy one.
  203  * A thread with a shared or lazy transaction lock cannot upgrade to an
  204  * exclusive one yet.
  205  */
  206 int
  207 _fstrans_start(struct mount *mp, enum fstrans_lock_type lock_type, int wait)
  208 {
  209         krwlock_t *lock_p;
  210         krw_t lock_op;
  211         struct fstrans_lwp_info *fli;
  212         struct fstrans_mount_info *fmi;
  213 
  214         ASSERT_SLEEPABLE();
  215 
  216         if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
  217                 return 0;
  218 
  219         fli = fstrans_get_lwp_info(mp);
  220 
  221         if (fli->fli_trans_cnt > 0) {
  222                 if (fli->fli_lock_type != FSTRANS_EXCL &&
  223                     lock_type == FSTRANS_EXCL)
  224                         panic("fstrans_start: cannot upgrade lock");
  225                 fli->fli_trans_cnt += 1;
  226                 return 0;
  227         }
  228 
  229         fmi = mp->mnt_transinfo;
  230 
  231         if (lock_type == FSTRANS_LAZY)
  232                 lock_p = &fmi->fmi_lazy_lock;
  233         else
  234                 lock_p = &fmi->fmi_shared_lock;
  235         lock_op = (lock_type == FSTRANS_EXCL ? RW_WRITER : RW_READER);
  236 
  237         if (wait)
  238                 rw_enter(lock_p, lock_op);
  239         else if (rw_tryenter(lock_p, lock_op) == 0)
  240                 return EBUSY;
  241 
  242         fli->fli_trans_cnt = 1;
  243         fli->fli_lock_type = lock_type;
  244 
  245         return 0;
  246 }
  247 
  248 /*
  249  * Finish a transaction.
  250  */
  251 void
  252 fstrans_done(struct mount *mp)
  253 {
  254         struct fstrans_lwp_info *fli;
  255         struct fstrans_mount_info *fmi;
  256 
  257         if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
  258                 return;
  259 
  260         for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
  261                 if (fli->fli_mount == mp) {
  262                         fli->fli_trans_cnt -= 1;
  263                         if (fli->fli_trans_cnt > 0)
  264                                 return;
  265                         break;
  266                 }
  267         }
  268 
  269         KASSERT(fli != NULL);
  270         KASSERT(fli->fli_mount == mp);
  271         KASSERT(fli->fli_trans_cnt == 0);
  272 
  273         fmi = mp->mnt_transinfo;
  274         KASSERT(fmi != NULL);
  275         if (fli->fli_lock_type == FSTRANS_LAZY)
  276                 rw_exit(&fmi->fmi_lazy_lock);
  277         else
  278                 rw_exit(&fmi->fmi_shared_lock);
  279 }
  280 
  281 /*
  282  * Check if this thread has an exclusive lock.
  283  */
  284 int
  285 fstrans_is_owner(struct mount *mp)
  286 {
  287         struct fstrans_lwp_info *fli;
  288 
  289         if (mp == NULL)
  290                 return 0;
  291         if ((mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
  292                 return 0;
  293 
  294         for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ)
  295                 if (fli->fli_mount == mp)
  296                         break;
  297 
  298         if (fli == NULL || fli->fli_trans_cnt == 0)
  299                 return 0;
  300 
  301         KASSERT(fli->fli_mount == mp);
  302         KASSERT(fli->fli_trans_cnt > 0);
  303         return (fli->fli_lock_type == FSTRANS_EXCL);
  304 }
  305 
  306 /*
  307  * Set new file system state.
  308  */
  309 int
  310 fstrans_setstate(struct mount *mp, enum fstrans_state new_state)
  311 {
  312         struct fstrans_mount_info *fmi;
  313 
  314         fmi = mp->mnt_transinfo;
  315 
  316         switch (new_state) {
  317         case FSTRANS_SUSPENDING:
  318                 KASSERT(fmi->fmi_state == FSTRANS_NORMAL);
  319                 fstrans_start(mp, FSTRANS_EXCL);
  320                 fmi->fmi_state = FSTRANS_SUSPENDING;
  321                 break;
  322 
  323         case FSTRANS_SUSPENDED:
  324                 KASSERT(fmi->fmi_state == FSTRANS_NORMAL ||
  325                         fmi->fmi_state == FSTRANS_SUSPENDING);
  326                 KASSERT(fmi->fmi_state == FSTRANS_NORMAL ||
  327                         fstrans_is_owner(mp));
  328                 if (fmi->fmi_state == FSTRANS_NORMAL)
  329                         fstrans_start(mp, FSTRANS_EXCL);
  330                 rw_enter(&fmi->fmi_lazy_lock, RW_WRITER);
  331                 fmi->fmi_state = FSTRANS_SUSPENDED;
  332                 break;
  333 
  334         case FSTRANS_NORMAL:
  335                 KASSERT(fmi->fmi_state == FSTRANS_NORMAL ||
  336                         fstrans_is_owner(mp));
  337                 if (fmi->fmi_state == FSTRANS_SUSPENDED)
  338                         rw_exit(&fmi->fmi_lazy_lock);
  339                 if (fmi->fmi_state == FSTRANS_SUSPENDING ||
  340                     fmi->fmi_state == FSTRANS_SUSPENDED) {
  341                         fmi->fmi_state = FSTRANS_NORMAL;
  342                         fstrans_done(mp);
  343                 }
  344                 break;
  345 
  346         default:
  347                 panic("%s: illegal state %d", __func__, new_state);
  348         }
  349 
  350         return 0;
  351 }
  352 
  353 /*
  354  * Get current file system state
  355  */
  356 enum fstrans_state
  357 fstrans_getstate(struct mount *mp)
  358 {
  359         struct fstrans_mount_info *fmi;
  360 
  361         fmi = mp->mnt_transinfo;
  362 
  363         return fmi->fmi_state;
  364 }
  365 
  366 /*
  367  * Request a filesystem to suspend all operations.
  368  */
  369 int
  370 vfs_suspend(struct mount *mp, int nowait)
  371 {
  372         int error;
  373 
  374         if (nowait) {
  375                 if (!mutex_tryenter(&vfs_suspend_lock))
  376                         return EWOULDBLOCK;
  377         } else
  378                 mutex_enter(&vfs_suspend_lock);
  379 
  380         mutex_enter(&syncer_mutex);
  381 
  382         if ((error = VFS_SUSPENDCTL(mp, SUSPEND_SUSPEND)) != 0) {
  383                 mutex_exit(&syncer_mutex);
  384                 mutex_exit(&vfs_suspend_lock);
  385         }
  386 
  387         return error;
  388 }
  389 
  390 /*
  391  * Request a filesystem to resume all operations.
  392  */
  393 void
  394 vfs_resume(struct mount *mp)
  395 {
  396 
  397         VFS_SUSPENDCTL(mp, SUSPEND_RESUME);
  398         mutex_exit(&syncer_mutex);
  399         mutex_exit(&vfs_suspend_lock);
  400 }
  401 
  402 #if defined(DDB)
  403 void fstrans_dump(int);
  404 
  405 static void
  406 fstrans_print_lwp(struct proc *p, struct lwp *l, int verbose)
  407 {
  408         char prefix[9];
  409         struct fstrans_lwp_info *fli;
  410 
  411         snprintf(prefix, sizeof(prefix), "%d.%d", p->p_pid, l->l_lid);
  412         for (fli = _lwp_getspecific_by_lwp(l, lwp_data_key);
  413              fli;
  414              fli = fli->fli_succ) {
  415                 if (!verbose && fli->fli_trans_cnt == 0)
  416                         continue;
  417                 printf("%-8s", prefix);
  418                 if (verbose)
  419                         printf(" @%p", fli);
  420                 if (fli->fli_mount != NULL)
  421                         printf(" (%s)", fli->fli_mount->mnt_stat.f_mntonname);
  422                 else
  423                         printf(" NULL");
  424                 switch (fli->fli_lock_type) {
  425                 case FSTRANS_LAZY:
  426                         printf(" lazy");
  427                         break;
  428                 case FSTRANS_SHARED:
  429                         printf(" shared");
  430                         break;
  431                 case FSTRANS_EXCL:
  432                         printf(" excl");
  433                         break;
  434                 default:
  435                         printf(" %#x", fli->fli_lock_type);
  436                         break;
  437                 }
  438                 printf(" %d\n", fli->fli_trans_cnt);
  439                 prefix[0] = '\0';
  440         }
  441 }
  442 
  443 static void
  444 fstrans_print_mount(struct mount *mp, int verbose)
  445 {
  446         struct fstrans_mount_info *fmi;
  447 
  448         fmi = mp->mnt_transinfo;
  449         if (!verbose && (fmi == NULL || fmi->fmi_state == FSTRANS_NORMAL))
  450                 return;
  451 
  452         printf("%-16s ", mp->mnt_stat.f_mntonname);
  453         if (fmi == NULL) {
  454                 printf("(null)\n");
  455                 return;
  456         }
  457         switch (fmi->fmi_state) {
  458         case FSTRANS_NORMAL:
  459                 printf("state normal\n");
  460                 break;
  461         case FSTRANS_SUSPENDING:
  462                 printf("state suspending\n");
  463                 break;
  464         case FSTRANS_SUSPENDED:
  465                 printf("state suspended\n");
  466                 break;
  467         default:
  468                 printf("state %#x\n", fmi->fmi_state);
  469                 break;
  470         }
  471         printf("%16s r=%d w=%d\n", "lock_lazy:",
  472             rw_read_held(&fmi->fmi_lazy_lock),
  473             rw_write_held(&fmi->fmi_lazy_lock));
  474         printf("%16s r=%d w=%d\n", "lock_shared:",
  475             rw_read_held(&fmi->fmi_shared_lock),
  476             rw_write_held(&fmi->fmi_shared_lock));
  477 }
  478 
  479 void
  480 fstrans_dump(int full)
  481 {
  482         const struct proclist_desc *pd;
  483         struct proc *p;
  484         struct lwp *l;
  485         struct mount *mp;
  486 
  487         printf("Fstrans locks by lwp:\n");
  488         for (pd = proclists; pd->pd_list != NULL; pd++)
  489                 PROCLIST_FOREACH(p, pd->pd_list)
  490                         LIST_FOREACH(l, &p->p_lwps, l_sibling)
  491                                 fstrans_print_lwp(p, l, full == 1);
  492 
  493         printf("Fstrans state by mount:\n");
  494         CIRCLEQ_FOREACH(mp, &mountlist, mnt_list)
  495                 fstrans_print_mount(mp, full == 1);
  496 }
  497 #endif /* defined(DDB) */
  498 
  499 int
  500 fscow_establish(struct mount *mp, int (*func)(void *, struct buf *, bool),
  501     void *arg)
  502 {
  503         struct fstrans_mount_info *fmi;
  504         struct fscow_handler *new;
  505 
  506         if ((mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
  507                 return EINVAL;
  508 
  509         fmi = mp->mnt_transinfo;
  510 
  511         if ((new = kmem_alloc(sizeof(*new), KM_SLEEP)) == NULL)
  512                 return ENOMEM;
  513         new->ch_func = func;
  514         new->ch_arg = arg;
  515         rw_enter(&fmi->fmi_cow_lock, RW_WRITER);
  516         SLIST_INSERT_HEAD(&fmi->fmi_cow_handler, new, ch_list);
  517         rw_exit(&fmi->fmi_cow_lock);
  518 
  519         return 0;
  520 }
  521 
  522 int
  523 fscow_disestablish(struct mount *mp, int (*func)(void *, struct buf *, bool),
  524     void *arg)
  525 {
  526         struct fstrans_mount_info *fmi;
  527         struct fscow_handler *hp = NULL;
  528 
  529         if ((mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
  530                 return EINVAL;
  531 
  532         fmi = mp->mnt_transinfo;
  533 
  534         rw_enter(&fmi->fmi_cow_lock, RW_WRITER);
  535         SLIST_FOREACH(hp, &fmi->fmi_cow_handler, ch_list)
  536                 if (hp->ch_func == func && hp->ch_arg == arg)
  537                         break;
  538         if (hp != NULL) {
  539                 SLIST_REMOVE(&fmi->fmi_cow_handler, hp, fscow_handler, ch_list);
  540                 kmem_free(hp, sizeof(*hp));
  541         }
  542         rw_exit(&fmi->fmi_cow_lock);
  543 
  544         return hp ? 0 : EINVAL;
  545 }
  546 
  547 int
  548 fscow_run(struct buf *bp, bool data_valid)
  549 {
  550         int error = 0;
  551         struct mount *mp;
  552         struct fstrans_lwp_info *fli;
  553         struct fstrans_mount_info *fmi;
  554         struct fscow_handler *hp;
  555 
  556         if ((bp->b_flags & B_COWDONE))
  557                 goto done;
  558         if (bp->b_vp == NULL)
  559                 goto done;
  560         if (bp->b_vp->v_type == VBLK)
  561                 mp = bp->b_vp->v_specmountpoint;
  562         else
  563                 mp = bp->b_vp->v_mount;
  564         if (mp == NULL || (mp->mnt_iflag & IMNT_HAS_TRANS) == 0)
  565                 goto done;
  566 
  567         fli = fstrans_get_lwp_info(mp);
  568         fmi = mp->mnt_transinfo;
  569 
  570         if (fli->fli_cow_cnt++ == 0)
  571                 rw_enter(&fmi->fmi_cow_lock, RW_READER);
  572 
  573         SLIST_FOREACH(hp, &fmi->fmi_cow_handler, ch_list)
  574                 if ((error = (*hp->ch_func)(hp->ch_arg, bp, data_valid)) != 0)
  575                         break;
  576 
  577         if (--fli->fli_cow_cnt == 0)
  578                 rw_exit(&fmi->fmi_cow_lock);
  579 
  580  done:
  581         if (error == 0)
  582                 bp->b_flags |= B_COWDONE;
  583 
  584         return error;
  585 }

Cache object: ef3bf1bdbb4e7c6d8d111cf8605081a2


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.