21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/Service.h"
24#include "pedigree/kernel/ServiceFeatures.h"
25#include "pedigree/kernel/ServiceManager.h"
26#include "pedigree/kernel/TargetInfo.h"
27#include "pedigree/kernel/machine/Pci.h"
28#include "pedigree/kernel/panic.h"
29#include "pedigree/kernel/process/Thread.h"
30#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
31#include "pedigree/kernel/processor/types.h"
32#include "pedigree/kernel/time/Time.h"
33#include "pedigree/kernel/utilities/Cache.h"
34#include "pedigree/kernel/utilities/PointerGuard.h"
35#include "pedigree/kernel/utilities/assert.h"
36#include "pedigree/kernel/utilities/utility.h"
38#include "ScsiCommands.h"
39#include "ScsiController.h"
42#define SCSI_DEBUG_LOG DEBUG_LOG
44#define SCSI_DEBUG_LOG(...)
50size_t cacheExtentLength(
size_t validLength) {
51 if (!validLength || validLength > (~
static_cast<size_t>(0) - (ScsiCachePageBytes - 1))) {
54 return (validLength + ScsiCachePageBytes - 1) & ~(ScsiCachePageBytes - 1);
57bool discardEditingRange(
Cache& cache, uintptr_t key,
size_t length) {
58 bool discarded =
true;
59 for (
size_t offset = 0; offset < length; offset += ScsiCachePageBytes) {
60 if (!cache.discardEditing(key + offset)) {
69 CacheFillGuard(
Cache& cache, uintptr_t key,
size_t length)
70 : m_Cache(cache), m_Key(key), m_Length(length), m_Published(false) {}
77 if (!discardEditingRange(m_Cache, m_Key, m_Length)) {
79 "ScsiDisk could not discard every page from a failed cache "
86 m_Cache.markNoLongerEditing(m_Key, m_Length);
91 CacheFillGuard(
const CacheFillGuard&) =
delete;
92 CacheFillGuard& operator=(
const CacheFillGuard&) =
delete;
101#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
102Mutex ScsiDisk::m_HostedReadRequestHookLock;
103ScsiDisk::HostedReadRequestHook ScsiDisk::m_HostedReadRequestHook =
nullptr;
104void* ScsiDisk::m_HostedReadRequestHookContext =
nullptr;
106void ScsiDisk::setHostedReadRequestHookForTest(HostedReadRequestHook hook,
void* context) {
108 m_HostedReadRequestHookContext = context;
109 m_HostedReadRequestHook = hook;
113ScsiDisk::CacheRangeAdmission::CacheRangeAdmission(
ScsiDisk& disk, uint64_t start,
size_t length,
122 m_TerminationDeferral(),
123 m_StackDiscardScope(&CacheRangeAdmission::discard, this) {
124 m_Disk.enterCacheRange(*
this);
127ScsiDisk::CacheRangeAdmission::~CacheRangeAdmission() {
128 m_Disk.leaveCacheRange(*
this);
131void ScsiDisk::CacheRangeAdmission::discard(
void* context) {
132 CacheRangeAdmission* admission =
reinterpret_cast<CacheRangeAdmission*
>(context);
133 admission->m_Disk.leaveCacheRange(*admission);
136bool ScsiDisk::cacheRangesOverlap(uint64_t firstStart,
size_t firstLength, uint64_t secondStart,
137 size_t secondLength) {
138 if (!firstLength || !secondLength) {
142 if (firstStart <= secondStart) {
143 return (secondStart - firstStart) < firstLength;
145 return (firstStart - secondStart) < secondLength;
148bool ScsiDisk::cacheRangeBlocked(
const CacheRangeAdmission& admission)
const {
149 for (CacheRangeAdmission* earlier = m_FirstCacheRangeAdmission; earlier != &admission;
150 earlier = earlier->m_Next) {
152 if ((admission.m_Retire || earlier->m_Retire) &&
153 cacheRangesOverlap(admission.m_Start, admission.m_Length, earlier->m_Start,
154 earlier->m_Length)) {
161void ScsiDisk::enterCacheRange(CacheRangeAdmission& admission) {
163 auto guard = m_CacheRangeWaiters.acquire();
164 if (!admission.m_Linked) {
165 admission.m_Previous = m_LastCacheRangeAdmission;
166 if (m_LastCacheRangeAdmission) {
167 m_LastCacheRangeAdmission->m_Next = &admission;
169 m_FirstCacheRangeAdmission = &admission;
171 m_LastCacheRangeAdmission = &admission;
172 admission.m_Linked =
true;
175 if (!cacheRangeBlocked(admission)) {
179 const WaitQueue::WakeReason reason =
180 guard.waitForCompletion(
WaitQueue::Channel(
this), Thread::CondWait, admission.m_Start);
185void ScsiDisk::leaveCacheRange(CacheRangeAdmission& admission) {
186 auto guard = m_CacheRangeWaiters.acquire();
187 if (!admission.m_Linked) {
190 if (admission.m_Previous) {
191 admission.m_Previous->m_Next = admission.m_Next;
193 m_FirstCacheRangeAdmission = admission.m_Next;
195 if (admission.m_Next) {
196 admission.m_Next->m_Previous = admission.m_Previous;
198 m_LastCacheRangeAdmission = admission.m_Previous;
200 admission.m_Linked =
false;
201 admission.m_Previous =
nullptr;
202 admission.m_Next =
nullptr;
206bool ScsiDisk::cacheCallback(CacheConstants::CallbackCause cause, uintptr_t loc, uintptr_t page,
211 case CacheConstants::WriteBack: {
216 case CacheConstants::Eviction:
221 "ScsiDisk: unknown cache callback -- could indicate "
222 "potential future I/O issues.");
229 if (!disk || !page) {
238 const uint64_t result =
239 controller->
addRequest(0, RequestQueue::NewRequest, SCSI_REQUEST_WRITE_DIRECT,
240 reinterpret_cast<uint64_t
>(disk), key, page);
244ScsiDisk::ScsiDisk(
size_t cacheConstraints)
246 m_Cache(cacheConstraints),
248 m_CacheRangeWaiters(),
249 m_FirstCacheRangeAdmission(nullptr),
250 m_LastCacheRangeAdmission(nullptr),
252 m_HasShiftedCacheAlignment(false),
254 m_BlockSize(ScsiCachePageBytes),
255 m_NativeBlockSize(0),
256 m_DeviceType(NoDevice) {
263ScsiDisk::~ScsiDisk() {
270 panic(
"SCSI: cache shutdown failed; unwritten data remains");
273void ScsiDisk::shutdownDeviceCache() {
278 if (!controller || !controller->addRequest(0, RequestQueue::NewRequest, SCSI_REQUEST_SYNC,
279 reinterpret_cast<uint64_t
>(
this), SyncWholeDevice))
280 panic(
"SCSI: shutdown aborted after final device cache flush failure");
285 m_pController = pController;
294 bool success = sendCommand(pCommand,
reinterpret_cast<uintptr_t
>(m_Inquiry),
sizeof(
Inquiry));
296 ERROR(
"ScsiDisk: INQUIRY failed!");
303 m_DeviceType =
static_cast<ScsiPeripheralType
>(m_Inquiry->Peripheral & 0x1F);
311 SCSI_DEBUG_LOG(
"ScsiDisk: Unit not yet ready, sense data: [sk="
312 << s->SenseKey <<
", asc=" << s->Asc <<
", ascq=" << s->AscQ <<
"]");
314 if (s->SenseKey == 0x2) {
320 success = sendCommand(pCommand, 0, 0,
true);
323 ERROR(
"ScsiDisk: unit startup failed! Sense data: [sk="
324 << s->SenseKey <<
", asc=" << s->Asc <<
", ascq=" << s->AscQ <<
"]");
331 Time::delay(100 * Time::Multiplier::Millisecond);
336 SCSI_DEBUG_LOG(
"ScsiDisk: Unit not yet ready, sense data: [sk="
337 << s->SenseKey <<
", asc=" << s->Asc <<
", ascq=" << s->AscQ <<
"]");
339 Time::delay(100 * Time::Multiplier::Millisecond);
343 ERROR(
"ScsiDisk: disk never became ready. Sense data: [sk="
344 << s->SenseKey <<
", asc=" << s->Asc <<
", ascq=" << s->AscQ <<
"]");
351 if (!getCapacityInternal(&m_NumBlocks, &m_NativeBlockSize)) {
352 ERROR(
"ScsiDisk: could not determine device capacity");
355 if (!m_NativeBlockSize || m_NativeBlockSize > ScsiCachePageBytes ||
356 (ScsiCachePageBytes % m_NativeBlockSize)) {
357 ERROR(
"ScsiDisk: native block size " << m_NativeBlockSize
358 <<
" is incompatible with target cache pages of "
359 << ScsiCachePageBytes <<
" bytes");
362 SCSI_DEBUG_LOG(
"ScsiDisk: Capacity: "
363 <<
Dec << m_NumBlocks <<
" blocks, each " << m_NativeBlockSize <<
" bytes - "
364 << (m_NativeBlockSize * m_NumBlocks) <<
Hex <<
" bytes in total.");
372 NOTICE(
"Attempting to inform the partitioner of our presence...");
375 sizeof(
static_cast<Disk*
>(
this))))
376 NOTICE(
"Successful.");
381 "ScsiDisk: Couldn't tell the partition service about the new "
384 ERROR(
"ScsiDisk: Partition service doesn't appear to support touch");
389 ByteSet(sense, 0xFF,
sizeof(
Sense));
394 uint8_t* response =
new uint8_t[
sizeof(
Sense)];
395 bool success = sendCommand(pCommand,
reinterpret_cast<uintptr_t
>(response),
sizeof(
Sense));
397 WARNING(
"ScsiDisk: SENSE command failed");
403 MemoryCopy(sense, response,
sizeof(
Sense));
407 return ((sense->ResponseCode & 0x70) == 0x70);
412 bool success = sendCommand(pCommand, 0, 0);
420bool ScsiDisk::getCapacityInternal(
size_t* blockNumber,
size_t* blockSize) {
423 "ScsiDisk::getCapacityInternal - returning to defaults, unit "
430 Capacity* capacity =
new Capacity;
432 ByteSet(capacity, 0,
sizeof(Capacity));
436 sendCommand(pCommand,
reinterpret_cast<uintptr_t
>(capacity),
sizeof(Capacity),
false);
439 WARNING(
"ScsiDisk::getCapacityInternal - READ CAPACITY command failed");
443 *blockNumber =
static_cast<size_t>(BIG_TO_HOST32(capacity->LBA)) + 1;
444 uint32_t blockSz = BIG_TO_HOST32(capacity->BlockSize);
450bool ScsiDisk::sendCommand(
ScsiCommand* pCommand, uintptr_t pRespBuffer, uint16_t nRespBytes,
452 uintptr_t pCommandBuffer = 0;
453 size_t nCommandSize = pCommand->serialise(pCommandBuffer);
454 return m_pController->sendCommand(m_nUnit, pCommandBuffer, nCommandSize, pRespBuffer, nRespBytes,
461 if (!acquireUse(diskUse))
464 return acquireView(location,
true, token);
467BufferView ScsiDisk::acquireView(uint64_t location,
bool writable, uint64_t& token) {
474 if (!pParent->acquireDiskOperation(operation)) {
481 ERROR(
"ScsiDisk::read - invalid cache or native block size.");
485 ERROR(
"ScsiDisk::read - location too high (" << location <<
" of " <<
getSize() <<
")");
489 if (blockNum >= getBlockCount()) {
490 ERROR(
"ScsiDisk::read - location too high (block " << blockNum <<
" > " << getBlockCount()
496 const uint64_t pageLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
497 token = pageLocation;
498 const size_t pageOffset = location - pageLocation;
502 size_t loc = location - ((location - alignPoint) % fillSize);
504 const size_t cacheLength = cacheExtentLength(fillLength);
506 if (!cacheLength || pageLocation < loc || (pageLocation - loc) >= cacheLength ||
507 pageOffset >= validPageLength) {
513 CacheRangeAdmission admission(*
this, loc, cacheLength,
true);
516 if ((buffer = m_Cache.
lookup(pageLocation))) {
521 return BufferView::fromAddress(buffer + pageOffset, validPageLength - pageOffset);
527 : pParent->addRequest(0, SCSI_REQUEST_READ, reinterpret_cast<uint64_t>(this), loc);
528 if (numRead < fillLength) {
530 WARNING(
"ScsiDisk::read - short read!");
533#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
534 HostedReadRequestHook readRequestHook =
nullptr;
535 void* readRequestHookContext =
nullptr;
538 readRequestHook = m_HostedReadRequestHook;
539 readRequestHookContext = m_HostedReadRequestHookContext;
541 if (readRequestHook) {
542 readRequestHook(
this, pageLocation, readRequestHookContext);
545 buffer = m_Cache.
lookup(pageLocation);
553 return BufferView::fromAddress(buffer + pageOffset, validPageLength - pageOffset);
557 return transferBufferRange(location, buffer, length,
false);
561 if (count > MaxReadBuffers || (count && !buffers))
563 for (
size_t i = 0; i < count; ++i)
564 buffers[i].complete =
false;
569 if (!acquireUse(diskUse))
573 if (!controller || !controller->acquireDiskOperation(operation))
577 if (!supportsBufferTransfers() || !native || ScsiCachePageBytes % native ||
578 hasShiftedCacheAlignment())
581 uint64_t keys[MaxReadBuffers];
582 uint64_t first = ~uint64_t{0};
584 for (
size_t i = 0; i < count; ++i) {
585 const auto& request = buffers[i];
586 if ((!request.buffer && request.length) || request.location >
getSize() ||
587 request.length >
getSize() - request.location)
591 const uint64_t key = request.location - request.location % ScsiCachePageBytes;
592 if (!request.length || request.location % native || request.length % native ||
593 request.length > ScsiCachePageBytes - (request.location - key) ||
594 key > ~uint64_t{0} - ScsiCachePageBytes || key > ~uintptr_t{0})
599 if (key + ScsiCachePageBytes > end)
600 end = key + ScsiCachePageBytes;
602 if (end - first > ~
size_t{0})
605 for (
size_t attempt = 0; attempt < 8; ++attempt) {
606 uint64_t retryKey = 0;
612 if (hasShiftedCacheAlignment())
615 size_t indices[MaxReadBuffers];
617 for (
size_t i = 0; i < count; ++i) {
618 if (buffers[i].complete)
628 MemoryCopy(buffers[i].buffer,
629 reinterpret_cast<void*
>(page + buffers[i].location - keys[i]),
631 buffers[i].complete =
true;
633 indices[nPending] = i;
634 pending[nPending++] = buffers[i];
638 const bool success = !nPending || transferReadBuffers(pending, nPending);
639 for (
size_t i = 0; i < nPending; ++i)
640 buffers[indices[i]].complete = pending[i].complete;
646 if (attempt == 7 || !m_Cache.
lookupStable(retryKey, page,
true))
654bool ScsiDisk::transferReadBuffers(ReadBuffer* buffers,
size_t count) {
656 for (
size_t i = 0; i < count; ++i) {
657 auto& request = buffers[i];
658 request.complete = transferBuffer(request.location, request.buffer, request.length,
false);
659 success &= request.complete;
667 if (!acquireUse(diskUse))
671 if (!controller || !controller->acquireDiskOperation(operation) || location >
getSize() ||
680 location % ScsiCachePageBytes || length % ScsiCachePageBytes)
684 const uintptr_t existing = m_Cache.
lookup(location);
685 const uintptr_t page = existing ? existing : m_Cache.
insert(location);
692 ByteSet(
reinterpret_cast<void*
>(page), 0, ScsiCachePageBytes);
697 m_Cache.endMutableLoan(location);
700 location += ScsiCachePageBytes;
701 length -= ScsiCachePageBytes;
706bool ScsiDisk::writeFrom(uint64_t location,
const void* buffer,
size_t length) {
708 return !length && location <=
getSize();
710 return transferBufferRange(location,
const_cast<void*
>(buffer), length,
true);
714bool ScsiDisk::transferBuffer(uint64_t location,
void* buffer,
size_t length,
bool writing) {
718bool ScsiDisk::transferBufferRange(uint64_t location,
void* buffer,
size_t length,
bool writing) {
721 if (!acquireUse(diskUse))
725 if (!controller || !controller->acquireDiskOperation(operation))
728 if ((!buffer && length) || location >
getSize() || length >
getSize() - location)
736 if (!supportsBufferTransfers() || !native || ScsiCachePageBytes % native ||
737 hasShiftedCacheAlignment()) {
738 return writing ? Disk::writeFrom(location, buffer, length)
742 auto* bytes =
static_cast<uint8_t*
>(buffer);
745 const uint64_t key = location - ((location - alignment) % ScsiCachePageBytes);
746 const size_t offset = location - key;
748 if (key > ~uintptr_t{0} || offset >= validLength || key % native || validLength % native)
750 const size_t available = validLength - offset;
751 const size_t chunk = length < available ? length : available;
756 bool complete =
false;
757 for (
size_t attempt = 0; attempt < 8 && !complete; ++attempt) {
759 CacheRangeAdmission admission(*
this, key, ScsiCachePageBytes,
true);
762 if (hasShiftedCacheAlignment())
768 if (ready && !page && (location % native || chunk % native)) {
769 if (
doRead(key) < validLength)
776 auto* cached =
reinterpret_cast<uint8_t*
>(page) + offset;
781 MemoryCopy(cached, bytes, chunk);
783 m_Cache.endMutableLoan(key);
784 const uintptr_t cacheKey = key;
788 auto* disk = static_cast<ScsiDisk*>(context);
789 for (size_t i = 0; i < count; ++i) {
790 Cache::DirectWritebackLease lease;
791 PciBus::DmaMapping mapping;
793 disk->supportsDirectCacheWrite() && disk->dmaDevice() &&
794 lease.acquire(disk->m_Cache, pages[i].key, pages[i].location) &&
795 PciBus::instance().mapDmaPage(
796 disk->dmaDevice(), lease.physical(),
797 disk->getCachePageValidLength(pages[i].key), mapping);
801 const uint64_t written =
802 direct ? disk->doWriteDirectPhysical(pages[i].key, pages[i].location,
804 : disk->doWriteDirect(pages[i].key, pages[i].location);
805 if (written != disk->getCachePageValidLength(pages[i].key)) {
814 MemoryCopy(bytes, cached, chunk);
816 }
else if (!transferBuffer(location, bytes, chunk, writing)) {
825 uintptr_t retryPage = 0;
826 if (attempt == 7 || !m_Cache.
lookupStable(key, retryPage,
true))
840 if (count > MaxWriteBuffers || (count && !buffers))
842 for (
size_t i = 0; i < count; ++i)
843 buffers[i].complete =
false;
853 if (!acquireUse(use) || !controller || !controller->acquireDiskOperation(operation))
856 bool eligible = supportsBufferTransfers() && native && !(ScsiCachePageBytes % native) &&
857 !hasShiftedCacheAlignment();
858 uint64_t first = ~uint64_t{0}, end = 0;
859 for (
size_t i = 0; i < count; ++i) {
860 const auto& b = buffers[i];
861 if (!b.buffer || !b.length || b.location >=
getSize() || b.length >
getSize() - b.location)
863 eligible &= b.location % ScsiCachePageBytes == 0 && b.length == ScsiCachePageBytes;
864 if (b.location < first)
866 if (b.location + b.length > end)
867 end = b.location + b.length;
868 for (
size_t j = 0; j < i; ++j)
869 if (b.location < buffers[j].location + buffers[j].length &&
870 buffers[j].location < b.location + b.length)
873 if (eligible && end - first <= ~
size_t{0}) {
875 eligible = !hasShiftedCacheAlignment();
877 for (
size_t i = 0; eligible && i < count; ++i) {
879 eligible = m_Cache.
lookupStable(buffers[i].location, page);
881 m_Cache.
release(buffers[i].location);
886 return transferWriteBuffers(buffers, count);
892bool ScsiDisk::transferWriteBuffers(WriteBuffer* buffers,
size_t count) {
894 for (
size_t i = 0; i < count; ++i) {
895 auto& b = buffers[i];
896 b.complete = transferBuffer(b.location,
const_cast<void*
>(b.buffer), b.length,
true);
897 success = b.complete && success;
908 if (!acquireUse(diskUse))
912 if (!controller || !controller->acquireDiskOperation(operation))
914 return controller->addRequest(0, RequestQueue::NewRequest, SCSI_REQUEST_SYNC,
915 reinterpret_cast<uint64_t
>(
this), SyncWholeDevice) != 0;
922 if (!acquireUse(diskUse))
931 if (!pParent->acquireDiskOperation(operation)) {
937 if (!nativeBlockSize || (ScsiCachePageBytes % nativeBlockSize)) {
938 ERROR(
"ScsiDisk::write - incompatible cache and native block sizes.");
943 ERROR(
"ScsiDisk::write - location too high");
944 ERROR(
" -> " << location <<
" vs " <<
getSize());
949 ERROR(
"ScsiDisk::write - location too high");
950 ERROR(
" -> block " << (location /
getNativeBlockSize()) <<
" vs " << getBlockCount());
956 const uint64_t pageLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
958 if (!validLength || (pageLocation % nativeBlockSize) || (validLength % nativeBlockSize)) {
959 ERROR(
"ScsiDisk::write - invalid terminal cache page geometry.");
964 if (!(buffer = m_Cache.
lookup(pageLocation))) {
965 ERROR(
"ScsiDisk::write - no buffer!");
977 if (!
sync(location,
false)) {
978 WARNING(
"ScsiDisk::flush - writeback failed");
985 if (!acquireUse(diskUse))
994 if (!pParent->acquireDiskOperation(operation)) {
1002 const uint64_t pageLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
1004 return m_Cache.
sync(pageLocation,
true);
1012 const uintptr_t page = m_Cache.
lookup(pageLocation);
1017 const uintptr_t key = pageLocation;
1018 struct SyncContext {
1021 } context = {
this,
false};
1022 const bool succeeded = m_Cache.
syncBatch(
1025 auto* context =
static_cast<SyncContext*
>(metadata);
1026 context->submitted =
true;
1027 return syncCacheBatch(pages, count, context->disk);
1030 return succeeded && (context.submitted ||
syncData());
1035 static_assert(Disk::MaxSyncPages <= Cache::MaxWritebackPages);
1036 if (count > MaxSyncPages || (count && !locations))
1045 if (!acquireUse(diskUse))
1049 if (!controller || !controller->acquireDiskOperation(operation))
1052 if (!nativeBlockSize || ScsiCachePageBytes % nativeBlockSize)
1055 uintptr_t keys[MaxSyncPages] = {};
1056 size_t pageCount = 0;
1057 for (
size_t i = 0; i < count; ++i) {
1058 const uint64_t location = locations[i];
1059 if (location >=
getSize() || location % 512)
1062 const uint64_t key = location - ((location - alignment) % ScsiCachePageBytes);
1064 if (key > ~uintptr_t{0} || !length || key % nativeBlockSize || length % nativeBlockSize)
1066 bool duplicate =
false;
1067 for (
size_t j = 0; j < pageCount; ++j)
1068 duplicate |= keys[j] == key;
1070 keys[pageCount++] = key;
1072 return m_Cache.
syncBatch(keys, pageCount, syncCacheBatch,
this);
1077 auto* disk =
static_cast<ScsiDisk*
>(context);
1078 bool succeeded =
true;
1079 for (
size_t first = 0; first < count; first += MaxWriteBuffers) {
1080 const size_t n = count - first < MaxWriteBuffers ? count - first : MaxWriteBuffers;
1081 WriteBuffer buffers[MaxWriteBuffers];
1084 for (
size_t i = 0; i < n; ++i) {
1085 const auto& page = pages[first + i];
1086 buffers[i] = {page.key,
reinterpret_cast<const void*
>(page.location),
1088 if (disk->supportsDirectCacheWrite() && disk->dmaDevice() &&
1089 leases[i].acquire(disk->m_Cache, page.key, page.location)) {
1090 if (PciBus::instance().mapDmaPage(disk->dmaDevice(), leases[i].physical(),
1091 buffers[i].length, mappings[i])) {
1092 buffers[i].dmaPhysical = mappings[i].address();
1094 leases[i].release();
1098 if (disk->supportsBufferTransfers()) {
1099 succeeded = disk->transferWriteBuffers(buffers, n) && succeeded;
1102 for (
size_t i = 0; i < n; ++i) {
1103 const auto& b = buffers[i];
1104 const uint64_t written =
1106 ? disk->doWriteDirectPhysical(b.location,
reinterpret_cast<uintptr_t
>(b.buffer),
1108 : controller->addRequest(0,
RequestQueue::NewRequest, SCSI_REQUEST_WRITE_DIRECT,
1109 reinterpret_cast<uint64_t>(disk), b.location,
1110 reinterpret_cast<uintptr_t>(b.buffer));
1111 succeeded = written == b.length && succeeded;
1116 const bool durable = disk->
syncData();
1117 return succeeded && durable;
1123 if (!acquireUse(diskUse))
1134 if (!controller->acquireDiskOperation(operation)) {
1138 const bool cacheSucceeded = m_Cache.
syncAll(syncCacheBatch,
this);
1141 const uint64_t flushed =
1142 controller->
addRequest(0, RequestQueue::NewRequest, SCSI_REQUEST_SYNC,
1143 reinterpret_cast<uint64_t
>(
this), SyncWholeDevice);
1144 return cacheSucceeded && flushed != 0;
1151 if (!acquireUse(diskUse))
1160 if (!controller->acquireDiskOperation(operation)) {
1165 if (!nativeBlockSize || (ScsiCachePageBytes % nativeBlockSize)) {
1170 const uint64_t pageLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
1172 if (!validLength || (pageLocation % nativeBlockSize) || (validLength % nativeBlockSize)) {
1183 if (!pParent || !page) {
1188 if (!nativeBlockSize || (ScsiCachePageBytes % nativeBlockSize)) {
1189 ERROR(
"ScsiDisk::flush - incompatible cache and native block sizes.");
1194 ERROR(
"ScsiDisk::flush - location too high");
1199 ERROR(
"ScsiDisk::flush - location too high");
1205 const uint64_t pageLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
1207 if (!validLength || (pageLocation % nativeBlockSize) || (validLength % nativeBlockSize)) {
1208 ERROR(
"ScsiDisk::flush - invalid terminal cache page geometry.");
1217 supportsDirectCacheWrite() && dmaDevice() && lease.acquire(m_Cache, pageLocation, page) &&
1218 PciBus::instance().
mapDmaPage(dmaDevice(), lease.physical(), validLength, mapping);
1219 const uint64_t writeResult =
1220 direct ? doWriteDirectPhysical(pageLocation, page, mapping.address())
1221 : pParent->
addRequest(0, RequestQueue::NewRequest, SCSI_REQUEST_WRITE_DIRECT,
1222 reinterpret_cast<uint64_t
>(
this), pageLocation, page);
1225 const uint64_t syncResult =
1226 pParent->
addRequest(0, SCSI_REQUEST_SYNC,
reinterpret_cast<uint64_t
>(
this), pageLocation);
1227 const bool success = writeResult == validLength && syncResult != 0;
1229 WARNING(
"ScsiDisk::flush - write or synchronise request failed");
1241 if (!acquireUse(diskUse))
1250 if (!pParent->acquireDiskOperation(operation)) {
1256 for (
size_t i = 0; i < m_AlignPoints.
count(); ++i) {
1257 if (m_AlignPoints[i] == location) {
1261 m_HasShiftedCacheAlignment |= location % ScsiCachePageBytes != 0;
1267 const size_t cacheLength = cacheExtentLength(fillSize);
1274 bool bReady =
false;
1275 for (
int i = 0; i < 3; i++) {
1281 ERROR(
"ScsiDisk::doRead - unit not ready");
1287 uintptr_t buffer = m_Cache.
lookup(location);
1289 WARNING(
"ScsiDisk::doRead(" << location <<
") - buffer was already in cache");
1293 bool didExist =
false;
1294 buffer = m_Cache.
insert(location, cacheLength, &didExist);
1296 WARNING(
"ScsiDisk::doRead - could not allocate a complete cache extent");
1302 CacheFillGuard fillGuard(m_Cache, location, cacheLength);
1303 ByteSet(
reinterpret_cast<void*
>(buffer), 0, cacheLength);
1312 if (m_DeviceType == CdDvdDevice) {
1322 "ScsiDisk::doRead - could not find data track (READ TOC "
1328 bool bHaveTrack =
false;
1329 const size_t responseLength =
static_cast<size_t>((toc[0] << 8) | toc[1]) + 2;
1331 WARNING(
"ScsiDisk::doRead - malformed READ TOC response");
1336 const size_t descriptorCount = (responseLength - 4) /
sizeof(*Toc);
1337 for (i = 0; i < descriptorCount; i++) {
1338 if (Toc[i].Flags & 0x04) {
1345 WARNING(
"ScsiDisk::doRead - could not find data track (no data track)");
1349 uint32_t trackStart = BIG_TO_HOST32(Toc[i].TrackStart);
1350 if ((blockNum + trackStart) < blockNum) {
1351 WARNING(
"ScsiDisk::doRead - TOC overflow");
1355 blockNum += trackStart;
1358 for (
int i = 0; i < 3 && !bOk; i++) {
1359 SCSI_DEBUG_LOG(
"SCSI: trying read(10)");
1361 bOk = sendCommand(pCommand, buffer, fillSize);
1364 for (
int i = 0; i < 3 && !bOk; i++) {
1365 SCSI_DEBUG_LOG(
"SCSI: trying read(12)");
1367 bOk = sendCommand(pCommand, buffer, fillSize);
1370 for (
int i = 0; i < 3 && !bOk; i++) {
1371 SCSI_DEBUG_LOG(
"SCSI: trying read(16)");
1373 bOk = sendCommand(pCommand, buffer, fillSize);
1378 fillGuard.publish();
1380 ERROR(
"SCSI: reading failed?");
1390 if (!preferred || !native || (preferred % ScsiCachePageBytes) || (preferred % native) ||
1391 location >=
getSize() || (location % native)) {
1395 size_t length = preferred;
1396 const uint64_t remaining =
getSize() - location;
1397 if (length > remaining) {
1398 length =
static_cast<size_t>(remaining);
1401 length -= length % native;
1405uint64_t ScsiDisk::doWrite(uint64_t location) {
1407 bool bReady =
false;
1408 for (
int i = 0; i < 3; i++) {
1414 ERROR(
"ScsiDisk::doWrite - unit not ready");
1420 uintptr_t buffer = m_Cache.
lookup(location);
1422 WARNING(
"ScsiDisk::doWrite(" << location <<
") - buffer was not in cache");
1439 for (
int i = 0; i < 3; ++i) {
1446 ERROR(
"ScsiDisk::doWriteDirect - unit not ready");
1454uint64_t ScsiDisk::doWriteDirectPhysical(uint64_t location, uintptr_t page,
1455 physical_uintptr_t physical) {
1463 if (!page || !nativeBlockSize || !validLength || validLength > 0xffff ||
1464 (location % nativeBlockSize) || (validLength % nativeBlockSize)) {
1468 size_t block = location / nativeBlockSize;
1469 size_t count = validLength / nativeBlockSize;
1474 for (
int i = 0; i < 3; i++) {
1475 SCSI_DEBUG_LOG(
"SCSI: trying write(10)");
1477 bOk = sendCommand(pCommand, page, validLength,
true);
1483 for (
int i = 0; i < 3; i++) {
1484 SCSI_DEBUG_LOG(
"SCSI: trying write(12)");
1486 bOk = sendCommand(pCommand, page, validLength,
true);
1493 for (
int i = 0; i < 3; i++) {
1494 SCSI_DEBUG_LOG(
"SCSI: trying write(16)");
1496 bOk = sendCommand(pCommand, page, validLength,
true);
1504 ERROR(
"SCSI: writing failed?");
1510uint64_t ScsiDisk::doSync(uint64_t location) {
1512 bool bReady =
false;
1513 for (
int i = 0; i < 3; i++) {
1519 ERROR(
"ScsiDisk::doSync - unit not ready");
1523 const bool wholeDevice = location == SyncWholeDevice;
1526 if (!nativeBlockSize || !
getSize() || !validLength ||
1527 (!wholeDevice && ((location % nativeBlockSize) || (validLength % nativeBlockSize)))) {
1532 const size_t block = wholeDevice ? 0 : location / nativeBlockSize;
1533 const size_t count = wholeDevice ? 0 : validLength / nativeBlockSize;
1540 for (
int i = 0; i < 3; i++) {
1541 SCSI_DEBUG_LOG(
"SCSI: trying synchronise(10)");
1543 bOk = sendCommand(pCommand, 0, 0);
1550 for (
int i = 0; i < 3; i++) {
1551 SCSI_DEBUG_LOG(
"SCSI: trying synchronise(16)");
1553 bOk = sendCommand(pCommand, 0, 0);
1560 return bOk ? validLength : 0;
1566 if (!acquireUse(diskUse))
1575 if (!pParent->acquireDiskOperation(operation)) {
1584 const uint64_t cacheLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
1585 if (!m_Cache.
pin(cacheLocation))
1589 m_Cache.
release(cacheLocation);
1599 const uint64_t cacheLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
1600 releaseView(cacheLocation,
true);
1603void ScsiDisk::releaseView(uint64_t cacheLocation,
bool writable) {
1606 m_Cache.endMutableLoan(cacheLocation);
1607 m_Cache.
release(cacheLocation);
1615 const uint64_t remaining =
getSize() - location;
1616 return remaining < ScsiCachePageBytes ? static_cast<size_t>(remaining) : ScsiCachePageBytes;
1621 uint64_t alignPoint = 0;
1622 for (
size_t i = 0; i < m_AlignPoints.
count(); ++i) {
1623 if (m_AlignPoints[i] <= location && m_AlignPoints[i] > alignPoint) {
1624 alignPoint = m_AlignPoints[i];
1630bool ScsiDisk::hasShiftedCacheAlignment()
const {
1632 return m_HasShiftedCacheAlignment;
void setBackgroundWriteback(writeback_batch_t callback)
MUST_USE_RESULT bool retireWriteback(uintptr_t key, retirement_writeback_t callback, void *meta)
MUST_USE_RESULT bool syncAll()
void setCallback(writeback_t newCallback, void *meta)
void release(uintptr_t key)
MUST_USE_RESULT bool lookupStable(uintptr_t key, uintptr_t &location, bool wait=false)
uintptr_t insert(uintptr_t key, bool *alreadyExisted=nullptr)
MUST_USE_RESULT bool beginMutableLoan(uintptr_t key)
MUST_USE_RESULT bool syncBatch(const uintptr_t *keys, size_t count, writeback_batch_t callback, void *metadata)
void markDirty(uintptr_t key)
bool sync(uintptr_t key, bool async)
void markNoLongerEditing(uintptr_t key, size_t length=0)
uintptr_t lookup(uintptr_t key)
void setDirtyTracking(DirtyTracking tracking)
MUST_USE_RESULT bool pin(uintptr_t key)
virtual MUST_USE_RESULT bool readIntoBatch(ReadBuffer *buffers, size_t count)
virtual MUST_USE_RESULT bool zero(uint64_t location, size_t length)
virtual MUST_USE_RESULT bool writeFromBatch(WriteBuffer *buffers, size_t count)
bool mapDmaPage(Device *device, physical_uintptr_t physical, size_t bytes, DmaMapping &mapping)
MUST_USE_RESULT uint64_t addRequest(size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
virtual bool supportsConcurrentReads() const
virtual uint64_t doRead(uint64_t location)
bool initialise(class ScsiController *pController, size_t nUnit)
virtual MUST_USE_RESULT bool retireCachePage(uint64_t location)
static bool retireCachePageCallback(uintptr_t key, uintptr_t page, void *meta)
size_t getCachePageValidLength(uint64_t location) const
bool writePageBuffer(uint64_t location, uintptr_t page)
virtual void flush(uint64_t location)
Flush a cached page to disk.
bool syncPages(const uint64_t *locations, size_t count) override
uint64_t getAlignmentPoint(uint64_t location) const
virtual void write(uint64_t location)
virtual size_t getSize() const
Gets the size of the disk.
bool readIntoBatch(ReadBuffer *buffers, size_t count) override
virtual bool pin(uint64_t location)
Pins a cache page.
size_t getCacheFillLength(uint64_t location) const
virtual void unpin(uint64_t location)
virtual void align(uint64_t location)
Sets the page boundary alignment after a specific location on the disk.
virtual MUST_USE_RESULT uint64_t doWriteDirect(uint64_t location, uintptr_t page)
virtual MUST_USE_RESULT bool syncAll()
virtual size_t getCacheFillSize() const
virtual size_t defaultBlockSize()
virtual BufferView read(uint64_t location)
bool readInto(uint64_t location, void *buffer, size_t length) override
bool writeFromBatch(WriteBuffer *buffers, size_t count) override
bool zero(uint64_t location, size_t length) override
bool flushCachePage(uint64_t location, uintptr_t page)
virtual bool sync(uint64_t location, bool async)
virtual size_t getNativeBlockSize() const
virtual bool provides(Type service)
Service * getService(const String &serviceName)
ServiceFeatures * enumerateOperations(const String &serviceName)
virtual bool serve(ServiceFeatures::Type type, void *pData, size_t dataLen)=0
static constexpr size_t getPageSize() noexcept
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
void pushBack(const T &value)