From a22bfe1cdff1b0b640a9bee3e060edc1d08efd04 Mon Sep 17 00:00:00 2001 From: Tomasz Wroblewski Date: Thu, 28 Jan 2010 16:22:23 +0000 Subject: [PATCH] [xenvm] Add support for virtual wireless network device (vwif!) --- xenops/device.ml | 150 ++++++++++++++++++++++++++++++++++++++- xenops/device.mli | 17 +++++ xenops/device_common.ml | 6 +- xenops/device_common.mli | 2 +- xenops/hotplug.ml | 2 +- xenvm/vmact.ml | 5 +- xenvm/vmconfig.ml | 17 +++-- 7 files changed, 185 insertions(+), 14 deletions(-) diff --git a/xenops/device.ml b/xenops/device.ml index 5abd727..7831090 100644 --- a/xenops/device.ml +++ b/xenops/device.ml @@ -43,7 +43,6 @@ let add_device ~xs device backend_list frontend_list = let frontend_path = frontend_path_of_device ~xs device and backend_path = backend_path_of_device ~xs device and hotplug_path = Hotplug.get_hotplug_path device in - debug "adding device B%d[%s] F%d[%s] H[%s]" device.backend.domid backend_path device.frontend.domid frontend_path hotplug_path; Xs.transaction xs (fun t -> begin try @@ -761,6 +760,153 @@ let release ~xs (x: device) = Hotplug.release ~xs x end +(****************************************************************************************) +(** VWIFs: *) +module Vwif = struct + +exception Invalid_Mac of string + +let check_mac mac = + try + if String.length mac <> 17 then failwith "mac length"; + Scanf.sscanf mac "%2x:%2x:%2x:%2x:%2x:%2x" (fun a b c d e f -> ()); + mac + with _ -> + raise (Invalid_Mac mac) + +let get_backend_dev ~xs (x: device) = + try + let path = Hotplug.get_hotplug_path x in + xs.Xs.read (path ^ "/vif") + with Xb.Noent -> + raise (Hotplug_script_expecting_field (x, "vif")) + +(** Plug in the backend of a guest's VIF in dom0. Note that a guest may disconnect and + then reconnect their network interface: we have to re-run this code every time we + see a hotplug online event. *) +let plug ~xs ~netty ~mac ?(mtu=0) ?rate ?protocol (x: device) = + let backend_dev = get_backend_dev xs x in + + if mtu > 0 then + Netdev.set_mtu backend_dev mtu; + Netman.online backend_dev netty; + + (* set /hotplug-status = connected to interact nicely with the + xs-xen.pq.hq:91e986b8e49f netback-wait-for-hotplug patch *) + xs.Xs.write (Hotplug.connected_node ~xs x) "connected"; + + x + + +let add ~xs ~devid ~netty ~mac ?mtu ?(rate=None) ?(protocol=Protocol_Native) ?(backend_domid=0) domid = + debug "Device.Vwif.add domid=%d devid=%d mac=%s rate=%s" domid devid mac + (match rate with None -> "none" | Some (a, b) -> sprintf "(%Ld,%Ld)" a b); + let frontend = { domid = domid; kind = Vwif; devid = devid } in + let backend = { domid = backend_domid; kind = Vif; devid = devid } in + let device = { backend = backend; frontend = frontend } in + + let mac = check_mac mac in + + let back_options = + match rate with + | None -> [] + | Some (kbytes_per_s, timeslice_us) -> + let (^*) = Int64.mul and (^/) = Int64.div in + let timeslice_us = + if timeslice_us > 0L then + timeslice_us + else + 50000L (* 50ms by default *) in + let bytes_per_interval = ((kbytes_per_s ^* 1024L) ^* timeslice_us) + ^/ 1000000L in + if bytes_per_interval > 0L && bytes_per_interval < 0xffffffffL then + [ "rate", sprintf "%Lu,%Lu" bytes_per_interval timeslice_us ] + else ( + debug "VIF qos: invalid value for byte/interval: %Lu" bytes_per_interval; + [] + ) + in + + let back = [ + "frontend-id", sprintf "%u" domid; + "online", "1"; + "state", string_of_int (Xenbus.int_of Xenbus.Initialising); + "script", "/etc/xensource/scripts/vif"; + "mac", mac; + "handle", string_of_int devid + ] @ back_options in + + let front_options = + if protocol <> Protocol_Native then + [ "protocol", string_of_protocol protocol; ] + else + [] in + + let front = [ + "backend-id", string_of_int backend_domid; + "state", string_of_int (Xenbus.int_of Xenbus.Initialising); + "handle", string_of_int devid; + "mac", mac; + "rssi", "-65"; + "link-quality", "95"; + "ssid", "XenWireless"; + ] @ front_options in + + + Generic.add_device ~xs device back front; + Hotplug.wait_for_plug ~xs device; + plug ~xs ~netty ~mac ?rate ?mtu device + +(** When hot-unplugging a device we ask nicely *) +let request_closure ~xs (x: device) = + let backend_path = backend_path_of_device ~xs x in + let state_path = backend_path ^ "/state" in + Xs.transaction xs (fun t -> + let online_path = backend_path ^ "/online" in + debug "xenstore-write %s = 0" online_path; + t.Xst.write online_path "0"; + let state = try Xenbus.of_string (t.Xst.read state_path) with _ -> Xenbus.Closed in + if state == Xenbus.Connected then ( + debug "Device.del_device setting backend to Closing"; + t.Xst.write state_path (Xenbus.string_of Xenbus.Closing); + ) + ) + +let unplug_watch ~xs (x: device) = Watch.map (fun () -> "") (Watch.key_to_disappear (Hotplug.status_node x)) +let error_watch ~xs (x: device) = Watch.value_to_appear (error_path_of_device ~xs x) + +let clean_shutdown ~xs (x: device) = + debug "Device.Vwif.clean_shutdown %s" (string_of_device x); + + request_closure ~xs x; + match Watch.wait_for ~xs (Watch.any_of [ `OK, unplug_watch ~xs x; `Failed, error_watch ~xs x ]) with + | `OK, _ -> + (* Delete the trees (otherwise attempting to plug the device in again doesn't + work. This also clears any stale error nodes. *) + Generic.rm_device_state ~xs x + | `Failed, error -> + debug "Device.Vwif.shutdown_common: read an error: %s" error; + raise (Device_error (x, error)) + +let hard_shutdown ~xs (x: device) = + debug "Device.Vwif.hard_shutdown %s" (string_of_device x); + + let backend_path = backend_path_of_device ~xs x in + let online_path = backend_path ^ "/online" in + debug "xenstore-write %s = 0" online_path; + xs.Xs.write online_path "0"; + (* blow away the frontend *) + debug "Device.Vwif.hard_shutdown about to blow away frontend"; + let frontend_path = frontend_path_of_device ~xs x in + xs.Xs.rm frontend_path; + + ignore(Watch.wait_for ~xs (unplug_watch ~xs x)) + +let release ~xs (x: device) = + debug "Device.Vwif.release %s" (string_of_device x); + Hotplug.release ~xs x +end + (*****************************************************************************) (** Vcpus: *) module Vcpu = struct @@ -1140,6 +1286,7 @@ end let hard_shutdown ~xs (x: device) = match x.backend.kind with | Vif -> Vif.hard_shutdown ~xs x + | Vwif -> Vwif.hard_shutdown ~xs x | Vbd | Tap -> Vbd.hard_shutdown ~xs x | Pci -> PCI.hard_shutdown ~xs x | Vfb -> Vfb.hard_shutdown ~xs x @@ -1147,6 +1294,7 @@ let hard_shutdown ~xs (x: device) = match x.backend.kind with let clean_shutdown ~xs (x: device) = match x.backend.kind with | Vif -> Vif.clean_shutdown ~xs x + | Vwif -> Vwif.clean_shutdown ~xs x | Vbd | Tap -> Vbd.clean_shutdown ~xs x | Pci -> PCI.clean_shutdown ~xs x | Vfb -> Vfb.clean_shutdown ~xs x diff --git a/xenops/device.mli b/xenops/device.mli index eb96ddc..baf2af0 100644 --- a/xenops/device.mli +++ b/xenops/device.mli @@ -79,6 +79,7 @@ sig val hard_shutdown_complete : xs:Xs.xsh -> device -> string Watch.t end +(** Virtual network interface *) module Vif : sig exception Invalid_Mac of string @@ -94,6 +95,22 @@ sig val release : xs:Xs.xsh -> device -> unit end +(** Virtual wireless network interface *) +module Vwif : +sig + exception Invalid_Mac of string + val get_backend_dev : xs:Xs.xsh -> device -> string + val add : xs:Xs.xsh -> devid:int -> netty:Netman.netty + -> mac:string -> ?mtu:int -> ?rate:(int64 * int64) option + -> ?protocol:protocol -> ?backend_domid:Xc.domid -> Xc.domid + -> device + val plug : xs:Xs.xsh -> netty:Netman.netty + -> mac:string -> ?mtu:int -> ?rate:(int64 * int64) option + -> ?protocol:protocol -> device + -> device + val release : xs:Xs.xsh -> device -> unit +end + val clean_shutdown : xs:Xs.xsh -> device -> unit val hard_shutdown : xs:Xs.xsh -> device -> unit diff --git a/xenops/device_common.ml b/xenops/device_common.ml index 9025908..b8a4017 100644 --- a/xenops/device_common.ml +++ b/xenops/device_common.ml @@ -19,7 +19,7 @@ open Stringext open Hashtblext open Pervasiveext -type kind = Vif | Vbd | Tap | Pci | Vfb | Vkb +type kind = Vif | Vwif | Vbd | Tap | Pci | Vfb | Vkb type devid = int (** Represents one end of a device *) @@ -46,9 +46,9 @@ open D open Printf let string_of_kind = function - | Vif -> "vif" | Vbd -> "vbd" | Tap -> "tap" | Pci -> "pci" | Vfb -> "vfb" | Vkb -> "vkbd" + | Vif -> "vif" | Vwif -> "vwif" | Vbd -> "vbd" | Tap -> "tap" | Pci -> "pci" | Vfb -> "vfb" | Vkb -> "vkbd" let kind_of_string = function - | "vif" -> Vif | "vbd" -> Vbd | "tap" -> Tap | "pci" -> Pci | "vfb" -> Vfb | "vkbd" -> Vkb + | "vif" -> Vif | "vwif" -> Vwif | "vbd" -> Vbd | "tap" -> Tap | "pci" -> Pci | "vfb" -> Vfb | "vkbd" -> Vkb | x -> raise (Unknown_device_type x) let string_of_endpoint (x: endpoint) = diff --git a/xenops/device_common.mli b/xenops/device_common.mli index 69d17a0..b26901b 100644 --- a/xenops/device_common.mli +++ b/xenops/device_common.mli @@ -15,7 +15,7 @@ * GNU Lesser General Public License for more details. *) -type kind = Vif | Vbd | Tap | Pci | Vfb | Vkb +type kind = Vif | Vwif | Vbd | Tap | Pci | Vfb | Vkb type devid = int diff --git a/xenops/hotplug.ml b/xenops/hotplug.ml index 4b2f972..fe01582 100644 --- a/xenops/hotplug.ml +++ b/xenops/hotplug.ml @@ -93,7 +93,7 @@ let device_is_online ~xs (x: device) = match x.backend.kind with | Pci | Vfb | Vkb -> assert false (* PCI/Vfb backend doesn't create online node *) - | Vif -> backend_hotplug () + | Vif | Vwif -> backend_hotplug () | ( Vbd | Tap ) -> if backend_request () then not(backend_shutdown ()) diff --git a/xenvm/vmact.ml b/xenvm/vmact.ml index 084e894..18ce103 100644 --- a/xenvm/vmact.ml +++ b/xenvm/vmact.ml @@ -167,8 +167,9 @@ let add_pci_to_vm ~xs state pci = let add_nic_to_vm ~xs state nic = let netty = Netman.Bridge nic.nic_bridge in let (_: Device_common.device) = - Device.Vif.add ~xs ~devid:nic.nic_id ~netty ~mac:nic.nic_mac - ~protocol:(devproto_of_state state) state.vm_domid in + let add = if nic.nic_wireless then Device.Vwif.add else Device.Vif.add in + add ~xs ~devid:nic.nic_id ~netty ~mac:nic.nic_mac + ~protocol:(devproto_of_state state) state.vm_domid in state.vm_nics <- {ns_id=nic.nic_id; ns_bridge=nic.nic_bridge}::state.vm_nics; () diff --git a/xenvm/vmconfig.ml b/xenvm/vmconfig.ml index 0cf50b4..da5a9dd 100644 --- a/xenvm/vmconfig.ml +++ b/xenvm/vmconfig.ml @@ -101,6 +101,7 @@ type config_nic = { nic_mac: string; nic_model: string; nic_dynadded: bool; + nic_wireless: bool; } type config = { @@ -171,6 +172,7 @@ let default_nic = nic_mac = ""; nic_model = ""; nic_dynadded = false; + nic_wireless = false; } module Config = struct @@ -223,7 +225,8 @@ let config_nic_of_string s = let id = ref (-1) and bridge = ref "" and model = ref "" - and mac = ref "" in + and mac = ref "" + and wireless = ref false in List.iter (fun v -> let lv = String.split '=' v in @@ -231,11 +234,12 @@ let config_nic_of_string s = and value = List.nth lv 1 in match lvalue with - | "id" -> id := int_of_string value - | "bridge" -> bridge := value - | "model" -> model := value - | "mac" -> mac := value - | _ -> () + | "id" -> id := int_of_string value + | "bridge" -> bridge := value + | "model" -> model := value + | "mac" -> mac := value + | "wireless" -> wireless := bool_of_string value + | _ -> () ) ls; { default_nic with @@ -243,6 +247,7 @@ let config_nic_of_string s = nic_bridge = !bridge; nic_mac = !mac; nic_model = !model; + nic_wireless = !wireless; } (* Where NIC IDs have been left blank (or explicitly set to -1), here we allocate them a reasonable number. We also allocate a bridge and a mac, if necessary *) -- 2.39.5