support for PnP disks, part 2: adapt current caching

git-svn-id: svn://kolibrios.org@2140 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
CleverMouse 2011-08-31 16:26:51 +00:00
parent 2d55c22d0a
commit 57633abf05
4 changed files with 672 additions and 33 deletions

View File

@ -5,6 +5,8 @@
;; ;; ;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
$Revision$
; ============================================================================= ; =============================================================================
; ================================= Constants ================================= ; ================================= Constants =================================
; ============================================================================= ; =============================================================================
@ -76,6 +78,11 @@ struct DISKFUNC
; Note that read/write are called by the cache manager, so a driver should not ; Note that read/write are called by the cache manager, so a driver should not
; create a software cache. This function is implemented for flushing a hardware ; create a software cache. This function is implemented for flushing a hardware
; cache, if it exists. ; cache, if it exists.
.adjust_cache_size dd ?
; The pointer to the function which returns the cache size for this device.
; Optional, may be NULL.
; unsigned int adjust_cache_size(unsigned int suggested_size);
; Return value: 0 = disable cache, otherwise = used cache size in bytes.
ends ends
; This structure holds an information about a media. ; This structure holds an information about a media.
@ -90,6 +97,20 @@ struct DISKMEDIAINFO
; Size of the media in sectors. ; Size of the media in sectors.
ends ends
; This structure represents disk cache. To follow the old implementation,
; there are two distinct caches for a disk, one for "system" data, other
; for "application" data.
struct DISKCACHE
.Lock MUTEX
; Lock to protect the cache.
; The following fields are inherited from data32.inc:cache_ideX.
.pointer rd 1
.data_size rd 1 ; not use
.data rd 1
.sad_size rd 1
.search_start rd 1
ends
; This structure represents a disk device and its media for the kernel. ; This structure represents a disk device and its media for the kernel.
; This structure is allocated by the kernel in the 'disk_add' function, ; This structure is allocated by the kernel in the 'disk_add' function,
; freed in the 'disk_dereference' function. ; freed in the 'disk_dereference' function.
@ -146,6 +167,11 @@ struct DISK
; Number of partitions on this media. ; Number of partitions on this media.
.Partitions dd ? .Partitions dd ?
; Pointer to array of .NumPartitions pointers to PARTITION structures. ; Pointer to array of .NumPartitions pointers to PARTITION structures.
.cache_size dd ?
; inherited from cache_ideX_size
.SysCache DISKCACHE
.AppCache DISKCACHE
; Two caches for the disk.
ends ends
; This structure represents one partition for the kernel. This is a base ; This structure represents one partition for the kernel. This is a base
@ -156,6 +182,8 @@ struct PARTITION
; First sector of the partition. ; First sector of the partition.
.Length dq ? .Length dq ?
; Length of the partition in sectors. ; Length of the partition in sectors.
.Disk dd ?
; Pointer to parent DISK structure.
.FSUserFunctions dd ? .FSUserFunctions dd ?
; Handlers for the sysfunction 70h. This field is a pointer to the following ; Handlers for the sysfunction 70h. This field is a pointer to the following
; array. The first dword is a number of supported subfunctions, other dwords ; array. The first dword is a number of supported subfunctions, other dwords
@ -242,12 +270,13 @@ disk_list_mutex MUTEX
endg endg
iglobal iglobal
; The function 'disk_scan_partitions' needs two 512-byte buffers for ; The function 'disk_scan_partitions' needs three 512-byte buffers for
; MBR and bootsectors data. It can not use the static buffers always, ; MBR, bootsector and fs-temporary sector data. It can not use the static
; since it can be called for two or more disks in parallel. However, this ; buffers always, since it can be called for two or more disks in parallel.
; case is not typical. We reserve two static 512-byte buffers and a flag ; However, this case is not typical. We reserve three static 512-byte buffers
; that these buffers are currently used. If 'disk_scan_partitions' detects that ; and a flag that these buffers are currently used. If 'disk_scan_partitions'
; the buffers are currently used, it allocates buffers from the heap. ; detects that the buffers are currently used, it allocates buffers from the
; heap.
; The flag is implemented as a global dword variable. When the static buffers ; The flag is implemented as a global dword variable. When the static buffers
; are not used, the value is -1. When the static buffers are used, the value ; are not used, the value is -1. When the static buffers are used, the value
; is normally 0 and temporarily can become greater. The function increments ; is normally 0 and temporarily can become greater. The function increments
@ -258,10 +287,11 @@ iglobal
partition_buffer_users dd -1 partition_buffer_users dd -1
endg endg
uglobal uglobal
; The static buffers for MBR and bootsectors data. ; The static buffers for MBR, bootsector and fs-temporary sector data.
align 16 align 16
mbr_buffer rb 512 mbr_buffer rb 512
bootsect_buffer rb 512 bootsect_buffer rb 512
fs_tmp_buffer rb 512
endg endg
iglobal iglobal
@ -276,6 +306,7 @@ disk_default_callbacks:
dd disk_default_read dd disk_default_read
dd disk_default_write dd disk_default_write
dd disk_default_flush dd disk_default_flush
dd disk_default_adjust_cache_size
endg endg
; ============================================================================= ; =============================================================================
@ -312,45 +343,44 @@ disk_add:
jz .nothing jz .nothing
; 2. Copy disk name to the DISK structure. ; 2. Copy disk name to the DISK structure.
; 2a. Get length of the name, including the terminating zero. ; 2a. Get length of the name, including the terminating zero.
mov esi, [esp+8+8] ; esi = pointer to name mov ebx, [esp+8+8] ; ebx = pointer to name
push eax ; save allocated pointer to DISK push eax ; save allocated pointer to DISK
xor eax, eax ; the argument of malloc() is in eax xor eax, eax ; the argument of malloc() is in eax
@@: @@:
inc eax inc eax
cmp byte [esi+eax-1], 0 cmp byte [ebx+eax-1], 0
jnz @b jnz @b
; 2b. Call the heap manager. ; 2b. Call the heap manager.
call malloc call malloc
; 2c. Check the result. If allocation failed, go to 7. ; 2c. Check the result. If allocation failed, go to 7.
pop ebx ; restore allocated pointer to DISK pop esi ; restore allocated pointer to DISK
test eax, eax test eax, eax
jz .free jz .free
; 2d. Store the allocated pointer to the DISK structure. ; 2d. Store the allocated pointer to the DISK structure.
mov [ebx+DISK.Name], eax mov [esi+DISK.Name], eax
; 2e. Copy the name. ; 2e. Copy the name.
@@: @@:
mov dl, [esi] mov dl, [ebx]
mov [eax], dl mov [eax], dl
inc esi inc ebx
inc eax inc eax
test dl, dl test dl, dl
jnz @b jnz @b
; 3. Copy other arguments of the function to the DISK structure. ; 3. Copy other arguments of the function to the DISK structure.
mov eax, [esp+4+8] mov eax, [esp+4+8]
mov [ebx+DISK.Functions], eax mov [esi+DISK.Functions], eax
mov eax, [esp+12+8] mov eax, [esp+12+8]
mov [ebx+DISK.UserData], eax mov [esi+DISK.UserData], eax
mov eax, [esp+16+8] mov eax, [esp+16+8]
mov [ebx+DISK.DriverFlags], eax mov [esi+DISK.DriverFlags], eax
; 4. Initialize other fields of the DISK structure. ; 4. Initialize other fields of the DISK structure.
; Media is not inserted, initialized state of mutex is zero, ; Media is not inserted, reference counter is 1.
; reference counter is 1. lea ecx, [esi+DISK.MediaLock]
lea ecx, [ebx+DISK.MediaLock]
call mutex_init call mutex_init
xor eax, eax xor eax, eax
mov dword [ebx+DISK.MediaInserted], eax mov dword [esi+DISK.MediaInserted], eax
inc eax inc eax
mov [ebx+DISK.RefCount], eax mov [esi+DISK.RefCount], eax
; The DISK structure is initialized. ; The DISK structure is initialized.
; 5. Insert the new structure to the global list. ; 5. Insert the new structure to the global list.
; 5a. Acquire the mutex. ; 5a. Acquire the mutex.
@ -358,16 +388,16 @@ disk_add:
call mutex_lock call mutex_lock
; 5b. Insert item to the tail of double-linked list. ; 5b. Insert item to the tail of double-linked list.
mov edx, disk_list mov edx, disk_list
list_add_tail ebx, edx ;ebx= new edx= list head list_add_tail esi, edx ;esi= new edx= list head
; 5c. Release the mutex. ; 5c. Release the mutex.
call mutex_unlock call mutex_unlock
; 6. Return with eax = pointer to DISK. ; 6. Return with eax = pointer to DISK.
xchg eax, ebx xchg eax, esi
jmp .nothing jmp .nothing
.free: .free:
; Memory allocation for DISK structure succeeded, but for disk name failed. ; Memory allocation for DISK structure succeeded, but for disk name failed.
; 7. Free the DISK structure. ; 7. Free the DISK structure.
xchg eax, ebx xchg eax, esi
call free call free
; 8. Return with eax = 0. ; 8. Return with eax = 0.
xor eax, eax xor eax, eax
@ -474,10 +504,12 @@ lock dec [esi+DISK.MediaRefCount]
jnz .freeloop jnz .freeloop
.nofree: .nofree:
pop edi esi pop edi esi
; 3b. Call the driver. ; 3b. Free the cache.
call disk_free_cache
; 3c. Call the driver.
mov al, DISKFUNC.closemedia mov al, DISKFUNC.closemedia
stdcall disk_call_driver stdcall disk_call_driver
; 3c. Clear the flag. ; 3d. Clear the flag.
mov [esi+DISK.MediaUsed], 0 mov [esi+DISK.MediaUsed], 0
.nothing: .nothing:
ret ret
@ -525,12 +557,16 @@ disk_media_changed:
; 3b. Check the result of the callback. Abort if it failed. ; 3b. Check the result of the callback. Abort if it failed.
test eax, eax test eax, eax
jnz .noinsert jnz .noinsert
; 3c. Acquire the lifetime reference for the media object. ; 3c. Allocate the cache unless disabled by the driver. Abort if failed.
call disk_init_cache
test al, al
jz .noinsert
; 3d. Acquire the lifetime reference for the media object.
inc [esi+DISK.MediaRefCount] inc [esi+DISK.MediaRefCount]
; 3d. Scan for partitions. Ignore result; the list of partitions is valid even ; 3e. Scan for partitions. Ignore result; the list of partitions is valid even
; on errors. ; on errors.
call disk_scan_partitions call disk_scan_partitions
; 3e. Media is inserted and available for use. ; 3f. Media is inserted and available for use.
inc [esi+DISK.MediaInserted] inc [esi+DISK.MediaInserted]
.noinsert: .noinsert:
; 4. Return. ; 4. Return.
@ -586,6 +622,11 @@ disk_default_flush:
xor eax, eax xor eax, eax
ret 4 ret 4
; The default implementation of DISKFUNC.adjust_cache_size.
disk_default_adjust_cache_size:
mov eax, [esp+4]
ret 4
; This is an internal function called from 'disk_media_changed' when new media ; This is an internal function called from 'disk_media_changed' when new media
; is detected. It creates the list of partitions for the media. ; is detected. It creates the list of partitions for the media.
; If media is not partitioned, then the list consists of one partition which ; If media is not partitioned, then the list consists of one partition which
@ -609,7 +650,7 @@ disk_scan_partitions:
lock inc [partition_buffer_users] lock inc [partition_buffer_users]
jz .buffer_acquired ; yes, it is free jz .buffer_acquired ; yes, it is free
lock dec [partition_buffer_users] ; no, we must allocate lock dec [partition_buffer_users] ; no, we must allocate
stdcall kernel_alloc, 1024 stdcall kernel_alloc, 512*3
test eax, eax test eax, eax
jz .nothing jz .nothing
xchg eax, ebx xchg eax, ebx
@ -712,6 +753,7 @@ lock dec [partition_buffer_users] ; no, we must allocate
; compatibility problems. ; compatibility problems.
pop eax ; load extended partition pop eax ; load extended partition
add ebp, eax add ebp, eax
jc .mbr_failed
; 12c. If extended partition has not yet started, start it. ; 12c. If extended partition has not yet started, start it.
test eax, eax test eax, eax
jnz @f jnz @f
@ -849,7 +891,7 @@ proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword
; The range [.FirstSector, .FirstSector+.Length) must be either entirely to ; The range [.FirstSector, .FirstSector+.Length) must be either entirely to
; the left of [start, start+length) or entirely to the right. ; the left of [start, start+length) or entirely to the right.
; 2c. Subtract .FirstSector - start. The possible overflow distinguish between ; 2c. Subtract .FirstSector - start. The possible overflow distinguish between
; cases "to the left" (2?) and "to the right" (2d). ; cases "to the left" (2e) and "to the right" (2d).
mov eax, dword [ecx+PARTITION.FirstSector] mov eax, dword [ecx+PARTITION.FirstSector]
mov edx, dword [ecx+PARTITION.FirstSector+4] mov edx, dword [ecx+PARTITION.FirstSector+4]
sub eax, dword [start] sub eax, dword [start]

View File

@ -0,0 +1,592 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2011. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
$Revision$
; This function is intended to replace the old 'hd_read' function when
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
; eax is relative to partition start
; out: eax = error code; 0 = ok
fs_read32_sys:
; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure,
; this request should be processed by hd_read.
cmp [ebp+PARTITION.Disk], 'old'
jnz @f
mov [hdd_appl_data], 0
call hd_read
mov [hdd_appl_data], 1 ; restore to default state
ret
@@:
; In the normal case, save ecx, set ecx to SysCache and let the common part
; do its work.
push ecx
mov ecx, [ebp+PARTITION.Disk]
add ecx, DISK.SysCache
jmp fs_read32_common
; This function is intended to replace the old 'hd_read' function when
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
; eax is relative to partition start
; out: eax = error code; 0 = ok
fs_read32_app:
; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure,
; this request should be processed by hd_read.
cmp [ebp+PARTITION.Disk], 'old'
jnz @f
mov [hdd_appl_data], 1
jmp hd_read
@@:
; In the normal case, save ecx, set ecx to AppCache and let the common part
; do its work.
push ecx
mov ecx, [ebp+PARTITION.Disk]
add ecx, DISK.AppCache
; This label is the common part of fs_read32_sys and fs_read32_app.
fs_read32_common:
; 1. Check that the required sector is inside the partition. If no, return
; DISK_STATUS_END_OF_MEDIA.
cmp dword [ebp+PARTITION.Length+4], 0
jnz @f
cmp dword [ebp+PARTITION.Length], eax
ja @f
mov eax, DISK_STATUS_END_OF_MEDIA
pop ecx
ret
@@:
; 2. Get the absolute sector on the disk.
push edx
xor edx, edx
add eax, dword [ebp+PARTITION.FirstSector]
adc edx, dword [ebp+PARTITION.FirstSector+4]
; 3. If there is no cache for this disk, just pass the request to the driver.
cmp [ecx+DISKCACHE.pointer], 0
jnz .scancache
push 1
push esp ; numsectors
push edx ; startsector
push eax ; startsector
push ebx ; buffer
mov al, DISKFUNC.read
call disk_call_driver
pop ecx
pop edx
pop ecx
ret
.scancache:
; 4. Scan the cache.
push esi edi ecx ; scan cache
push edx eax
virtual at esp
.sector_lo dd ?
.sector_hi dd ?
.cache dd ?
end virtual
; The following code is inherited from hd_read. The differences are:
; all code is protected by the cache lock; instead of static calls
; to hd_read_dma/hd_read_pio/bd_read the dynamic call to DISKFUNC.read is used;
; sector is 64-bit, not 32-bit.
call mutex_lock
mov eax, [.sector_lo]
mov edx, [.sector_hi]
mov esi, [ecx+DISKCACHE.pointer]
mov ecx, [ecx+DISKCACHE.sad_size]
add esi, 12
mov edi, 1
.hdreadcache:
cmp dword [esi+8], 0 ; empty
je .nohdcache
cmp [esi], eax ; correct sector
jne .nohdcache
cmp [esi+4], edx ; correct sector
je .yeshdcache
.nohdcache:
add esi, 12
inc edi
dec ecx
jnz .hdreadcache
mov esi, [.cache]
call find_empty_slot64 ; ret in edi
test eax, eax
jnz .read_done
push 1
push esp
push edx
push [.sector_lo+12]
mov ecx, [.cache]
mov eax, edi
shl eax, 9
add eax, [ecx+DISKCACHE.data]
push eax
mov esi, [ebp+PARTITION.Disk]
mov al, DISKFUNC.read
call disk_call_driver
pop ecx
dec ecx
jnz .read_done
mov ecx, [.cache]
lea eax, [edi*3]
mov esi, [ecx+DISKCACHE.pointer]
lea esi, [eax*4+esi]
mov eax, [.sector_lo]
mov edx, [.sector_hi]
mov [esi], eax ; sector number
mov [esi+4], edx ; sector number
mov dword [esi+8], 1; hd read - mark as same as in hd
.yeshdcache:
mov esi, edi
mov ecx, [.cache]
shl esi, 9
add esi, [ecx+DISKCACHE.data]
mov edi, ebx
mov ecx, 512/4
rep movsd ; move data
xor eax, eax ; successful read
.read_done:
mov ecx, [.cache]
push eax
call mutex_unlock
pop eax
add esp, 12
pop edi esi edx ecx
ret
; This function is intended to replace the old 'hd_write' function when
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
; eax is relative to partition start
; out: eax = error code; 0 = ok
fs_write32_sys:
; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure,
; this request should be processed by hd_write.
cmp [ebp+PARTITION.Disk], 'old'
jnz @f
mov [hdd_appl_data], 0
call hd_write
mov [hdd_appl_data], 1 ; restore to default state
ret
@@:
; In the normal case, save ecx, set ecx to SysCache and let the common part
; do its work.
push ecx
mov ecx, [ebp+PARTITION.Disk]
add ecx, DISK.SysCache
jmp fs_write32_common
; This function is intended to replace the old 'hd_write' function when
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
; eax is relative to partition start
; out: eax = error code; 0 = ok
fs_write32_app:
; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure,
; this request should be processed by hd_write.
cmp [ebp+PARTITION.Disk], 'old'
jnz @f
mov [hdd_appl_data], 1
jmp hd_write
@@:
; In the normal case, save ecx, set ecx to AppCache and let the common part
; do its work.
push ecx
mov ecx, [ebp+PARTITION.Disk]
add ecx, DISK.AppCache
; This label is the common part of fs_read32_sys and fs_read32_app.
fs_write32_common:
; 1. Check that the required sector is inside the partition. If no, return
; DISK_STATUS_END_OF_MEDIA.
cmp dword [ebp+PARTITION.Length+4], 0
jnz @f
cmp dword [ebp+PARTITION.Length], eax
ja @f
mov eax, DISK_STATUS_END_OF_MEDIA
pop ecx
ret
@@:
push edx
; 2. Get the absolute sector on the disk.
xor edx, edx
add eax, dword [ebp+PARTITION.FirstSector]
adc edx, dword [ebp+PARTITION.FirstSector+4]
; 3. If there is no cache for this disk, just pass request to the driver.
cmp [ecx+DISKCACHE.pointer], 0
jnz .scancache
push 1
push esp ; numsectors
push edx ; startsector
push eax ; startsector
push ebx ; buffer
mov al, DISKFUNC.write
call disk_call_driver
pop ecx
pop edx
pop ecx
ret
.scancache:
; 4. Scan the cache.
push esi edi ecx ; scan cache
push edx eax
virtual at esp
.sector_lo dd ?
.sector_hi dd ?
.cache dd ?
end virtual
; The following code is inherited from hd_write. The differences are:
; all code is protected by the cache lock;
; sector is 64-bit, not 32-bit.
call mutex_lock
; check if the cache already has the sector and overwrite it
mov eax, [.sector_lo]
mov edx, [.sector_hi]
mov esi, [ecx+DISKCACHE.pointer]
mov ecx, [ecx+DISKCACHE.sad_size]
add esi, 12
mov edi, 1
.hdwritecache:
cmp dword [esi+8], 0 ; if cache slot is empty
je .not_in_cache_write
cmp [esi], eax ; if the slot has the sector
jne .not_in_cache_write
cmp [esi+4], edx ; if the slot has the sector
je .yes_in_cache_write
.not_in_cache_write:
add esi, 12
inc edi
dec ecx
jnz .hdwritecache
; sector not found in cache
; write the block to a new location
mov esi, [.cache]
call find_empty_slot64 ; ret in edi
test eax, eax
jne .hd_write_access_denied
mov ecx, [.cache]
lea eax, [edi*3]
mov esi, [ecx+DISKCACHE.pointer]
lea esi, [eax*4+esi]
mov eax, [.sector_lo]
mov edx, [.sector_hi]
mov [esi], eax ; sector number
mov [esi+4], edx ; sector number
.yes_in_cache_write:
mov dword [esi+4], 2 ; write - differs from hd
shl edi, 9
mov ecx, [.cache]
add edi, [ecx+DISKCACHE.data]
mov esi, ebx
mov ecx, 512/4
rep movsd ; move data
xor eax, eax ; success
.hd_write_access_denied:
mov ecx, [.cache]
push eax
call mutex_unlock
pop eax
add esp, 12
pop edi esi edx ecx
ret
; This internal function is called from fs_read32_* and fs_write32_*. It is the
; analogue of find_empty_slot for 64-bit sectors.
find_empty_slot64:
;-----------------------------------------------------------
; find empty or read slot, flush cache if next 12.5% is used by write
; output : edi = cache slot
;-----------------------------------------------------------
.search_again:
mov ecx, [esi+DISKCACHE.sad_size]
mov edi, [esi+DISKCACHE.search_start]
shr ecx, 3
.search_for_empty:
inc edi
cmp edi, [esi+DISKCACHE.sad_size]
jbe .inside_cache
mov edi, 1
.inside_cache:
lea eax, [edi*3]
shl eax, 2
add eax, [esi+DISKCACHE.pointer]
cmp dword [eax+8], 2
jb .found_slot ; it's empty or read
dec ecx
jnz .search_for_empty
call write_cache64 ; no empty slots found, write all
test eax, eax
jne .found_slot_access_denied
jmp .search_again ; and start again
.found_slot:
mov [esi+DISKCACHE.search_start], edi
xor eax, eax ; success
.found_slot_access_denied:
ret
; This function is intended to replace the old 'write_cache' function.
proc write_cache64 uses ecx edx esi edi
locals
cache_chain_started dd ?
cache_chain_size dd ?
cache_chain_pos dd ?
cache_chain_ptr dd ?
endl
; If there is no cache for this disk, nothing to do.
cmp [esi+DISKCACHE.pointer], 0
jz .flush
;-----------------------------------------------------------
; write all changed sectors to disk
;-----------------------------------------------------------
; write difference ( 2 ) from cache to DISK
mov ecx, [esi+DISKCACHE.sad_size]
mov esi, [esi+DISKCACHE.pointer]
add esi, 12
mov edi, 1
.write_cache_more:
cmp dword [esi+8], 2 ; if cache slot is not different
jne .write_chain
mov dword [esi+8], 1 ; same as in hd
mov eax, [esi]
mov edx, [esi+4] ; edx:eax = sector to write
; Îáúåäèíÿåì çàïèñü öåïî÷êè ïîñëåäîâàòåëüíûõ ñåêòîðîâ â îäíî îáðàùåíèå ê äèñêó
cmp ecx, 1
jz .nonext
cmp dword [esi+12+8], 2
jnz .nonext
push eax edx
add eax, 1
adc edx, 0
cmp eax, [esi+12]
jnz @f
cmp edx, [esi+12+4]
@@:
pop edx eax
jnz .nonext
cmp [cache_chain_started], 1
jz @f
mov [cache_chain_started], 1
mov [cache_chain_size], 0
mov [cache_chain_pos], edi
mov [cache_chain_ptr], esi
@@:
inc [cache_chain_size]
cmp [cache_chain_size], 16
jnz .continue
jmp .write_chain
.nonext:
call .flush_cache_chain
test eax, eax
jnz .nothing
mov [cache_chain_size], 1
mov [cache_chain_ptr], esi
call .write_cache_sector
test eax, eax
jnz .nothing
jmp .continue
.write_chain:
call .flush_cache_chain
test eax, eax
jnz .nothing
.continue:
add esi, 12
inc edi
dec ecx
jnz .write_cache_more
call .flush_cache_chain
test eax, eax
jnz .nothing
.flush:
mov esi, [ebp]
mov esi, [esi+PARTITION.Disk]
mov al, DISKFUNC.flush
call disk_call_driver
.nothing:
ret
.flush_cache_chain:
xor eax, eax
cmp [cache_chain_started], eax
jz @f
call .write_cache_chain
mov [cache_chain_started], 0
@@:
retn
.write_cache_sector:
mov [cache_chain_size], 1
mov [cache_chain_pos], edi
.write_cache_chain:
pusha
mov edi, [cache_chain_pos]
mov ecx, [ebp-12]
shl edi, 9
add edi, [ecx+DISKCACHE.data]
mov ecx, [cache_chain_size]
push ecx
push esp ; numsectors
mov eax, [cache_chain_ptr]
pushd [eax+4]
pushd [eax] ; startsector
push edi ; buffer
mov esi, [ebp]
mov esi, [esi+PARTITION.Disk]
mov al, DISKFUNC.write
call disk_call_driver
pop ecx
mov [esp+28], eax
popa
retn
endp
; This internal function is called from disk_add to initialize the caching for
; a new DISK.
; The algorithm is inherited from getcache.inc: take 1/32 part of the available
; physical memory, round down to 8 pages, limit by 128K from below and by 1M
; from above. Reserve 1/8 part of the cache for system data and 7/8 for app
; data.
; After the size is calculated, but before the cache is allocated, the device
; driver can adjust the size. In particular, setting size to zero disables
; caching: there is no sense in a cache for a ramdisk. In fact, such action
; is most useful example of a non-trivial adjustment.
; esi = pointer to DISK structure
disk_init_cache:
; 1. Calculate the suggested cache size.
; 1a. Get the size of free physical memory in pages.
mov eax, [pg_data.pages_free]
; 1b. Use the value to calculate the size.
shl eax, 12 - 5 ; 1/32 of it in bytes
and eax, -8*4096 ; round down to the multiple of 8 pages
; 1c. Force lower and upper limits.
cmp eax, 1024*1024
jb @f
mov eax, 1024*1024
@@:
cmp eax, 128*1024
ja @f
mov eax, 128*1024
@@:
; 1d. Give a chance to the driver to adjust the size.
push eax
mov al, DISKFUNC.adjust_cache_size
call disk_call_driver
; Cache size calculated.
mov [esi+DISK.cache_size], eax
test eax, eax
jz .nocache
; 2. Allocate memory for the cache.
; 2a. Call the allocator.
stdcall kernel_alloc, eax
test eax, eax
jnz @f
; 2b. If it failed, say a message and return with eax = 0.
dbgstr 'no memory for disk cache'
jmp .nothing
@@:
; 3. Fill two DISKCACHE structures.
mov [esi+DISK.SysCache.pointer], eax
lea ecx, [esi+DISK.SysCache.Lock]
call mutex_init
lea ecx, [esi+DISK.AppCache.Lock]
call mutex_init
; The following code is inherited from getcache.inc.
mov edx, [esi+DISK.SysCache.pointer]
and [esi+DISK.SysCache.search_start], 0
and [esi+DISK.AppCache.search_start], 0
mov eax, [esi+DISK.cache_size]
shr eax, 3
mov [esi+DISK.SysCache.data_size], eax
add edx, eax
imul eax, 7
mov [esi+DISK.AppCache.data_size], eax
mov [esi+DISK.AppCache.pointer], edx
mov eax, [esi+DISK.SysCache.data_size]
push ebx
call calculate_for_hd
pop ebx
add eax, [esi+DISK.SysCache.pointer]
mov [esi+DISK.SysCache.data], eax
mov [esi+DISK.SysCache.sad_size], ecx
push edi
mov edi, [esi+DISK.SysCache.pointer]
lea ecx, [ecx*3]
xor eax, eax
rep stosd
pop edi
mov eax, [esi+DISK.AppCache.data_size]
push ebx
call calculate_for_hd
pop ebx
add eax, [esi+DISK.AppCache.pointer]
mov [esi+DISK.AppCache.data], eax
mov [esi+DISK.AppCache.sad_size], ecx
push edi
mov edi, [esi+DISK.AppCache.pointer]
lea ecx, [ecx*3]
xor eax, eax
rep stosd
pop edi
; 4. Return with nonzero al.
mov al, 1
; 5. Return.
.nothing:
ret
; No caching is required for this driver. Zero cache pointers and return with
; nonzero al.
.nocache:
mov [esi+DISK.SysCache.pointer], eax
mov [esi+DISK.AppCache.pointer], eax
mov al, 1
ret
; This internal function is called from disk_media_dereference to free the
; allocated cache, if there is one.
; esi = pointer to DISK structure
disk_free_cache:
; The algorithm is straightforward.
mov eax, [esi+DISK.SysCache.pointer]
test eax, eax
jz .nothing
stdcall kernel_free, eax
.nothing:
ret

View File

@ -53,6 +53,10 @@ struc DISKFUNC
; Flushes the hardware cache, if it exists. Note that a driver should not ; Flushes the hardware cache, if it exists. Note that a driver should not
; implement a software cache for read/write, since they are called from the ; implement a software cache for read/write, since they are called from the
; kernel cache manager. ; kernel cache manager.
.adjust_cache_size dd ?
; unsigned int adjust_cache_size(unsigned int suggested_size);
; Optional.
; Returns the cache size for this device in bytes. 0 = disable cache.
} }
struc DISKMEDIAINFO struc DISKMEDIAINFO
{ {

View File

@ -235,7 +235,8 @@ include "gui/button.inc"
; file system ; file system
include "fs/disk.inc" ; support for plug-n-play disks include "blkdev/disk.inc" ; support for plug-n-play disks
include "blkdev/disk_cache.inc" ; caching for plug-n-play disks
include "fs/fs.inc" ; syscall include "fs/fs.inc" ; syscall
include "fs/fat32.inc" ; read / write for fat32 filesystem include "fs/fat32.inc" ; read / write for fat32 filesystem
include "fs/ntfs.inc" ; read / write for ntfs filesystem include "fs/ntfs.inc" ; read / write for ntfs filesystem