blob: b7e8177579c17a0b1bb663a8f3f63c3522d71e7b [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVICES_BLOCK_DRIVERS_VIRTIO_SCSI_H_
#define SRC_DEVICES_BLOCK_DRIVERS_VIRTIO_SCSI_H_
#include <lib/scsi/scsilib_controller.h>
#include <lib/sync/completion.h>
#include <lib/virtio/backends/backend.h>
#include <lib/virtio/device.h>
#include <lib/virtio/ring.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <zircon/compiler.h>
#include <atomic>
#include <memory>
#include <ddktl/device.h>
#include <fbl/auto_lock.h>
#include <fbl/condition_variable.h>
#include <virtio/scsi.h>
namespace virtio {
constexpr int MAX_IOS = 16;
class ScsiDevice : public virtio::Device,
public scsi::Controller,
public ddk::Device<ScsiDevice, ddk::Unbindable> {
public:
enum Queue {
CONTROL = 0,
EVENT = 1,
REQUEST = 2,
};
ScsiDevice(zx_device_t* device, zx::bti bti, std::unique_ptr<Backend> backend)
: virtio::Device(device, std::move(bti), std::move(backend)),
ddk::Device<ScsiDevice, ddk::Unbindable>(device) {}
// virtio::Device overrides
zx_status_t Init() override;
void DdkUnbind(ddk::UnbindTxn txn);
void DdkRelease();
// Invoked for most device interrupts.
virtual void IrqRingUpdate() override;
// Invoked on config change interrupts.
void IrqConfigChange() override {}
const char* tag() const override { return "virtio-scsi"; }
static void FillLUNStructure(struct virtio_scsi_req_cmd* req, uint8_t target, uint16_t lun);
private:
zx_status_t TargetMaxXferSize(uint8_t target, uint16_t lun, uint32_t& xfer_size_sectors);
zx_status_t ExecuteCommandSync(uint8_t target, uint16_t lun, struct iovec cdb,
struct iovec data_out, struct iovec data_in) override;
zx_status_t ExecuteCommandAsync(uint8_t target, uint16_t lun, struct iovec cdb,
struct iovec data_out, struct iovec data_in,
void (*cb)(void*, zx_status_t), void* cookie) override;
zx_status_t WorkerThread();
// Latched copy of virtio-scsi device configuration.
struct virtio_scsi_config config_ TA_GUARDED(lock_) = {};
struct scsi_io_slot {
io_buffer_t request_buffer;
bool avail;
vring_desc* tail_desc;
void* cookie;
void (*callback)(void* cookie, zx_status_t status);
struct iovec data_in;
void* data_in_region;
io_buffer_t* request_buffers;
struct virtio_scsi_resp_cmd* response;
};
scsi_io_slot* GetIO() TA_REQ(lock_);
void FreeIO(scsi_io_slot* io_slot) TA_REQ(lock_);
size_t request_buffers_size_;
scsi_io_slot scsi_io_slot_table_[MAX_IOS] TA_GUARDED(lock_) = {};
Ring control_ring_ TA_GUARDED(lock_) = {this};
Ring request_queue_ = {this};
thrd_t worker_thread_;
bool worker_thread_should_exit_ TA_GUARDED(lock_) = {};
// Synchronizes virtio rings and worker thread control.
fbl::Mutex lock_;
// We use the condvar to control the number of IO's in flight
// as well as to wait for descs to become available.
fbl::ConditionVariable ioslot_cv_ __TA_GUARDED(lock_);
fbl::ConditionVariable desc_cv_ __TA_GUARDED(lock_);
uint32_t active_ios_ __TA_GUARDED(lock_);
uint64_t scsi_transport_tag_ __TA_GUARDED(lock_);
};
} // namespace virtio
#endif // SRC_DEVICES_BLOCK_DRIVERS_VIRTIO_SCSI_H_