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() {
269 if (!m_Cache.
shutdown(deviceAvailable ? Cache::ShutdownMode::WriteBack
270 : Cache::ShutdownMode::Discard)) {
271 panic(
"SCSI: cache shutdown failed; unwritten data remains");
275void ScsiDisk::shutdownDeviceCache() {
280 if (!controller || !controller->addRequest(0, RequestQueue::NewRequest, SCSI_REQUEST_SYNC,
281 reinterpret_cast<uint64_t
>(
this), SyncWholeDevice))
282 panic(
"SCSI: shutdown aborted after final device cache flush failure");
287 m_pController = pController;
296 bool success = sendCommand(pCommand,
reinterpret_cast<uintptr_t
>(m_Inquiry),
sizeof(
Inquiry));
298 ERROR(
"ScsiDisk: INQUIRY failed!");
305 m_DeviceType =
static_cast<ScsiPeripheralType
>(m_Inquiry->Peripheral & 0x1F);
313 SCSI_DEBUG_LOG(
"ScsiDisk: Unit not yet ready, sense data: [sk="
314 << s->SenseKey <<
", asc=" << s->Asc <<
", ascq=" << s->AscQ <<
"]");
316 if (s->SenseKey == 0x2) {
322 success = sendCommand(pCommand, 0, 0,
true);
325 ERROR(
"ScsiDisk: unit startup failed! Sense data: [sk="
326 << s->SenseKey <<
", asc=" << s->Asc <<
", ascq=" << s->AscQ <<
"]");
333 Time::delay(100 * Time::Multiplier::Millisecond);
338 SCSI_DEBUG_LOG(
"ScsiDisk: Unit not yet ready, sense data: [sk="
339 << s->SenseKey <<
", asc=" << s->Asc <<
", ascq=" << s->AscQ <<
"]");
341 Time::delay(100 * Time::Multiplier::Millisecond);
345 ERROR(
"ScsiDisk: disk never became ready. Sense data: [sk="
346 << s->SenseKey <<
", asc=" << s->Asc <<
", ascq=" << s->AscQ <<
"]");
353 if (!getCapacityInternal(&m_NumBlocks, &m_NativeBlockSize)) {
354 ERROR(
"ScsiDisk: could not determine device capacity");
357 if (!m_NativeBlockSize || m_NativeBlockSize > ScsiCachePageBytes ||
358 (ScsiCachePageBytes % m_NativeBlockSize)) {
359 ERROR(
"ScsiDisk: native block size " << m_NativeBlockSize
360 <<
" is incompatible with target cache pages of "
361 << ScsiCachePageBytes <<
" bytes");
364 SCSI_DEBUG_LOG(
"ScsiDisk: Capacity: "
365 <<
Dec << m_NumBlocks <<
" blocks, each " << m_NativeBlockSize <<
" bytes - "
366 << (m_NativeBlockSize * m_NumBlocks) <<
Hex <<
" bytes in total.");
374 NOTICE(
"Attempting to inform the partitioner of our presence...");
377 sizeof(
static_cast<Disk*
>(
this))))
378 NOTICE(
"Successful.");
383 "ScsiDisk: Couldn't tell the partition service about the new "
386 ERROR(
"ScsiDisk: Partition service doesn't appear to support touch");
391 ByteSet(sense, 0xFF,
sizeof(
Sense));
396 uint8_t* response =
new uint8_t[
sizeof(
Sense)];
397 bool success = sendCommand(pCommand,
reinterpret_cast<uintptr_t
>(response),
sizeof(
Sense));
399 WARNING(
"ScsiDisk: SENSE command failed");
405 MemoryCopy(sense, response,
sizeof(
Sense));
409 return ((sense->ResponseCode & 0x70) == 0x70);
414 bool success = sendCommand(pCommand, 0, 0);
422bool ScsiDisk::getCapacityInternal(
size_t* blockNumber,
size_t* blockSize) {
425 "ScsiDisk::getCapacityInternal - returning to defaults, unit "
432 Capacity* capacity =
new Capacity;
434 ByteSet(capacity, 0,
sizeof(Capacity));
438 sendCommand(pCommand,
reinterpret_cast<uintptr_t
>(capacity),
sizeof(Capacity),
false);
441 WARNING(
"ScsiDisk::getCapacityInternal - READ CAPACITY command failed");
445 *blockNumber =
static_cast<size_t>(BIG_TO_HOST32(capacity->LBA)) + 1;
446 uint32_t blockSz = BIG_TO_HOST32(capacity->BlockSize);
452bool ScsiDisk::sendCommand(
ScsiCommand* pCommand, uintptr_t pRespBuffer, uint16_t nRespBytes,
454 uintptr_t pCommandBuffer = 0;
455 size_t nCommandSize = pCommand->serialise(pCommandBuffer);
456 return m_pController->sendCommand(m_nUnit, pCommandBuffer, nCommandSize, pRespBuffer, nRespBytes,
463 if (!acquireUse(diskUse))
466 return acquireView(location,
true, token);
469BufferView ScsiDisk::acquireView(uint64_t location,
bool writable, uint64_t& token) {
476 if (!pParent->acquireDiskOperation(operation)) {
483 ERROR(
"ScsiDisk::read - invalid cache or native block size.");
487 ERROR(
"ScsiDisk::read - location too high (" << location <<
" of " <<
getSize() <<
")");
491 if (blockNum >= getBlockCount()) {
492 ERROR(
"ScsiDisk::read - location too high (block " << blockNum <<
" > " << getBlockCount()
498 const uint64_t pageLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
499 token = pageLocation;
500 const size_t pageOffset = location - pageLocation;
504 size_t loc = location - ((location - alignPoint) % fillSize);
506 const size_t cacheLength = cacheExtentLength(fillLength);
508 if (!cacheLength || pageLocation < loc || (pageLocation - loc) >= cacheLength ||
509 pageOffset >= validPageLength) {
515 CacheRangeAdmission admission(*
this, loc, cacheLength,
true);
518 if ((buffer = m_Cache.
lookup(pageLocation))) {
523 return BufferView::fromAddress(buffer + pageOffset, validPageLength - pageOffset);
529 : pParent->addRequest(0, SCSI_REQUEST_READ, reinterpret_cast<uint64_t>(this), loc);
530 if (numRead < fillLength) {
532 WARNING(
"ScsiDisk::read - short read!");
535#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
536 HostedReadRequestHook readRequestHook =
nullptr;
537 void* readRequestHookContext =
nullptr;
540 readRequestHook = m_HostedReadRequestHook;
541 readRequestHookContext = m_HostedReadRequestHookContext;
543 if (readRequestHook) {
544 readRequestHook(
this, pageLocation, readRequestHookContext);
547 buffer = m_Cache.
lookup(pageLocation);
555 return BufferView::fromAddress(buffer + pageOffset, validPageLength - pageOffset);
559 return transferBufferRange(location, buffer, length,
false);
563 if (count > MaxReadBuffers || (count && !buffers))
565 for (
size_t i = 0; i < count; ++i)
566 buffers[i].complete =
false;
571 if (!acquireUse(diskUse))
575 if (!controller || !controller->acquireDiskOperation(operation))
579 if (!supportsBufferTransfers() || !native || ScsiCachePageBytes % native ||
580 hasShiftedCacheAlignment())
583 uint64_t keys[MaxReadBuffers];
584 uint64_t first = ~uint64_t{0};
586 for (
size_t i = 0; i < count; ++i) {
587 const auto& request = buffers[i];
588 if ((!request.buffer && request.length) || request.location >
getSize() ||
589 request.length >
getSize() - request.location)
593 const uint64_t key = request.location - request.location % ScsiCachePageBytes;
594 if (!request.length || request.location % native || request.length % native ||
595 request.length > ScsiCachePageBytes - (request.location - key) ||
596 key > ~uint64_t{0} - ScsiCachePageBytes || key > ~uintptr_t{0})
601 if (key + ScsiCachePageBytes > end)
602 end = key + ScsiCachePageBytes;
604 if (end - first > ~
size_t{0})
607 for (
size_t attempt = 0; attempt < 8; ++attempt) {
608 uint64_t retryKey = 0;
614 if (hasShiftedCacheAlignment())
617 size_t indices[MaxReadBuffers];
619 for (
size_t i = 0; i < count; ++i) {
620 if (buffers[i].complete)
630 MemoryCopy(buffers[i].buffer,
631 reinterpret_cast<void*
>(page + buffers[i].location - keys[i]),
633 buffers[i].complete =
true;
635 indices[nPending] = i;
636 pending[nPending++] = buffers[i];
640 const bool success = !nPending || transferReadBuffers(pending, nPending);
641 for (
size_t i = 0; i < nPending; ++i)
642 buffers[indices[i]].complete = pending[i].complete;
648 if (attempt == 7 || !m_Cache.
lookupStable(retryKey, page,
true))
656bool ScsiDisk::transferReadBuffers(ReadBuffer* buffers,
size_t count) {
658 for (
size_t i = 0; i < count; ++i) {
659 auto& request = buffers[i];
660 request.complete = transferBuffer(request.location, request.buffer, request.length,
false);
661 success &= request.complete;
669 if (!acquireUse(diskUse))
673 if (!controller || !controller->acquireDiskOperation(operation) || location >
getSize() ||
682 location % ScsiCachePageBytes || length % ScsiCachePageBytes)
686 const uintptr_t existing = m_Cache.
lookup(location);
687 const uintptr_t page = existing ? existing : m_Cache.
insert(location);
694 ByteSet(
reinterpret_cast<void*
>(page), 0, ScsiCachePageBytes);
699 m_Cache.endMutableLoan(location);
702 location += ScsiCachePageBytes;
703 length -= ScsiCachePageBytes;
708bool ScsiDisk::writeFrom(uint64_t location,
const void* buffer,
size_t length) {
710 return !length && location <=
getSize();
712 return transferBufferRange(location,
const_cast<void*
>(buffer), length,
true);
716bool ScsiDisk::transferBuffer(uint64_t location,
void* buffer,
size_t length,
bool writing) {
720bool ScsiDisk::transferBufferRange(uint64_t location,
void* buffer,
size_t length,
bool writing) {
723 if (!acquireUse(diskUse))
727 if (!controller || !controller->acquireDiskOperation(operation))
730 if ((!buffer && length) || location >
getSize() || length >
getSize() - location)
738 if (!supportsBufferTransfers() || !native || ScsiCachePageBytes % native ||
739 hasShiftedCacheAlignment()) {
740 return writing ? Disk::writeFrom(location, buffer, length)
744 auto* bytes =
static_cast<uint8_t*
>(buffer);
747 const uint64_t key = location - ((location - alignment) % ScsiCachePageBytes);
748 const size_t offset = location - key;
750 if (key > ~uintptr_t{0} || offset >= validLength || key % native || validLength % native)
752 const size_t available = validLength - offset;
753 const size_t chunk = length < available ? length : available;
758 bool complete =
false;
759 for (
size_t attempt = 0; attempt < 8 && !complete; ++attempt) {
761 CacheRangeAdmission admission(*
this, key, ScsiCachePageBytes,
true);
764 if (hasShiftedCacheAlignment())
770 if (ready && !page && (location % native || chunk % native)) {
771 if (
doRead(key) < validLength)
778 auto* cached =
reinterpret_cast<uint8_t*
>(page) + offset;
783 MemoryCopy(cached, bytes, chunk);
785 m_Cache.endMutableLoan(key);
786 const uintptr_t cacheKey = key;
790 auto* disk = static_cast<ScsiDisk*>(context);
791 for (size_t i = 0; i < count; ++i) {
792 Cache::DirectWritebackLease lease;
793 PciBus::DmaMapping mapping;
795 disk->supportsDirectCacheWrite() && disk->dmaDevice() &&
796 lease.acquire(disk->m_Cache, pages[i].key, pages[i].location) &&
797 PciBus::instance().mapDmaPage(
798 disk->dmaDevice(), lease.physical(),
799 disk->getCachePageValidLength(pages[i].key), mapping);
803 const uint64_t written =
804 direct ? disk->doWriteDirectPhysical(pages[i].key, pages[i].location,
806 : disk->doWriteDirect(pages[i].key, pages[i].location);
807 if (written != disk->getCachePageValidLength(pages[i].key)) {
816 MemoryCopy(bytes, cached, chunk);
818 }
else if (!transferBuffer(location, bytes, chunk, writing)) {
827 uintptr_t retryPage = 0;
828 if (attempt == 7 || !m_Cache.
lookupStable(key, retryPage,
true))
842 if (count > MaxWriteBuffers || (count && !buffers))
844 for (
size_t i = 0; i < count; ++i)
845 buffers[i].complete =
false;
855 if (!acquireUse(use) || !controller || !controller->acquireDiskOperation(operation))
858 bool eligible = supportsBufferTransfers() && native && !(ScsiCachePageBytes % native) &&
859 !hasShiftedCacheAlignment();
860 uint64_t first = ~uint64_t{0}, end = 0;
861 for (
size_t i = 0; i < count; ++i) {
862 const auto& b = buffers[i];
863 if (!b.buffer || !b.length || b.location >=
getSize() || b.length >
getSize() - b.location)
865 eligible &= b.location % ScsiCachePageBytes == 0 && b.length == ScsiCachePageBytes;
866 if (b.location < first)
868 if (b.location + b.length > end)
869 end = b.location + b.length;
870 for (
size_t j = 0; j < i; ++j)
871 if (b.location < buffers[j].location + buffers[j].length &&
872 buffers[j].location < b.location + b.length)
875 if (eligible && end - first <= ~
size_t{0}) {
877 eligible = !hasShiftedCacheAlignment();
879 for (
size_t i = 0; eligible && i < count; ++i) {
881 eligible = m_Cache.
lookupStable(buffers[i].location, page);
883 m_Cache.
release(buffers[i].location);
888 return transferWriteBuffers(buffers, count);
894bool ScsiDisk::transferWriteBuffers(WriteBuffer* buffers,
size_t count) {
896 for (
size_t i = 0; i < count; ++i) {
897 auto& b = buffers[i];
898 b.complete = transferBuffer(b.location,
const_cast<void*
>(b.buffer), b.length,
true);
899 success = b.complete && success;
910 if (!acquireUse(diskUse))
914 if (!controller || !controller->acquireDiskOperation(operation))
916 return controller->addRequest(0, RequestQueue::NewRequest, SCSI_REQUEST_SYNC,
917 reinterpret_cast<uint64_t
>(
this), SyncWholeDevice) != 0;
924 if (!acquireUse(diskUse))
933 if (!pParent->acquireDiskOperation(operation)) {
939 if (!nativeBlockSize || (ScsiCachePageBytes % nativeBlockSize)) {
940 ERROR(
"ScsiDisk::write - incompatible cache and native block sizes.");
945 ERROR(
"ScsiDisk::write - location too high");
946 ERROR(
" -> " << location <<
" vs " <<
getSize());
951 ERROR(
"ScsiDisk::write - location too high");
952 ERROR(
" -> block " << (location /
getNativeBlockSize()) <<
" vs " << getBlockCount());
958 const uint64_t pageLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
960 if (!validLength || (pageLocation % nativeBlockSize) || (validLength % nativeBlockSize)) {
961 ERROR(
"ScsiDisk::write - invalid terminal cache page geometry.");
966 if (!(buffer = m_Cache.
lookup(pageLocation))) {
967 ERROR(
"ScsiDisk::write - no buffer!");
979 if (!
sync(location,
false)) {
980 WARNING(
"ScsiDisk::flush - writeback failed");
987 if (!acquireUse(diskUse))
996 if (!pParent->acquireDiskOperation(operation)) {
1004 const uint64_t pageLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
1006 return m_Cache.
sync(pageLocation,
true);
1014 const uintptr_t page = m_Cache.
lookup(pageLocation);
1019 const uintptr_t key = pageLocation;
1020 struct SyncContext {
1023 } context = {
this,
false};
1024 const bool succeeded = m_Cache.
syncBatch(
1027 auto* context =
static_cast<SyncContext*
>(metadata);
1028 context->submitted =
true;
1029 return syncCacheBatch(pages, count, context->disk);
1032 return succeeded && (context.submitted ||
syncData());
1037 static_assert(Disk::MaxSyncPages <= Cache::MaxWritebackPages);
1038 if (count > MaxSyncPages || (count && !locations))
1047 if (!acquireUse(diskUse))
1051 if (!controller || !controller->acquireDiskOperation(operation))
1054 if (!nativeBlockSize || ScsiCachePageBytes % nativeBlockSize)
1057 uintptr_t keys[MaxSyncPages] = {};
1058 size_t pageCount = 0;
1059 for (
size_t i = 0; i < count; ++i) {
1060 const uint64_t location = locations[i];
1061 if (location >=
getSize() || location % 512)
1064 const uint64_t key = location - ((location - alignment) % ScsiCachePageBytes);
1066 if (key > ~uintptr_t{0} || !length || key % nativeBlockSize || length % nativeBlockSize)
1068 bool duplicate =
false;
1069 for (
size_t j = 0; j < pageCount; ++j)
1070 duplicate |= keys[j] == key;
1072 keys[pageCount++] = key;
1074 return m_Cache.
syncBatch(keys, pageCount, syncCacheBatch,
this);
1079 auto* disk =
static_cast<ScsiDisk*
>(context);
1080 bool succeeded =
true;
1081 for (
size_t first = 0; first < count; first += MaxWriteBuffers) {
1082 const size_t n = count - first < MaxWriteBuffers ? count - first : MaxWriteBuffers;
1083 WriteBuffer buffers[MaxWriteBuffers];
1086 for (
size_t i = 0; i < n; ++i) {
1087 const auto& page = pages[first + i];
1088 buffers[i] = {page.key,
reinterpret_cast<const void*
>(page.location),
1090 if (disk->supportsDirectCacheWrite() && disk->dmaDevice() &&
1091 leases[i].acquire(disk->m_Cache, page.key, page.location)) {
1092 if (PciBus::instance().mapDmaPage(disk->dmaDevice(), leases[i].physical(),
1093 buffers[i].length, mappings[i])) {
1094 buffers[i].dmaPhysical = mappings[i].address();
1096 leases[i].release();
1100 if (disk->supportsBufferTransfers()) {
1101 succeeded = disk->transferWriteBuffers(buffers, n) && succeeded;
1104 for (
size_t i = 0; i < n; ++i) {
1105 const auto& b = buffers[i];
1106 const uint64_t written =
1108 ? disk->doWriteDirectPhysical(b.location,
reinterpret_cast<uintptr_t
>(b.buffer),
1110 : controller->addRequest(0,
RequestQueue::NewRequest, SCSI_REQUEST_WRITE_DIRECT,
1111 reinterpret_cast<uint64_t>(disk), b.location,
1112 reinterpret_cast<uintptr_t>(b.buffer));
1113 succeeded = written == b.length && succeeded;
1118 const bool durable = disk->
syncData();
1119 return succeeded && durable;
1125 if (!acquireUse(diskUse))
1136 if (!controller->acquireDiskOperation(operation)) {
1140 const bool cacheSucceeded = m_Cache.
syncAll(syncCacheBatch,
this);
1143 const uint64_t flushed =
1144 controller->
addRequest(0, RequestQueue::NewRequest, SCSI_REQUEST_SYNC,
1145 reinterpret_cast<uint64_t
>(
this), SyncWholeDevice);
1146 return cacheSucceeded && flushed != 0;
1153 if (!acquireUse(diskUse))
1162 if (!controller->acquireDiskOperation(operation)) {
1167 if (!nativeBlockSize || (ScsiCachePageBytes % nativeBlockSize)) {
1172 const uint64_t pageLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
1174 if (!validLength || (pageLocation % nativeBlockSize) || (validLength % nativeBlockSize)) {
1185 if (!pParent || !page) {
1190 if (!nativeBlockSize || (ScsiCachePageBytes % nativeBlockSize)) {
1191 ERROR(
"ScsiDisk::flush - incompatible cache and native block sizes.");
1196 ERROR(
"ScsiDisk::flush - location too high");
1201 ERROR(
"ScsiDisk::flush - location too high");
1207 const uint64_t pageLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
1209 if (!validLength || (pageLocation % nativeBlockSize) || (validLength % nativeBlockSize)) {
1210 ERROR(
"ScsiDisk::flush - invalid terminal cache page geometry.");
1219 supportsDirectCacheWrite() && dmaDevice() && lease.acquire(m_Cache, pageLocation, page) &&
1220 PciBus::instance().
mapDmaPage(dmaDevice(), lease.physical(), validLength, mapping);
1221 const uint64_t writeResult =
1222 direct ? doWriteDirectPhysical(pageLocation, page, mapping.address())
1223 : pParent->
addRequest(0, RequestQueue::NewRequest, SCSI_REQUEST_WRITE_DIRECT,
1224 reinterpret_cast<uint64_t
>(
this), pageLocation, page);
1227 const uint64_t syncResult =
1228 pParent->
addRequest(0, SCSI_REQUEST_SYNC,
reinterpret_cast<uint64_t
>(
this), pageLocation);
1229 const bool success = writeResult == validLength && syncResult != 0;
1231 WARNING(
"ScsiDisk::flush - write or synchronise request failed");
1243 if (!acquireUse(diskUse))
1252 if (!pParent->acquireDiskOperation(operation)) {
1258 for (
size_t i = 0; i < m_AlignPoints.
count(); ++i) {
1259 if (m_AlignPoints[i] == location) {
1263 m_HasShiftedCacheAlignment |= location % ScsiCachePageBytes != 0;
1269 const size_t cacheLength = cacheExtentLength(fillSize);
1276 bool bReady =
false;
1277 for (
int i = 0; i < 3; i++) {
1283 ERROR(
"ScsiDisk::doRead - unit not ready");
1289 uintptr_t buffer = m_Cache.
lookup(location);
1291 WARNING(
"ScsiDisk::doRead(" << location <<
") - buffer was already in cache");
1295 bool didExist =
false;
1296 buffer = m_Cache.
insert(location, cacheLength, &didExist);
1298 WARNING(
"ScsiDisk::doRead - could not allocate a complete cache extent");
1304 CacheFillGuard fillGuard(m_Cache, location, cacheLength);
1305 ByteSet(
reinterpret_cast<void*
>(buffer), 0, cacheLength);
1314 if (m_DeviceType == CdDvdDevice) {
1324 "ScsiDisk::doRead - could not find data track (READ TOC "
1330 bool bHaveTrack =
false;
1331 const size_t responseLength =
static_cast<size_t>((toc[0] << 8) | toc[1]) + 2;
1333 WARNING(
"ScsiDisk::doRead - malformed READ TOC response");
1338 const size_t descriptorCount = (responseLength - 4) /
sizeof(*Toc);
1339 for (i = 0; i < descriptorCount; i++) {
1340 if (Toc[i].Flags & 0x04) {
1347 WARNING(
"ScsiDisk::doRead - could not find data track (no data track)");
1351 uint32_t trackStart = BIG_TO_HOST32(Toc[i].TrackStart);
1352 if ((blockNum + trackStart) < blockNum) {
1353 WARNING(
"ScsiDisk::doRead - TOC overflow");
1357 blockNum += trackStart;
1360 for (
int i = 0; i < 3 && !bOk; i++) {
1361 SCSI_DEBUG_LOG(
"SCSI: trying read(10)");
1363 bOk = sendCommand(pCommand, buffer, fillSize);
1366 for (
int i = 0; i < 3 && !bOk; i++) {
1367 SCSI_DEBUG_LOG(
"SCSI: trying read(12)");
1369 bOk = sendCommand(pCommand, buffer, fillSize);
1372 for (
int i = 0; i < 3 && !bOk; i++) {
1373 SCSI_DEBUG_LOG(
"SCSI: trying read(16)");
1375 bOk = sendCommand(pCommand, buffer, fillSize);
1380 fillGuard.publish();
1382 ERROR(
"SCSI: reading failed?");
1392 if (!preferred || !native || (preferred % ScsiCachePageBytes) || (preferred % native) ||
1393 location >=
getSize() || (location % native)) {
1397 size_t length = preferred;
1398 const uint64_t remaining =
getSize() - location;
1399 if (length > remaining) {
1400 length =
static_cast<size_t>(remaining);
1403 length -= length % native;
1407uint64_t ScsiDisk::doWrite(uint64_t location) {
1409 bool bReady =
false;
1410 for (
int i = 0; i < 3; i++) {
1416 ERROR(
"ScsiDisk::doWrite - unit not ready");
1422 uintptr_t buffer = m_Cache.
lookup(location);
1424 WARNING(
"ScsiDisk::doWrite(" << location <<
") - buffer was not in cache");
1441 for (
int i = 0; i < 3; ++i) {
1448 ERROR(
"ScsiDisk::doWriteDirect - unit not ready");
1456uint64_t ScsiDisk::doWriteDirectPhysical(uint64_t location, uintptr_t page,
1457 physical_uintptr_t physical) {
1465 if (!page || !nativeBlockSize || !validLength || validLength > 0xffff ||
1466 (location % nativeBlockSize) || (validLength % nativeBlockSize)) {
1470 size_t block = location / nativeBlockSize;
1471 size_t count = validLength / nativeBlockSize;
1476 for (
int i = 0; i < 3; i++) {
1477 SCSI_DEBUG_LOG(
"SCSI: trying write(10)");
1479 bOk = sendCommand(pCommand, page, validLength,
true);
1485 for (
int i = 0; i < 3; i++) {
1486 SCSI_DEBUG_LOG(
"SCSI: trying write(12)");
1488 bOk = sendCommand(pCommand, page, validLength,
true);
1495 for (
int i = 0; i < 3; i++) {
1496 SCSI_DEBUG_LOG(
"SCSI: trying write(16)");
1498 bOk = sendCommand(pCommand, page, validLength,
true);
1506 ERROR(
"SCSI: writing failed?");
1512uint64_t ScsiDisk::doSync(uint64_t location) {
1514 bool bReady =
false;
1515 for (
int i = 0; i < 3; i++) {
1521 ERROR(
"ScsiDisk::doSync - unit not ready");
1525 const bool wholeDevice = location == SyncWholeDevice;
1528 if (!nativeBlockSize || !
getSize() || !validLength ||
1529 (!wholeDevice && ((location % nativeBlockSize) || (validLength % nativeBlockSize)))) {
1534 const size_t block = wholeDevice ? 0 : location / nativeBlockSize;
1535 const size_t count = wholeDevice ? 0 : validLength / nativeBlockSize;
1542 for (
int i = 0; i < 3; i++) {
1543 SCSI_DEBUG_LOG(
"SCSI: trying synchronise(10)");
1545 bOk = sendCommand(pCommand, 0, 0);
1552 for (
int i = 0; i < 3; i++) {
1553 SCSI_DEBUG_LOG(
"SCSI: trying synchronise(16)");
1555 bOk = sendCommand(pCommand, 0, 0);
1562 return bOk ? validLength : 0;
1568 if (!acquireUse(diskUse))
1577 if (!pParent->acquireDiskOperation(operation)) {
1586 const uint64_t cacheLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
1587 if (!m_Cache.
pin(cacheLocation))
1591 m_Cache.
release(cacheLocation);
1601 const uint64_t cacheLocation = location - ((location - alignPoint) % ScsiCachePageBytes);
1602 releaseView(cacheLocation,
true);
1605void ScsiDisk::releaseView(uint64_t cacheLocation,
bool writable) {
1608 m_Cache.endMutableLoan(cacheLocation);
1609 m_Cache.
release(cacheLocation);
1617 const uint64_t remaining =
getSize() - location;
1618 return remaining < ScsiCachePageBytes ? static_cast<size_t>(remaining) : ScsiCachePageBytes;
1623 uint64_t alignPoint = 0;
1624 for (
size_t i = 0; i < m_AlignPoints.
count(); ++i) {
1625 if (m_AlignPoints[i] <= location && m_AlignPoints[i] > alignPoint) {
1626 alignPoint = m_AlignPoints[i];
1632bool ScsiDisk::hasShiftedCacheAlignment()
const {
1634 return m_HasShiftedCacheAlignment;
void setBackgroundWriteback(writeback_batch_t callback)
MUST_USE_RESULT bool retireWriteback(uintptr_t key, retirement_writeback_t callback, void *meta)
bool shutdown(ShutdownMode mode=ShutdownMode::WriteBack)
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()
void shutdownCache(bool deviceAvailable=true)
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)