The Pedigree Project 0.1
ScsiController.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "ScsiController.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/panic.h"
23#include "pedigree/kernel/process/TerminationDeferral.h"
24#include "pedigree/kernel/utilities/new"
25
26#include "ScsiDisk.h"
27
28ScsiController::ScsiController(Controller* pDev, bool startWorker)
29 : Controller(pDev), RequestQueue(MakeConstantString("ScsiController")) {
30 if (startWorker)
31 initialise();
32}
33
34ScsiController::ScsiController() : RequestQueue(MakeConstantString("ScsiController")) {
35 // Start the RequestQueue
36 initialise();
37}
38
39ScsiController::~ScsiController() {
40 // Known controllers close this before retiring their disk caches. Keep the
41 // base teardown safe if a controller never reached that phase.
44}
45
46bool ScsiController::acquireDiskOperation(OperationBarrier::Lease& operation) {
47 return m_DiskOperations.tryAcquire(operation);
48}
49
50void ScsiController::shutdownDiskCaches(bool deviceAvailable) {
51 // Retained paging channels must finish while their request worker and I/O
52 // mappings remain available. Retiring endpoints rejects fresh selectors.
53 for (size_t i = 0; i < getNumChildren(); ++i)
54 static_cast<ScsiDisk*>(getChild(i))->retireEndpoint();
55
56 // No new client may publish queue work or acquire cache ownership after
57 // this point. Cache callbacks deliberately bypass this gate below.
59
60 if (!RequestQueue::drain()) {
61 panic("SCSI controller could not drain work before cache shutdown");
62 }
63
64 for (size_t i = 0; i < getNumChildren(); ++i) {
65 static_cast<ScsiDisk*>(getChild(i))->shutdownCache(deviceAvailable);
66 }
67
68 if (!RequestQueue::drain()) {
69 panic("SCSI controller cache shutdown left queued work behind");
70 }
71
72 if (deviceAvailable) {
73 for (size_t i = 0; i < getNumChildren(); ++i) {
74 static_cast<ScsiDisk*>(getChild(i))->shutdownDeviceCache();
75 }
76 }
77}
78
79void ScsiController::searchDisks() {
80 for (size_t i = 0; i < getNumUnits(); i++) {
81 ScsiDisk* pDisk = new ScsiDisk();
82 if (pDisk->initialise(this, i))
83 addChild(pDisk);
84 else
85 delete pDisk;
86 }
87}
88
89uint64_t ScsiController::executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4,
90 uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8) {
91 ScsiDisk* pDisk = reinterpret_cast<ScsiDisk*>(p2);
92 if (p1 == SCSI_REQUEST_PAGING) {
93 auto* request = reinterpret_cast<PagingRequest*>(p3);
94 request->result = pDisk->doPagingTransfer(request->operation, request->offset, request->page);
95 return request->result == PagingStatus::Success;
96 }
97 if (p1 == SCSI_REQUEST_READ)
98 return pDisk->doRead(p3);
99 else if (p1 == SCSI_REQUEST_WRITE) {
100 uint64_t result = pDisk->doWrite(p3);
101 // write() transfers its cache pin to the request. Cancellation and
102 // successful execution must release that ownership symmetrically.
103 pDisk->unpin(p3);
104 return result;
105 } else if (p1 == SCSI_REQUEST_SYNC)
106 return pDisk->doSync(p3);
107 else if (p1 == SCSI_REQUEST_WRITE_DIRECT)
108 return pDisk->doWriteDirect(p3, static_cast<uintptr_t>(p4));
109 else
110 return 0;
111}
112
114 if (request.p1 == SCSI_REQUEST_PAGING && request.p3)
115 reinterpret_cast<PagingRequest*>(request.p3)->result = PagingStatus::Closed;
116
117 if (request.p1 == SCSI_REQUEST_WRITE && request.p2) {
118 ScsiDisk* disk = reinterpret_cast<ScsiDisk*>(request.p2);
119 disk->unpin(request.p3);
120 }
121}
122
123bool ScsiController::prepareDiskRemoval() {
125 return false;
126 TerminationDeferral lifetime;
127 for (size_t i = 0; i < getNumChildren(); ++i) {
128 if (!static_cast<ScsiDisk*>(getChild(i))->tryCloseEndpoint()) {
129 cancelDiskRemoval();
130 return false;
131 }
132 }
133 // Endpoints stay closed after each temporary cache loan check. A successful
134 // check therefore cannot be invalidated by a new caller-owned cache view.
135 for (size_t i = 0; i < getNumChildren(); ++i) {
136 if (!static_cast<ScsiDisk*>(getChild(i))->hasNoCacheLoans()) {
137 cancelDiskRemoval();
138 return false;
139 }
140 }
141 return true;
142}
143void ScsiController::cancelDiskRemoval() {
144 for (size_t i = 0; i < getNumChildren(); ++i)
145 static_cast<ScsiDisk*>(getChild(i))->reopenEndpoint();
146}
size_t getNumChildren()
Definition Device.cc:143
Device * getChild(size_t n)
Definition Device.cc:139
void addChild(Device *pDevice)
Definition Device.cc:135
MUST_USE_RESULT bool tryAcquire(Lease &lease)
bool canWaitForCompletion()
virtual void destroy()
void shutdownDiskCaches(bool deviceAvailable=true)
virtual void cancelRequest(const Request &request)
virtual uint64_t executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8)
OperationBarrier m_DiskOperations
virtual uint64_t doRead(uint64_t location)
Definition ScsiDisk.cc:1267
bool initialise(class ScsiController *pController, size_t nUnit)
Definition ScsiDisk.cc:286
virtual void unpin(uint64_t location)
Definition ScsiDisk.cc:1595
virtual MUST_USE_RESULT uint64_t doWriteDirect(uint64_t location, uintptr_t page)
Definition ScsiDisk.cc:1435
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118