package mirage-block-solo5

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file block.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
(*
 * Copyright (c) 2011 Anil Madhavapeddy <anil@recoil.org>
 * Copyright (c) 2012 Citrix Systems Inc
 * Copyright (c) 2018 Martin Lucina <martin@lucina.net>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *)

open Solo5_os.Solo5

type t = { name : string; handle : int64; info : Mirage_block.info }

type error =
  [ Mirage_block.error
  | `Invalid_argument
  | `Unspecified_error
  | `Buffer_alignment ]

let pp_error ppf = function
  | #Mirage_block.error as e -> Mirage_block.pp_error ppf e
  | `Invalid_argument -> Fmt.string ppf "Invalid argument"
  | `Unspecified_error -> Fmt.string ppf "Unspecified error"
  | `Buffer_alignment ->
      Fmt.string ppf "Invalid argument: buffers must be sector aligned"

type write_error =
  [ Mirage_block.write_error
  | `Invalid_argument
  | `Unspecified_error
  | `Buffer_alignment ]

let pp_write_error ppf = function
  | #Mirage_block.write_error as e -> Mirage_block.pp_write_error ppf e
  | `Invalid_argument -> Fmt.string ppf "Invalid argument"
  | `Unspecified_error -> Fmt.string ppf "Unspecified error"
  | `Buffer_alignment ->
      Fmt.string ppf "Invalid argument: buffers must be sector aligned"

type solo5_block_info = { capacity : int64; block_size : int64 }

external solo5_block_acquire : string -> solo5_result * int64 * solo5_block_info
  = "mirage_solo5_block_acquire"

external solo5_block_read :
  int64 -> int64 -> Cstruct.buffer -> int -> int -> solo5_result
  = "mirage_solo5_block_read_3"

external solo5_block_write :
  int64 -> int64 -> Cstruct.buffer -> int -> int -> solo5_result
  = "mirage_solo5_block_write_3"

let disconnect _id =
  (* not implemented *)
  Lwt.return_unit

let connect name =
  match solo5_block_acquire name with
  | SOLO5_R_OK, handle, bi ->
      let sector_size = Int64.to_int bi.block_size in
      let size_sectors = Int64.div bi.capacity bi.block_size in
      let read_write = true in
      let t =
        { name; handle; info = { sector_size; size_sectors; read_write } }
      in
      Lwt.return t
  | SOLO5_R_AGAIN, _, _ ->
      assert false (* not returned by solo5_block_acquire *)
  | SOLO5_R_EINVAL, _, _ ->
      Lwt.fail_with (Fmt.str "Block: connect(%s): Invalid argument" name)
  | SOLO5_R_EUNSPEC, _, _ ->
      Lwt.fail_with (Fmt.str "Block: connect(%s): Unspecified error" name)

(* XXX: also applies to read: unclear if mirage actually issues I/O requests
 * that are >1 sector in size *per buffer*. mirage-skeleton device-usage/block
 * does not exhibit this behaviour. in any case, this will be caught at the
 * Solo5 layer and return an error back if it happens.
 *)

let buffers_aligned sector_size =
  List.for_all (fun b -> Cstruct.length b mod sector_size = 0)

let do_write1 h offset b =
  let r =
    match
      solo5_block_write h offset b.Cstruct.buffer b.Cstruct.off b.Cstruct.len
    with
    | SOLO5_R_OK -> Ok ()
    | SOLO5_R_AGAIN -> assert false
    | SOLO5_R_EINVAL -> Error `Invalid_argument
    | SOLO5_R_EUNSPEC -> Error `Unspecified_error
  in
  Lwt.return r

let rec do_write h sector_size offset buffers =
  match buffers with
  | [] -> Lwt.return (Ok ())
  | b :: bs ->
      (* the current solo5 implementation limits max I/O size to sector_size *)
      let b, b' = Cstruct.split b (min (Cstruct.length b) sector_size) in
      let new_offset = Int64.(add offset (of_int (Cstruct.length b))) in
      Lwt.bind (do_write1 h offset b) (fun result ->
          match result with
          | Error e -> Lwt.return (Error e)
          | Ok () ->
              if Cstruct.is_empty b' then do_write h sector_size new_offset bs
              else do_write h sector_size new_offset (b' :: bs))

let write x sector_start buffers =
  let offset = Int64.(mul sector_start (of_int x.info.sector_size)) in
  if buffers_aligned x.info.sector_size buffers then
    do_write x.handle x.info.sector_size offset buffers
  else Lwt.return (Error `Buffer_alignment)

let do_read1 h offset b =
  let r =
    match
      solo5_block_read h offset b.Cstruct.buffer b.Cstruct.off b.Cstruct.len
    with
    | SOLO5_R_OK -> Ok ()
    | SOLO5_R_AGAIN -> assert false
    | SOLO5_R_EINVAL -> Error `Invalid_argument
    | SOLO5_R_EUNSPEC -> Error `Unspecified_error
  in
  Lwt.return r

let rec do_read h sector_size offset buffers =
  match buffers with
  | [] -> Lwt.return (Ok ())
  | b :: bs ->
      (* the current solo5 implementation limits max I/O size to sector_size *)
      let b, b' = Cstruct.split b (min (Cstruct.length b) sector_size) in
      let new_offset = Int64.(add offset (of_int (Cstruct.length b))) in
      Lwt.bind (do_read1 h offset b) (fun result ->
          match result with
          | Error e -> Lwt.return (Error e)
          | Ok () ->
              if Cstruct.is_empty b' then do_read h sector_size new_offset bs
              else do_read h sector_size new_offset (b' :: bs))

let read x sector_start buffers =
  let offset = Int64.(mul sector_start (of_int x.info.sector_size)) in
  if buffers_aligned x.info.sector_size buffers then
    do_read x.handle x.info.sector_size offset buffers
  else Lwt.return (Error `Buffer_alignment)

let get_info t = Lwt.return t.info
OCaml

Innovation. Community. Security.