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.
do_write(zx_handle_t fifo,block_fifo_request_t * request,size_t count)15 static 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
do_read(zx_handle_t fifo,block_fifo_response_t * response)42 static 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
62 typedef struct block_completion {
63 sync_completion_t completion;
64 zx_status_t status;
65 } block_sync_completion_t;
66
67 typedef struct fifo_client {
68 zx_handle_t fifo;
69 block_sync_completion_t groups[MAX_TXN_GROUP_COUNT];
70 } fifo_client_t;
71
block_fifo_create_client(zx_handle_t fifo,fifo_client_t ** out)72 zx_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
block_fifo_release_client(fifo_client_t * client)83 void 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
block_fifo_txn(fifo_client_t * client,block_fifo_request_t * requests,size_t count)92 zx_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