1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <assert.h> 6#include <unistd.h> 7 8#include <block-client/client.h> 9#include <zircon/compiler.h> 10#include <zircon/device/block.h> 11#include <zircon/syscalls.h> 12#include <lib/sync/completion.h> 13 14// Writes on a FIFO, repeating the write later if the FIFO is full. 15static zx_status_t do_write(zx_handle_t fifo, block_fifo_request_t* request, size_t count) { 16 zx_status_t status; 17 while (true) { 18 size_t actual; 19 status = zx_fifo_write(fifo, sizeof(block_fifo_request_t), request, count, &actual); 20 if (status == ZX_ERR_SHOULD_WAIT) { 21 zx_signals_t signals; 22 if ((status = zx_object_wait_one(fifo, 23 ZX_FIFO_WRITABLE | ZX_FIFO_PEER_CLOSED, 24 ZX_TIME_INFINITE, &signals)) != ZX_OK) { 25 return status; 26 } else if (signals & ZX_FIFO_PEER_CLOSED) { 27 return ZX_ERR_PEER_CLOSED; 28 } 29 // Try writing again... 30 } else if (status == ZX_OK) { 31 count -= actual; 32 request += actual; 33 if (count == 0) { 34 return ZX_OK; 35 } 36 } else { 37 return status; 38 } 39 } 40} 41 42static zx_status_t do_read(zx_handle_t fifo, block_fifo_response_t* response) { 43 zx_status_t status; 44 while (true) { 45 status = zx_fifo_read(fifo, sizeof(*response), response, 1, NULL); 46 if (status == ZX_ERR_SHOULD_WAIT) { 47 zx_signals_t signals; 48 if ((status = zx_object_wait_one(fifo, 49 ZX_FIFO_READABLE | ZX_FIFO_PEER_CLOSED, 50 ZX_TIME_INFINITE, &signals)) != ZX_OK) { 51 return status; 52 } else if (signals & ZX_FIFO_PEER_CLOSED) { 53 return ZX_ERR_PEER_CLOSED; 54 } 55 // Try reading again... 56 } else { 57 return status; 58 } 59 } 60} 61 62typedef struct block_completion { 63 sync_completion_t completion; 64 zx_status_t status; 65} block_sync_completion_t; 66 67typedef struct fifo_client { 68 zx_handle_t fifo; 69 block_sync_completion_t groups[MAX_TXN_GROUP_COUNT]; 70} fifo_client_t; 71 72zx_status_t block_fifo_create_client(zx_handle_t fifo, fifo_client_t** out) { 73 fifo_client_t* client = calloc(sizeof(fifo_client_t), 1); 74 if (client == NULL) { 75 zx_handle_close(fifo); 76 return ZX_ERR_NO_MEMORY; 77 } 78 client->fifo = fifo; 79 *out = client; 80 return ZX_OK; 81} 82 83void block_fifo_release_client(fifo_client_t* client) { 84 if (client == NULL) { 85 return; 86 } 87 88 zx_handle_close(client->fifo); 89 free(client); 90} 91 92zx_status_t block_fifo_txn(fifo_client_t* client, block_fifo_request_t* requests, size_t count) { 93 if (count == 0) { 94 return ZX_OK; 95 } 96 97 groupid_t group = requests[0].group; 98 assert(group < MAX_TXN_GROUP_COUNT); 99 sync_completion_reset(&client->groups[group].completion); 100 client->groups[group].status = ZX_ERR_IO; 101 102 zx_status_t status; 103 for (size_t i = 0; i < count; i++) { 104 assert(requests[i].group == group); 105 requests[i].opcode = (requests[i].opcode & BLOCKIO_OP_MASK) | BLOCKIO_GROUP_ITEM; 106 } 107 108 requests[0].opcode |= BLOCKIO_BARRIER_BEFORE; 109 requests[count - 1].opcode |= BLOCKIO_GROUP_LAST | BLOCKIO_BARRIER_AFTER; 110 111 if ((status = do_write(client->fifo, &requests[0], count)) != ZX_OK) { 112 return status; 113 } 114 115 // As expected by the protocol, when we send one "BLOCKIO_GROUP_LAST" message, we 116 // must read a reply message. 117 block_fifo_response_t response; 118 if ((status = do_read(client->fifo, &response)) != ZX_OK) { 119 return status; 120 } 121 122 // Wake up someone who is waiting (it might be ourselves) 123 groupid_t response_group = response.group; 124 client->groups[response_group].status = response.status; 125 sync_completion_signal(&client->groups[response_group].completion); 126 127 // Wait for someone to signal us. 128 sync_completion_wait(&client->groups[group].completion, ZX_TIME_INFINITE); 129 130 return client->groups[group].status; 131} 132