packages feed

hans-3.0.2: src/Hans/Device/Xen.hs

{-# LANGUAGE RecordWildCards #-}

module Hans.Device.Xen where

import Hans.Device.Types
import Hans.Ethernet.Types (readMac)
import Hans.Threads (forkNamed)
import Hans.Types

import           Control.Concurrent (newMVar,modifyMVar_,killThread)
import           Control.Concurrent.BoundedChan
                     (BoundedChan,newBoundedChan,tryWriteChan,readChan)
import           Control.Exception(try)
import           Control.Monad (forever)
import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as S8
import qualified Data.ByteString.Lazy as L
import           Hypervisor.ErrorCodes(ErrorCode)
import           Hypervisor.XenStore (XenStore)
import           XenDevice.NIC (NIC,listNICs,openNIC,setReceiveHandler,sendPacket)

listDevices :: XenStore -> IO [DeviceName]
listDevices xs =
  do nics <- listNICs xs
     return (map S8.pack nics)

openDevice :: XenStore -> NetworkStack -> DeviceName -> DeviceConfig -> IO Device
openDevice xs ns devName devConfig =
  do let macStr = S8.unpack devName
     nic <- openNIC xs macStr

     devStats     <- newDeviceStats
     devSendQueue <- newBoundedChan (dcSendQueueLen devConfig)

     thread <- newMVar Nothing

     devMac <- case readMac macStr of
                 [(x,_)] -> return x
                 _       -> fail ("Unable to parse mac address: " ++ macStr)

     let dev = Device { .. }

         devStart = modifyMVar_ thread $ \ mbTid ->
           case mbTid of

             Nothing ->
               do sendThread <- forkNamed "xenSendLoop"
                      (xenSendLoop devStats nic devSendQueue)

                  setReceiveHandler nic (xenRecv ns dev)

                  return (Just sendThread)

             Just {} ->
                  return mbTid

         devStop = modifyMVar_ thread $ \ mbTid ->
           case mbTid of

             Just tid ->
               do killThread tid
                  return Nothing

             Nothing ->
                  return Nothing

         -- NOTE: there's no way to cleanup a NIC
         devCleanup =
             return ()

     return dev


-- NOTE: No way to update stats here, as we can't tell if sendPacket failed.
xenSendLoop :: DeviceStats -> NIC -> BoundedChan L.ByteString -> IO ()
xenSendLoop stats nic chan = forever $
  do bs  <- readChan chan
     res <- try $ sendPacket nic bs

     case res :: Either ErrorCode () of
       Right () ->
         do updateBytes   statTX stats (fromIntegral (L.length bs))
            updatePackets statTX stats
       Left _ ->
            updateError   statTX stats

xenRecv :: NetworkStack -> Device -> L.ByteString -> IO ()
xenRecv ns dev @ Device { .. } = \ bytes ->
  do let bytes' = L.toStrict bytes
     success <- tryWriteChan (nsInput ns) $! FromDevice dev bytes'
     if success
        then do updateBytes   statRX devStats (S.length bytes')
                updatePackets statRX devStats

        else updateError statRX devStats