1/*
2 * Copyright (c) 2014, University of Washington.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <stdlib.h>
11#include <stdio.h>
12#include <assert.h>
13#include <unistd.h>
14#include <errno.h>
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <fcntl.h>
18#include <string.h>
19#include <errors/errno.h>
20#include <storage/vsic.h>
21#include <storage/vsa.h>
22
23//#define DO_ACTUAL_IO
24
25#define MAX_CBS         1000
26
27#define CPU_FREQ        2200            // MHz
28#define READ_LATENCY    (1 * CPU_FREQ)  // us
29#define WRITE_LATENCY   (15 * CPU_FREQ) // us
30#define FLUSH_LATENCY   (0 * CPU_FREQ)  // us
31
32enum ramcb_cmd {
33    CMD_READ,
34    CMD_WRITE,
35    CMD_FLUSH
36};
37
38struct ramcb {
39    uint64_t execute_time;
40    off_t offset;
41    void *buf;
42    size_t nbytes;
43    void *handle;
44    enum ramcb_cmd cmd;
45};
46
47struct ram_vsic {
48    void *vsa_area;
49    struct ramcb cb[MAX_CBS];
50    const struct ramcb *cb_list[MAX_CBS];
51};
52
53#ifndef BARRELFISH
54static inline uint64_t rdtsc(void)
55{
56    uint32_t eax, edx;
57    __asm volatile ("rdtsc" : "=a" (eax), "=d" (edx));
58    return ((uint64_t)edx << 32) | eax;
59}
60#endif
61
62static struct ramcb *get_ramcb(struct ram_vsic *vsic)
63{
64    for(int i = 0; i < MAX_CBS; i++) {
65        if(vsic->cb_list[i] == NULL) {
66            vsic->cb_list[i] = &vsic->cb[i];
67            return &vsic->cb[i];
68        }
69    }
70
71    return NULL;
72}
73
74static errval_t vsic_write(struct storage_vsic *vsic, struct storage_vsa *vsa,
75                           off_t offset, size_t size, void *buffer)
76{
77    assert(vsic != NULL);
78    assert(vsa != NULL);
79    assert(buffer != NULL);
80    struct ram_vsic *mydata = vsic->data;
81    struct ramcb *cb = get_ramcb(mydata);
82    assert(cb != NULL);
83
84#ifdef DO_ACTUAL_IO
85    memcpy(mydata->vsa_area + offset, buffer, size);
86#endif
87
88    cb->execute_time = rdtsc() + WRITE_LATENCY;
89    cb->offset = offset;
90    cb->buf = buffer;
91    cb->nbytes = size;
92    cb->cmd = CMD_WRITE;
93    cb->handle = NULL;
94
95    return SYS_ERR_OK;
96}
97
98static errval_t vsic_read(struct storage_vsic *vsic, struct storage_vsa *vsa,
99                          off_t offset, size_t size, void *buffer)
100{
101    assert(vsic != NULL);
102    assert(vsa != NULL);
103    assert(buffer != NULL);
104    struct ram_vsic *mydata = vsic->data;
105    struct ramcb *cb = get_ramcb(mydata);
106    assert(cb != NULL);
107
108    cb->execute_time = rdtsc() + READ_LATENCY;
109    cb->offset = offset;
110    cb->buf = buffer;
111    cb->nbytes = size;
112    cb->cmd = CMD_READ;
113    cb->handle = NULL;
114
115    return SYS_ERR_OK;
116}
117
118static errval_t vsic_flush(struct storage_vsic *vsic, struct storage_vsa *vsa)
119{
120    assert(vsic != NULL);
121    assert(vsa != NULL);
122    struct ram_vsic *mydata = vsic->data;
123    struct ramcb *cb = get_ramcb(mydata);
124    assert(cb != NULL);
125
126    cb->execute_time = rdtsc() + FLUSH_LATENCY;
127    cb->cmd = CMD_FLUSH;
128    cb->handle = NULL;
129
130    return SYS_ERR_OK;
131}
132
133static errval_t vsic_flush2(struct storage_vsic *vsic, struct storage_vsa *vsa, void *handle)
134{
135    assert(vsic != NULL);
136    assert(vsa != NULL);
137    struct ram_vsic *mydata = vsic->data;
138    struct ramcb *cb = get_ramcb(mydata);
139    assert(cb != NULL);
140
141    cb->execute_time = rdtsc() + FLUSH_LATENCY;
142    cb->cmd = CMD_FLUSH;
143    cb->handle = handle;
144
145    return SYS_ERR_OK;
146}
147
148static errval_t vsic_poll(struct storage_vsic *vsic, void **handle)
149{
150    assert(vsic != NULL);
151    struct ram_vsic *mydata = vsic->data;
152
153    for(int i = 0; i < MAX_CBS; i++) {
154      if(mydata->cb_list[i] != NULL) {
155	const struct ramcb *cb = mydata->cb_list[i];
156
157	if(rdtsc() >= cb->execute_time) {
158#ifdef DO_ACTUAL_IO
159	  if(cb->cmd == CMD_READ) {
160	    memcpy(cb->buf, mydata->vsa_area + cb->offset, cb->size);
161	  }
162#endif
163
164	  // Completed successfully
165	  mydata->cb_list[i] = NULL;
166	  *handle = cb->handle;
167	  return SYS_ERR_OK;
168	}
169      }
170    }
171
172    return FLOUNDER_ERR_TX_BUSY;
173}
174
175static errval_t vsic_wait(struct storage_vsic *vsic)
176{
177    assert(vsic != NULL);
178    struct ram_vsic *mydata = vsic->data;
179
180    for(;;) {
181        int entries = 0;
182
183        for(int i = 0; i < MAX_CBS; i++) {
184            if(mydata->cb_list[i] != NULL) {
185                const struct ramcb *cb = mydata->cb_list[i];
186
187                if(rdtsc() < cb->execute_time) {
188                    // Still in progress
189                    entries++;
190                } else {
191#ifdef DO_ACTUAL_IO
192                    if(cb->cmd == CMD_READ) {
193                        memcpy(cb->buf, mydata->vsa_area + cb->offset, cb->size);
194                    }
195#endif
196
197                    // Completed successfully
198                    mydata->cb_list[i] = NULL;
199                }
200            }
201        }
202
203        if(entries == 0) {
204            break;
205        }
206
207        // TODO: Might want to sleep
208    }
209
210    return SYS_ERR_OK;
211}
212
213static struct storage_vsic_ops file_ops = {
214    .write = vsic_write,
215    .read = vsic_read,
216    .flush = vsic_flush,
217    .wait = vsic_wait,
218    .poll = vsic_poll,
219    .flush2 = vsic_flush2,
220};
221
222errval_t storage_vsic_driver_init(int argc, const char **argv,
223				  struct storage_vsic *vsic)
224{
225    assert(vsic != NULL);
226    struct ram_vsic *mydata = malloc(sizeof(struct ram_vsic));
227    assert(mydata != NULL);
228    memset(mydata, 0, sizeof(struct ram_vsic));
229
230    mydata->vsa_area = malloc(10 * 1024 * 1024);
231    assert(mydata->vsa_area != NULL);
232
233    // Init VSIC data structures
234    vsic->ops = file_ops;
235    vsic->data = mydata;
236    vsic->blocksize = 512;	// XXX: Determine from drive?
237
238    return SYS_ERR_OK;
239}
240
241errval_t storage_vsa_acquire(struct storage_vsa *vsa, const char *name,
242			     size_t size)
243{
244    return SYS_ERR_OK;
245}
246
247#if 0
248errval_t storage_vsa_resize(struct storage_vsa *vsa, size_t size)
249{
250    // No-op
251    return SYS_ERR_OK;
252}
253#endif
254