xref: /reactos/drivers/filesystems/ext2/src/linux.c (revision c2c66aff)
1 /*
2  * COPYRIGHT:        See COPYRIGHT.TXT
3  * PROJECT:          Ext2 File System Driver for WinNT/2K/XP
4  * FILE:             linux.c
5  * PROGRAMMER:       Matt Wu <mattwu@163.com>
6  * HOMEPAGE:         http://www.ext2fsd.com
7  * UPDATE HISTORY:
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <ext2fs.h>
13 #include <linux/jbd.h>
14 #include <linux/errno.h>
15 
16 /* GLOBALS ***************************************************************/
17 
18 extern PEXT2_GLOBAL Ext2Global;
19 
20 /* DEFINITIONS *************************************************************/
21 
22 #ifdef ALLOC_PRAGMA
23 #pragma alloc_text(PAGE, kzalloc)
24 #endif
25 
26 struct task_struct current_task = {
27     /* pid  */ 0,
28     /* tid  */ 1,
29     /* comm */ "current\0",
30     /* journal_info */ NULL
31 };
32 struct task_struct *current = &current_task;
33 
34 void *kzalloc(int size, int flags)
35 {
36     void *buffer = kmalloc(size, flags);
37     if (buffer) {
38         memset(buffer, 0, size);
39     }
40     return buffer;
41 }
42 
43 //
44 // slab routines
45 //
46 
47 kmem_cache_t *
48 kmem_cache_create(
49     const char *    name,
50     size_t          size,
51     size_t          offset,
52     unsigned long   flags,
53     kmem_cache_cb_t ctor
54 )
55 {
56     kmem_cache_t *kc = NULL;
57 
58     kc = kmalloc(sizeof(kmem_cache_t), GFP_KERNEL);
59     if (kc == NULL) {
60         goto errorout;
61     }
62 
63     memset(kc, 0, sizeof(kmem_cache_t));
64     ExInitializeNPagedLookasideList(
65         &kc->la,
66         NULL,
67         NULL,
68         0,
69         size,
70         'JBKC',
71         0);
72 
73     kc->size = size;
74     strncpy(kc->name, name, 31);
75     kc->constructor = ctor;
76 
77 errorout:
78 
79     return kc;
80 }
81 
82 int kmem_cache_destroy(kmem_cache_t * kc)
83 {
84     ASSERT(kc != NULL);
85 
86     ExDeleteNPagedLookasideList(&(kc->la));
87     kfree(kc);
88 
89     return 0;
90 }
91 
92 void* kmem_cache_alloc(kmem_cache_t *kc, int flags)
93 {
94     PVOID  ptr = NULL;
95     ptr = ExAllocateFromNPagedLookasideList(&(kc->la));
96     if (ptr) {
97         atomic_inc(&kc->count);
98         atomic_inc(&kc->acount);
99     }
100     return ptr;
101 }
102 
103 void kmem_cache_free(kmem_cache_t *kc, void *p)
104 {
105     if (p) {
106         atomic_dec(&kc->count);
107         ExFreeToNPagedLookasideList(&(kc->la), p);
108     }
109 }
110 
111 //
112 // wait queue routines
113 //
114 
115 void init_waitqueue_head(wait_queue_head_t *q)
116 {
117     spin_lock_init(&q->lock);
118     INIT_LIST_HEAD(&q->task_list);
119 }
120 
121 struct __wait_queue *
122 wait_queue_create()
123 {
124     struct __wait_queue * wait = NULL;
125     wait = kmalloc(sizeof(struct __wait_queue), GFP_KERNEL);
126     if (!wait) {
127         return NULL;
128     }
129 
130     memset(wait, 0, sizeof(struct __wait_queue));
131     wait->flags = WQ_FLAG_AUTO_REMOVAL;
132     wait->private = (void *)KeGetCurrentThread();
133     INIT_LIST_HEAD(&wait->task_list);
134     KeInitializeEvent(&(wait->event),
135                       SynchronizationEvent,
136                       FALSE);
137 
138     return wait;
139 }
140 
141 void
142 wait_queue_destroy(struct __wait_queue * wait)
143 {
144     kfree(wait);
145 }
146 
147 static inline void __add_wait_queue(wait_queue_head_t *head, struct __wait_queue *new)
148 {
149     list_add(&new->task_list, &head->task_list);
150 }
151 
152 /*
153  * Used for wake-one threads:
154  */
155 static inline void __add_wait_queue_tail(wait_queue_head_t *head,
156         struct __wait_queue *new)
157 {
158     list_add_tail(&new->task_list, &head->task_list);
159 }
160 
161 static inline void __remove_wait_queue(wait_queue_head_t *head,
162                                        struct __wait_queue *old)
163 {
164     list_del(&old->task_list);
165 }
166 
167 void add_wait_queue(wait_queue_head_t *q, wait_queue_t *waiti)
168 {
169     unsigned long flags;
170     struct __wait_queue *wait = *waiti;
171 
172     wait->flags &= ~WQ_FLAG_EXCLUSIVE;
173     spin_lock_irqsave(&q->lock, flags);
174     __add_wait_queue(q, wait);
175     spin_unlock_irqrestore(&q->lock, flags);
176 }
177 
178 void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *waiti)
179 {
180     unsigned long flags;
181     struct __wait_queue *wait = *waiti;
182 
183     wait->flags |= WQ_FLAG_EXCLUSIVE;
184     spin_lock_irqsave(&q->lock, flags);
185     __add_wait_queue_tail(q, wait);
186     spin_unlock_irqrestore(&q->lock, flags);
187 }
188 
189 void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *waiti)
190 {
191     unsigned long flags;
192     struct __wait_queue *wait = *waiti;
193 
194     spin_lock_irqsave(&q->lock, flags);
195     __remove_wait_queue(q, wait);
196     spin_unlock_irqrestore(&q->lock, flags);
197 }
198 
199 /*
200  * Note: we use "set_current_state()" _after_ the wait-queue add,
201  * because we need a memory barrier there on SMP, so that any
202  * wake-function that tests for the wait-queue being active
203  * will be guaranteed to see waitqueue addition _or_ subsequent
204  * tests in this thread will see the wakeup having taken place.
205  *
206  * The spin_unlock() itself is semi-permeable and only protects
207  * one way (it only protects stuff inside the critical region and
208  * stops them from bleeding out - it would still allow subsequent
209  * loads to move into the critical region).
210  */
211 void
212 prepare_to_wait(wait_queue_head_t *q, wait_queue_t *waiti, int state)
213 {
214     unsigned long flags;
215     struct __wait_queue *wait = *waiti;
216 
217     wait->flags &= ~WQ_FLAG_EXCLUSIVE;
218     spin_lock_irqsave(&q->lock, flags);
219     if (list_empty(&wait->task_list))
220         __add_wait_queue(q, wait);
221     /*
222      * don't alter the task state if this is just going to
223      * queue an async wait queue callback
224      */
225     if (is_sync_wait(wait))
226         set_current_state(state);
227     spin_unlock_irqrestore(&q->lock, flags);
228 }
229 
230 void
231 prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *waiti, int state)
232 {
233     unsigned long flags;
234     struct __wait_queue *wait = *waiti;
235 
236     wait->flags |= WQ_FLAG_EXCLUSIVE;
237     spin_lock_irqsave(&q->lock, flags);
238     if (list_empty(&wait->task_list))
239         __add_wait_queue_tail(q, wait);
240     /*
241      * don't alter the task state if this is just going to
242       * queue an async wait queue callback
243      */
244     if (is_sync_wait(wait))
245         set_current_state(state);
246     spin_unlock_irqrestore(&q->lock, flags);
247 }
248 EXPORT_SYMBOL(prepare_to_wait_exclusive);
249 
250 void finish_wait(wait_queue_head_t *q, wait_queue_t *waiti)
251 {
252     unsigned long flags;
253     struct __wait_queue *wait = *waiti;
254 
255     __set_current_state(TASK_RUNNING);
256     /*
257      * We can check for list emptiness outside the lock
258      * IFF:
259      *  - we use the "careful" check that verifies both
260      *    the next and prev pointers, so that there cannot
261      *    be any half-pending updates in progress on other
262      *    CPU's that we haven't seen yet (and that might
263      *    still change the stack area.
264      * and
265      *  - all other users take the lock (ie we can only
266      *    have _one_ other CPU that looks at or modifies
267      *    the list).
268      */
269     if (!list_empty_careful(&wait->task_list)) {
270         spin_lock_irqsave(&q->lock, flags);
271         list_del_init(&wait->task_list);
272         spin_unlock_irqrestore(&q->lock, flags);
273     }
274 
275     /* free wait */
276     wait_queue_destroy(wait);
277 }
278 
279 int wake_up(wait_queue_head_t *queue)
280 {
281     return 0; /* KeSetEvent(&wait->event, 0, FALSE); */
282 }
283 
284 
285 //
286 // kernel timer routines
287 //
288 
289 //
290 // buffer head routines
291 //
292 
293 struct _EXT2_BUFFER_HEAD {
294     kmem_cache_t *  bh_cache;
295     atomic_t        bh_count;
296     atomic_t        bh_acount;
297 } g_jbh = {NULL, ATOMIC_INIT(0)};
298 
299 int
300 ext2_init_bh()
301 {
302     g_jbh.bh_count.counter = 0;
303     g_jbh.bh_acount.counter = 0;
304     g_jbh.bh_cache = kmem_cache_create(
305                          "ext2_bh",   /* bh */
306                          sizeof(struct buffer_head),
307                          0,		        /* offset */
308                          SLAB_TEMPORARY,	/* flags */
309                          NULL);		    /* ctor */
310     if (g_jbh.bh_cache == NULL) {
311         printk(KERN_EMERG "JBD: failed to create handle cache\n");
312         return -ENOMEM;
313     }
314     return 0;
315 }
316 
317 void
318 ext2_destroy_bh()
319 {
320     if (g_jbh.bh_cache) {
321         kmem_cache_destroy(g_jbh.bh_cache);
322         g_jbh.bh_cache = NULL;
323     }
324 }
325 
326 struct buffer_head *
327 new_buffer_head()
328 {
329     struct buffer_head * bh = NULL;
330     bh = kmem_cache_alloc(g_jbh.bh_cache, GFP_NOFS);
331     if (bh) {
332         atomic_inc(&g_jbh.bh_count);
333         atomic_inc(&g_jbh.bh_acount);
334 
335         memset(bh, 0, sizeof(struct buffer_head));
336         InitializeListHead(&bh->b_link);
337         KeQuerySystemTime(&bh->b_ts_creat);
338         DEBUG(DL_BH, ("bh=%p allocated.\n", bh));
339         INC_MEM_COUNT(PS_BUFF_HEAD, bh, sizeof(struct buffer_head));
340     }
341 
342     return bh;
343 }
344 
345 void
346 free_buffer_head(struct buffer_head * bh)
347 {
348     if (bh) {
349         if (bh->b_mdl) {
350 
351             DEBUG(DL_BH, ("bh=%p mdl=%p (Flags:%xh VA:%p) released.\n", bh, bh->b_mdl,
352                           bh->b_mdl->MdlFlags, bh->b_mdl->MappedSystemVa));
353             if (IsFlagOn(bh->b_mdl->MdlFlags, MDL_MAPPED_TO_SYSTEM_VA)) {
354                 MmUnmapLockedPages(bh->b_mdl->MappedSystemVa, bh->b_mdl);
355             }
356             Ext2DestroyMdl(bh->b_mdl);
357         }
358         if (bh->b_bcb) {
359             CcUnpinDataForThread(bh->b_bcb, (ERESOURCE_THREAD)bh | 0x3);
360         }
361 
362         DEBUG(DL_BH, ("bh=%p freed.\n", bh));
363         DEC_MEM_COUNT(PS_BUFF_HEAD, bh, sizeof(struct buffer_head));
364         kmem_cache_free(g_jbh.bh_cache, bh);
365         atomic_dec(&g_jbh.bh_count);
366     }
367 }
368 
369 //
370 // Red-black tree insert routine.
371 //
372 
373 static struct buffer_head *__buffer_head_search(struct rb_root *root,
374                        sector_t blocknr)
375 {
376     struct rb_node *new = root->rb_node;
377 
378     /* Figure out where to put new node */
379     while (new) {
380         struct buffer_head *bh =
381             container_of(new, struct buffer_head, b_rb_node);
382         s64 result = blocknr - bh->b_blocknr;
383 
384         if (result < 0)
385             new = new->rb_left;
386         else if (result > 0)
387             new = new->rb_right;
388         else
389             return bh;
390 
391     }
392 
393     return NULL;
394 }
395 
396 static int buffer_head_blocknr_cmp(struct rb_node *a, struct rb_node *b)
397 {
398     struct buffer_head *a_bh, *b_bh;
399     s64 result;
400     a_bh = container_of(a, struct buffer_head, b_rb_node);
401     b_bh = container_of(b, struct buffer_head, b_rb_node);
402     result = a_bh->b_blocknr - b_bh->b_blocknr;
403 
404     if (result < 0)
405         return -1;
406     if (result > 0)
407         return 1;
408     return 0;
409 }
410 
411 static struct buffer_head *buffer_head_search(struct block_device *bdev,
412                      sector_t blocknr)
413 {
414     struct rb_root *root;
415     root = &bdev->bd_bh_root;
416     return __buffer_head_search(root, blocknr);
417 }
418 
419 static void buffer_head_insert(struct block_device *bdev, struct buffer_head *bh)
420 {
421     rb_insert(&bdev->bd_bh_root, &bh->b_rb_node, buffer_head_blocknr_cmp);
422 }
423 
424 static void buffer_head_remove(struct block_device *bdev, struct buffer_head *bh)
425 {
426     rb_erase(&bh->b_rb_node, &bdev->bd_bh_root);
427 }
428 
429 struct buffer_head *
430 get_block_bh_mdl(
431     struct block_device *   bdev,
432     sector_t                block,
433     unsigned long           size,
434     int                     zero
435 )
436 {
437     PEXT2_VCB Vcb = bdev->bd_priv;
438     LARGE_INTEGER offset;
439     PVOID         bcb = NULL;
440     PVOID         ptr = NULL;
441 
442     struct list_head *entry;
443 
444     /* allocate buffer_head and initialize it */
445     struct buffer_head *bh = NULL, *tbh = NULL;
446 
447     /* check the block is valid or not */
448     if (block >= TOTAL_BLOCKS) {
449         DbgBreak();
450         goto errorout;
451     }
452 
453     /* search the bdev bh list */
454     ExAcquireSharedStarveExclusive(&bdev->bd_bh_lock, TRUE);
455     tbh = buffer_head_search(bdev, block);
456     if (tbh) {
457         bh = tbh;
458         get_bh(bh);
459         ExReleaseResourceLite(&bdev->bd_bh_lock);
460         goto errorout;
461     }
462     ExReleaseResourceLite(&bdev->bd_bh_lock);
463 
464     bh = new_buffer_head();
465     if (!bh) {
466         goto errorout;
467     }
468     bh->b_bdev = bdev;
469     bh->b_blocknr = block;
470     bh->b_size = size;
471     bh->b_data = NULL;
472 
473 again:
474 
475     offset.QuadPart = (s64) bh->b_blocknr;
476     offset.QuadPart <<= BLOCK_BITS;
477 
478     if (zero) {
479         if (!CcPreparePinWrite(Vcb->Volume,
480                             &offset,
481                             bh->b_size,
482                             FALSE,
483                             PIN_WAIT | PIN_EXCLUSIVE,
484                             &bcb,
485                             &ptr)) {
486             Ext2Sleep(100);
487             goto again;
488         }
489     } else {
490         if (!CcPinRead( Vcb->Volume,
491                         &offset,
492                         bh->b_size,
493                         PIN_WAIT,
494                         &bcb,
495                         &ptr)) {
496             Ext2Sleep(100);
497             goto again;
498         }
499         set_buffer_uptodate(bh);
500     }
501 
502     bh->b_mdl = Ext2CreateMdl(ptr, bh->b_size, IoModifyAccess);
503     if (bh->b_mdl) {
504         /* muse map the PTE to NonCached zone. journal recovery will
505            access the PTE under spinlock: DISPATCH_LEVEL IRQL */
506         bh->b_data = MmMapLockedPagesSpecifyCache(
507                          bh->b_mdl, KernelMode, MmNonCached,
508                          NULL,FALSE, HighPagePriority);
509         /* bh->b_data = MmMapLockedPages(bh->b_mdl, KernelMode); */
510     }
511     if (!bh->b_mdl || !bh->b_data) {
512         free_buffer_head(bh);
513         bh = NULL;
514         goto errorout;
515     }
516 
517     get_bh(bh);
518 
519     DEBUG(DL_BH, ("getblk: Vcb=%p bhcount=%u block=%u bh=%p mdl=%p (Flags:%xh VA:%p)\n",
520                   Vcb, atomic_read(&g_jbh.bh_count), block, bh, bh->b_mdl, bh->b_mdl->MdlFlags, bh->b_data));
521 
522     ExAcquireResourceExclusiveLite(&bdev->bd_bh_lock, TRUE);
523     /* do search again here */
524     tbh = buffer_head_search(bdev, block);
525     if (tbh) {
526         free_buffer_head(bh);
527         bh = tbh;
528         get_bh(bh);
529         RemoveEntryList(&bh->b_link);
530         InitializeListHead(&bh->b_link);
531         ExReleaseResourceLite(&bdev->bd_bh_lock);
532         goto errorout;
533     } else {
534         buffer_head_insert(bdev, bh);
535     }
536     ExReleaseResourceLite(&bdev->bd_bh_lock);
537 
538     /* we get it */
539 errorout:
540 
541     if (bcb)
542         CcUnpinData(bcb);
543 
544     return bh;
545 }
546 
547 int submit_bh_mdl(int rw, struct buffer_head *bh)
548 {
549     struct block_device *bdev = bh->b_bdev;
550     PEXT2_VCB            Vcb  = bdev->bd_priv;
551     PBCB                 Bcb;
552     PVOID                Buffer;
553     LARGE_INTEGER        Offset;
554 
555     ASSERT(Vcb->Identifier.Type == EXT2VCB);
556     ASSERT(bh->b_data);
557 
558     if (rw == WRITE) {
559 
560         if (IsVcbReadOnly(Vcb)) {
561             goto errorout;
562         }
563 
564         SetFlag(Vcb->Volume->Flags, FO_FILE_MODIFIED);
565         Offset.QuadPart = ((LONGLONG)bh->b_blocknr) << BLOCK_BITS;
566         if (CcPreparePinWrite(
567                     Vcb->Volume,
568                     &Offset,
569                     BLOCK_SIZE,
570                     FALSE,
571                     PIN_WAIT | PIN_EXCLUSIVE,
572                     &Bcb,
573                     &Buffer )) {
574 #if 0
575             if (memcmp(Buffer, bh->b_data, BLOCK_SIZE) != 0) {
576                 DbgBreak();
577             }
578             memmove(Buffer, bh->b_data, BLOCK_SIZE);
579 #endif
580             CcSetDirtyPinnedData(Bcb, NULL);
581             Ext2AddBlockExtent( Vcb, NULL,
582                                 (ULONG)bh->b_blocknr,
583                                 (ULONG)bh->b_blocknr,
584                                 (bh->b_size >> BLOCK_BITS));
585             CcUnpinData(Bcb);
586         } else {
587 
588             Ext2AddBlockExtent( Vcb, NULL,
589                                 (ULONG)bh->b_blocknr,
590                                 (ULONG)bh->b_blocknr,
591                                 (bh->b_size >> BLOCK_BITS));
592         }
593 
594     } else {
595 
596         DbgBreak();
597     }
598 
599 errorout:
600 
601     unlock_buffer(bh);
602     put_bh(bh);
603     return 0;
604 }
605 
606 struct buffer_head *
607 get_block_bh_pin(
608     struct block_device *   bdev,
609     sector_t                block,
610     unsigned long           size,
611     int                     zero
612 )
613 {
614     PEXT2_VCB Vcb = bdev->bd_priv;
615     LARGE_INTEGER offset;
616 
617     struct list_head *entry;
618 
619     /* allocate buffer_head and initialize it */
620     struct buffer_head *bh = NULL, *tbh = NULL;
621 
622     /* check the block is valid or not */
623     if (block >= TOTAL_BLOCKS) {
624         DbgBreak();
625         goto errorout;
626     }
627 
628     /* search the bdev bh list */
629     ExAcquireSharedStarveExclusive(&bdev->bd_bh_lock, TRUE);
630     tbh = buffer_head_search(bdev, block);
631     if (tbh) {
632         bh = tbh;
633         get_bh(bh);
634         ExReleaseResourceLite(&bdev->bd_bh_lock);
635         goto errorout;
636     }
637     ExReleaseResourceLite(&bdev->bd_bh_lock);
638 
639     bh = new_buffer_head();
640     if (!bh) {
641         goto errorout;
642     }
643     bh->b_bdev = bdev;
644     bh->b_blocknr = block;
645     bh->b_size = size;
646     bh->b_data = NULL;
647 
648 again:
649 
650     offset.QuadPart = (s64) bh->b_blocknr;
651     offset.QuadPart <<= BLOCK_BITS;
652 
653     if (zero) {
654         if (!CcPreparePinWrite(Vcb->Volume,
655                             &offset,
656                             bh->b_size,
657                             FALSE,
658                             PIN_WAIT,
659                             &bh->b_bcb,
660                             (PVOID *)&bh->b_data)) {
661             Ext2Sleep(100);
662             goto again;
663         }
664     } else {
665         if (!CcPinRead( Vcb->Volume,
666                         &offset,
667                         bh->b_size,
668                         PIN_WAIT,
669                         &bh->b_bcb,
670                         (PVOID *)&bh->b_data)) {
671             Ext2Sleep(100);
672             goto again;
673         }
674         set_buffer_uptodate(bh);
675     }
676 
677     if (bh->b_bcb)
678         CcSetBcbOwnerPointer(bh->b_bcb, (PVOID)((ERESOURCE_THREAD)bh | 0x3));
679 
680     if (!bh->b_data) {
681         free_buffer_head(bh);
682         bh = NULL;
683         goto errorout;
684     }
685     get_bh(bh);
686 
687     DEBUG(DL_BH, ("getblk: Vcb=%p bhcount=%u block=%u bh=%p ptr=%p.\n",
688                   Vcb, atomic_read(&g_jbh.bh_count), block, bh, bh->b_data));
689 
690     ExAcquireResourceExclusiveLite(&bdev->bd_bh_lock, TRUE);
691     /* do search again here */
692     tbh = buffer_head_search(bdev, block);
693     if (tbh) {
694         get_bh(tbh);
695         ExReleaseResourceLite(&bdev->bd_bh_lock);
696         free_buffer_head(bh);
697         bh = tbh;
698         RemoveEntryList(&bh->b_link);
699         InitializeListHead(&bh->b_link);
700         goto errorout;
701     } else {
702         buffer_head_insert(bdev, bh);
703     }
704     ExReleaseResourceLite(&bdev->bd_bh_lock);
705 
706     /* we get it */
707 errorout:
708 
709     return bh;
710 }
711 
712 int submit_bh_pin(int rw, struct buffer_head *bh)
713 {
714     struct block_device *bdev = bh->b_bdev;
715     PEXT2_VCB            Vcb  = bdev->bd_priv;
716     PVOID                Buffer;
717     LARGE_INTEGER        Offset;
718 
719     ASSERT(Vcb->Identifier.Type == EXT2VCB);
720     ASSERT(bh->b_data && bh->b_bcb);
721 
722     if (rw == WRITE) {
723 
724         if (IsVcbReadOnly(Vcb)) {
725             goto errorout;
726         }
727 
728         SetFlag(Vcb->Volume->Flags, FO_FILE_MODIFIED);
729         Offset.QuadPart = ((LONGLONG)bh->b_blocknr) << BLOCK_BITS;
730 
731         CcSetDirtyPinnedData(bh->b_bcb, NULL);
732         Ext2AddBlockExtent( Vcb, NULL,
733                             (ULONG)bh->b_blocknr,
734                             (ULONG)bh->b_blocknr,
735                             (bh->b_size >> BLOCK_BITS));
736     } else {
737         DbgBreak();
738     }
739 
740 errorout:
741 
742     unlock_buffer(bh);
743     put_bh(bh);
744     return 0;
745 }
746 
747 #if 0
748 
749 struct buffer_head *
750 get_block_bh(
751     struct block_device *   bdev,
752     sector_t                block,
753     unsigned long           size,
754     int                     zero
755 )
756 {
757     return get_block_bh_mdl(bdev, block, size, zero);
758 }
759 
760 int submit_bh(int rw, struct buffer_head *bh)
761 {
762     return submit_bh_mdl(rw, bh);
763 }
764 
765 #else
766 
767 struct buffer_head *
768 get_block_bh(
769     struct block_device *   bdev,
770     sector_t                block,
771     unsigned long           size,
772     int                     zero
773 )
774 {
775     return get_block_bh_pin(bdev, block, size, zero);
776 }
777 
778 int submit_bh(int rw, struct buffer_head *bh)
779 {
780     return submit_bh_pin(rw, bh);
781 }
782 #endif
783 
784 struct buffer_head *
785 __getblk(
786     struct block_device *   bdev,
787     sector_t                block,
788     unsigned long           size
789 )
790 {
791     return get_block_bh(bdev, block, size, 0);
792 }
793 
794 void __brelse(struct buffer_head *bh)
795 {
796     struct block_device *bdev = bh->b_bdev;
797     PEXT2_VCB Vcb = (PEXT2_VCB)bdev->bd_priv;
798 
799     ASSERT(Vcb->Identifier.Type == EXT2VCB);
800 
801     /* write data in case it's dirty */
802     while (buffer_dirty(bh)) {
803         ll_rw_block(WRITE, 1, &bh);
804     }
805 
806     if (1 == atomic_read(&bh->b_count)) {
807     } else if (atomic_dec_and_test(&bh->b_count)) {
808         atomic_inc(&bh->b_count);
809     } else {
810         return;
811     }
812 
813     ExAcquireResourceExclusiveLite(&bdev->bd_bh_lock, TRUE);
814     if (atomic_dec_and_test(&bh->b_count)) {
815         ASSERT(0 == atomic_read(&bh->b_count));
816     } else {
817         ExReleaseResourceLite(&bdev->bd_bh_lock);
818         return;
819     }
820     buffer_head_remove(bdev, bh);
821     KeQuerySystemTime(&bh->b_ts_drop);
822     InsertTailList(&Vcb->bd.bd_bh_free, &bh->b_link);
823     KeClearEvent(&Vcb->bd.bd_bh_notify);
824     ExReleaseResourceLite(&bdev->bd_bh_lock);
825     KeSetEvent(&Ext2Global->bhReaper.Wait, 0, FALSE);
826 
827     DEBUG(DL_BH, ("brelse: cnt=%u size=%u blk=%10.10xh bh=%p ptr=%p\n",
828                   atomic_read(&g_jbh.bh_count) - 1, bh->b_size,
829                   bh->b_blocknr, bh, bh->b_data ));
830 }
831 
832 
833 void __bforget(struct buffer_head *bh)
834 {
835     clear_buffer_dirty(bh);
836     __brelse(bh);
837 }
838 
839 void __lock_buffer(struct buffer_head *bh)
840 {
841 }
842 
843 void unlock_buffer(struct buffer_head *bh)
844 {
845     clear_buffer_locked(bh);
846 }
847 
848 void __wait_on_buffer(struct buffer_head *bh)
849 {
850 }
851 
852 void ll_rw_block(int rw, int nr, struct buffer_head * bhs[])
853 {
854     int i;
855 
856     for (i = 0; i < nr; i++) {
857 
858         struct buffer_head *bh = bhs[i];
859 
860         if (rw == SWRITE)
861             lock_buffer(bh);
862         else if (test_set_buffer_locked(bh))
863             continue;
864 
865         if (rw == WRITE || rw == SWRITE) {
866             if (test_clear_buffer_dirty(bh)) {
867                 get_bh(bh);
868                 submit_bh(WRITE, bh);
869                 continue;
870             }
871         } else {
872             if (!buffer_uptodate(bh)) {
873                 get_bh(bh);
874                 submit_bh(rw, bh);
875                 continue;
876             }
877         }
878         unlock_buffer(bh);
879     }
880 }
881 
882 int bh_submit_read(struct buffer_head *bh)
883 {
884 	ll_rw_block(READ, 1, &bh);
885     return 0;
886 }
887 
888 int sync_dirty_buffer(struct buffer_head *bh)
889 {
890     int ret = 0;
891 
892     ASSERT(atomic_read(&bh->b_count) <= 1);
893     lock_buffer(bh);
894     if (test_clear_buffer_dirty(bh)) {
895         get_bh(bh);
896         ret = submit_bh(WRITE, bh);
897         wait_on_buffer(bh);
898     } else {
899         unlock_buffer(bh);
900     }
901     return ret;
902 }
903 
904 void mark_buffer_dirty(struct buffer_head *bh)
905 {
906     set_buffer_dirty(bh);
907 }
908 
909 int sync_blockdev(struct block_device *bdev)
910 {
911     PEXT2_VCB Vcb = (PEXT2_VCB) bdev->bd_priv;
912     Ext2FlushVolume(NULL, Vcb, FALSE);
913     return 0;
914 }
915 
916 /*
917  * Perform a pagecache lookup for the matching buffer.  If it's there, refre
918  * it in the LRU and mark it as accessed.  If it is not present then return
919  * NULL
920  */
921 struct buffer_head *
922 __find_get_block(struct block_device *bdev, sector_t block, unsigned long size)
923 {
924     return __getblk(bdev, block, size);
925 }
926 
927 //
928 // inode block mapping
929 //
930 
931 ULONGLONG bmap(struct inode *i, ULONGLONG b)
932 {
933     ULONGLONG lcn = 0;
934     struct super_block *s = i->i_sb;
935 
936     PEXT2_MCB  Mcb = (PEXT2_MCB)i->i_priv;
937     PEXT2_VCB  Vcb = (PEXT2_VCB)s->s_priv;
938     PEXT2_EXTENT extent = NULL;
939     ULONGLONG  offset = (ULONGLONG)b;
940     NTSTATUS   status;
941 
942     if (!Mcb || !Vcb) {
943         goto errorout;
944     }
945 
946     offset <<= BLOCK_BITS;
947     status = Ext2BuildExtents(
948                  NULL,
949                  Vcb,
950                  Mcb,
951                  offset,
952                  BLOCK_SIZE,
953                  FALSE,
954                  &extent
955              );
956 
957     if (!NT_SUCCESS(status)) {
958         goto errorout;
959     }
960 
961     if (extent == NULL) {
962         goto errorout;
963     }
964 
965     lcn = (unsigned long)(extent->Lba >> BLOCK_BITS);
966 
967 errorout:
968 
969     if (extent) {
970         Ext2FreeExtent(extent);
971     }
972 
973     return lcn;
974 }
975 
976 void iget(struct inode *inode)
977 {
978     atomic_inc(&inode->i_count);
979 }
980 
981 void iput(struct inode *inode)
982 {
983     if (atomic_dec_and_test(&inode->i_count)) {
984         kfree(inode);
985     }
986 }
987 
988 //
989 // initialzer and destructor
990 //
991 
992 int
993 ext2_init_linux()
994 {
995     int rc = 0;
996 
997     rc = ext2_init_bh();
998     if (rc != 0) {
999         goto errorout;
1000     }
1001 
1002 errorout:
1003 
1004     return rc;
1005 }
1006 
1007 void
1008 ext2_destroy_linux()
1009 {
1010     ext2_destroy_bh();
1011 }
1012