Skip to content

Commit 47de2ed

Browse files
committed
Generate a unique ID for each Netfront request
Previously we used the grant ref, but this isn't unique. This could possibly cause pages to be returned to the free pool while they were still being transmitted. Since IDs must be 16-bit, this patch also limits the size of the pool (to 256 pages, which is roughly the size of the ring).
1 parent 6c13bbe commit 47de2ed

File tree

3 files changed

+43
-23
lines changed

3 files changed

+43
-23
lines changed

lib/frontend.ml

+1-2
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,11 @@ module Make(C: S.CONFIGURATION with type 'a io = 'a Lwt.t) = struct
283283
* remaining (unsent) data and a thread which will return when the data has
284284
* been ack'd by netback. *)
285285
let write_request ?size ~flags nf datav =
286-
Shared_page_pool.use nf.t.tx_pool (fun gref shared_block ->
286+
Shared_page_pool.use nf.t.tx_pool (fun ~id gref shared_block ->
287287
let len, datav = Cstruct.fillv ~src:datav ~dst:shared_block in
288288
(* [size] includes extra pages to follow later *)
289289
let size = match size with |None -> len |Some s -> s in
290290
Stats.tx nf.t.stats (Int64.of_int size);
291-
let id = gref in
292291
let request = { TX.Request.
293292
id;
294293
gref = Int32.of_int gref;

lib/shared_page_pool.ml

+39-19
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,33 @@ open Lwt.Infix
1818

1919
let return = Lwt.return
2020

21-
type block = Gnt.gntref * Cstruct.t
21+
let max_pages = 256
22+
23+
type block = {
24+
id : Cstruct.uint16;
25+
gref : Gnt.gntref;
26+
data : Cstruct.t;
27+
}
2228
type t = {
2329
grant : Gnt.gntref -> Io_page.t -> unit;
30+
mutable next_id : Cstruct.uint16;
2431
mutable blocks : block list;
2532
mutable in_use : int;
2633
mutable shutdown : bool;
34+
avail : unit Lwt_condition.t; (* Fires when free list becomes non-empty *)
2735
}
2836

2937
let page_size = Io_page.round_to_page_size 1
3038
let block_size = page_size / 2
3139

32-
let make grant = { grant; blocks = []; shutdown = false; in_use = 0 }
40+
let make grant = { next_id = 0; grant; blocks = []; shutdown = false; in_use = 0; avail = Lwt_condition.create () }
3341

3442
let shutdown t =
3543
t.shutdown <- true;
44+
Lwt_condition.broadcast t.avail (); (* Wake anyone who's still waiting for free pages *)
3645
if t.in_use = 0 then (
37-
t.blocks |> List.iter (fun (gref, block) ->
38-
if block.Cstruct.off = 0 then (
46+
t.blocks |> List.iter (fun {id = _; gref; data} ->
47+
if data.Cstruct.off = 0 then (
3948
Gnt.Gntshr.end_access gref;
4049
Gnt.Gntshr.put gref;
4150
)
@@ -52,33 +61,44 @@ let alloc t =
5261
return (gnt, Io_page.to_cstruct page)
5362

5463
let put t block =
64+
let was_empty = (t.blocks = []) in
5565
t.blocks <- block :: t.blocks;
5666
t.in_use <- t.in_use - 1;
67+
if was_empty then Lwt_condition.broadcast t.avail ();
5768
if t.in_use = 0 && t.shutdown then shutdown t
5869

59-
let use t fn =
70+
let use_block t fn block =
71+
let {id; gref; data} = block in
72+
t.in_use <- t.in_use + 1;
73+
Lwt.try_bind
74+
(fun () -> fn ~id gref data)
75+
(fun (_, release as result) ->
76+
Lwt.on_termination release (fun () -> put t block);
77+
return result
78+
)
79+
(fun ex -> put t block; Lwt.fail ex)
80+
81+
let rec use t fn =
6082
if t.shutdown then
6183
failwith "Shared_page_pool.use after shutdown";
62-
begin match t.blocks with
84+
match t.blocks with
85+
| [] when t.next_id >= max_pages ->
86+
MProf.Trace.label "Shared_page_pool waiting for free";
87+
Lwt_condition.wait t.avail >>= fun () -> use t fn
6388
| [] ->
6489
(* Frames normally fit within 2048 bytes, so we split each page in half. *)
65-
alloc t >>= fun (gntref, page) ->
90+
alloc t >>= fun (gref, page) ->
6691
let b1 = Cstruct.sub page 0 block_size in
6792
let b2 = Cstruct.shift page block_size in
68-
t.blocks <- (gntref, b2) :: t.blocks;
69-
return (gntref, b1)
93+
let id1 = t.next_id in
94+
let id2 = t.next_id + 1 in
95+
t.next_id <- t.next_id + 2;
96+
t.blocks <- {id = id2; gref; data = b2} :: t.blocks;
97+
Lwt_condition.broadcast t.avail ();
98+
use_block t fn {id = id1; gref; data = b1}
7099
| hd :: tl ->
71100
t.blocks <- tl;
72-
return hd
73-
end >>= fun (gntref, block as grant) ->
74-
t.in_use <- t.in_use + 1;
75-
Lwt.try_bind
76-
(fun () -> fn gntref block)
77-
(fun (_, release as result) ->
78-
Lwt.on_termination release (fun () -> put t grant);
79-
return result
80-
)
81-
(fun ex -> put t grant; Lwt.fail ex)
101+
use_block t fn hd
82102

83103
let blocks_needed bytes =
84104
(bytes + block_size - 1) / block_size

lib/shared_page_pool.mli

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ val make : (Gnt.gntref -> Io_page.t -> unit) -> t
2424
(** [make grant_access] is a shared pool of blocks. When a new page is first
2525
allocated, [grant_access] is called to share it. *)
2626

27-
val use : t -> (Gnt.gntref -> Cstruct.t -> ('a * unit Lwt.t) Lwt.t) -> ('a * unit Lwt.t) Lwt.t
28-
(** [use t fn] calls [fn gref block] with a free shared block of memory.
27+
val use : t -> (id:Cstruct.uint16 -> Gnt.gntref -> Cstruct.t -> ('a * unit Lwt.t) Lwt.t) -> ('a * unit Lwt.t) Lwt.t
28+
(** [use t fn] calls [fn ~id gref block] with a free shared block of memory
29+
and that block's unique ID (note: the [gref] is NOT unique).
2930
The function should return a thread that indicates when the request has
3031
been added to the queue, by returning a result value and a second thread
3132
indicating when the block can be returned to the pool. *)

0 commit comments

Comments
 (0)